30#include "NCMLParser.h"
32#include "AggregationElement.h"
33#include "AggregationUtil.h"
34#include <BESConstraintFuncs.h>
35#include <BESDataDDSResponse.h>
36#include <BESDDSResponse.h>
38#include <BESStopWatch.h>
40#include "DimensionElement.h"
41#include <libdap/AttrTable.h>
42#include <libdap/BaseType.h>
43#include <libdap/DAS.h>
44#include <libdap/DDS.h>
46#include <libdap/Structure.h>
50#include "NCMLElement.h"
52#include "NetcdfElement.h"
53#include "OtherXMLParser.h"
54#include <libdap/parser.h>
55#include "SaxParserWrapper.h"
59#define DEBUG_NCML_PARSER_INTERNALS 1
66static const unsigned int MAX_DAP_STRING_SIZE = 32767;
69bool NCMLParser::sThrowExceptionOnUnknownElements =
true;
75static const int NO_CURRENT_PARSE_LINE_NUMBER = -1;
79AttrTableLazyPtr::AttrTableLazyPtr(
const NCMLParser& parser, AttrTable* pAT) :
80 _parser(parser), _pAttrTable(pAT), _loaded(pAT)
84AttrTableLazyPtr::~AttrTableLazyPtr()
117void AttrTableLazyPtr::loadAndSetAttrTable()
123 DDS* pDDS = pDataset->
getDDS();
125 set(&(pDDS->get_attr_table()));
135 _filename(
""), _loader(loader), _responseType(
DDSLoader::eRT_RequestDDX), _response(0), _rootDataset(0), _currentDataset(
136 0), _pVar(0), _pCurrentTable(*this, 0), _elementStack(), _scope(), _namespaceStack(), _pOtherXMLParser(0), _currentParseLine(
137 NO_CURRENT_PARSE_LINE_NUMBER)
139 BESDEBUG(
"ncml",
"Created NCMLParser." << endl);
142NCMLParser::~NCMLParser()
155 parseInto(ncmlFilename, responseType, response.get());
168 "NCMLParser::parseInto: got wrong response object for given type.");
170 _responseType = responseType;
171 _response = response;
174 THROW_NCML_INTERNAL_ERROR(
"Illegal Operation: NCMLParser::parse called while already parsing!");
177 BESDEBUG(
"ncml",
"Beginning NcML parse of file=" << ncmlFilename << endl);
180 _filename = ncmlFilename;
185 parser.
parse(ncmlFilename);
197 return !_filename.empty();
202 return _currentParseLine;
208 return _namespaceStack;
211void NCMLParser::onStartDocument()
213 BESDEBUG(
"ncml",
"onStartDocument." << endl);
216void NCMLParser::onEndDocument()
218 BESDEBUG(
"ncml",
"onEndDocument." << endl);
224 if (isParsingOtherXML()) {
225 VALID_PTR(_pOtherXMLParser);
230 processStartNCMLElement(name, attrs);
259 if (isParsingOtherXML()) {
260 VALID_PTR(_pOtherXMLParser);
262 if (shouldStopOtherXMLParse(elt, name, *_pOtherXMLParser)) {
267 _pOtherXMLParser = 0;
268 processEndNCMLElement(name);
278 processEndNCMLElement(name);
286 if (isParsingOtherXML()) {
287 VALID_PTR(_pOtherXMLParser);
293 _namespaceStack.push(namespaces);
294 processStartNCMLElement(localname, attributes);
299 const std::string& uri)
305 if (isParsingOtherXML()) {
306 VALID_PTR(_pOtherXMLParser);
308 if (shouldStopOtherXMLParse(elt, localname, *_pOtherXMLParser)) {
313 _pOtherXMLParser = 0;
314 processEndNCMLElement(localname);
324 processEndNCMLElement(localname);
325 _namespaceStack.pop();
332 if (isParsingOtherXML()) {
333 VALID_PTR(_pOtherXMLParser);
349 BESDEBUG(
"ncml",
"PARSE WARNING: LibXML msg={" << msg <<
"}. Attempting to continue parse." << endl);
355 THROW_NCML_PARSE_ERROR(
getParseLineNumber(),
"libxml SAX2 parser error! msg={" + msg +
"} Terminating parse!");
360 _currentParseLine = line;
367bool NCMLParser::isScopeAtomicAttribute()
const
369 return (!_scope.
empty()) && (_scope.topType() == ScopeStack::ATTRIBUTE_ATOMIC);
372bool NCMLParser::isScopeAttributeContainer()
const
374 return (!_scope.
empty()) && (_scope.topType() == ScopeStack::ATTRIBUTE_CONTAINER);
377bool NCMLParser::isScopeSimpleVariable()
const
379 return (!_scope.
empty()) && (_scope.topType() == ScopeStack::VARIABLE_ATOMIC);
382bool NCMLParser::isScopeCompositeVariable()
const
384 return (!_scope.
empty()) && (_scope.topType() == ScopeStack::VARIABLE_CONSTRUCTOR);
387bool NCMLParser::isScopeVariable()
const
389 return (isScopeSimpleVariable() || isScopeCompositeVariable());
392bool NCMLParser::isScopeGlobal()
const
394 return withinNetcdf() && _scope.
empty();
399bool NCMLParser::isScopeNetcdf()
const
402 return (!_elementStack.empty() &&
dynamic_cast<NetcdfElement*
>(_elementStack.back()));
405bool NCMLParser::isScopeAggregation()
const
408 return (!_elementStack.empty() &&
dynamic_cast<AggregationElement*
>(_elementStack.back()));
411bool NCMLParser::withinNetcdf()
const
413 return _currentDataset != 0;
416bool NCMLParser::withinVariable()
const
418 return withinNetcdf() && _pVar;
422NCMLParser::getDDSLoader()
const
428NCMLParser::getCurrentDataset()
const
430 return _currentDataset;
434NCMLParser::getRootDataset()
const
440NCMLParser::getDDSForCurrentDataset()
const
442 NetcdfElement* dataset = getCurrentDataset();
443 NCML_ASSERT_MSG(dataset,
"getDDSForCurrentDataset() called when we're not processing a <netcdf> location!");
444 return dataset->getDDS();
447void NCMLParser::pushCurrentDataset(NetcdfElement* dataset)
454 bool thisIsRoot = !_rootDataset;
456 _rootDataset = dataset;
457 VALID_PTR(_response);
461 addChildDatasetToCurrentDataset(dataset);
465 setCurrentDataset(dataset);
470void NCMLParser::popCurrentDataset(NetcdfElement* dataset)
472 if (dataset && dataset != _currentDataset) {
473 THROW_NCML_INTERNAL_ERROR(
474 "NCMLParser::popCurrentDataset(): the dataset we expect on the top of the stack is not correct!");
477 dataset = getCurrentDataset();
481 if (dataset == _rootDataset) {
484 setCurrentDataset(0);
488 NetcdfElement* parentDataset = dataset->getParentDataset();
489 NCML_ASSERT_MSG(parentDataset,
"NCMLParser::popCurrentDataset() got non-root dataset, but it had no parent!!");
490 setCurrentDataset(parentDataset);
494void NCMLParser::setCurrentDataset(NetcdfElement* dataset)
498 NCML_ASSERT(dataset->isValid());
499 _currentDataset = dataset;
507 if (_currentDataset == _rootDataset) {
510 _pCurrentTable.
set(_pCurrentTable.
get());
514 BESDEBUG(
"ncml",
"NCMLParser::setCurrentDataset(): setting to NULL..." << endl);
520void NCMLParser::addChildDatasetToCurrentDataset(NetcdfElement* dataset)
526 THROW_NCML_INTERNAL_ERROR(
527 "NCMLParser::addChildDatasetToCurrentDataset(): current dataset has no aggregation element! We can't add it!");
531 agg->addChildDataset(dataset);
534 dataset->createResponseObject(_responseType);
537bool NCMLParser::parsingDataRequest()
const
540 return (pDataDDSResponse);
547 _loader.
loadInto(location, responseType, response);
550void NCMLParser::resetParseState()
554 _pCurrentTable.
set(0);
559 _responseType = DDSLoader::eRT_RequestDDX;
571 _namespaceStack.clear();
577 _pOtherXMLParser = 0;
580bool NCMLParser::isNameAlreadyUsedAtCurrentScope(
const std::string& name)
582 return (getVariableInCurrentVariableContainer(name) || attributeExistsAtCurrentScope(name));
586NCMLParser::getVariableInCurrentVariableContainer(
const string& name)
588 return getVariableInContainer(name, _pVar);
592NCMLParser::getVariableInContainer(
const string& varName, BaseType* pContainer)
599 Constructor* pCtor =
dynamic_cast<Constructor*
>(pContainer);
602 "WARNING: NCMLParser::getVariableInContainer: " "Expected a BaseType of subclass Constructor, but didn't get it!" << endl);
610 return getVariableInDDS(varName);
617NCMLParser::getVariableInDDS(
const string& varName)
621 DDS* pDDS = getDDSForCurrentDataset();
630void NCMLParser::addCopyOfVariableAtCurrentScope(BaseType& varTemplate)
633 if (isNameAlreadyUsedAtCurrentScope(varTemplate.name())) {
634 THROW_NCML_PARSE_ERROR(
getParseLineNumber(),
"NCMLParser::addNewVariableAtCurrentScope:"
635 " Cannot add variable since a variable or attribute of the same name exists at current scope."
636 " Name= " + varTemplate.name());
640 if (!(isScopeCompositeVariable() || isScopeGlobal())) {
641 THROW_NCML_INTERNAL_ERROR(
642 "NCMLParser::addNewVariableAtCurrentScope: current scope not valid for adding variable. Scope="
643 + getTypedScopeString());
649 NCML_ASSERT_MSG(_pVar->is_constructor_type(),
"Expected _pVar is a container type!");
650 _pVar->add_var(&varTemplate);
655 "Adding new variable to DDS top level. Variable name=" << varTemplate.name() <<
" and typename=" << varTemplate.type_name() << endl);
656 DDS* pDDS = getDDSForCurrentDataset();
657 pDDS->add_var(&varTemplate);
661void NCMLParser::deleteVariableAtCurrentScope(
const string& name)
663 if (!(isScopeCompositeVariable() || isScopeGlobal())) {
664 THROW_NCML_INTERNAL_ERROR(
665 "NCMLParser::deleteVariableAtCurrentScope called when we do not have a variable container at current scope!");
671 Structure* pVarContainer =
dynamic_cast<Structure*
>(_pVar);
672 if (!pVarContainer) {
674 "NCMLParser::deleteVariableAtCurrentScope called with _pVar not a "
675 "Structure class variable! "
676 "We can only delete variables from top DDS or within a Structure now. scope="
677 + getTypedScopeString());
680 BaseType* pToBeNuked = pVarContainer->var(name);
683 "Tried to remove variable from a Structure, but couldn't find the variable with name=" + name
684 +
"at scope=" + getScopeString());
687 pVarContainer->del_var(name);
692 DDS* pDDS = getDDSForCurrentDataset();
699NCMLParser::getCurrentVariable()
const
704void NCMLParser::setCurrentVariable(BaseType* pVar)
709 setCurrentAttrTable(&(pVar->get_attr_table()));
711 else if (getDDSForCurrentDataset())
713 DDS* dds = getDDSForCurrentDataset();
714 setCurrentAttrTable(&(dds->get_attr_table()));
718 setCurrentAttrTable(0);
722bool NCMLParser::typeCheckDAPVariable(
const BaseType& var,
const string& expectedType)
725 if (expectedType.empty()) {
732 BaseType& varSemanticConst =
const_cast<BaseType&
>(var);
733 return varSemanticConst.is_constructor_type();
736 return (var.type_name() == expectedType);
742NCMLParser::getCurrentAttrTable()
const
747 return _pCurrentTable.
get();
750void NCMLParser::setCurrentAttrTable(AttrTable* pAT)
752 _pCurrentTable.
set(pAT);
756NCMLParser::getGlobalAttrTable()
const
759 DDS* pDDS = getDDSForCurrentDataset();
761 pAT = &(pDDS->get_attr_table());
766bool NCMLParser::attributeExistsAtCurrentScope(
const string& name)
const
769 AttrTable::Attr_iter attr;
770 bool foundIt = findAttribute(name, attr);
774bool NCMLParser::findAttribute(
const string& name, AttrTable::Attr_iter& attr)
const
776 AttrTable* pAT = getCurrentAttrTable();
778 attr = pAT->simple_find(name);
779 return (attr != pAT->attr_end());
786int NCMLParser::tokenizeAttrValues(vector<string>& tokens,
const string& values,
const string& dapAttrTypeName,
787 const string& separator)
790 AttrType dapType = String_to_AttrType(dapAttrTypeName);
791 if (dapType == Attr_unknown) {
793 "Attempting to tokenize attribute value failed since"
794 " we found an unknown internal DAP type=" + dapAttrTypeName
795 +
" for the current fully qualified attribute=" + _scope.
getScopeString());
799 int numTokens = tokenizeValuesForDAPType(tokens, values, dapType, separator);
800 if (numTokens == 0 && ((dapType == Attr_string) || (dapType == Attr_url) || (dapType == Attr_other_xml))) {
801 tokens.push_back(
"");
807#if DEBUG_NCML_PARSER_INTERNALS
810 BESDEBUG(
"ncml",
"Got non-default separators for tokenize. separator=\"" << separator <<
"\"" << endl);
814 for (
unsigned int i = 0; i < tokens.size(); i++) {
822 BESDEBUG(
"ncml",
"Tokenize got " << numTokens <<
" tokens:\n" << msg << endl);
829int NCMLParser::tokenizeValuesForDAPType(vector<string>& tokens,
const string& values, AttrType dapType,
830 const string& separator)
835 if (dapType == Attr_unknown) {
838 "Warning: tokenizeValuesForDAPType() got unknown DAP type! Attempting to continue..." << endl);
839 tokens.push_back(values);
842 else if (dapType == Attr_container) {
844 BESDEBUG(
"ncml",
"Warning: tokenizeValuesForDAPType() got container type, we should not have values!" << endl);
845 tokens.push_back(
"");
848 else if (dapType == Attr_string) {
867typedef std::map<string, string> TypeConverter;
871static const bool ALLOW_DAP_TYPES_AS_NCML_TYPES =
true;
889static TypeConverter* makeTypeConverter()
891 TypeConverter* ptc =
new TypeConverter();
892 TypeConverter& tc = *ptc;
895 tc[
"byte"] =
"Int16";
896 tc[
"short"] =
"Int16";
898 tc[
"long"] =
"Int32";
899 tc[
"float"] =
"Float32";
900 tc[
"double"] =
"Float64";
901 tc[
"string"] =
"String";
902 tc[
"String"] =
"String";
903 tc[
"Structure"] =
"Structure";
904 tc[
"structure"] =
"Structure";
908 if (ALLOW_DAP_TYPES_AS_NCML_TYPES) {
910 tc[
"Int16"] =
"Int16";
911 tc[
"UInt16"] =
"UInt16";
912 tc[
"Int32"] =
"Int32";
913 tc[
"UInt32"] =
"UInt32";
914 tc[
"Float32"] =
"Float32";
915 tc[
"Float64"] =
"Float64";
919 tc[
"OtherXML"] =
"OtherXML";
926static const TypeConverter& getTypeConverter()
928 static TypeConverter* singleton = 0;
930 singleton = makeTypeConverter();
937static bool isDAPType(
const string& type)
939 return (String_to_AttrType(type) != Attr_unknown);
949 NCML_ASSERT_MSG(!daType.empty(),
"Logic error: convertNcmlTypeToCanonicalType disallows empty() input.");
953 string daType = ncmlType;
957 const TypeConverter& tc = getTypeConverter();
958 TypeConverter::const_iterator it = tc.find(daType);
960 if (it == tc.end()) {
982 vector<string>::const_iterator it;
983 vector<string>::const_iterator endIt = tokens.end();
984 for (it = tokens.begin(); it != endIt; ++it) {
985 if (type ==
"Byte") {
986 valid &= check_byte(it->c_str());
988 else if (type ==
"Int16") {
989 valid &= check_int16(it->c_str());
991 else if (type ==
"UInt16") {
992 valid &= check_uint16(it->c_str());
994 else if (type ==
"Int32") {
995 valid &= check_int32(it->c_str());
997 else if (type ==
"UInt32") {
998 valid &= check_uint32(it->c_str());
1000 else if (type ==
"Float32") {
1001 valid &= check_float32(it->c_str());
1003 else if (type ==
"Float64") {
1004 valid &= check_float64(it->c_str());
1007 else if (type ==
"URL" || type ==
"Url" || type ==
"String") {
1010 valid &= (it->size() <= MAX_DAP_STRING_SIZE);
1012 std::stringstream msg;
1013 msg <<
"Invalid Value: The " << type <<
" attribute value (not shown) exceeded max string length of "
1014 << MAX_DAP_STRING_SIZE <<
" at scope=" << _scope.
getScopeString() << endl;
1021 "Invalid Value: The " + type +
" attribute value (not shown) has an invalid non-ascii character.");
1028 else if (type ==
"OtherXML") {
1034 THROW_NCML_INTERNAL_ERROR(
"checkDataIsValidForCanonicalType() got unknown data type=" + type);
1040 "Invalid Value given for type=" + type +
" with value=" + (*it)
1041 +
" was invalidly formed or out of range" + _scope.
getScopeString());
1047void NCMLParser::clearAllAttrTables(DDS* dds)
1054 dds->get_attr_table().erase();
1057 for (DDS::Vars_iter it = dds->var_begin(); it != dds->var_end(); ++it) {
1059 clearVariableMetadataRecursively(*it);
1063void NCMLParser::clearVariableMetadataRecursively(BaseType* var)
1067 var->get_attr_table().erase();
1069 if (var->is_constructor_type()) {
1070 Constructor *compositeVar =
dynamic_cast<Constructor*
>(var);
1071 if (!compositeVar) {
1072 THROW_NCML_INTERNAL_ERROR(
1073 "clearVariableMetadataRecursively: Unexpected cast error on dynamic_cast<Constructor*>");
1075 for (Constructor::Vars_iter it = compositeVar->var_begin(); it != compositeVar->var_end(); ++it) {
1076 clearVariableMetadataRecursively(*it);
1083 _scope.push(name, type);
1084 BESDEBUG(
"ncml",
"Entering scope: " << _scope.top().getTypedName() << endl);
1085 BESDEBUG(
"ncml",
"New scope=\"" << _scope.
getScopeString() <<
"\"" << endl);
1088void NCMLParser::exitScope()
1090 NCML_ASSERT_MSG(!_scope.
empty(),
"Logic Error: Scope Stack Underflow!");
1091 BESDEBUG(
"ncml",
"Exiting scope " << _scope.top().getTypedName() << endl);
1093 BESDEBUG(
"ncml",
"New scope=\"" << _scope.
getScopeString() <<
"\"" << endl);
1096void NCMLParser::printScope()
const
1098 BESDEBUG(
"ncml",
"Scope=\"" << _scope.
getScopeString() <<
"\"" << endl);
1101string NCMLParser::getScopeString()
const
1106string NCMLParser::getTypedScopeString()
const
1111int NCMLParser::getScopeDepth()
const
1113 return _scope.
size();
1115void NCMLParser::pushElement(NCMLElement* elt)
1118 _elementStack.push_back(elt);
1122void NCMLParser::popElement()
1124 NCMLElement* elt = _elementStack.back();
1125 _elementStack.pop_back();
1128 string infoOnDeletedDude = ((elt->getRefCount() == 1) ? (elt->toString()) : (
string(
"")));
1131 if (elt->unref() == 0) {
1132 BESDEBUG(
"ncml:memory",
1133 "NCMLParser::popElement: ref count hit 0 so we deleted element=" << infoOnDeletedDude << endl);
1138NCMLParser::getCurrentElement()
const
1140 if (_elementStack.empty()) {
1144 return _elementStack.back();
1148void NCMLParser::clearElementStack()
1150 while (!_elementStack.empty()) {
1151 NCMLElement* elt = _elementStack.back();
1152 _elementStack.pop_back();
1156 _elementStack.resize(0);
1159void NCMLParser::processStartNCMLElement(
const std::string& name,
const XMLAttributeMap& attrs)
1169 pushElement(elt.get());
1173 if (sThrowExceptionOnUnknownElements) {
1175 "Unknown element type=" + name +
" found in NcML parse with scope=" + _scope.
getScopeString());
1178 BESDEBUG(
"ncml",
"Start of <" << name <<
"> element. Element unsupported, ignoring." << endl);
1183void NCMLParser::processEndNCMLElement(
const std::string& name)
1185 NCMLElement* elt = getCurrentElement();
1189 if (elt->getTypeName() == name) {
1195 BESDEBUG(
"ncml",
"End of <" << name <<
"> element unsupported currently, ignoring." << endl);
1199const DimensionElement*
1200NCMLParser::getDimensionAtLexicalScope(
const string& dimName)
const
1202 const DimensionElement* ret = 0;
1203 if (getCurrentDataset()) {
1209string NCMLParser::printAllDimensionsAtLexicalScope()
const
1212 NetcdfElement* dataset = getCurrentDataset();
1214 ret += dataset->printDimensions();
1215 dataset = dataset->getParentDataset();
1220void NCMLParser::enterOtherXMLParsingState(OtherXMLParser* pOtherXMLParser)
1222 BESDEBUG(
"ncml",
"Entering state for parsing OtherXML!" << endl);
1223 _pOtherXMLParser = pOtherXMLParser;
1226bool NCMLParser::isParsingOtherXML()
const
1228 return _pOtherXMLParser;
1231void NCMLParser::cleanup()
Represents an OPeNDAP DAP response object within the BES.
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
static bool IsSet(const std::string &flagName)
see if the debug context flagName is set to true
virtual bool start(std::string name)
static libdap::BaseType * getVariableNoRecurse(const libdap::DDS &dds, const std::string &name)
static std::unique_ptr< BESDapResponse > makeResponseForType(ResponseType type)
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.
static bool checkResponseIsValidType(ResponseType type, BESDapResponse *pResponse)
void cleanup()
restore dhi to clean state
A reference to an RCObject which automatically ref() and deref() on creation and destruction.
RCPtr< NCMLElement > makeElement(const std::string &eltTypeName, const XMLAttributeMap &attrs, NCMLParser &parser)
Base class for NcML element concrete classes.
virtual void handleContent(const std::string &content)
virtual const std::string & getTypeName() const =0
virtual void onParseWarning(std::string msg)
virtual void onEndElement(const std::string &name)
const XMLNamespaceStack & getXMLNamespaceStack() const
virtual void onParseError(std::string msg)
int getParseLineNumber() const
virtual void onStartElement(const std::string &name, const XMLAttributeMap &attrs)
static string convertNcmlTypeToCanonicalType(const string &ncmlType)
virtual void onStartElementWithNamespace(const std::string &localname, const std::string &prefix, const std::string &uri, const XMLAttributeMap &attributes, const XMLNamespaceMap &namespaces)
void checkDataIsValidForCanonicalTypeOrThrow(const string &type, const vector< string > &tokens) const
Make sure the given tokens are valid for the listed type. For example, makes sure floats are well for...
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...
static const string STRUCTURE_TYPE
virtual void onEndElementWithNamespace(const std::string &localname, const std::string &prefix, const std::string &uri)
std::unique_ptr< BESDapResponse > parse(const std::string &ncmlFilename, agg_util::DDSLoader::ResponseType type)
Parse the NcML filename, returning a newly allocated DDS response containing the underlying dataset t...
virtual void onCharacters(const std::string &content)
NCMLParser(agg_util::DDSLoader &loader)
Create a structure that can parse an NCML filename and returned a transformed response of requested t...
virtual void setParseLineNumber(int line)
static int tokenize(const std::string &str, std::vector< std::string > &tokens, const std::string &delimiters=" \t")
static const std::string WHITESPACE
static void trimAll(std::vector< std::string > &tokens, const std::string &trimChars=WHITESPACE)
static bool isAscii(const std::string &str)
Concrete class for NcML <netcdf> element.
void unborrowResponseObject(BESDapResponse *pResponse)
const DimensionElement * getDimensionInFullScope(const std::string &name) const
virtual const libdap::DDS * getDDS() const
void borrowResponseObject(BESDapResponse *pResponse)
AggregationElement * getChildAggregation() const
virtual void onStartElementWithNamespace(const std::string &localname, const std::string &prefix, const std::string &uri, const XMLAttributeMap &attributes, const XMLNamespaceMap &namespaces)
int getParseDepth() const
virtual void onEndElement(const std::string &name)
virtual void onStartElement(const std::string &name, const XMLAttributeMap &attrs)
virtual void onCharacters(const std::string &content)
virtual void onEndElementWithNamespace(const std::string &localname, const std::string &prefix, const std::string &uri)
Wrapper for libxml SAX parser C callbacks into C++.
bool parse(const std::string &ncmlFilename)
Do a SAX parse of the ncmlFilename and pass the calls to wrapper parser.
std::string getTypedScopeString() const
std::string getScopeString() const
Helper class for temporarily hijacking an existing dhi to load a DDX response for one particular file...
NcML Parser for adding/modifying/removing metadata (attributes) to existing local datasets using NcML...