bes  Updated for version 3.20.6
ValuesElement.cc
1 // 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 "BaseType.h"
33 #include "Array.h"
34 #include "Byte.h"
35 #include "Float32.h"
36 #include "Float64.h"
37 #include "Grid.h"
38 #include "Int16.h"
39 #include "Int32.h"
40 #include "Sequence.h"
41 #include "Str.h"
42 #include "Structure.h"
43 #include "UInt16.h"
44 #include "UInt32.h"
45 #include "Url.h"
46 
47 #include "NCMLDebug.h"
48 #include "NCMLParser.h"
49 #include "NCMLUtil.h"
50 #include <sstream>
51 #include "VariableElement.h"
52 
53 using namespace libdap;
54 
55 namespace ncml_module {
56 const string ValuesElement::_sTypeName = "values";
57 const vector<string> ValuesElement::_sValidAttributes = getValidAttributes();
58 
59 ValuesElement::ValuesElement() :
60  RCObjectInterface(), NCMLElement(0), _start(""), _increment(""), _separator(""), _gotContent(false), _tokens()
61 {
62  _tokens.reserve(256);
63 }
64 
65 ValuesElement::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 
75 ValuesElement::~ValuesElement()
76 {
77  _tokens.resize(0);
78 }
79 
80 const string&
81 ValuesElement::getTypeName() const
82 {
83  return _sTypeName;
84 }
85 
87 ValuesElement::clone() const
88 {
89  return new ValuesElement(*this);
90 }
91 
92 void 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 
111 void 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 
148 void 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 
216 void 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 
277 string ValuesElement::toString() const
278 {
279  return "<" + _sTypeName + " " + ((_start.empty()) ? ("") : ("start=\"" + _start + "\" "))
280  + ((_increment.empty()) ? ("") : ("increment=\"" + _increment + "\" "))
281  + ((_separator == NCMLUtil::WHITESPACE) ? ("") : ("separator=\"" + _separator + "\" ")) + ">";
282 }
283 
284 void ValuesElement::validateStartAndIncrementForVariableTypeOrThrow(libdap::BaseType& /* pVar */) const
285 {
286  // TODO IMPL ME
287  // Look up the types...
288 }
289 
290 template<class DAPType, typename ValueType>
291 void 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 
316 template<>
317 void 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 
339 template<>
340 void 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 
350 template<>
351 void 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 
358 template<typename DAPType>
359 void 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 
391 template<>
392 void 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 
403 void 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 
419 void 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 
428 void 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 
450 void 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 
522 void 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 
599 template<typename DAPType>
600 void 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 
644 void 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 
709 std::string ValuesElement::getNCMLTypeForVariable(NCMLParser& p) const
710 {
711  const VariableElement* pMyParent = getContainingVariableElement(p);
712  VALID_PTR(pMyParent);
713  return pMyParent->type();
714 }
715 
716 const VariableElement*
717 ValuesElement::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 
735 void 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
745 void 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 
754 vector<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 }
ncml_module::VariableElement::toString
virtual string toString() const
Definition: VariableElement.cc:129
ncml_module::VariableElement::isNewVariable
bool isNewVariable() const
Definition: VariableElement.cc:136
ncml_module::NCMLParser
Definition: NCMLParser.h:158
ncml_module::XMLAttributeMap
Definition: XMLHelpers.h:93
ncml_module::VariableElement::checkGotValues
bool checkGotValues() const
Definition: VariableElement.cc:141
ncml_module::ValuesElement
Definition: ValuesElement.h:47
Type
Type
Type of JSON value.
Definition: cmr_module/rapidjson/rapidjson.h:603
libdap
Definition: BESDapFunctionResponseCache.h:35
ncml_module::VariableElement
Concrete class for NcML <variable> element.
Definition: VariableElement.h:58
ncml_module
NcML Parser for adding/modifying/removing metadata (attributes) to existing local datasets using NcML...
Definition: AggregationElement.cc:72
ncml_module::XMLAttributeMap::getValueForLocalNameOrDefault
const std::string getValueForLocalNameOrDefault(const std::string &localname, const std::string &defVal="") const
Definition: XMLHelpers.cc:181