OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
NCMLParser.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 "config.h"
30 #include "NCMLParser.h" // ncml_module
31 
32 #include "AggregationElement.h" // ncml_module
33 #include "AggregationUtil.h" // agg_util
34 #include <BESConstraintFuncs.h>
35 #include <BESDataDDSResponse.h>
36 #include <BESDDSResponse.h>
37 #include <BESDebug.h>
38 #include "DDSLoader.h" // ncml_module
39 #include "DimensionElement.h" // ncml_module
40 #include <AttrTable.h> // libdap
41 #include <BaseType.h> // libdap
42 #include <DAS.h> // libdap
43 #include <DDS.h> // libdap
44 //#include <mime_util.h>
45 #include <Structure.h> // libdap
46 #include <map>
47 #include <memory>
48 #include "NCMLDebug.h" // ncml_module
49 #include "NCMLElement.h" // ncml_module
50 #include "NCMLUtil.h" // ncml_module
51 #include "NetcdfElement.h" // ncml_module
52 #include "OtherXMLParser.h" // ncml_module
53 #include <parser.h> // libdap for the type checking...
54 #include "SaxParserWrapper.h" // ncml_module
55 #include <sstream>
56 
57 // For extra debug spew for now.
58 #define DEBUG_NCML_PARSER_INTERNALS 1
59 
60 using namespace agg_util;
61 
62 namespace ncml_module {
63 
64  // From the DAP 2 guide....
65  static const unsigned int MAX_DAP_STRING_SIZE = 32767;
66 
67 // Consider filling this with a compilation flag.
68 /* static */ bool NCMLParser::sThrowExceptionOnUnknownElements = true;
69 
70 // An attribute or variable with type "Structure" will match this string.
71 const string NCMLParser::STRUCTURE_TYPE("Structure");
72 
73 // Just cuz I hate magic -1. Used in _currentParseLine
74 static const int NO_CURRENT_PARSE_LINE_NUMBER = -1;
75 
76 
78 // Helper class.
79 AttrTableLazyPtr::AttrTableLazyPtr(const NCMLParser& parser, AttrTable* pAT/*=0*/)
80 : _parser(parser)
81 , _pAttrTable(pAT)
82 , _loaded(pAT)
83 {
84 }
85 
87 {
88  _pAttrTable = 0;
89  _loaded = false;
90 }
91 
92 AttrTable*
94 {
95  if (!_loaded)
96  {
97  const_cast<AttrTableLazyPtr*>(this)->loadAndSetAttrTable();
98  }
99  return _pAttrTable;
100 }
101 
102 void
103 AttrTableLazyPtr::set(AttrTable* pAT)
104 {
105  _pAttrTable = pAT;
106  if (pAT)
107  {
108  _loaded = true;
109  }
110  else
111  {
112  _loaded = false;
113  }
114 }
115 
116 void
118 {
119  // force it to load next get().
120  _pAttrTable = 0;
121  _loaded = false;
122 }
123 
124 void
125 AttrTableLazyPtr::loadAndSetAttrTable()
126 {
127  set(0);
128  NetcdfElement* pDataset = _parser.getCurrentDataset();
129  if (pDataset)
130  {
131  // The lazy load actually occurs in here
132  DDS* pDDS = pDataset->getDDS();
133  if (pDDS)
134  {
135  set( &(pDDS->get_attr_table()) );
136  _loaded = true;
137  }
138  }
139 }
140 
141 
144 
146 : _filename("")
147 , _loader(loader)
148 , _responseType(DDSLoader::eRT_RequestDDX)
149 , _response(0)
150 , _rootDataset(0)
151 , _currentDataset(0)
152 , _pVar(0)
153 , _pCurrentTable(*this,0)
154 , _elementStack()
155 , _scope()
156 , _namespaceStack()
157 , _pOtherXMLParser(0)
158 , _currentParseLine(NO_CURRENT_PARSE_LINE_NUMBER)
159 {
160  BESDEBUG("ncml", "Created NCMLParser." << endl);
161 }
162 
164 {
165  // clean other stuff up
166  cleanup();
167 }
168 
169 auto_ptr<BESDapResponse>
170 NCMLParser::parse(const string& ncmlFilename, DDSLoader::ResponseType responseType)
171 {
172  // Parse into a newly created object.
173  auto_ptr<BESDapResponse> response = DDSLoader::makeResponseForType(responseType);
174 
175  // Parse into the response. We still got it in the auto_ptr in this scope, so we're safe
176  // on exception since the auto_ptr in this func will cleanup the memory.
177  parseInto(ncmlFilename, responseType, response.get());
178 
179  // Relinquish it to the caller
180  return response;
181 }
182 
183 void
184 NCMLParser::parseInto(const string& ncmlFilename, DDSLoader::ResponseType responseType, BESDapResponse* response)
185 {
186  VALID_PTR(response);
187  NCML_ASSERT_MSG(DDSLoader::checkResponseIsValidType(responseType, response),
188  "NCMLParser::parseInto: got wrong response object for given type.");
189 
190  _responseType = responseType;
191  _response = response;
192 
193  if (parsing())
194  {
195  THROW_NCML_INTERNAL_ERROR("Illegal Operation: NCMLParser::parse called while already parsing!");
196  }
197 
198  BESDEBUG("ncml", "Beginning NcML parse of file=" << ncmlFilename << endl);
199 
200  // In case we care.
201  _filename = ncmlFilename;
202 
203  // Invoke the libxml sax parser
204  SaxParserWrapper parser(*this);
205  parser.parse(ncmlFilename);
206 
207  // Prepare for a new parse, making sure it's all cleaned up (with the exception of the _ddsResponse
208  // which where's about to send off)
209  resetParseState();
210 
211  // we're done with it.
212  _response = 0;
213 }
214 
215 bool
217 {
218  return !_filename.empty();
219 }
220 
221 int
223 {
224  return _currentParseLine;
225 }
226 
227 const XMLNamespaceStack&
229 {
230  return _namespaceStack;
231 }
232 
233 void
235 {
236  BESDEBUG("ncml", "onStartDocument." << endl);
237 }
238 
239 void
241 {
242  BESDEBUG("ncml", "onEndDocument." << endl);
243 }
244 
245 void
246 NCMLParser::onStartElement(const std::string& name, const XMLAttributeMap& attrs)
247 {
248  // If we have a proxy set for OtherXML, pass calls there.
249  if (isParsingOtherXML())
250  {
251  VALID_PTR(_pOtherXMLParser);
252  _pOtherXMLParser->onStartElement(name, attrs);
253  }
254  else // Otherwise do the standard NCML parse
255  {
256  processStartNCMLElement(name, attrs);
257  }
258 }
259 
260 // Local helper for below...
261 // Sees whether we are closing the element on top
262 // of the NCMLElement stack and that we're not parsing
263 // OtherXML, or if we are that its depth is now zero.
264 static bool shouldStopOtherXMLParse(NCMLElement* top, const string& closingElement, OtherXMLParser& rProxyParser)
265 {
266  // If the stack top element name is the same as the element we are closing...
267  // and the parse depth is 0, then we're done.
268  // We MUST check the parse depth in case the other XML has an Attribute in it!
269  // We want to be sure we're closing the right one.
270  if (top->getTypeName() == closingElement &&
271  rProxyParser.getParseDepth() == 0)
272  {
273  return true;
274  }
275  else // we're not done.
276  {
277  return false;
278  }
279 }
280 
281 void
282 NCMLParser::onEndElement(const std::string& name)
283 {
284  NCMLElement* elt = getCurrentElement();
285  VALID_PTR(elt);
286 
287  // First, handle the OtherXML proxy parsing case
288  if (isParsingOtherXML())
289  {
290  VALID_PTR(_pOtherXMLParser);
291  // If we're closing the element that caused the OtherXML parse...
292  if (shouldStopOtherXMLParse(elt, name, *_pOtherXMLParser))
293  {
294  // Then we want to clear the proxy from this and
295  // call the end on the top of the element stack.
296  // We assume it has access to the OtherXML parser
297  // and will use the data.
298  _pOtherXMLParser = 0;
299  processEndNCMLElement(name);
300  }
301  else
302  {
303  // Pass through to proxy
304  _pOtherXMLParser->onEndElement(name);
305  }
306  }
307  else // Do the regular NCMLElement call.
308  {
309  // Call the regular NCMLElement end element.
310  processEndNCMLElement(name);
311  }
312 }
313 
314 void
316  const std::string& localname ,
317  const std::string& prefix,
318  const std::string& uri,
319  const XMLAttributeMap& attributes,
320  const XMLNamespaceMap& namespaces)
321 {
322  // If we have a proxy set for OtherXML, pass calls there.
323  if (isParsingOtherXML())
324  {
325  VALID_PTR(_pOtherXMLParser);
326  _pOtherXMLParser->onStartElementWithNamespace(localname,
327  prefix,
328  uri,
329  attributes,
330  namespaces);
331  }
332  else // Otherwise do the standard NCML parse
333  // but keep the namespaces on the stack. We don't do this for OtherXML.
334  {
335  _namespaceStack.push(namespaces);
336  processStartNCMLElement(localname, attributes);
337  }
338 }
339 
340 void
342  const std::string& localname,
343  const std::string& prefix,
344  const std::string& uri)
345 {
346  NCMLElement* elt = getCurrentElement();
347  VALID_PTR(elt);
348 
349  // First, handle the OtherXML proxy parsing case
350  if (isParsingOtherXML())
351  {
352  VALID_PTR(_pOtherXMLParser);
353  // If we're closing the element that caused the OtherXML parse...
354  if (shouldStopOtherXMLParse(elt, localname, *_pOtherXMLParser))
355  {
356  // Then we want to clear the proxy from this and
357  // call the end on the top of the element stack.
358  // We assume it has access to the OtherXML parser
359  // and will use the data.
360  _pOtherXMLParser = 0;
361  processEndNCMLElement(localname);
362  }
363  else
364  {
365  // Pass through to proxy
366  _pOtherXMLParser->onEndElementWithNamespace(localname, prefix, uri);
367  }
368  }
369  else // Do the regular NCMLElement call.
370  {
371  // Call the regular NCMLElement end element.
372  processEndNCMLElement(localname);
373  _namespaceStack.pop();
374  }
375 }
376 
377 void
378 NCMLParser::onCharacters(const std::string& content)
379 {
380  // If we're parsing OtherXML, send the call to the proxy.
381  if (isParsingOtherXML())
382  {
383  VALID_PTR(_pOtherXMLParser);
384  _pOtherXMLParser->onCharacters(content);
385  }
386  else // Standard NCML parse
387  {
388  // If we got an element on the stack, hand it off. Otherwise, do nothing.
389  NCMLElement* elt = getCurrentElement();
390  if (elt)
391  {
392  elt->handleContent(content);
393  }
394  }
395 }
396 
397 void
399 {
400  // TODO We may want to make a flag for considering warnings errors as well.
401  BESDEBUG("ncml", "PARSE WARNING: LibXML msg={" << msg << "}. Attempting to continue parse." << endl);
402 }
403 
404 void
405 NCMLParser::onParseError(std::string msg)
406 {
407  // Pretty much have to give up on malformed XML.
409  "libxml SAX2 parser error! msg={" + msg + "} Terminating parse!");
410 }
411 
412 void
414 {
415  _currentParseLine = line;
416  // BESDEBUG("ncml", "******** Now parsing line: " << line << endl);
417 }
418 
420 // Non-public Implemenation
421 
422 bool
423 NCMLParser::isScopeAtomicAttribute() const
424 {
425  return (!_scope.empty()) && (_scope.topType() == ScopeStack::ATTRIBUTE_ATOMIC);
426 }
427 
428 bool
429 NCMLParser::isScopeAttributeContainer() const
430 {
431  return (!_scope.empty()) && (_scope.topType() == ScopeStack::ATTRIBUTE_CONTAINER);
432 }
433 
434 bool
435 NCMLParser::isScopeSimpleVariable() const
436 {
437  return (!_scope.empty()) && (_scope.topType() == ScopeStack::VARIABLE_ATOMIC);
438 }
439 
440 bool
441 NCMLParser::isScopeCompositeVariable() const
442 {
443  return (!_scope.empty()) && (_scope.topType() == ScopeStack::VARIABLE_CONSTRUCTOR);
444 }
445 
446 bool
447 NCMLParser::isScopeVariable() const
448 {
449  return (isScopeSimpleVariable() || isScopeCompositeVariable());
450 }
451 
452 bool
453 NCMLParser::isScopeGlobal() const
454 {
455  return withinNetcdf() && _scope.empty();
456 }
457 
458 // TODO Clean up these next two calls with a parser state or something....
459 // Dynamic casting all the time isn't super fast or clean if not needed...
460 bool
461 NCMLParser::isScopeNetcdf() const
462 {
463  // see if the last thing parsed was <netcdf>
464  return (!_elementStack.empty() && dynamic_cast<NetcdfElement*>(_elementStack.back()));
465 }
466 
467 bool
468 NCMLParser::isScopeAggregation() const
469 {
470  // see if the last thing parsed was <netcdf>
471  return (!_elementStack.empty() && dynamic_cast<AggregationElement*>(_elementStack.back()));
472 }
473 
474 bool
475 NCMLParser::withinNetcdf() const
476 {
477  return _currentDataset != 0;
478 }
479 
480 bool
481 NCMLParser::withinVariable() const
482 {
483  return withinNetcdf() && _pVar;
484 }
485 
487 NCMLParser::getDDSLoader() const
488 {
489  return _loader;
490 }
491 
492 NetcdfElement*
493 NCMLParser::getCurrentDataset() const
494 {
495  return _currentDataset;
496 }
497 
498 NetcdfElement*
499 NCMLParser::getRootDataset() const
500 {
501  return _rootDataset;
502 }
503 
504 DDS*
505 NCMLParser::getDDSForCurrentDataset() const
506 {
507  NetcdfElement* dataset = getCurrentDataset();
508  NCML_ASSERT_MSG(dataset, "getDDSForCurrentDataset() called when we're not processing a <netcdf> location!");
509  return dataset->getDDS();
510 }
511 
512 void
513 NCMLParser::pushCurrentDataset(NetcdfElement* dataset)
514 {
515  VALID_PTR(dataset);
516  // The first one we get is the root It's special!
517  // We tell it to use the top level response object for the
518  // parser, since that's what ultimately is returned
519  // and we don't want the root making its own we need to copy.
520  bool thisIsRoot = !_rootDataset;
521  if (thisIsRoot)
522  {
523  _rootDataset = dataset;
524  VALID_PTR(_response);
525  _rootDataset->borrowResponseObject(_response);
526  }
527  else
528  {
529  addChildDatasetToCurrentDataset(dataset);
530  }
531 
532  // Also invalidates the AttrTable so it gets cached again.
533  setCurrentDataset(dataset);
534 
535  // TODO: What do we do with the scope stack for a nested dataset?!
536 }
537 
538 void
539 NCMLParser::popCurrentDataset(NetcdfElement* dataset)
540 {
541  if (dataset && dataset != _currentDataset)
542  {
543  THROW_NCML_INTERNAL_ERROR("NCMLParser::popCurrentDataset(): the dataset we expect on the top of the stack is not correct!");
544  }
545 
546  dataset = getCurrentDataset();
547  VALID_PTR(dataset);
548 
549  // If it's the root, we're done and need to clear up the state.
550  if (dataset == _rootDataset)
551  {
552  _rootDataset->unborrowResponseObject(_response);
553  _rootDataset = 0;
554  setCurrentDataset(0);
555  }
556  else
557  {
558  // If it's not the root, it should have a parent, so go get it and make that the new current.
559  NetcdfElement* parentDataset = dataset->getParentDataset();
560  NCML_ASSERT_MSG(parentDataset, "NCMLParser::popCurrentDataset() got non-root dataset, but it had no parent!!");
561  setCurrentDataset(parentDataset);
562  }
563 }
564 
565 void
566 NCMLParser::setCurrentDataset(NetcdfElement* dataset)
567 {
568  if (dataset)
569  {
570  // Make sure it's state is ready to go with operations before making it current
571  NCML_ASSERT(dataset->isValid());
572  _currentDataset = dataset;
573  // We don't set the current attr table, rather it is lazy eval
574  // from getCurrentAttrTable() only if called. This call tells it to do that.
575  _pCurrentTable.invalidate();
576 
577  // UNLESS it's the root dataset, which we want to force to load
578  // since a passthrough file will generate an empty metadata set otherwise
579  // since the table is never requested.
580  if (_currentDataset == _rootDataset)
581  {
582  // Force it to cache so we actually laod the metadata for the root set.
583  // Chidl sets are aggregations so we don't load those unless needed.
584  _pCurrentTable.set(_pCurrentTable.get());
585  }
586  }
587  else
588  {
589  BESDEBUG("ncml", "NCMLParser::setCurrentDataset(): setting to NULL..." << endl);
590  _currentDataset = 0;
591  _pCurrentTable.invalidate();
592  }
593 }
594 
595 void
596 NCMLParser::addChildDatasetToCurrentDataset(NetcdfElement* dataset)
597 {
598  VALID_PTR(dataset);
599 
600  AggregationElement* agg = _currentDataset->getChildAggregation();
601  if (!agg)
602  {
603  THROW_NCML_INTERNAL_ERROR("NCMLParser::addChildDatasetToCurrentDataset(): current dataset has no aggregation element! We can't add it!");
604  }
605 
606  // This will add as strong ref to dataset from agg (child) and a weak to agg from dataset (parent)
607  agg->addChildDataset(dataset);
608 
609  // Force the dataset to create an internal response object for the request type we're processing
610  dataset->createResponseObject(_responseType);
611 }
612 
613 bool
614 NCMLParser::parsingDataRequest() const
615 {
616  const BESDataDDSResponse* const pDataDDSResponse = dynamic_cast<const BESDataDDSResponse* const>(_response);
617  return (pDataDDSResponse);
618 }
619 
620 void
621 NCMLParser::loadLocation(const std::string& location, agg_util::DDSLoader::ResponseType responseType, BESDapResponse* response)
622 {
623  VALID_PTR(response);
624  _loader.loadInto(location, responseType, response);
625 }
626 
627 void
628 NCMLParser::resetParseState()
629 {
630  _filename = "";
631  _pVar = 0;
632  _pCurrentTable.set(0);
633 
634  _scope.clear();
635 
636  // Not that this matters...
637  _responseType = DDSLoader::eRT_RequestDDX;
638 
639  // We never own the memory in this, so just clear it.
640  _response = 0;
641 
642  // We don't own these either.
643  _rootDataset = 0;
644  _currentDataset = 0;
645 
646  // Cleanup any memory in the _elementStack
647  clearElementStack();
648 
649  _namespaceStack.clear();
650 
651  // just in case
652  _loader.cleanup();
653 
654  // In case we had one, null it. The setter is in charge of the memory.
655  _pOtherXMLParser = 0;
656 }
657 
658 bool
659 NCMLParser::isNameAlreadyUsedAtCurrentScope(const std::string& name)
660 {
661  return ( getVariableInCurrentVariableContainer(name) ||
662  attributeExistsAtCurrentScope(name) );
663 }
664 
665 BaseType*
666 NCMLParser::getVariableInCurrentVariableContainer(const string& name)
667 {
668  return getVariableInContainer(name, _pVar);
669 }
670 
671 BaseType*
672 NCMLParser::getVariableInContainer(const string& varName, BaseType* pContainer)
673 {
674  // BaseType::btp_stack varContext;
675  if (pContainer)
676  {
677  // @@@ Old code... recurses and uses dots as field separators... Not good.
678  //return pContainer->var(varName, varContext);
679  // It has to be a Constructor!
680  Constructor* pCtor = dynamic_cast<Constructor*>(pContainer);
681  if (!pCtor)
682  {
683  BESDEBUG("ncml", "WARNING: NCMLParser::getVariableInContainer: "
684  "Expected a BaseType of subclass Constructor, but didn't get it!" << endl);
685  return 0;
686  }
687  else
688  {
689  return agg_util::AggregationUtil::getVariableNoRecurse(*pCtor, varName);
690  }
691  }
692  else
693  {
694  return getVariableInDDS(varName);
695  }
696 }
697 
698 // Not that this should take a fully qualified one too, but without a scoping operator (.) it will
699 // just search the top level variables.
700 BaseType*
701 NCMLParser::getVariableInDDS(const string& varName)
702 {
703  // BaseType::btp_stack varContext;
704  // return getDDSForCurrentDataset()->var(varName, varContext);
705  DDS* pDDS = getDDSForCurrentDataset();
706  if (pDDS)
707  {
709  }
710  else
711  {
712  return 0;
713  }
714 }
715 
716 void
717 NCMLParser::addCopyOfVariableAtCurrentScope(BaseType& varTemplate)
718 {
719  // make sure the name is free
720  if (isNameAlreadyUsedAtCurrentScope(varTemplate.name()))
721  {
723  "NCMLParser::addNewVariableAtCurrentScope:"
724  " Cannot add variable since a variable or attribute of the same name exists at current scope."
725  " Name= " + varTemplate.name());
726  }
727 
728  // Also an internal error if the caller tries it.
729  if (!(isScopeCompositeVariable() || isScopeGlobal()))
730  {
731  THROW_NCML_INTERNAL_ERROR("NCMLParser::addNewVariableAtCurrentScope: current scope not valid for adding variable. Scope=" +
732  getTypedScopeString());
733  }
734 
735  // OK, we know we can add it now. But to what?
736  if (_pVar) // Constructor variable
737  {
738  NCML_ASSERT_MSG(_pVar->is_constructor_type(), "Expected _pVar is a container type!");
739  _pVar->add_var(&varTemplate);
740  }
741  else // Top level DDS for current dataset
742  {
743  BESDEBUG("ncml", "Adding new variable to DDS top level. Variable name=" << varTemplate.name() <<
744  " and typename=" << varTemplate.type_name() << endl);
745  DDS* pDDS = getDDSForCurrentDataset();
746  pDDS->add_var(&varTemplate);
747  }
748 }
749 
750 void
751 NCMLParser::deleteVariableAtCurrentScope(const string& name)
752 {
753  if (! (isScopeCompositeVariable() || isScopeGlobal()) )
754  {
755  THROW_NCML_INTERNAL_ERROR("NCMLParser::deleteVariableAtCurrentScope called when we do not have a variable container at current scope!");
756  }
757 
758  if (_pVar) // In container?
759  {
760  // Given interfaces, unfortunately it needs to be a Structure or we can't do this operation.
761  Structure* pVarContainer = dynamic_cast<Structure*>(_pVar);
762  if (!pVarContainer)
763  {
765  "NCMLParser::deleteVariableAtCurrentScope called with _pVar not a "
766  "Structure class variable! "
767  "We can only delete variables from top DDS or within a Structure now. scope=" +
768  getTypedScopeString());
769  }
770  // First, make sure it exists so we can warn if not. The call fails silently.
771  BaseType* pToBeNuked = pVarContainer->var(name);
772  if (!pToBeNuked)
773  {
775  "Tried to remove variable from a Structure, but couldn't find the variable with name=" + name +
776  "at scope=" + getScopeString());
777  }
778  // Silently fails, so assume it worked.
779  pVarContainer->del_var(name);
780  }
781  else // Global
782  {
783  // we better have a DDS if we get here!
784  DDS* pDDS = getDDSForCurrentDataset();
785  VALID_PTR(pDDS);
786  pDDS->del_var(name);
787  }
788 }
789 
790 BaseType*
791 NCMLParser::getCurrentVariable() const
792 {
793  return _pVar;
794 }
795 
796 void
797 NCMLParser::setCurrentVariable(BaseType* pVar)
798 {
799  _pVar = pVar;
800  if (pVar) // got a variable
801  {
802  setCurrentAttrTable( &(pVar->get_attr_table()) );
803  }
804  else if (getDDSForCurrentDataset()) // null pvar but we have a dds, use global table
805  {
806  DDS* dds = getDDSForCurrentDataset();
807  setCurrentAttrTable( &(dds->get_attr_table()) );
808  }
809  else // just clear it out, no context
810  {
811  setCurrentAttrTable(0);
812  }
813 }
814 
815 bool
816 NCMLParser::typeCheckDAPVariable(const BaseType& var, const string& expectedType)
817 {
818  // Match all types.
819  if (expectedType.empty())
820  {
821  return true;
822  }
823  else
824  {
825  // If the type specifies a Structure, it better be a Constructor type.
826  if (expectedType == STRUCTURE_TYPE)
827  {
828  // Calls like is_constructor_type really should be const...
829  BaseType& varSemanticConst = const_cast<BaseType&>(var);
830  return varSemanticConst.is_constructor_type();
831  }
832  else
833  {
834  return (var.type_name() == expectedType);
835  }
836  }
837 }
838 
839 AttrTable*
840 NCMLParser::getCurrentAttrTable() const
841 {
842  // will load the DDS of current dataset if required.
843  // The end result of calling AttrTableLazyPtr::get() is that the NCMLParser
844  // field '_pAttrTable' points to the DDS' AttrTable.
845  return _pCurrentTable.get();
846 }
847 
848 void
849 NCMLParser::setCurrentAttrTable(AttrTable* pAT)
850 {
851  _pCurrentTable.set(pAT);
852 }
853 
854 AttrTable*
855 NCMLParser::getGlobalAttrTable() const
856 {
857  AttrTable* pAT = 0;
858  DDS* pDDS = getDDSForCurrentDataset();
859  if (pDDS)
860  {
861  pAT = &(pDDS->get_attr_table());
862  }
863  return pAT;
864 }
865 
866 bool
867 NCMLParser::attributeExistsAtCurrentScope(const string& name) const
868 {
869  // Lookup the given attribute in the current table.
870  AttrTable::Attr_iter attr;
871  bool foundIt = findAttribute(name, attr);
872  return foundIt;
873 }
874 
875 bool
876 NCMLParser::findAttribute(const string& name, AttrTable::Attr_iter& attr) const
877 {
878  AttrTable* pAT = getCurrentAttrTable();
879  if (pAT)
880  {
881  attr = pAT->simple_find(name);
882  return (attr != pAT->attr_end());
883  }
884  else
885  {
886  return false;
887  }
888 }
889 
890 
891 int
892 NCMLParser::tokenizeAttrValues(vector<string>& tokens, const string& values, const string& dapAttrTypeName, const string& separator)
893 {
894  // Convert the type string into a DAP AttrType to be sure
895  AttrType dapType = String_to_AttrType(dapAttrTypeName);
896  if (dapType == Attr_unknown)
897  {
899  "Attempting to tokenize attribute value failed since"
900  " we found an unknown internal DAP type=" + dapAttrTypeName +
901  " for the current fully qualified attribute=" + _scope.getScopeString());
902  }
903 
904  // If we're valid type, tokenize us according to type.
905  int numTokens = tokenizeValuesForDAPType(tokens, values, dapType, separator);
906  if (numTokens == 0 &&
907  ( (dapType == Attr_string) || (dapType == Attr_url) || (dapType == Attr_other_xml)))
908  {
909  tokens.push_back(""); // 0 tokens will cause a problem later, so push empty string!
910  }
911 
912  // Now type check the tokens are valid strings for the type.
914 
915 #if DEBUG_NCML_PARSER_INTERNALS
916 
917  if (separator != NCMLUtil::WHITESPACE)
918  {
919  BESDEBUG("ncml", "Got non-default separators for tokenize. separator=\"" << separator << "\"" << endl);
920  }
921 
922  string msg = "";
923  for (unsigned int i=0; i<tokens.size(); i++)
924  {
925  if (i > 0)
926  {
927  msg += ",";
928  }
929  msg += "\"";
930  msg += tokens[i];
931  msg += "\"";
932  }
933  BESDEBUG("ncml", "Tokenize got " << numTokens << " tokens:\n" << msg << endl);
934 
935 #endif // DEBUG_NCML_PARSER_INTERNALS
936 
937  return numTokens;
938 }
939 
940 int
941 NCMLParser::tokenizeValuesForDAPType(vector<string>& tokens, const string& values, AttrType dapType, const string& separator)
942 {
943  tokens.resize(0); // Start empty.
944  int numTokens = 0;
945 
946  if (dapType == Attr_unknown)
947  {
948  // Do out best to recover....
949  BESDEBUG("ncml", "Warning: tokenizeValuesForDAPType() got unknown DAP type! Attempting to continue..." << endl);
950  tokens.push_back(values);
951  numTokens = 1;
952  }
953  else if (dapType == Attr_container)
954  {
955  // Not supposed to have values, just push empty string....
956  BESDEBUG("ncml", "Warning: tokenizeValuesForDAPType() got container type, we should not have values!" << endl);
957  tokens.push_back("");
958  numTokens = 1;
959  }
960  else if (dapType == Attr_string)
961  {
962  // Don't use whitespace as default separator for strings.
963  // If they explicitly set it, then fine.
964  // We don't trim strings either. All whitespace, trailing or leading, is left.
965  numTokens = NCMLUtil::tokenize(values, tokens, separator);
966  }
967  else // For all other atomic types, do a split on separator
968  {
969  // Use whitespace as default if sep not set
970  string sep = ((separator.empty())?(NCMLUtil::WHITESPACE):(separator));
971  numTokens = NCMLUtil::tokenize(values, tokens, sep);
972  NCMLUtil::trimAll(tokens);
973  }
974  return numTokens;
975 }
976 
977 
979 
980 // Used below to convert NcML data type to a DAP data type.
981 typedef std::map<string, string> TypeConverter;
982 
983 // If true, we allow the specification of a DAP scalar type
984 // in a location expecting an NcML type.
985 static const bool ALLOW_DAP_TYPES_AS_NCML_TYPES = true;
986 
987 /* Ncml DataType:
988  <xsd:enumeration value="char"/>
989  <xsd:enumeration value="byte"/>
990  <xsd:enumeration value="short"/>
991  <xsd:enumeration value="int"/>
992  <xsd:enumeration value="long"/>
993  <xsd:enumeration value="float"/>
994  <xsd:enumeration value="double"/>
995  <xsd:enumeration value="String"/>
996  <xsd:enumeration value="string"/>
997  <xsd:enumeration value="Structure"/>
998  */
999 static TypeConverter* makeTypeConverter()
1000 {
1001  TypeConverter* ptc = new TypeConverter();
1002  TypeConverter& tc = *ptc;
1003  // NcML to DAP conversions
1004  tc["char"] = "Byte"; // char is a C char, let's use a Byte and special parse it as a char not numeric
1005  tc["byte"] = "Int16"; // Since NcML byte's can be signed, we must promote them to not lose the sign bit.
1006  tc["short"] = "Int16";
1007  tc["int"] = "Int32";
1008  tc["long"] = "Int32"; // not sure of this one
1009  tc["float"] = "Float32";
1010  tc["double"] = "Float64";
1011  tc["string"] = "String"; // allow lower case.
1012  tc["String"] = "String";
1013  tc["Structure"] = "Structure";
1014  tc["structure"] = "Structure"; // allow lower case for this as well
1015 
1016  // If we allow DAP types to be specified directly,
1017  // then make them be passthroughs in the converter...
1018  if (ALLOW_DAP_TYPES_AS_NCML_TYPES)
1019  {
1020  tc["Byte"] = "Byte"; // DAP Byte can fit in Byte tho, unlike NcML "byte"!
1021  tc["Int16"] = "Int16";
1022  tc["UInt16"] = "UInt16";
1023  tc["Int32"] = "Int32";
1024  tc["UInt32"] = "UInt32";
1025  tc["Float32"] = "Float32";
1026  tc["Float64"] = "Float64";
1027  // allow both url cases due to old bug where "Url" is returned in dds rather then DAP2 spec "URL"
1028  tc["Url"] = "URL";
1029  tc["URL"] = "URL";
1030  tc["OtherXML"] = "OtherXML"; // Pass it through
1031  }
1032 
1033  return ptc;
1034 }
1035 
1036 // Singleton
1037 static const TypeConverter& getTypeConverter()
1038 {
1039  static TypeConverter* singleton = 0;
1040  if (!singleton)
1041  {
1042  singleton = makeTypeConverter();
1043  }
1044  return *singleton;
1045 }
1046 
1047 #if 0 // Unused right now... might be later, but I hate compiler warnings.
1048 // Is the given type a DAP type?
1049 static bool isDAPType(const string& type)
1050 {
1051  return (String_to_AttrType(type) != Attr_unknown);
1052 }
1053 #endif // 0
1054 
1055 
1056 /* static */
1057 string
1059 {
1060  NCML_ASSERT_MSG(!ncmlType.empty(), "Logic error: convertNcmlTypeToCanonicalType disallows empty() input.");
1061 
1062  const TypeConverter& tc = getTypeConverter();
1063  TypeConverter::const_iterator it = tc.find(ncmlType);
1064 
1065  if (it == tc.end())
1066  {
1067  return ""; // error condition
1068  }
1069  else
1070  {
1071  return it->second;
1072  }
1073 }
1074 
1075 void
1076 NCMLParser::checkDataIsValidForCanonicalTypeOrThrow(const string& type, const vector<string>& tokens) const
1077 {
1078 /* Byte
1079  Int16
1080  UInt16
1081  Int32
1082  UInt32
1083  Float32
1084  Float64
1085  String
1086  URL
1087  OtherXML
1088 */
1089  bool valid = true;
1090  vector<string>::const_iterator it;
1091  vector<string>::const_iterator endIt = tokens.end();
1092  for (it = tokens.begin(); it != endIt; ++it)
1093  {
1094  if (type == "Byte")
1095  {
1096  valid &= check_byte(it->c_str());
1097  }
1098  else if (type == "Int16")
1099  {
1100  valid &= check_int16(it->c_str());
1101  }
1102  else if (type == "UInt16")
1103  {
1104  valid &= check_uint16(it->c_str());
1105  }
1106  else if (type == "Int32")
1107  {
1108  valid &= check_int32(it->c_str());
1109  }
1110  else if (type == "UInt32")
1111  {
1112  valid &= check_uint32(it->c_str());
1113  }
1114  else if (type == "Float32")
1115  {
1116  valid &= check_float32(it->c_str());
1117  }
1118  else if (type == "Float64")
1119  {
1120  valid &= check_float64(it->c_str());
1121  }
1122  // Doh! The DAP2 specifies case as "URL" but internally libdap uses "Url" Allow both...
1123  else if (type == "URL" || type == "Url" || type == "String")
1124  {
1125  // TODO the DAP call check_url is currently a noop. do we want to check for well-formed URL?
1126  // This isn't an NcML type now, so straight up NcML users might enter URL as String anyway.
1127  valid &= (it->size() <= MAX_DAP_STRING_SIZE);
1128  if (!valid)
1129  {
1130  std::stringstream msg;
1131  msg << "Invalid Value: The "<< type << " attribute value (not shown) exceeded max string length of " << MAX_DAP_STRING_SIZE <<
1132  " at scope=" << _scope.getScopeString() << endl;
1134  }
1135 
1136  valid &= NCMLUtil::isAscii(*it);
1137  if (!valid)
1138  {
1140  "Invalid Value: The " + type +
1141  " attribute value (not shown) has an invalid non-ascii character.");
1142  }
1143  }
1144 
1145  // For OtherXML, there's nothing to check so just say it's OK.
1146  // The SAX parser checks it for wellformedness already,
1147  // but ultimately it's just an arbitrary string...
1148  else if (type == "OtherXML")
1149  {
1150  valid &= true;
1151  }
1152 
1153  else
1154  {
1155  // We probably shouldn't get here, but...
1156  THROW_NCML_INTERNAL_ERROR("checkDataIsValidForCanonicalType() got unknown data type=" + type);
1157  }
1158 
1159  // Early throw so we know which token it was.
1160  if (!valid)
1161  {
1163  "Invalid Value given for type=" + type + " with value=" + (*it) + " was invalidly formed or out of range" +
1164  _scope.getScopeString());
1165  }
1166  }
1167  // All is good if we get here.
1168 }
1169 
1170 void
1171 NCMLParser::clearAllAttrTables(DDS* dds)
1172 {
1173  if (!dds)
1174  {
1175  return;
1176  }
1177 
1178  // Blow away the global attribute table.
1179  dds->get_attr_table().erase();
1180 
1181  // Hit all variables, recursing on containers.
1182  for (DDS::Vars_iter it = dds->var_begin(); it != dds->var_end(); ++it)
1183  {
1184  // this will clear not only *it's table, but it's children if it's composite.
1185  clearVariableMetadataRecursively(*it);
1186  }
1187 }
1188 
1189 void
1190 NCMLParser::clearVariableMetadataRecursively(BaseType* var)
1191 {
1192  VALID_PTR(var);
1193  // clear the table
1194  var->get_attr_table().erase();
1195 
1196  if (var->is_constructor_type())
1197  {
1198  Constructor *compositeVar = dynamic_cast<Constructor*>(var);
1199  if (!compositeVar)
1200  {
1201  THROW_NCML_INTERNAL_ERROR("clearVariableMetadataRecursively: Unexpected cast error on dynamic_cast<Constructor*>");
1202  }
1203  for (Constructor::Vars_iter it = compositeVar->var_begin(); it != compositeVar->var_end(); ++it)
1204  {
1205  clearVariableMetadataRecursively(*it);
1206  }
1207  }
1208 }
1209 
1210 void
1211 NCMLParser::enterScope(const string& name, ScopeStack::ScopeType type)
1212 {
1213  _scope.push(name, type);
1214  BESDEBUG("ncml", "Entering scope: " << _scope.top().getTypedName() << endl);
1215  BESDEBUG("ncml", "New scope=\"" << _scope.getScopeString() << "\"" << endl);
1216 }
1217 
1218 void
1219 NCMLParser::exitScope()
1220 {
1221  NCML_ASSERT_MSG(!_scope.empty(), "Logic Error: Scope Stack Underflow!");
1222  BESDEBUG("ncml", "Exiting scope " << _scope.top().getTypedName() << endl);
1223  _scope.pop();
1224  BESDEBUG("ncml", "New scope=\"" << _scope.getScopeString() << "\"" << endl);
1225 }
1226 
1227 void
1228 NCMLParser::printScope() const
1229 {
1230  BESDEBUG("ncml", "Scope=\"" << _scope.getScopeString() << "\"" << endl);
1231 }
1232 
1233 string
1234 NCMLParser::getScopeString() const
1235 {
1236  return _scope.getScopeString();
1237 }
1238 
1239 string
1240 NCMLParser::getTypedScopeString() const
1241 {
1242  return _scope.getTypedScopeString();
1243 }
1244 
1245 int
1246 NCMLParser::getScopeDepth() const
1247 {
1248  return _scope.size();
1249 }
1250 void
1251 NCMLParser::pushElement(NCMLElement* elt)
1252 {
1253  VALID_PTR(elt);
1254  _elementStack.push_back(elt);
1255  elt->ref(); // up the count!
1256 }
1257 
1258 void
1259 NCMLParser::popElement()
1260 {
1261  NCMLElement* elt = _elementStack.back();
1262  _elementStack.pop_back();
1263 
1264  // Keep the toString around if we plan to nuke him
1265  string infoOnDeletedDude = ((elt->getRefCount() == 1)?(elt->toString()):(string("")));
1266 
1267  // Drop the ref count. If that forced a delete, print out the saved string.
1268  if (elt->unref() == 0)
1269  {
1270  BESDEBUG("ncml:memory", "NCMLParser::popElement: ref count hit 0 so we deleted element=" << infoOnDeletedDude << endl);
1271  }
1272 }
1273 
1274 NCMLElement*
1275 NCMLParser::getCurrentElement() const
1276 {
1277  if (_elementStack.empty())
1278  {
1279  return 0;
1280  }
1281  else
1282  {
1283  return _elementStack.back();
1284  }
1285 }
1286 
1287 void
1288 NCMLParser::clearElementStack()
1289 {
1290  while (!_elementStack.empty())
1291  {
1292  NCMLElement* elt = _elementStack.back();
1293  _elementStack.pop_back();
1294  // unref() them... The Factory will take care of dangling memory...
1295  elt->unref();
1296  }
1297  _elementStack.resize(0);
1298 }
1299 
1300 void
1301 NCMLParser::processStartNCMLElement(const std::string& name, const XMLAttributeMap& attrs)
1302 {
1303  // Store it in a shared ptr in case this function exceptions before we store it in the element stack.
1304  RCPtr<NCMLElement> elt = _elementFactory.makeElement(name, attrs, *this);
1305 
1306  // If we actually created an element of the given type name
1307  if (elt.get())
1308  {
1309  elt->handleBegin();
1310  // tell the container to push the raw element, which will also ref() it on success
1311  // otherwise ~RCPtr will unref() to 0 and thus nuke it on exception.
1312  pushElement(elt.get());
1313  }
1314  else // Unknown element...
1315  {
1316  if (sThrowExceptionOnUnknownElements)
1317  {
1319  "Unknown element type=" + name +
1320  " found in NcML parse with scope=" + _scope.getScopeString());
1321  }
1322  else
1323  {
1324  BESDEBUG("ncml", "Start of <" << name << "> element. Element unsupported, ignoring." << endl);
1325  }
1326  }
1327 }
1328 
1329 void
1330 NCMLParser::processEndNCMLElement(const std::string& name)
1331 {
1332  NCMLElement* elt = getCurrentElement();
1333  VALID_PTR(elt);
1334 
1335  // If it matches the one on the top of the stack, then process and pop.
1336  if (elt->getTypeName() == name)
1337  {
1338  elt->handleEnd();
1339  popElement(); // handles delete
1340  }
1341  else // the names don't match, so just ignore it.
1342  {
1343  BESDEBUG("ncml", "End of <" << name << "> element unsupported currently, ignoring." << endl);
1344  }
1345 }
1346 
1347 
1348 const DimensionElement*
1349 NCMLParser::getDimensionAtLexicalScope(const string& dimName) const
1350 {
1351  const DimensionElement* ret = 0;
1352  if (getCurrentDataset())
1353  {
1354  ret = getCurrentDataset() -> getDimensionInFullScope(dimName);
1355  }
1356  return ret;
1357 }
1358 
1359 string
1360 NCMLParser::printAllDimensionsAtLexicalScope() const
1361 {
1362  string ret("");
1363  NetcdfElement* dataset = getCurrentDataset();
1364  while(dataset)
1365  {
1366  ret += dataset->printDimensions();
1367  dataset = dataset->getParentDataset();
1368  }
1369  return ret;
1370 }
1371 
1372 void
1373 NCMLParser::enterOtherXMLParsingState(OtherXMLParser* pOtherXMLParser)
1374 {
1375  BESDEBUG("ncml", "Entering state for parsing OtherXML!" << endl);
1376  _pOtherXMLParser = pOtherXMLParser;
1377 }
1378 
1379 bool
1380 NCMLParser::isParsingOtherXML() const
1381 {
1382  return _pOtherXMLParser;
1383 }
1384 
1385 void
1386 NCMLParser::cleanup()
1387 {
1388  // The only memory we own is the _response, which is in an auto_ptr so will
1389  // either be returned to caller in parse() and cleared, or else
1390  // delete'd by our dtor via auto_ptr
1391 
1392  // All other objects point into _response temporarily, so nothing to destroy there.
1393 
1394  // Just for completeness.
1395  resetParseState();
1396 }
1397 
1398 
1399 
1400 } // namespace ncml_module
1401 
1402 
1403 
int getParseDepth() const
Get the current parse depth (how many elements we've opened with onStartElement and not closed yet) I...
virtual void onEndElement(const std::string &name)
Definition: NCMLParser.cc:282
ScopeType topType() const
Definition: ScopeStack.h:102
virtual void onCharacters(const std::string &content)
Called when characters are encountered within an element.
Definition: NCMLParser.cc:378
static bool isAscii(const std::string &str)
Does the string contain only ASCII 7-bit characters according to isascii()?
Definition: NCMLUtil.cc:96
string getScopeString() const
Return a fully qualifed name for the scope, such as "" for global scope or "MetaData.Info.Name" for an attribute container, etc.
Definition: ScopeStack.cc:103
ResponseType
For telling the loader what type of BESDapResponse to load and return.
Definition: DDSLoader.h:97
virtual void onStartDocument()
Definition: NCMLParser.cc:234
bool parsing() const
Are we currently parsing?
Definition: NCMLParser.cc:216
void checkDataIsValidForCanonicalTypeOrThrow(const string &type, const vector< string > &tokens) const
Make sure the given tokens are valid for the listed type.
Definition: NCMLParser.cc:1076
#define NCML_ASSERT(cond)
Definition: NCMLDebug.h:80
An abstract superclass for NCMLArray that handles the non-parameterized functionality and allows u...
virtual void onParseError(std::string msg)
An unrecoverable parse error occurred.
Definition: NCMLParser.cc:405
Helper class for temporarily hijacking an existing dhi to load a DDX response for one particular file...
#define NCML_ASSERT_MSG(cond, msg)
Definition: NCMLDebug.h:83
virtual void onParseWarning(std::string msg)
A recoverable parse error occured.
Definition: NCMLParser.cc:398
Concrete class for NcML element.
Definition: NetcdfElement.h:63
virtual const libdap::DDS * getDDS() const
Return the DDS for this dataset, loading it in if needed.
virtual void onStartElementWithNamespace(const std::string &localname, const std::string &prefix, const std::string &uri, const XMLAttributeMap &attributes, const XMLNamespaceMap &namespaces)
SAX2 start element call with gets namespace information.
Definition: NCMLParser.cc:315
bool parse(const string &ncmlFilename)
Do a SAX parse of the ncmlFilename and pass the calls to wrapper parser.
Wrapper for libxml SAX parser C callbacks into C++.
friend class DimensionElement
Definition: NCMLParser.h:172
ScopeType
The current scope is either global attribute table, within a variable's attribute table...
Definition: ScopeStack.h:63
virtual void onEndElementWithNamespace(const std::string &localname, const std::string &prefix, const std::string &uri)
SAX2 End element with namespace information.
const XMLNamespaceStack & getXMLNamespaceStack() const
If using namespaces, get the current stack of namespaces.
Definition: NCMLParser.cc:228
static const string STRUCTURE_TYPE
The string describing the type "Structure".
Definition: NCMLParser.h:578
int getParseLineNumber() const
Get the line of the NCML file the parser is currently parsing.
Definition: NCMLParser.cc:222
virtual void onStartElement(const std::string &name, const XMLAttributeMap &attrs)
virtual void setParseLineNumber(int line)
Before any of the callbacks are issued, this function is called to let the implementing parser know w...
Definition: NCMLParser.cc:413
void loadInto(const std::string &location, ResponseType type, BESDapResponse *pResponse)
Load a DDX or DataDDS response into the given pResponse object, which must be non-null.
Definition: DDSLoader.cc:141
friend class NetcdfElement
Definition: NCMLParser.h:174
virtual void onCharacters(const std::string &content)
Called when characters are encountered within an element.
#define THROW_NCML_PARSE_ERROR(parseLine, msg)
Definition: NCMLDebug.h:69
string getTypedName() const
Definition: ScopeStack.h:76
Class used to handle parsing in an attribute of type=="OtherXML" which basically just has to keep app...
static libdap::BaseType * getVariableNoRecurse(const libdap::DDS &dds, const std::string &name)
Return the variable in dds top level (no recursing, no fully qualified name dot notation) if it exist...
A reference to an RCObject which automatically ref() and deref() on creation and destruction.
Definition: RCObject.h:279
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
void unborrowResponseObject(BESDapResponse *pResponse)
Kind of superfluous, but tells this object to clear its reference to pReponse, which had better match...
string getTypedScopeString() const
Similar to getScopeString(), but appends the type of the scope to the name in the form "Name" f...
Definition: ScopeStack.cc:119
Base class for NcML element concrete classes.
Definition: NCMLElement.h:64
AttrTable * get() const
Get the table, loading it from the current dataset in _parser if !_loaded yet (hasn't been set() )...
Definition: NCMLParser.cc:93
void parseInto(const string &ncmlFilename, agg_util::DDSLoader::ResponseType responseType, BESDapResponse *response)
Same as parse, but the response object to parse into is passed down by the caller rather than created...
Definition: NCMLParser.cc:184
virtual const std::string & getTypeName() const =0
Return the type of the element, which should be: the same as ConcreteClassName::getTypeName() ...
Represents an OPeNDAP DAP response object within the BES.
virtual void handleContent(const std::string &content)
Handle the characters content for the element.
Definition: NCMLElement.cc:180
#define THROW_NCML_INTERNAL_ERROR(msg)
Definition: NCMLDebug.h:61
void invalidate()
Dirty the cache so the next get() goes and gets the AttrTable for the current dataset, whatever that is.
Definition: NCMLParser.cc:117
RCPtr< NCMLElement > makeElement(const std::string &eltTypeName, const XMLAttributeMap &attrs, NCMLParser &parser)
Create an element of the proper type with the given AttrMap for its defined attributes.
Definition: NCMLElement.cc:126
NCMLParser(agg_util::DDSLoader &loader)
Create a structure that can parse an NCML filename and returned a transformed response of requested t...
Definition: NCMLParser.cc:145
const Entry & top() const
Definition: ScopeStack.cc:85
void set(AttrTable *pAT)
Once set is called, _loaded it true unless pAT is null.
Definition: NCMLParser.cc:103
bool empty() const
If there are no entries pushed.
Definition: ScopeStack.cc:91
virtual void onStartElementWithNamespace(const std::string &localname, const std::string &prefix, const std::string &uri, const XMLAttributeMap &attributes, const XMLNamespaceMap &namespaces)
SAX2 start element call with gets namespace information.
static void trimAll(std::vector< std::string > &tokens, const std::string &trimChars=WHITESPACE)
Call trim on each string in tokens.
Definition: NCMLUtil.cc:136
#define VALID_PTR(ptr)
Definition: NCMLDebug.h:88
int size() const
How many things are on the stack.
Definition: ScopeStack.cc:97
T * get() const
Definition: RCObject.h:334
static string convertNcmlTypeToCanonicalType(const string &ncmlType)
Convert the NCML type in ncmlType into a canonical type we will use in the parser.
Definition: NCMLParser.cc:1058
virtual void onEndDocument()
Definition: NCMLParser.cc:240
void push(const string &name, ScopeType type)
Definition: ScopeStack.h:98
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
virtual void onEndElement(const std::string &name)
void push(const XMLNamespaceMap &nsMap)
Definition: XMLHelpers.cc:497
static const std::string WHITESPACE
Delimiter set for tokenizing whitespace separated data.
Definition: NCMLUtil.h:79
std::map< string, string > TypeConverter
Definition: NCMLParser.cc:981
friend class AggregationElement
Definition: NCMLParser.h:169
virtual void onEndElementWithNamespace(const std::string &localname, const std::string &prefix, const std::string &uri)
SAX2 End element with namespace information.
Definition: NCMLParser.cc:341
void borrowResponseObject(BESDapResponse *pResponse)
Used by the NCMLParser to let us know to borrow the response object and not own it.
virtual void onStartElement(const std::string &name, const XMLAttributeMap &attrs)
Definition: NCMLParser.cc:246
auto_ptr< BESDapResponse > parse(const string &ncmlFilename, agg_util::DDSLoader::ResponseType type)
Parse the NcML filename, returning a newly allocated DDS response containing the underlying dataset t...
Definition: NCMLParser.cc:170
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
void cleanup()
restore dhi to clean state
Definition: DDSLoader.cc:209