bes Updated for version 3.20.13
NetcdfElement.cc
1
2// This file is part of the "NcML Module" project, a BES module designed
3// to allow NcML files to be used to be used as a wrapper to add
4// AIS to existing datasets of any format.
5//
6// Copyright (c) 2009 OPeNDAP, Inc.
7// Author: Michael Johnson <m.johnson@opendap.org>
8//
9// For more information, please also see the main website: http://opendap.org/
10//
11// This library is free software; you can redistribute it and/or
12// modify it under the terms of the GNU Lesser General Public
13// License as published by the Free Software Foundation; either
14// version 2.1 of the License, or (at your option) any later version.
15//
16// This library is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19// Lesser General Public License for more details.
20//
21// You should have received a copy of the GNU Lesser General Public
22// License along with this library; if not, write to the Free Software
23// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24//
25// Please see the files COPYING and COPYRIGHT for more information on the GLPL.
26//
27// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
29
30#include "config.h"
31
32#include <libdap/BaseType.h> // libdap
33#include <BESDapResponse.h> // bes
34
35#include "AggMemberDataset.h" // agg_util
36#include "AggMemberDatasetDDSWrapper.h" // agg_util
37#include "AggMemberDatasetSharedDDSWrapper.h" // agg_util
38#include "AggMemberDatasetUsingLocationRef.h" // agg_util
39#include "AggregationElement.h"
40#include "DimensionElement.h"
41#include "NetcdfElement.h"
42#include "NCMLDebug.h"
43#include "NCMLParser.h"
44#include "NCMLUtil.h"
45#include "VariableElement.h"
46
47#include <assert.h>
48#include <sstream>
49
50using namespace std;
51using libdap::DDS;
52using namespace agg_util;
53
54namespace ncml_module {
55
56const string NetcdfElement::_sTypeName = "netcdf";
57const vector<string> NetcdfElement::_sValidAttributes = getValidAttributes();
58
59NetcdfElement::NetcdfElement() :
60 NCMLElement(0), _location(""), _id(""), _title(""), _ncoords(""), _enhance(""), _addRecords(""), _coordValue(""), _fmrcDefinition(
61 ""), _gotMetadataDirective(false), _weOwnResponse(false), _loaded(false), _response(0), _aggregation(0), _parentAgg(
62 0), _dimensions(), _variableValidator(this)
63{
64}
65
66NetcdfElement::NetcdfElement(const NetcdfElement& proto) :
67 RCObjectInterface(), DDSAccessInterface(), DDSAccessRCInterface(), NCMLElement(proto), _location(proto._location), _id(
68 proto._id), _title(proto._title), _ncoords(proto._ncoords), _enhance(proto._enhance), _addRecords(
69 proto._addRecords), _coordValue(proto._coordValue), _fmrcDefinition(proto._fmrcDefinition), _gotMetadataDirective(
70 false), _weOwnResponse(false), _loaded(false), _response(0), _aggregation(0), _parentAgg(0) // we can't really set this to the proto one or we break an invariant...
71 , _dimensions(), _variableValidator(this) // start it empty rather than copy to avoid ref counting errors...
72{
73 // we can't copy the proto response object... I'd say just don't allow this.
74 // if it's needed later, we can implement a proper full clone on a DDS, but the
75 // current one is buggy.
76 if (proto._response) {
77 THROW_NCML_INTERNAL_ERROR("Can't clone() a NetcdfElement that contains a response!");
78 }
79
80 // Yuck clone the whole aggregation.
81 if (proto._aggregation.get()) {
82 setChildAggregation(proto._aggregation.get()->clone());
83 }
84
85 // Deep copy the dimension table so they don't side effect each other...
86 vector<DimensionElement*>::const_iterator endIt = proto._dimensions.end();
87 vector<DimensionElement*>::const_iterator it;
88 for (it = proto._dimensions.begin(); it != endIt; ++it) {
89 DimensionElement* elt = *it;
90 addDimension(elt->clone());
91 }
92}
93
94NetcdfElement::~NetcdfElement()
95{
96 BESDEBUG("ncml:memory", "~NetcdfElement called...");
97 // Only if its ours do we nuke it.
98 if (_weOwnResponse) {
99 SAFE_DELETE(_response);
100 }
101 _response = 0; // but null it in all cases.
102 _parentAgg = 0;
103 clearDimensions();
104
105 // _aggregation dtor will take care of the ref itself.
106}
107
108const string&
109NetcdfElement::getTypeName() const
110{
111 return _sTypeName;
112}
113
115NetcdfElement::clone() const
116{
117 return new NetcdfElement(*this);
118}
119
120void NetcdfElement::setAttributes(const XMLAttributeMap& attrs)
121{
122 // Make sure they exist in the schema, even if we don't support them.
123 validateAttributes(attrs, _sValidAttributes);
124
125 // set them
126 _location = attrs.getValueForLocalNameOrDefault("location");
127 _id = attrs.getValueForLocalNameOrDefault("id");
128 _title = attrs.getValueForLocalNameOrDefault("title");
129 _enhance = attrs.getValueForLocalNameOrDefault("enhance");
130 _addRecords = attrs.getValueForLocalNameOrDefault("addRecords");
131 // Aggregation children only below!
132 _ncoords = attrs.getValueForLocalNameOrDefault("ncoords");
133 _coordValue = attrs.getValueForLocalNameOrDefault("coordValue");
134 _fmrcDefinition = attrs.getValueForLocalNameOrDefault("fmrcDefinition");
135
136 throwOnUnsupportedAttributes();
137}
138
139void NetcdfElement::handleBegin()
140{
141 BESDEBUG("ncml", "NetcdfElement::handleBegin on " << toString() << endl);
142 NCMLParser& p = *_parser;
143
144 // Make sure that we are in an AggregationElement if we're not root!
145 if (p.getRootDataset() && !p.isScopeAggregation()) {
146 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
147 "Got a nested <netcdf> element which was NOT a direct child of an <aggregation>!");
148 }
149
150 // Tell the parser we got a new current dataset.
151 // If this is the root, it also needs to set up our response!!
152 p.pushCurrentDataset(this);
153
154 // Make sure the attributes that are set are valid for context
155 // that we just pushed.
156 validateAttributeContextOrThrow();
157}
158
159void NetcdfElement::handleContent(const string& content)
160{
161 if (!NCMLUtil::isAllWhitespace(content)) {
162 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
163 "Got non-whitespace for element content and didn't expect it. Element=" + toString() + " content=\""
164 + content + "\"");
165 }
166}
167
168void NetcdfElement::handleEnd()
169{
170 BESDEBUG("ncml", "NetcdfElement::handleEnd called!" << endl);
171
172 if (!_parser->isScopeNetcdf()) {
173 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(), "Got close of <netcdf> node while not within one!");
174 }
175
176 // If we had an aggregation, we must tell it to finish any post-processing that
177 // it needs to do to make the aggregations correct (like add Grid map vectors
178 // for new dimensions)
179 if (_aggregation.get()) {
180 _aggregation->processParentDatasetComplete();
181 }
182
183 // At this point, any deferred new variables MUST have their values set
184 // to preserve an invariant (and avoid cryptic internal error in libdap).
185 // So validate any deferred new variables now:
186 _variableValidator.validate(); // throws parse error if failure....
187
188 // Tell the parser to close the current dataset and figure out what the new current one is!
189 // We pass our ptr to make sure that we're the current one to avoid logic bugs!!
190 _parser->popCurrentDataset(this);
191
192 // We'll leave our element table around until we're destroyed since people are allowed to use it if they
193 // maintained a ref to us....
194}
195
196string NetcdfElement::toString() const
197{
198 return "<" + _sTypeName + " " + "location=\"" + _location + "\""
199 + // always print this one even in empty.
200 printAttributeIfNotEmpty("id", _id) + printAttributeIfNotEmpty("title", _title)
201 + printAttributeIfNotEmpty("enhance", _enhance) + printAttributeIfNotEmpty("addRecords", _addRecords)
202 + printAttributeIfNotEmpty("ncoords", _ncoords) + printAttributeIfNotEmpty("coordValue", _coordValue)
203 + printAttributeIfNotEmpty("fmrcDefinition", _fmrcDefinition) + ">";
204}
205
206const libdap::DDS*
207NetcdfElement::getDDS() const
208{
209 return const_cast<NetcdfElement*>(this)->getDDS();
210}
211
212libdap::DDS*
213NetcdfElement::getDDS()
214{
215 // lazy eval loading the dds
216 if (!_loaded) {
217 BESDEBUG("ncml", "Lazy loading DDX for location=" << location() << endl);
218 loadLocation();
219 }
220
221 if (_response) {
222 return NCMLUtil::getDDSFromEitherResponse(_response);
223 }
224 else {
225 return 0;
226 }
227}
228
229bool NetcdfElement::isValid() const
230{
231 // Right now we need these ptrs valid to be ready...
232 // Technically handleBegin() sets the parser,
233 // so we're not ready until after that has successfully completed.
234 return _response && _parser;
235}
236
237unsigned int NetcdfElement::getNcoordsAsUnsignedInt() const
238{
239 NCML_ASSERT_MSG(hasNcoords(),
240 "NetcdfElement::getNCoords(): called illegally when no ncoords attribute was specified!");
241 unsigned int num = 0;
242 if (!NCMLUtil::toUnsignedInt(_ncoords, num)) {
243 THROW_NCML_PARSE_ERROR(line(), "A <netcdf> element has an invalid ncoords attribute set. Bad value was:"
244 "\"" + _ncoords + "\"");
245 }
246 return num;
247}
248
249void NetcdfElement::borrowResponseObject(BESDapResponse* pResponse)
250{
251 NCML_ASSERT_MSG(!_response, "_response object should be NULL for proper logic of borrowResponseObject call!");
252 _response = pResponse;
253 _weOwnResponse = false;
254}
255
256void NetcdfElement::unborrowResponseObject(BESDapResponse* pResponse)
257{
258 NCML_ASSERT_MSG(pResponse == _response,
259 "NetcdfElement::unborrowResponseObject() called with a response we are not borrowing.");
260 _response = 0;
261}
262
263void NetcdfElement::createResponseObject(DDSLoader::ResponseType type)
264{
265 if (_response) {
266 THROW_NCML_INTERNAL_ERROR(
267 "NetcdfElement::createResponseObject(): Called when we already had a _response! Logic error!");
268 }
269
270 VALID_PTR(_parser);
271
272 // Make a new response and store the raw ptr, noting that we need to delete it in dtor.
273 std::unique_ptr<BESDapResponse> newResponse = _parser->getDDSLoader().makeResponseForType(type);
274 VALID_PTR(newResponse.get());
275 _response = newResponse.release();
276 _weOwnResponse = true;
277}
278
279RCPtr<agg_util::AggMemberDataset> NetcdfElement::getAggMemberDataset() const
280{
281 // If not created yet, make a new
282 // one in an RCPtr, store a weak ref,
283 // and return it.
285 if (_pDatasetWrapper.empty()) {
286 if (_location.empty()) {
287 // if the location is empty, we must assume we have a valid DDS
288 // that has been created virtually or as a nested aggregation
289 // We create a wrapper for the NetcdfElement in this case
290 // using the ref-counted DDS accessor interface.
291 const DDSAccessRCInterface* pDDSHolder = this;
293 }
294 else {
295 pAGM = new AggMemberDatasetUsingLocationRef(_location, _parser->getDDSLoader());
296 }
297
298 VALID_PTR(pAGM.get());
299
300 // Make a weak ref to it, semantically const
301 const_cast<NetcdfElement*>(this)->_pDatasetWrapper = WeakRCPtr<AggMemberDataset>(pAGM.get());
302 }
303
304 // Either way the weak ref is valid here, so return a new RCPtr to it
305 NCML_ASSERT(!_pDatasetWrapper.empty());
306 return _pDatasetWrapper.lock();
307}
308
309const DimensionElement*
310NetcdfElement::getDimensionInLocalScope(const string& name) const
311{
312 const DimensionElement* ret = 0;
313 vector<DimensionElement*>::const_iterator endIt = _dimensions.end();
314 for (vector<DimensionElement*>::const_iterator it = _dimensions.begin(); it != endIt; ++it) {
315 const DimensionElement* pElt = *it;
316 VALID_PTR(pElt);
317 if (pElt->name() == name) {
318 ret = pElt;
319 break;
320 }
321 }
322 return ret;
323}
324
325const DimensionElement*
326NetcdfElement::getDimensionInFullScope(const string& name) const
327{
328 // Base case...
329 const DimensionElement* elt = 0;
330 elt = getDimensionInLocalScope(name);
331 if (!elt) {
332 NetcdfElement* parentDataset = getParentDataset();
333 if (parentDataset) {
334 elt = parentDataset->getDimensionInFullScope(name);
335 }
336 }
337 return elt;
338}
339
340void NetcdfElement::addDimension(DimensionElement* dim)
341{
342 VALID_PTR(dim);
343 if (getDimensionInLocalScope(dim->name())) {
344 THROW_NCML_INTERNAL_ERROR(
345 "NCMLParser::addDimension(): already found dimension with name while adding " + dim->toString());
346 }
347
348 _dimensions.push_back(dim);
349 dim->ref(); // strong reference!
350
351 BESDEBUG("ncml", "Added dimension to dataset. Dimension Table is now: " << printDimensions() << endl);
352}
353
354string NetcdfElement::printDimensions() const
355{
356 string ret = "Dimensions = {\n";
357 vector<DimensionElement*>::const_iterator endIt = _dimensions.end();
358 vector<DimensionElement*>::const_iterator it;
359 for (it = _dimensions.begin(); it != endIt; ++it) {
360 ret += (*it)->toString() + "\n";
361 }
362 ret += "}";
363 return ret;
364}
365
366void NetcdfElement::clearDimensions()
367{
368 while (!_dimensions.empty()) {
369 DimensionElement* elt = _dimensions.back();
370 elt->unref();
371 _dimensions.pop_back();
372 }
373 _dimensions.resize(0);
374}
375
376const std::vector<DimensionElement*>&
377NetcdfElement::getDimensionElements() const
378{
379 return _dimensions;
380}
381
382void NetcdfElement::setChildAggregation(AggregationElement* agg, bool throwIfExists/*=true*/)
383{
384 if (_aggregation.get() && throwIfExists) {
385 THROW_NCML_INTERNAL_ERROR(
386 "NetcdfElement::setAggregation: We were called but we already contain a non-NULL aggregation! Previous="
387 + _aggregation->toString() + " and the new one is: " + agg->toString());
388 }
389
390 // Otherwise, we can just set it and rely on the smart pointer to do the right thing
391 _aggregation = agg; // this will implicitly convert the raw ptr to a smart ptr
392
393 // Also set a weak reference to this as the parent of the aggregation for walking up the tree...
394 _aggregation->setParentDataset(this);
395}
396
398NetcdfElement::getParentDataset() const
399{
400 NetcdfElement* ret = 0;
401 if (_parentAgg && _parentAgg->getParentDataset()) {
402 ret = _parentAgg->getParentDataset();
403 }
404 return ret;
405}
406
408NetcdfElement::getParentAggregation() const
409{
410 return _parentAgg;
411}
412
413void NetcdfElement::setParentAggregation(AggregationElement* parent)
414{
415 _parentAgg = parent;
416}
417
419NetcdfElement::getChildAggregation() const
420{
421 return _aggregation.get();
422}
423
424#if 0 // not sure we need this yet...
425template <typename T>
426int
427NetcdfElement::getCoordValueVector(vector<T>& values) const
428{
429 // clear it out just in case.
430 values.resize(0);
431
432 // first, tokenize into strings
433 vector<string> tokens;
434 int numToks = NCMLUtil::tokenize(_coordValue, tokens, NCMLUtil::WHITESPACE);
435 values.reserve(numToks);
436
437 for (int i=0; i<numToks; ++i)
438 {
439 stringstream iss(tokens[i]);
440 T val;
441 iss >> val;
442 if (iss.fail())
443 {
444 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
445 "NetcdfElement::getCoordValueVector(): "
446 "Could not parse the token \"" + tokens[i] + "\""
447 " into the required data type.");
448 }
449 else
450 {
451 values.push_back(val);
452 }
453 }
454}
455#endif
456
457bool NetcdfElement::getCoordValueAsDouble(double& val) const
458{
459 if (_coordValue.empty()) {
460 return false;
461 }
462
463 std::istringstream iss(_coordValue);
464 double num;
465 iss >> num;
466 // eof() to make sure we parsed it all. >> can stop early on malformedness.
467 if (iss.fail() || !iss.eof()) {
468 return false;
469 }
470 else {
471 val = num;
472 return true;
473 }
474}
475
476void NetcdfElement::addVariableToValidateOnClose(libdap::BaseType* pNewVar, VariableElement* pVE)
477{
478 _variableValidator.addVariableToValidate(pNewVar, pVE);
479}
480
481void NetcdfElement::setVariableGotValues(libdap::BaseType* pVarToValidate, bool removeEntry)
482{
483 _variableValidator.setVariableGotValues(pVarToValidate);
484 if (removeEntry) {
485 _variableValidator.removeVariableToValidate(pVarToValidate);
486 }
487}
488
489/********** Class Methods ***********/
490
491bool NetcdfElement::isLocationLexicographicallyLessThan(const NetcdfElement* pLHS, const NetcdfElement* pRHS)
492{
493 // args should never be null, but let's catch the error in dev if we goof.
494 // I don't want to use VALID_PTR here to avoid exception overhead in an internal
495 // sort function.
496 assert(pLHS);
497 assert(pRHS);
498 return (pLHS->location().compare(pRHS->location()) < 0);
499}
500
501bool NetcdfElement::isCoordValueLexicographicallyLessThan(const NetcdfElement* pLHS, const NetcdfElement* pRHS)
502{
503 // args should never be null, but let's catch the error in dev if we goof.
504 // I don't want to use VALID_PTR here to avoid exception overhead in an internal
505 // sort function.
506 assert(pLHS);
507 assert(pRHS);
508 return (pLHS->coordValue().compare(pRHS->coordValue()) < 0);
509}
510
513
514void NetcdfElement::loadLocation()
515{
516 if (_location.empty()) {
517 // nothing to load, so we're loaded I guess.
518 _loaded = true;
519 return;
520 }
521
522 // We better have one! This gets created up front now. It will be an empty DDS
523 NCML_ASSERT_MSG(_response,
524 "NetcdfElement::loadLocation(): Requires a valid _response via borrowResponseObject() or createResponseObject() prior to call!");
525
526 // Use the loader to load the location
527 // If not found, this call will throw an exception and we'll just unwind out.
528 if (_parser) {
529 _parser->loadLocation(_location, _parser->_responseType, _response);
530 _loaded = true;
531 }
532}
533
534void NetcdfElement::throwOnUnsupportedAttributes()
535{
536 const string msgStart = "NetcdfElement: unsupported attribute: ";
537 const string msgEnd = " was declared.";
538 if (!_enhance.empty()) {
539 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(), msgStart + "enhance" + msgEnd);
540 }
541 if (!_addRecords.empty()) {
542 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(), msgStart + "addRecords" + msgEnd);
543 }
544
545 // not until fmrc
546 if (!_fmrcDefinition.empty()) {
547 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(), msgStart + "fmrcDefinition" + msgEnd);
548 }
549}
550
551bool NetcdfElement::validateAttributeContextOrThrow() const
552{
553 if (hasNcoords()) {
554 AggregationElement* pParentAgg = getParentAggregation();
555 if (!pParentAgg || !(pParentAgg->isJoinExistingAggregation())) {
556 THROW_NCML_PARSE_ERROR(line(), "Cannot specify netcdf@ncoords attribute while not within a "
557 "joinExisting aggregation!");
558 }
559 }
560 return true;
561}
562
563vector<string> NetcdfElement::getValidAttributes()
564{
565 vector<string> validAttrs;
566 validAttrs.push_back("schemaLocation");
567 validAttrs.push_back("location");
568 validAttrs.push_back("id");
569 validAttrs.push_back("title");
570 validAttrs.push_back("enhance");
571 validAttrs.push_back("addRecords");
572 // following only valid inside aggregation, will need to test for that later.
573 validAttrs.push_back("ncoords");
574 validAttrs.push_back("coordValue");
575 validAttrs.push_back("fmrcDefinition");
576
577 return validAttrs;
578}
579
580/******************** Inner Class Def of VariableValueValidator *********************/
581
582typedef vector<NetcdfElement::VariableValueValidator::VVVEntry> VVVEntries;
583
584NetcdfElement::VariableValueValidator::VariableValueValidator(NetcdfElement* pParent) :
585 _entries(), _pParent(pParent)
586{
587}
588
590{
591 // For each entry, deref the VariableElement and clear it out.
592 VVVEntries::iterator it;
593 VVVEntries::iterator endIt = _entries.end();
594 for (it = _entries.begin(); it != endIt; ++it) {
595 VVVEntry& entry = *it;
596 entry._pVarElt->unref();
597 entry.clear();
598 }
599 _entries.resize(0);
600}
601
603{
604 VALID_PTR(pNewVar);
605 VALID_PTR(pVE);
606 {
607 VVVEntry* pExisting = findEntryByLibdapVar(pNewVar);
608 NCML_ASSERT_MSG(!pExisting,
609 "NetcdfElement::VariableValueValidator::addVariableToValidate: var was already added!");
610 }
611
612 // Up the ref count to keep it around and add an entry for it
613 pVE->ref();
614 _entries.push_back(VVVEntry(pNewVar, pVE));
615}
616
618{
619 for (unsigned int i = 0; i < _entries.size(); ++i) {
620 if (_entries[i]._pNewVar == pVarToRemove) {
621 _entries[i]._pVarElt->unref();
622 _entries[i] = _entries[_entries.size() - 1];
623 _entries.pop_back();
624 break;
625 }
626 }
627}
628
630{
631 VALID_PTR(pVarToValidate);
632 VVVEntry* pEntry = findEntryByLibdapVar(pVarToValidate);
633 NCML_ASSERT_MSG(pEntry,
634 "NetcdfElement::VariableValueValidator::setVariableGotValues: expected to find the var name="
635 + pVarToValidate->name() + " but we did not!");
636 pEntry->_pVarElt->setGotValues();
637}
638
641{
642 return _variableValidator.findVariableElementForLibdapVar(pNewVar);
643}
644
646{
647 VVVEntries::iterator it;
648 VVVEntries::iterator endIt = _entries.end();
649 for (it = _entries.begin(); it != endIt; ++it) {
650 VVVEntry& entry = *it;
651 // If the values was never set...
652 if (!entry._pVarElt->checkGotValues()) {
653 THROW_NCML_PARSE_ERROR(_pParent->line(),
654 "On closing the <netcdf> element, we found a new variable name=" + entry._pNewVar->name()
655 + " that was added"
656 " to the dataset but which never had values set on it. This is illegal!"
657 " Please make sure all variables in this dataset have values set on them"
658 " or that they are new coordinate variables for a joinNew aggregation.");
659 }
660 }
661 return true;
662}
663
665NetcdfElement::VariableValueValidator::findEntryByLibdapVar(libdap::BaseType* pVarToFind)
666{
667 VALID_PTR(pVarToFind);
668 VVVEntry* pRetVal = 0;
669 VVVEntries::iterator it;
670 VVVEntries::iterator endIt = _entries.end();
671 for (it = _entries.begin(); it != endIt; ++it) {
672 VVVEntry& entry = *it;
673 if (entry._pNewVar == pVarToFind) {
674 pRetVal = &entry;
675 break;
676 }
677 }
678 return pRetVal;
679}
680
681VariableElement*
683{
684 VALID_PTR(pVarToFind);
685 VVVEntry* pRetVal = findEntryByLibdapVar(pVarToFind);
686 if (pRetVal) {
687 return pRetVal->_pVarElt;
688 }
689 else {
690 return 0;
691 }
692}
693
694} // namespace ncml_module
Represents an OPeNDAP DAP response object within the BES.
virtual int ref() const
Definition: RCObject.cc:71
virtual int unref() const
Definition: RCObject.cc:78
A reference to an RCObject which automatically ref() and deref() on creation and destruction.
Definition: RCObject.h:284
NetcdfElement * setParentDataset(NetcdfElement *parent)
Private Impl.
virtual std::string toString() const
void removeVariableToValidate(libdap::BaseType *pVarToRemove)
void setVariableGotValues(libdap::BaseType *pVarToValidate)
void addVariableToValidate(libdap::BaseType *pNewVar, VariableElement *pVE)
VariableElement * findVariableElementForLibdapVar(libdap::BaseType *pNewVar)
Concrete class for NcML <netcdf> element.
Definition: NetcdfElement.h:64
const DimensionElement * getDimensionInFullScope(const std::string &name) const
VariableElement * findVariableElementForLibdapVar(libdap::BaseType *pNewVar)
NetcdfElement * getParentDataset() const
Concrete class for NcML <variable> element.
const std::string getValueForLocalNameOrDefault(const std::string &localname, const std::string &defVal="") const
Definition: XMLHelpers.cc:181
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...