OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
VariableElement.cc
Go to the documentation of this file.
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 #include "VariableElement.h"
30 
31 #include <Array.h>
32 #include <BaseType.h>
33 #include <Structure.h>
34 #include <dods-limits.h>
35 #include <ctype.h>
36 #include "DimensionElement.h"
37 #include <memory>
38 #include "MyBaseTypeFactory.h"
39 #include "NCMLBaseArray.h"
40 #include "NCMLDebug.h"
41 #include "NCMLParser.h"
42 #include "NCMLUtil.h"
43 #include "NetcdfElement.h"
44 #include "RenamedArrayWrapper.h"
45 #include <sstream>
46 
47 using namespace libdap;
48 using std::vector;
49 using std::auto_ptr;
50 
51 namespace ncml_module
52 {
53 
54  const string VariableElement::_sTypeName = "variable";
55  const vector<string> VariableElement::_sValidAttributes = getValidAttributes();
56 
57  VariableElement::VariableElement()
58  : RCObjectInterface()
59  , NCMLElement(0)
60  , _name("")
61  , _type("")
62  , _shape("")
63  , _orgName("")
64  , _shapeTokens()
65  , _pNewlyCreatedVar(0)
66  , _gotValues(false)
67  {
68  }
69 
71  : RCObjectInterface()
72  , NCMLElement(proto)
73  {
74  _name = proto._name;
75  _type = proto._type;
76  _shape = proto._shape;
77  _orgName = proto._orgName;
78  _shapeTokens = proto._shapeTokens;
79  _pNewlyCreatedVar = 0; // not safe to copy the proto one, so pretend we didn't.
80  _gotValues = false; // to be consistent with previosu line
81  }
82 
84  {
85  // help clear up memory
86  _shapeTokens.resize(0);
87  _shapeTokens.clear();
88  }
89 
90  const string&
92  {
93  return _sTypeName;
94  }
95 
98  {
99  return new VariableElement(*this);
100  }
101 
102  void
104  {
106 
107  _name = attrs.getValueForLocalNameOrDefault("name", "");
108  _type = attrs.getValueForLocalNameOrDefault("type", "");
109  _shape = attrs.getValueForLocalNameOrDefault("shape", "");
110  _orgName = attrs.getValueForLocalNameOrDefault("orgName", "");
111  }
112 
113  void
115  {
117  processBegin(*_parser);
118  }
119 
120  void
121  VariableElement::handleContent(const string& content)
122  {
123  // Variables cannot have content like attribute. It must be within a <values> element.
124  if (!NCMLUtil::isAllWhitespace(content))
125  {
127  "Got non-whitespace for element content and didn't expect it. "
128  "Element=" + toString() + " content=\"" +
129  content + "\"");
130  }
131  }
132 
133  void
135  {
136  processEnd(*_parser);
137  }
138 
139  string
141  {
142  return "<" + _sTypeName +
143  " name=\"" + _name + "\"" +
144  " type=\"" + _type + "\"" +
145  ((!_shape.empty())?(" shape=\"" + _shape + "\""):("")) +
146  ((!_orgName.empty())?(" orgName=\"" + _orgName + "\""):("")) +
147  ">";
148  }
149 
150  bool
152  {
153  return _pNewlyCreatedVar;
154  }
155 
156  bool
158  {
159  return _gotValues;
160  }
161 
162  void
164  {
165  _gotValues = true;
166  }
167 
168 
170 
171  void
172  VariableElement::processBegin(NCMLParser& p)
173  {
174  BESDEBUG("ncml", "VariableElement::handleBegin called for " << toString() << endl);
175 
176  if (!p.withinNetcdf())
177  {
179  "Got element " + toString() + " while not in <netcdf> node!");
180  }
181 
182  // Can only specify variable globally or within a composite variable now.
183  if (!(p.isScopeGlobal() || p.isScopeCompositeVariable()))
184  {
187  "Got <variable> element while not within a <netcdf> or within variable container. scope=" +
188  p.getScopeString());
189  }
190 
191  // If a request to rename the variable
192  if (!_orgName.empty())
193  {
194  processRenameVariable(p);
195  }
196  else // Otherwise see if it's an existing or new variable _name at scope of p
197  {
198  // Lookup the variable in whatever the parser's current variable container is
199  // this could be the DDS top level or a container (constructor) variable.
200  BaseType* pVar = p.getVariableInCurrentVariableContainer(_name);
201  if (!pVar)
202  {
203  processNewVariable(p);
204  }
205  else
206  {
207  processExistingVariable(p, pVar);
208  }
209  }
210  }
211 
212  void
213  VariableElement::processEnd(NCMLParser& p)
214  {
215  BESDEBUG("ncml", "VariableElement::handleEnd called at scope:" << p.getScopeString() << endl);
216  if (!p.isScopeVariable())
217  {
220  "VariableElement::handleEnd called when not parsing a variable element! "
221  "Scope=" + p.getTypedScopeString());
222  }
223 
224  // We need to defer the setting of values until the dataset
225  // is closed since that's when a joinNew aggregation new map will have all its
226  // values to set, not before.
227  // But we need to allow the user to specify
228  // the metadata for the variable PRIOR to this happening, without specifying
229  // values. We need to catch the case of values not being set later...
230  // To handle this, we push new variables WITHOUT values onto a request queue
231  // in the containing dataset which will validate that they have gotten their values
232  // set at the point where the containing dataset is closed, but after the aggregations
233  // have run.
234  if (isNewVariable() && !checkGotValues())
235  {
236  BESDEBUG("ncml", "WARNING: at parse line: " << _parser->getParseLineNumber() <<
237  " the newly created variable=" << toString() <<
238  " did not have its values set! We will assume this is a placeholder variable"
239  " for an aggregation (such as the new outer dimension of a joinNew) and will"
240  " defer checking that required values are set until the point when this "
241  " netcdf element is closed..."
242  " Scope=" << p.getScopeString() <<
243  endl);
244  BESDEBUG("ncml", "Adding new variable name=" << _pNewlyCreatedVar->name() <<
245  " to the validation watch list for the closing of this netcdf." << endl);
246  _parser->getCurrentDataset()->addVariableToValidateOnClose(_pNewlyCreatedVar, this);
247  }
248 
249  NCML_ASSERT_MSG(p.getCurrentVariable(), "Error: VariableElement::handleEnd(): Expected non-null parser.getCurrentVariable()!");
250 
251  // Pop up the stack from this variable
252  exitScope(p);
253 
254  BaseType* pVar = p.getCurrentVariable();
255  BESDEBUG("ncml", "Variable scope now with name: " << ((pVar)?(pVar->name()):("<NULL>")) << endl);
256  }
257 
258  void
259  VariableElement::processExistingVariable(NCMLParser& p, BaseType* pVar)
260  {
261  BESDEBUG("ncml", "VariableElement::processExistingVariable() called with name=" << _name << " at scope=" << p.getTypedScopeString() << endl);
262 
263  // If undefined, look it up
264  if (!pVar)
265  {
266  pVar = p.getVariableInCurrentVariableContainer(_name);
267  }
268 
269  // It better exist by now
270  VALID_PTR(pVar);
271 
272  // Make sure the type matches. NOTE:
273  // We use "Structure" to mean Grid, Structure, Sequence!
274  // Also type="" will match ANY type.
275  // TODO This fails on Array as well due to NcML making arrays be a basic type with a non-zero dimension.
276  // We're gonna ignore that until we allow addition of variables, but let's leave this check here for now
277  if (!_type.empty() && !p.typeCheckDAPVariable(*pVar, p.convertNcmlTypeToCanonicalType(_type)))
278  {
281  "Type Mismatch in variable element with name=" + _name +
282  " at scope=" + p.getScopeString() +
283  " Expected type=" + _type +
284  " but found variable with type=" + pVar->type_name() +
285  " To match a variable of any type, please do not specify variable@type.");
286  }
287 
288  // Use this variable as the new scope until we get a handleEnd()
289  enterScope(p, pVar);
290  }
291 
292  void
293  VariableElement::processRenameVariable(NCMLParser& p)
294  {
295  BESDEBUG("ncml", "VariableElement::processRenameVariable() called on " + toString() << " at scope=" << p.getTypedScopeString() << endl);
296 
297  // First, make sure the data is valid
298  NCML_ASSERT_MSG(!_name.empty(), "Can't have an empty variable@name if variable@orgName is specified!");
299  NCML_ASSERT(!_orgName.empty()); // we shouldn't even call this if this is the case, but...
300 
301  // Lookup _orgName, which must exist or throw
302  BESDEBUG("ncml", "Looking up the existence of a variable with name=" << _orgName << "..." <<endl);
303  BaseType* pOrgVar = p.getVariableInCurrentVariableContainer(_orgName);
304  if (!pOrgVar)
305  {
307  "Renaming variable failed for element=" + toString() +
308  " since no variable with orgName=" + _orgName +
309  " exists at current parser scope=" + p.getScopeString());
310  }
311  BESDEBUG("ncml", "Found variable with name=" << _orgName << endl);
312 
313  // Lookup _name, which must NOT exist or throw
314  BESDEBUG("ncml", "Making sure new name=" << _name << " does not exist at this scope already..." << endl);
315  BaseType* pExisting = p.getVariableInCurrentVariableContainer(_name);
316  if (pExisting)
317  {
319  "Renaming variable failed for element=" + toString() +
320  " since a variable with name=" + _name +
321  " already exists at current parser scope=" + p.getScopeString());
322  }
323  BESDEBUG("ncml", "Success, new variable name is open at this scope." << endl);
324 
325  // Call set_name on the orgName variable.
326  BESDEBUG("ncml", "Renaming variable " << _orgName << " to " << _name << endl);
327 
328  // If we are doing data, we need to handle some variables (Array)
329  // specially since they might refer to underlying data by the new name
330  if (p.parsingDataRequest())
331  {
332  // If not an Array, force it to read or we won't find the new name in the file for HDF at least...
333  if (!dynamic_cast<Array*>(pOrgVar))
334  {
335  pOrgVar->read();
336  }
337  // If the variable is an Array, we need to wrap it in a RenamedArrayWrapper
338  // so that it finds it data correctly.
339  // This will remove the old one and replace our wrapper under the new _name if it's an Array subclass!
340  pOrgVar = replaceArrayIfNeeded(p, pOrgVar, _name);
341 
342  // This is safe whether we converted it or not. Rename!
343  NCMLUtil::setVariableNameProperly(pOrgVar, _name);
344  }
345  else
346  {
347  // The above branch will reorder the output for the DataDDS case,
348  // so we need to remove and readd even if we dont convert to preserve order!
349 
350  // BaseType::set_name fails for Vector (Array etc) subtypes since it doesn't
351  // set the template's BaseType var's name as well. This function does that until
352  // a fix in libdap lets us call pOrgName->set_name(_name) directly.
353  // pOrgVar->set_name(_name); // TODO switch to this call when bug is fixed.
354  //NCMLUtil::setVariableNameProperly(pOrgVar, _name);
355 
356  // Need to copy unfortunately, since delete will kill storage...
357  auto_ptr<BaseType> pCopy = auto_ptr<BaseType>(pOrgVar->ptr_duplicate());
358  NCMLUtil::setVariableNameProperly(pCopy.get(), _name);
359 
360  // Nuke the old
361  p.deleteVariableAtCurrentScope(pOrgVar->name());
362 
363  // Add the new, which copies under the hood. auto_ptr will clean pCopy.
364  p.addCopyOfVariableAtCurrentScope(*pCopy);
365  }
366 
367  // Make sure we find it under the new name
368  BaseType* pRenamedVar = p.getVariableInCurrentVariableContainer(_name);
369  NCML_ASSERT_MSG(pRenamedVar, "Renamed variable not found! Logic error!");
370 
371  // Finally change the scope to the variable.
372  enterScope(p, pRenamedVar);
373  BESDEBUG("ncml", "Entering scope of the renamed variable. Scope is now: " << p.getTypedScopeString() << endl);
374  }
375 
376  void
377  VariableElement::processNewVariable(NCMLParser& p)
378  {
379  BESDEBUG("ncml", "Entered VariableElement::processNewVariable..." << endl);
380 
381  // ASSERT: We know the variable with name doesn't exist, or we wouldn't call this function.
382 
383  // Type cannot be empty for a new variable!!
384  if (_type.empty())
385  {
387  "Must have non-empty variable@type when creating new variable=" + toString());
388  }
389 
390  // Convert the type to the canonical type...
391  string type = p.convertNcmlTypeToCanonicalType(_type);
392  if (_type.empty())
393  {
395  "Unknown type for new variable=" + toString());
396  }
397 
398  // Tokenize the _shape string
399  NCMLUtil::tokenize(_shape, _shapeTokens, NCMLUtil::WHITESPACE);
400 
401  // Add new variables of the given type...
402  // On exit, each of these leave _parser->getCurrentVariable() with
403  // the new variable that exists within the current dataset's DDS
404  // at the current containing scope.
405  if (_type == p.STRUCTURE_TYPE)
406  {
407  processNewStructure(p);
408  }
409  else if (_shape.empty()) // a scalar
410  {
411  processNewScalar(p, type);
412  }
413  else if (!_shape.empty())
414  {
415  processNewArray(p, type);
416  }
417  else
418  {
419  THROW_NCML_INTERNAL_ERROR("UNIMPLEMENTED METHOD: Cannot create non-scalar Array types yet.");
420  }
421 
422  // Keep track that it's new so we can error if we get values for non-new.
423  // All the process new will have entered it into the dataset as scope, so
424  // getCurrentVariable() on the parser is the actual one in the dataset always.
425  _pNewlyCreatedVar = _parser->getCurrentVariable();
426  }
427 
428  void
429  VariableElement::processNewStructure(NCMLParser& p)
430  {
431  // First, make sure we are at a parse scope that ALLOWS variables to be added!
432  if (!(p.isScopeCompositeVariable() || p.isScopeGlobal()))
433  {
435  "Cannot add a new Structure variable at current scope! "
436  "TypedScope=" + p.getTypedScopeString());
437  }
438 
439  auto_ptr<BaseType> pNewVar = MyBaseTypeFactory::makeVariable("Structure", _name);
440  NCML_ASSERT_MSG(pNewVar.get(), "VariableElement::processNewStructure: factory failed to make a new Structure variable for name=" + _name);
441 
442  // Add the copy, let auto_ptr clean up
443  p.addCopyOfVariableAtCurrentScope(*pNewVar);
444 
445  // Lookup the variable we just added since it is added as a copy!
446  BaseType* pActualVar = p.getVariableInCurrentVariableContainer(_name);
447  VALID_PTR(pActualVar);
448  // Make sure the copy mechanism did the right thing so we don't delete the var we just added.
449  NCML_ASSERT(pActualVar != pNewVar.get());
450 
451  // Make it be the scope for any incoming new attributes.
452  enterScope(p, pActualVar);
453 
454  // Structures technically don't have values, but we check later that we set them, so say we're good.
455  setGotValues();
456  }
457 
458  void
459  VariableElement::processNewScalar(NCMLParser&p, const string& dapType)
460  {
461  addNewVariableAndEnterScope(p, dapType);
462  }
463 
464  void
465  VariableElement::processNewArray(NCMLParser& p, const std::string& dapType)
466  {
467  // For now, we can reuse the processNewScalar to make the variable and enter scope and all that.
468  // Use the new template form for the Array so we get the NCMLArray<T> subclass that handles constraints.
469  addNewVariableAndEnterScope(p, "Array<" + dapType + ">");
470 
471  // Now look up the added variable so we can set it's template and dimensionality.
472  // it should be the current variable since we entered its scope above!
473  Array* pNewVar = dynamic_cast<Array*>(p.getCurrentVariable());
474  NCML_ASSERT_MSG(pNewVar, "VariableElement::processNewArray: Expected non-null getCurrentVariable() in parser but got NULL!");
475 
476  // Now make the template variable of the array entry type with the same name and add it
477  auto_ptr<BaseType> pTemplateVar = MyBaseTypeFactory::makeVariable(dapType, _name);
478  pNewVar->add_var(pTemplateVar.get());
479 
480  // For each dimension in the shape, append it to make an N-D array...
481  for (unsigned int i=0; i<_shapeTokens.size(); ++i)
482  {
483  unsigned int dim = getSizeForDimension(p, _shapeTokens.at(i));
484  string dimName = ((isDimensionNumericConstant(_shapeTokens.at(i)))?(""):(_shapeTokens.at(i)));
485  BESDEBUG("ncml", "Appending dimension name=\"" << dimName << "\" of size=" << dim << " to the Array name=" << pNewVar->name() << endl);
486  pNewVar->append_dim(dim, dimName);
487  }
488 
489  // Make sure the size of the flattened Array in memory (product of dimensions) is within the DAP2 limit...
490  if (getProductOfDimensionSizes(p) > static_cast<unsigned int>(DODS_MAX_ARRAY)) // actually the call itself will throw...
491  {
493  "Product of dimension sizes for Array must be < (2^31-1).");
494  }
495  }
496 
497 #if 0 // old school copy method
498  libdap::BaseType*
499  VariableElement::replaceArrayIfNeeded(NCMLParser& p, libdap::BaseType* pOrgVar, const string& name)
500  {
501  VALID_PTR(pOrgVar);
502  Array* pOrgArray = dynamic_cast<libdap::Array*>(pOrgVar);
503  if (!pOrgArray)
504  {
505  return pOrgVar;
506  }
507 
508  BESDEBUG("ncml", "VariableElement::replaceArray if needed. Renaming an Array means we need to convert it to NCMLArray." << endl);
509 
510  // Make a copy of it
511  auto_ptr<NCMLBaseArray> pNewArray = NCMLBaseArray::createFromArray(*pOrgArray);
512  VALID_PTR(pNewArray.get());
513 
514  // Remove the old one.
515  p.deleteVariableAtCurrentScope(pOrgArray->name());
516 
517  // Make sure the new name is set.
518  NCMLUtil::setVariableNameProperly(pNewArray.get(), name);
519 
520  // Add the new one. Unfortunately this copies it under the libdap hood. ARGH!
521  // So just use the get() and let the auto_ptr kill our copy.
522  p.addCopyOfVariableAtCurrentScope(*(pNewArray.get()));
523 
524  return p.getVariableInCurrentVariableContainer(name);
525  }
526 #endif
527 
528  libdap::BaseType*
529  VariableElement::replaceArrayIfNeeded(NCMLParser& p, libdap::BaseType* pOrgVar, const string& name)
530  {
531  VALID_PTR(pOrgVar);
532  Array* pOrgArray = dynamic_cast<libdap::Array*>(pOrgVar);
533  if (!pOrgArray)
534  {
535  return pOrgVar;
536  }
537 
538  BESDEBUG("ncml", "VariableElement::replaceArray if needed. Renaming an Array means we need to wrap it with RenamedArrayWrapper!" << endl);
539 
540  // Must make a clone() since deleteVariableAtCurrentScope from container below will destroy pOrgArray!
541  auto_ptr<RenamedArrayWrapper> pNewArray =
542  auto_ptr<RenamedArrayWrapper>(new RenamedArrayWrapper(dynamic_cast<Array*>(pOrgArray->ptr_duplicate())));
543  p.deleteVariableAtCurrentScope(pOrgArray->name());
544 
545  // Make sure the new name is set.
546  NCMLUtil::setVariableNameProperly(pNewArray.get(), name);
547 
548  // Add the new one. Unfortunately this copies it under the libdap hood. ARGH!
549  // So just use the get() and let the auto_ptr kill our copy.
550  p.addCopyOfVariableAtCurrentScope(*(pNewArray.get()));
551 
552  return p.getVariableInCurrentVariableContainer(name);
553  }
554 
555  void
556  VariableElement::addNewVariableAndEnterScope(NCMLParser& p, const std::string& dapType)
557  {
558  // First, make sure we are at a parse scope that ALLOWS variables to be added!
559  if (!(p.isScopeCompositeVariable() || p.isScopeGlobal()))
560  {
562  "Cannot add a new scalar variable at current scope! TypedScope=" + p.getTypedScopeString());
563  }
564 
565  // Destroy it no matter what sicne add_var copies it
566  auto_ptr<BaseType> pNewVar = MyBaseTypeFactory::makeVariable(dapType, _name);
567  NCML_ASSERT_MSG(pNewVar.get(), "VariableElement::addNewVariable: factory failed to make a new variable of type: " + dapType +
568  " for element: " + toString());
569 
570  // Now that we have it, we need to add it to the parser at current scope
571  // Internally, the add will copy the arg, not store it.
572  p.addCopyOfVariableAtCurrentScope(*pNewVar);
573 
574  // Lookup the variable we just added since it is added as a copy!
575  BaseType* pActualVar = p.getVariableInCurrentVariableContainer(_name);
576  VALID_PTR(pActualVar);
577  // Make sure the copy mechanism did the right thing so we don't delete the var we just added.
578  NCML_ASSERT(pActualVar != pNewVar.get());
579 
580  // Make it be the scope for any incoming new attributes.
581  enterScope(p, pActualVar);
582 
583  }
584 
585  void
586  VariableElement::enterScope(NCMLParser& p, BaseType* pVar)
587  {
588  VALID_PTR(pVar);
589 
590  // Add the proper variable scope to the stack
591  if (pVar->is_constructor_type())
592  {
593  p.enterScope(_name, ScopeStack::VARIABLE_CONSTRUCTOR);
594  }
595  else
596  {
597  p.enterScope(_name, ScopeStack::VARIABLE_ATOMIC);
598  }
599 
600  // this sets the _pCurrentTable to the variable's table.
601  p.setCurrentVariable(pVar);
602  }
603 
604  void
605  VariableElement::exitScope(NCMLParser& p)
606  {
607  // Set the new variable container to the parent of the current.
608  // This could be NULL if we're a top level variable, making the DDS the variable container.
609  p.setCurrentVariable(p.getCurrentVariable()->get_parent());
610  p.exitScope();
611  p.printScope();
612  }
613 
614  bool
615  VariableElement::isDimensionNumericConstant(const std::string& dimToken) const
616  {
617  // for now just test the first character is a number and assume it's a number
618  return isdigit(dimToken.at(0));
619  }
620 
621  unsigned int
622  VariableElement::getSizeForDimension(NCMLParser& p, const std::string& dimToken) const
623  {
624  unsigned int dim = 0;
625  // First, if the first char is a number, then assume it's an explicit non-negative integer
626  if (isDimensionNumericConstant(dimToken))
627  {
628  stringstream token;
629  token.str(dimToken);
630  token >> dim;
631  if (token.fail())
632  {
634  "Trying to get the dimension size in shape=" + _shape + " for token " + dimToken +
635  " failed to parse the unsigned int!");
636  }
637  }
638  else
639  {
640  const DimensionElement* pDim = p.getDimensionAtLexicalScope(dimToken);
641  if (pDim)
642  {
643  return pDim->getLengthNumeric();
644  }
645  else
646  {
648  "Failed to find a dimension with name=" + dimToken +
649  " for variable=" + toString() +
650  " with dimension table= " + p.printAllDimensionsAtLexicalScope() +
651  " at scope=" + p.getScopeString());
652  }
653  }
654  return dim;
655  }
656 
657  unsigned int
658  VariableElement::getProductOfDimensionSizes(NCMLParser& p) const
659  {
660  // If no shape, then it's size 0 (scalar)
661  if (_shape.empty())
662  {
663  return 0;
664  }
665 
666  // Otherwise compute it
667  unsigned int product = 1;
668  vector<string>::const_iterator endIt = _shapeTokens.end();
669  vector<string>::const_iterator it;
670  for (it = _shapeTokens.begin(); it != endIt; ++it)
671  {
672  const string& dimName = *it;
673  unsigned int dimSize = getSizeForDimension(p, dimName); // might throw if not found...
674  // if multiplying this in will cause over DODS_MAX_ARRAY, then error
675  if (dimSize > (DODS_MAX_ARRAY/product))
676  {
678  "Product of dimension sizes exceeds the maximum DAP2 size of 2147483647 (2^31-1)!");
679  }
680  // otherwise, multiply it in
681  product *= dimSize;
682  }
683  return product;
684  }
685 
686  vector<string>
687  VariableElement::getValidAttributes()
688  {
689  vector<string> validAttrs;
690  validAttrs.reserve(4);
691  validAttrs.push_back("name");
692  validAttrs.push_back("type");
693  validAttrs.push_back("shape");
694  validAttrs.push_back("orgName");
695  return validAttrs;
696  }
697 }
virtual bool validateAttributes(const XMLAttributeMap &attrs, const vector< string > &validAttrs, vector< string > *pInvalidAttrs=0, bool printInvalid=true, bool throwOnError=true)
Check that the given attributes are all in the valid set, otherwise fill in *pInvalidAttrs with the p...
Definition: NCMLElement.cc:191
void addVariableToValidateOnClose(libdap::BaseType *pNewVar, VariableElement *pVE)
Add the pNewvar created by pVE to this dataset's list of variables to validate for having values set ...
virtual void setAttributes(const XMLAttributeMap &attrs)
Set the attributes of this from the map.
const std::string type() const
#define NCML_ASSERT(cond)
Definition: NCMLDebug.h:80
An abstract superclass for NCMLArray that handles the non-parameterized functionality and allows u...
const string getValueForLocalNameOrDefault(const string &localname, const string &defVal="") const
If there is an attribute with localname, return its value, else return default.
Definition: XMLHelpers.cc:209
static bool isAllWhitespace(const std::string &str)
Is all the string whitespace as defined by chars in WHITESPACE ?
Definition: NCMLUtil.cc:110
#define NCML_ASSERT_MSG(cond, msg)
Definition: NCMLDebug.h:83
virtual void handleBegin()
Handle a begin on this element.
static class NCMLUtil overview
virtual string toString() const
Return a string describing the element.
int getParseLineNumber() const
Get the line of the NCML file the parser is currently parsing.
Definition: NCMLParser.cc:222
virtual void handleContent(const string &content)
Handle the characters content for the element.
#define THROW_NCML_PARSE_ERROR(parseLine, msg)
Definition: NCMLDebug.h:69
static std::auto_ptr< libdap::BaseType > makeVariable(const libdap::Type &type, const string &name)
Return a new variable of the given type.
virtual VariableElement * clone() const
Make and return a copy of this.
Base class for NcML element concrete classes.
Definition: NCMLElement.h:64
static void setVariableNameProperly(libdap::BaseType *pVar, const std::string &name)
Currently BaseType::set_name only sets in BaseType.
Definition: NCMLUtil.cc:398
static const string _sTypeName
#define THROW_NCML_INTERNAL_ERROR(msg)
Definition: NCMLDebug.h:61
static const vector< string > _sValidAttributes
Concrete class for NcML element.
virtual const string & getTypeName() const
Return the type of the element, which should be: the same as ConcreteClassName::getTypeName() ...
#define VALID_PTR(ptr)
Definition: NCMLDebug.h:88
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
static const std::string WHITESPACE
Delimiter set for tokenizing whitespace separated data.
Definition: NCMLUtil.h:79
void setGotValues()
Called once we set the values from ValuesElement so we are aware.
const std::string name() const
virtual void handleEnd()
Handle the closing of this element.
static int tokenize(const std::string &str, std::vector< std::string > &tokens, const std::string &delimiters=" \t")
Split str into tokens using the characters in delimiters as split boundaries.
Definition: NCMLUtil.cc:54
static auto_ptr< NCMLBaseArray > createFromArray(const libdap::Array &proto)
Make a new NCMLArray from the given proto, using the Array interface.