bes Updated for version 3.20.13
ValuesElement.cc
1
2// This file is part of the "NcML Module" project, a BES module designed
3// to allow NcML files to be used to be used as a wrapper to add
4// AIS to existing datasets of any format.
5//
6// Copyright (c) 2009 OPeNDAP, Inc.
7// Author: Michael Johnson <m.johnson@opendap.org>
8//
9// For more information, please also see the main website: http://opendap.org/
10//
11// This library is free software; you can redistribute it and/or
12// modify it under the terms of the GNU Lesser General Public
13// License as published by the Free Software Foundation; either
14// version 2.1 of the License, or (at your option) any later version.
15//
16// This library is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19// Lesser General Public License for more details.
20//
21// You should have received a copy of the GNU Lesser General Public
22// License along with this library; if not, write to the Free Software
23// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24//
25// Please see the files COPYING and COPYRIGHT for more information on the GLPL.
26//
27// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
29
30#include "ValuesElement.h"
31
32#include <libdap/BaseType.h>
33#include <libdap/Array.h>
34#include <libdap/Byte.h>
35#include <libdap/Float32.h>
36#include <libdap/Float64.h>
37#include <libdap/Grid.h>
38#include <libdap/Int16.h>
39#include <libdap/Int32.h>
40#include <libdap/Sequence.h>
41#include <libdap/Str.h>
42#include <libdap/Structure.h>
43#include <libdap/UInt16.h>
44#include <libdap/UInt32.h>
45#include <libdap/Url.h>
46
47#include "NCMLDebug.h"
48#include "NCMLParser.h"
49#include "NCMLUtil.h"
50#include <sstream>
51#include "VariableElement.h"
52
53using namespace libdap;
54
55namespace ncml_module {
56const string ValuesElement::_sTypeName = "values";
57const vector<string> ValuesElement::_sValidAttributes = getValidAttributes();
58
59ValuesElement::ValuesElement() :
60 RCObjectInterface(), NCMLElement(0), _start(""), _increment(""), _separator(""), _gotContent(false), _tokens()
61{
62 _tokens.reserve(256);
63}
64
65ValuesElement::ValuesElement(const ValuesElement& proto) :
66 RCObjectInterface(), NCMLElement(proto)
67{
68 _start = proto._start;
69 _increment = proto._increment;
70 _separator = proto._separator;
71 _gotContent = proto._gotContent;
72 _tokens = proto._tokens;
73}
74
75ValuesElement::~ValuesElement()
76{
77 _tokens.resize(0);
78}
79
80const string&
81ValuesElement::getTypeName() const
82{
83 return _sTypeName;
84}
85
87ValuesElement::clone() const
88{
89 return new ValuesElement(*this);
90}
91
92void ValuesElement::setAttributes(const XMLAttributeMap& attrs)
93{
94 validateAttributes(attrs, _sValidAttributes);
95
96 _start = attrs.getValueForLocalNameOrDefault("start");
97 _increment = attrs.getValueForLocalNameOrDefault("increment");
98 _separator = attrs.getValueForLocalNameOrDefault("separator", ""); // empty means "not specified" and becoems whitesoace for all but string
99
100 // Validate them... if _start is specified, then _increment must be as well!
101 if (!_start.empty() && _increment.empty()) {
102 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
103 "values element=" + toString() + " had a start attribute without a corresponding increment attribute!");
104 }
105 if (_start.empty() && !_increment.empty()) {
106 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
107 "values element=" + toString() + " had an increment attribute without a corresponding start attribute!");
108 }
109}
110
111void ValuesElement::handleBegin()
112{
113 VALID_PTR(_parser);
114 NCMLParser& p = *_parser;
115
116 BESDEBUG("ncml",
117 "ValuesElement::handleBegin called with element=" << toString() << " at scope=" << p.getScopeString() << endl);
118
119 // First, make sure we're in a current parse start for this elemnent
120 if (!p.isScopeVariable()) {
121 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
122 "Got values element while not parsing a variable! values=" + toString() + " at scope="
123 + p.getTypedScopeString());
124 }
125
126 // Make sure we're the first values element on the variable or there is a problem!
127 const VariableElement* pVarElt = getContainingVariableElement(p);
128 VALID_PTR(pVarElt);
129 if (pVarElt->checkGotValues()) {
130 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
131 "Got a values element when one was already specified for this variable=" + pVarElt->toString()
132 + " at scope=" + p.getScopeString());
133 }
134
135 // If we got a start and increment, we are supposed to auto-generate values for the variable
136 if (shouldAutoGenerateValues()) {
137 BaseType* pVar = p.getCurrentVariable();
138 NCML_ASSERT_MSG(pVar, "ValuesElement::handleBegin(): Expected non-null p.getCurrentVariable()!");
139 autogenerateAndSetVariableValues(p, *pVar);
140 }
141 // else we'll expect content
142
143 // We zero this out here in 'begin'; load it up with raw text in 'handlerContent'
144 // and parse it in 'end'. jhrg 10/12/11
145 _accumulated_content.resize(0);
146}
147
148void ValuesElement::handleContent(const string& content)
149{
150 NCMLParser& p = *_parser;
151
152 BESDEBUG("ncml", "ValuesElement::handleContent called for " << toString() << " with content=" << content << endl);
153
154 // N.B. Technically, we're still in isScopeVariable() since we don't push values elements on the scopestack,
155 // only the XML parser stack...
156
157 // Make sure we don't get non-whitespace content for autogenerated values!
158 if (shouldAutoGenerateValues() && !NCMLUtil::isAllWhitespace(content)) {
159 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
160 "Element: " + toString()
161 + " specified a start and increment to autogenerate values but also illegally specified content!");
162 }
163
164 // There had better be one or we goofed!
165 BaseType* pVar = p.getCurrentVariable();
166 NCML_ASSERT_MSG(pVar, "ValuesElement::handleContent: got unexpected null getCurrentVariable() from parser!!");
167
168 // Also, make sure the variable we plan to add values to is a new variable and not an existing one.
169 // We do not support changing existing dataset values currently.
170 const VariableElement* pVarElt = getContainingVariableElement(p);
171 VALID_PTR(pVarElt);
172 if (!pVarElt->isNewVariable()) {
173 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
174 "This version of the NCML Module cannot change the values of an existing variable! "
175 "However, we got " + toString() + " element for variable=" + pVarElt->toString() + " at scope="
176 + p.getScopeString());
177 }
178
179 // Ripped out this block; moved to 'handleEnd'. Just accumulate raw text here. jhrg 10/12/11
180 _accumulated_content.append(content);
181
182#if 0
183 // Tokenize the values for all cases EXCEPT if it's a scalar string.
184 // We'll make a special exception an assume the entire content is the token
185 // to avoid accidental tokenization with whitespace, which is clearly not intended
186 // by the NcML file author!
187 if (pVar->is_simple_type() &&
188 (pVar->type() == dods_str_c || pVar->type() == dods_url_c))
189 {
190 _tokens.resize(0);
191 _tokens.push_back(string(content));
192 }
193 // Don't tokenize a char array either, since we want to read all the char's in.
194 else if (pVar->is_vector_type() && getNCMLTypeForVariable(p) == "char")
195 {
196 NCMLUtil::tokenizeChars(content, _tokens); // tokenize with no separator so each char is token.
197 }
198 else if (pVar->is_vector_type() && getNCMLTypeForVariable(p) == "string")
199 {
200 string sep = ((_separator.empty())?(NCMLUtil::WHITESPACE):(_separator));
201 NCMLUtil::tokenize(content, _tokens, sep);
202 }
203 else // for arrays of other values, use whitespace separation for default if not specified.
204 {
205 string sep = ((_separator.empty())?(NCMLUtil::WHITESPACE):(_separator));
206 NCMLUtil::tokenize(content, _tokens, sep);
207 }
208#endif
209#if 0
210 setVariableValuesFromTokens(p, *pVar);
211 _gotContent = true;
212 setGotValuesOnOurVariableElement(p);
213#endif
214}
215
216void ValuesElement::handleEnd()
217{
218 BESDEBUG("ncml", "ValuesElement::handleEnd called for " << toString() << endl);
219
220 NCMLParser& p = *_parser;
221 // There had better be one or we goofed!
222 BaseType* pVar = p.getCurrentVariable();
223 NCML_ASSERT_MSG(pVar, "ValuesElement::handleContent: got unexpected null getCurrentVariable() from parser!!");
224
225 // I set _gotContent here because other methods depend on it.
226 _gotContent = !_accumulated_content.empty();
227#if 0
228 if (!shouldAutoGenerateValues() && !_gotContent)
229 {
230 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
231 "Values element=" + toString() + " expected content for values but didn't get any!");
232 }
233#endif
234 // Tokenize the values for all cases EXCEPT if it's a scalar string.
235 // We'll make a special exception an assume the entire content is the token
236 // to avoid accidental tokenization with whitespace, which is clearly not intended
237 // by the NcML file author!
238 if (pVar->is_simple_type() && (pVar->type() == dods_str_c || pVar->type() == dods_url_c)) {
239 _tokens.resize(0);
240 _tokens.push_back(string(_accumulated_content));
241 }
242 // Don't tokenize a char array either, since we want to read all the char's in.
243 else if (pVar->is_vector_type() && getNCMLTypeForVariable(p) == "char") {
244 NCMLUtil::tokenizeChars(_accumulated_content, _tokens); // tokenize with no separator so each char is token.
245 }
246 else if (pVar->is_vector_type() && getNCMLTypeForVariable(p) == "string") {
247 string sep = ((_separator.empty()) ? (NCMLUtil::WHITESPACE) : (_separator));
248 NCMLUtil::tokenize(_accumulated_content, _tokens, sep);
249 }
250 else // for arrays of other values, use whitespace separation for default if not specified.
251 {
252 string sep = ((_separator.empty()) ? (NCMLUtil::WHITESPACE) : (_separator));
253 NCMLUtil::tokenize(_accumulated_content, _tokens, sep);
254 }
255
256 if (!shouldAutoGenerateValues()) {
257 setVariableValuesFromTokens(p, *pVar);
258 setGotValuesOnOurVariableElement(p);
259 }
260
261 // In the original version of this method, the 'if' before this was
262 // if (!shouldAutoGenerateValues() && !_gotContent) and it throws/threw an
263 // exception (I moved that code up in my modification of this method). But
264 // dealWithEmptyStringValues() only does something when _gotContent is false,
265 // so there's no way to get to call dealWithEmptyStringValues here. I'm
266 // removing it and looking at the tests. jhrg 10/12/11
267#if 0
268 // if unspecified, string and url vars get set to empty string ""
269 if (!shouldAutoGenerateValues())
270 {
271 dealWithEmptyStringValues();
272 }
273 // Otherwise, we're all good.
274#endif
275}
276
277string ValuesElement::toString() const
278{
279 return "<" + _sTypeName + " " + ((_start.empty()) ? ("") : ("start=\"" + _start + "\" "))
280 + ((_increment.empty()) ? ("") : ("increment=\"" + _increment + "\" "))
281 + ((_separator == NCMLUtil::WHITESPACE) ? ("") : ("separator=\"" + _separator + "\" ")) + ">";
282}
283
284void ValuesElement::validateStartAndIncrementForVariableTypeOrThrow(libdap::BaseType& /* pVar */) const
285{
286 // TODO IMPL ME
287 // Look up the types...
288}
289
290template<class DAPType, typename ValueType>
291void ValuesElement::setScalarValue(libdap::BaseType& var, const string& valueAsToken)
292{
293 // Make sure we got the right subclass type
294 DAPType* pVar = dynamic_cast<DAPType*>(&var);
295 NCML_ASSERT_MSG(pVar, "setScalarValue() got called with BaseType not matching the expected type.");
296
297 // Parse the token using stringstream and the template ValueType.
298 std::stringstream sis;
299 sis.str(valueAsToken);
300 ValueType value;
301 sis >> value;
302 if (sis.fail()) {
303 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
304 "Setting array values failed to read the value token properly! value was for var name=" + var.name()
305 + " and the value token was " + valueAsToken);
306 }
307
308 // assuming it works, there should be a setValue(ValueType) on pVar now
309 pVar->set_value(value);
310}
311
316template<>
317void ValuesElement::setScalarValue<Byte, dods_byte>(libdap::BaseType& var, const string& valueAsToken)
318{
319 Byte* pVar = dynamic_cast<Byte*>(&var);
320 NCML_ASSERT_MSG(pVar, "setScalarValue() got called with BaseType not matching the expected type.");
321
322 std::stringstream sis;
323 sis.str(valueAsToken);
324 dods_uint16 value; // read it as an unsigned short.
325 sis >> value;
326 if (sis.fail()) {
327 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
328 "Setting array values failed to read the value token properly! value was for var name=" + var.name()
329 + " and the value token was " + valueAsToken);
330 }
331
332 // then cast it for the set...
333 pVar->set_value(static_cast<dods_byte>(value));
334}
335
339template<>
340void ValuesElement::setScalarValue<Str, string>(libdap::BaseType& var, const string& valueAsToken)
341{
342 Str* pVar = dynamic_cast<Str*>(&var);
343 NCML_ASSERT_MSG(pVar, "setScalarValue() got called with BaseType not matching the expected type.");
344 pVar->set_value(valueAsToken);
345}
346
350template<>
351void ValuesElement::setScalarValue<Url, string>(libdap::BaseType& var, const string& valueAsToken)
352{
353 Url* pVar = dynamic_cast<Url*>(&var);
354 NCML_ASSERT_MSG(pVar, "setScalarValue() got called with BaseType not matching the expected type.");
355 pVar->set_value(valueAsToken);
356}
357
358template<typename DAPType>
359void ValuesElement::setVectorValues(libdap::Array* pArray, const vector<string>& valueTokens)
360{
361 VALID_PTR(pArray);
362 // Make an array of values ready to read them all the tokens.
363 vector<DAPType> values;
364 values.reserve(valueTokens.size());
365
366 int count = 0; // only to help error output msg
367 vector<string>::const_iterator endIt = valueTokens.end();
368 for (vector<string>::const_iterator it = valueTokens.begin(); it != endIt; ++it) {
369 DAPType value;
370 stringstream valueTokenAsStream;
371 const std::string& token = *it;
372 valueTokenAsStream.str(token);
373 valueTokenAsStream >> value;
374 if (valueTokenAsStream.fail()) {
375 stringstream msg;
376 msg << "Got fail() on parsing a value token for an Array name=" << pArray->name()
377 << " for value token index " << count << " with token=" << (*it) << " for element " << toString();
378 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(), msg.str());
379 }
380 values.push_back(value);
381 count++;
382 }
383
384 // Call the overloaded set with the parsed vector.
385 pArray->set_value(values, values.size());
386}
387
391template<>
392void ValuesElement::setVectorValues<string>(libdap::Array* pArray, const vector<string>& valueTokens)
393{
394 VALID_PTR(pArray);
395 // Call it DIRECTLY with the given values, modulo const_cast
396 vector<string>& values = const_cast<vector<string>&>(valueTokens);
397 pArray->set_value(values, values.size());
398}
399
403void ValuesElement::parseAndSetCharValue(libdap::BaseType& var, const string& valueAsToken)
404{
405 Byte* pVar = dynamic_cast<Byte*>(&var);
406 NCML_ASSERT_MSG(pVar, "setScalarValue() got called with BaseType not matching the expected type.");
407
408 if (valueAsToken.size() != 1) {
409 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
410 "Parsing scalar char, expected single character but didnt get it. value was for var name=" + var.name()
411 + " and the value token was " + valueAsToken);
412 }
413
414 // Read the char and set it
415 dods_byte val = valueAsToken.at(0);
416 pVar->set_value(val);
417}
418
419void ValuesElement::parseAndSetCharValueArray(NCMLParser& /* p */, libdap::Array* pVecVar, const vector<string>& tokens)
420{
421 vector<dods_byte> values;
422 for (unsigned int i = 0; i < tokens.size(); ++i) {
423 values.push_back(static_cast<dods_byte>((tokens.at(i))[0]));
424 }
425 pVecVar->set_value(values, values.size());
426}
427
428void ValuesElement::setVariableValuesFromTokens(NCMLParser& p, libdap::BaseType& var)
429{
430 // It's an error to have <values> for a Structure variable!
431 if (var.type() == dods_structure_c) {
432 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
433 "Illegal to specify <values> element for a Structure type variable name=" + var.name() + " at scope="
434 + p.getScopeString());
435 }
436 // First, make sure the dimensionality matches or we're doomed from the get-go
437 if (var.is_simple_type()) {
438 setScalarVariableValuesFromTokens(p, var);
439 }
440 else if (var.is_vector_type()) {
441 setVectorVariableValuesFromTokens(p, var);
442 }
443 else {
444 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
445 "Can't call ValuesElement::setVariableValuesFromTokens for constructor type now!! "
446 "Variable named " + var.name() + " at scope=" + p.getScopeString());
447 }
448}
449
450void ValuesElement::setScalarVariableValuesFromTokens(NCMLParser& p, libdap::BaseType& var)
451{
452 // OK, we have a scalar, so make sure there's exactly one token!
453 if (_tokens.size() != 1) {
454 stringstream msg;
455 msg << "While setting scalar variable name=" << var.name()
456 << " we expected exactly 1 value in content but found " << _tokens.size() << " tokens.";
457 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(), msg.str());
458 }
459
460 // OK, we have one token for a scalar. Now make sure it's a valid value for the given type.
461 // legacy "char" type (internally a byte) is specified differently, so don't check for that case.
462 if (getNCMLTypeForVariable(p) != "char") {
463 p.checkDataIsValidForCanonicalTypeOrThrow(var.type_name(), _tokens);
464 }
465
466 // Just one of em
467 const string& valueToken = _tokens.at(0);
468
469 // OK, now it gets pretty ugly, but we hid most of it behind a template...
470 Type varType = var.type();
471 switch (varType) {
472 case dods_byte_c:
473 // Special case depending on whether the underlying NcML was type-converted
474 // from "char" or not. If char, we parse as char, not numeric
475 if (getNCMLTypeForVariable(p) == "char") {
476 parseAndSetCharValue(var, valueToken);
477 }
478 else {
479 setScalarValue<Byte, dods_byte>(var, valueToken);
480 }
481 break;
482
483 case dods_int16_c:
484 setScalarValue<Int16, dods_int16>(var, valueToken);
485 break;
486
487 case dods_uint16_c:
488 setScalarValue<UInt16, dods_uint16>(var, valueToken);
489 break;
490
491 case dods_int32_c:
492 setScalarValue<Int32, dods_int32>(var, valueToken);
493 break;
494
495 case dods_uint32_c:
496 setScalarValue<UInt32, dods_uint32>(var, valueToken);
497 break;
498
499 case dods_float32_c:
500 setScalarValue<Float32, dods_float32>(var, valueToken);
501 break;
502
503 case dods_float64_c:
504 setScalarValue<Float64, dods_float64>(var, valueToken);
505 break;
506
507 case dods_str_c:
508 setScalarValue<Str, string>(var, valueToken);
509 break;
510
511 case dods_url_c:
512 setScalarValue<Url, string>(var, valueToken);
513 break;
514
515 default:
516 THROW_NCML_INTERNAL_ERROR("Expected simple type but didn't find it!")
517 ;
518 break;
519 }
520}
521
522void ValuesElement::setVectorVariableValuesFromTokens(NCMLParser& p, libdap::BaseType& var)
523{
524 Array* pVecVar = dynamic_cast<Array*>(&var);
525 NCML_ASSERT_MSG(pVecVar, "ValuesElement::setVectorVariableValuesFromTokens expect var"
526 " to be castable to class Array but it wasn't!!");
527
528 // Make sure the Array length matches the number of tokens.
529 // Note that length() should be the product of dimension sizes since N-D arrays are flattened in row major order
530 if (pVecVar->length() > 0 && static_cast<unsigned int>(pVecVar->length()) != _tokens.size()) {
531 stringstream msg;
532 msg << "Dimension mismatch! Variable name=" << pVecVar->name() << " has dimension product="
533 << pVecVar->length() << " but we got " << _tokens.size() << " values in the values element " << toString();
534 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(), msg.str());
535 }
536
537 // Make sure all the tokens are valid for the given datatype
538 if (getNCMLTypeForVariable(p) != "char") {
539 BaseType* pTemplate = var.var();
540 VALID_PTR(pTemplate);
541 p.checkDataIsValidForCanonicalTypeOrThrow(pTemplate->type_name(), _tokens);
542 }
543
544 // Finally, go and parse the values in for the given type and set them in the vector
545 VALID_PTR(pVecVar->var());
546 libdap::Type valType = pVecVar->var()->type();
547 switch (valType) {
548 case dods_byte_c:
549 // Special case depending on whether the underlying NcML was type-converted
550 // from "char" or not. If so, read the ascii character not the numeric version of it.
551 if (getNCMLTypeForVariable(p) == "char") {
552 parseAndSetCharValueArray(p, pVecVar, _tokens);
553 }
554 else {
555 setVectorValues<dods_byte>(pVecVar, _tokens);
556 }
557 break;
558
559 case dods_int16_c:
560 setVectorValues<dods_int16>(pVecVar, _tokens);
561 break;
562
563 case dods_uint16_c:
564 setVectorValues<dods_uint16>(pVecVar, _tokens);
565 break;
566
567 case dods_int32_c:
568 setVectorValues<dods_int32>(pVecVar, _tokens);
569 break;
570
571 case dods_uint32_c:
572 setVectorValues<dods_uint32>(pVecVar, _tokens);
573 break;
574
575 case dods_float32_c:
576 setVectorValues<dods_float32>(pVecVar, _tokens);
577 break;
578
579 case dods_float64_c:
580 setVectorValues<dods_float64>(pVecVar, _tokens);
581 break;
582
583 case dods_str_c:
584 setVectorValues<string>(pVecVar, _tokens);
585 break;
586
587 case dods_url_c:
588 setVectorValues<string>(pVecVar, _tokens);
589 break;
590
591 default:
592 THROW_NCML_INTERNAL_ERROR("Expected Vector template type was a simple type but didn't find it!")
593 ;
594 break;
595 } // switch
596
597}
598
599template<typename DAPType>
600void ValuesElement::generateAndSetVectorValues(NCMLParser& p, libdap::Array* pArray)
601{
602 // 1) Check that the start and increment values can be parsed into the appropriate type
603 // 2) Find out the dimensionality
604 // 3) Generate a vector of values with the dimensionality
605 // 4) Set it on pArray
606
607 DAPType start;
608 {
609 stringstream sis;
610 sis.str(_start);
611 sis >> start;
612 if (sis.fail()) {
613 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
614 "Failed to parse the values@start=" + _start + " for " + toString() + " at scope=" + p.getScopeString());
615 }
616 }
617
618 DAPType increment;
619 {
620 stringstream sis;
621 sis.str(_increment);
622 sis >> increment;
623 if (sis.fail()) {
624 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
625 "Failed to parse the values@increment=" + _start + " for " + toString() + " at scope="
626 + p.getScopeString());
627 }
628 }
629
630 int numPoints = pArray->length();
631 NCML_ASSERT(numPoints >= 1);
632 vector<DAPType> values;
633 values.reserve(numPoints);
634 DAPType x = start;
635 values.push_back(x);
636 for (int i = 1; i < numPoints; ++i) {
637 x += increment;
638 values.push_back(x);
639 }
640 NCML_ASSERT(values.size() == static_cast<unsigned int>(numPoints));
641 pArray->set_value(values, values.size());
642}
643
644void ValuesElement::autogenerateAndSetVariableValues(NCMLParser& p, BaseType& var)
645{
646 // First, make sure var is an Array or we don't know what to do
647 libdap::Array* pArray = dynamic_cast<libdap::Array*>(&var);
648 if (!pArray) {
649 THROW_NCML_INTERNAL_ERROR(
650 "ValuesElement::autogenerateAndSetVariableValues: expected variable of type libdap::Array but failed to cast it!");
651 }
652
653 setGotValuesOnOurVariableElement(p);
654
655 // Next, find out the underlying type and use the class's template calls.
656 libdap::BaseType* pTemplate = pArray->var();
657 VALID_PTR(pTemplate);
658 switch (pTemplate->type()) {
659 case dods_byte_c:
660 // This doesn't work for char!
661 if (getNCMLTypeForVariable(p) == "char") {
662 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(), "Can't use values@start for non-numeric values!");
663 }
664 else // it works for bytes
665 {
666 generateAndSetVectorValues<dods_byte>(p, pArray);
667 }
668 break;
669
670 case dods_int16_c:
671 generateAndSetVectorValues<dods_int16>(p, pArray);
672 break;
673
674 case dods_uint16_c:
675 generateAndSetVectorValues<dods_uint16>(p, pArray);
676 break;
677
678 case dods_int32_c:
679 generateAndSetVectorValues<dods_int32>(p, pArray);
680 break;
681
682 case dods_uint32_c:
683 generateAndSetVectorValues<dods_uint32>(p, pArray);
684 break;
685
686 case dods_float32_c:
687 generateAndSetVectorValues<dods_float32>(p, pArray);
688 break;
689
690 case dods_float64_c:
691 generateAndSetVectorValues<dods_float64>(p, pArray);
692 break;
693
694 // User error
695 case dods_str_c:
696 case dods_url_c:
697 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(), "Can't use values@start for non-numeric values!")
698 ;
699 break;
700
701 default:
702 THROW_NCML_INTERNAL_ERROR("Expected Vector template type was a simple type but didn't find it!")
703 ;
704 break;
705 } // switch
706
707}
708
709std::string ValuesElement::getNCMLTypeForVariable(NCMLParser& p) const
710{
711 const VariableElement* pMyParent = getContainingVariableElement(p);
712 VALID_PTR(pMyParent);
713 return pMyParent->type();
714}
715
716const VariableElement*
717ValuesElement::getContainingVariableElement(NCMLParser& p) const
718{
719 const VariableElement* ret = 0;
720
721 // Get the parse stack for p and walk up it!
722 NCMLParser::ElementStackConstIterator it;
723 NCMLParser::ElementStackConstIterator endIt = p.getElementStackEnd();
724 for (it = p.getElementStackBegin(); it != endIt; ++it) {
725 const NCMLElement* pElt = *it;
726 const VariableElement* pVarElt = dynamic_cast<const VariableElement*>(pElt);
727 if (pVarElt) {
728 ret = pVarElt;
729 break;
730 }
731 }
732 return ret;
733}
734
735void ValuesElement::setGotValuesOnOurVariableElement(NCMLParser& p)
736{
737 // Ugh, I know I shouldn't do this, but...
738 VariableElement* pContainingVar = const_cast<VariableElement*>(getContainingVariableElement(p));
739 VALID_PTR(pContainingVar);
740 pContainingVar->setGotValues();
741}
742
743// I'm not sure I understand this method - I think it's use in handleEnd() is
744// no longer needed given the changes I made there. jhrg 10/12/11
745void ValuesElement::dealWithEmptyStringValues()
746{
747 // To reuse all the logic, we'll just explicitly call handleContent("")
748 // which will push an empty string token for scalar string and url.
749 if (!_gotContent) {
750 handleContent("");
751 }
752}
753
754vector<string> ValuesElement::getValidAttributes()
755{
756 vector<string> validAttrs;
757 validAttrs.reserve(3);
758 validAttrs.push_back("start");
759 validAttrs.push_back("increment");
760 validAttrs.push_back("separator");
761 // we disallow npts since it was deprecated and we'll be used for new files.
762 return validAttrs;
763}
764
765}
Concrete class for NcML <variable> element.
virtual string toString() const
const std::string getValueForLocalNameOrDefault(const std::string &localname, const std::string &defVal="") const
Definition: XMLHelpers.cc:181
NcML Parser for adding/modifying/removing metadata (attributes) to existing local datasets using NcML...
Type
Type of JSON value.
Definition: rapidjson.h:664