33#include <libdap/Array.h>
34#include <libdap/BaseType.h>
35#include <libdap/Structure.h>
36#include <libdap/Grid.h>
37#include <libdap/dods-limits.h>
40#include "MyBaseTypeFactory.h"
41#include "NCMLBaseArray.h"
43#include "NCMLParser.h"
45#include "NetcdfElement.h"
46#include "RenamedArrayWrapper.h"
47#include "AggregationElement.h"
48#include "DimensionElement.h"
50#include "VariableElement.h"
58const string VariableElement::_sTypeName =
"variable";
59const vector<string> VariableElement::_sValidAttributes = getValidAttributes();
61VariableElement::VariableElement() :
62 RCObjectInterface(), NCMLElement(0), _name(
""), _type(
""), _shape(
""), _orgName(
""), _shapeTokens(), _pNewlyCreatedVar(
67VariableElement::VariableElement(
const VariableElement& proto) :
68 RCObjectInterface(), NCMLElement(proto)
72 _shape = proto._shape;
73 _orgName = proto._orgName;
74 _shapeTokens = proto._shapeTokens;
75 _pNewlyCreatedVar = 0;
79VariableElement::~VariableElement()
82 _shapeTokens.resize(0);
87VariableElement::getTypeName()
const
93VariableElement::clone()
const
100 validateAttributes(attrs, _sValidAttributes);
108void VariableElement::handleBegin()
111 processBegin(*_parser);
114void VariableElement::handleContent(
const string& content)
117 if (!NCMLUtil::isAllWhitespace(content)) {
118 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
119 "Got non-whitespace for element content and didn't expect it. "
120 "Element='" + toString() +
"' content=\"" + content +
"\"");
124void VariableElement::handleEnd()
126 processEnd(*_parser);
129string VariableElement::toString()
const
131 return "<" + _sTypeName +
" name=\"" + _name +
"\"" +
" type=\"" + _type +
"\""
132 + ((!_shape.empty()) ? (
" shape=\"" + _shape +
"\"") : (
""))
133 + ((!_orgName.empty()) ? (
" orgName=\"" + _orgName +
"\"") : (
"")) +
">";
136bool VariableElement::isNewVariable()
const
138 return _pNewlyCreatedVar;
141bool VariableElement::checkGotValues()
const
146void VariableElement::setGotValues()
153void VariableElement::processBegin(
NCMLParser& p)
155 BESDEBUG(
"ncml",
"VariableElement::handleBegin called for " << toString() << endl);
157 if (!p.withinNetcdf()) {
158 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
159 "Got element '" + toString() +
"' while not in <netcdf> node!");
163 if (!(p.isScopeGlobal() || p.isScopeCompositeVariable())) {
164 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
165 "Got <variable> element while not within a <netcdf> or within variable container. scope='"
166 + p.getScopeString() +
"'");
170 if (!_orgName.empty()) {
171 processRenameVariable(p);
177 BaseType* pVar = p.getVariableInCurrentVariableContainer(_name);
179 processNewVariable(p);
182 processExistingVariable(p, pVar);
187void VariableElement::processEnd(NCMLParser& p)
189 BESDEBUG(
"ncml",
"VariableElement::handleEnd called at scope: '" << p.getScopeString() <<
"'" << endl);
190 if (!p.isScopeVariable()) {
191 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
192 "VariableElement::handleEnd called when not parsing a variable element! "
193 "Scope='" + p.getTypedScopeString()+
"'");
206 if (isNewVariable() && !checkGotValues()) {
208 "WARNING: at parse line: " << _parser->getParseLineNumber() <<
" the newly created variable='" << toString() <<
"' did not have its values set! We will assume this is a placeholder variable"
209 " for an aggregation (such as the new outer dimension of a joinNew) and will"
210 " defer checking that required values are set until the point when this "
211 " netcdf element is closed..." " Scope='" << p.getScopeString() <<
"'" << endl);
213 "Adding new variable name='" << _pNewlyCreatedVar->name() <<
"' to the validation watch list for the closing of this netcdf." << endl);
214 _parser->getCurrentDataset()->addVariableToValidateOnClose(_pNewlyCreatedVar,
this);
217 NCML_ASSERT_MSG(p.getCurrentVariable(),
218 "Error: VariableElement::handleEnd(): Expected non-null parser.getCurrentVariable()!");
223 BaseType* pVar = p.getCurrentVariable();
224 BESDEBUG(
"ncml",
"Variable scope now with name: " << ((pVar)?(pVar->name()):(
"<NULL>")) << endl);
227void VariableElement::processExistingVariable(NCMLParser& p, BaseType* pVar)
230 "VariableElement::processExistingVariable() called with name='" << _name <<
"' at scope='" << p.getTypedScopeString() <<
"'" << endl);
234 pVar = p.getVariableInCurrentVariableContainer(_name);
245 if (!_type.empty() && !p.typeCheckDAPVariable(*pVar, p.convertNcmlTypeToCanonicalType(_type))) {
246 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
247 "Type Mismatch in variable element with name='" + _name +
"' at scope='" + p.getScopeString()
248 +
"' Expected type='" + _type +
"' but found variable with type='" + pVar->type_name()
249 +
"' In order to match a variable of any type, please do not specify variable@type attribute in your NcML file.");
264void VariableElement::processRenameVariableDataWorker(NCMLParser& p, BaseType* pOrgVar)
267 if (pOrgVar->is_vector_type()) {
271 pOrgVar = replaceArrayIfNeeded(p, pOrgVar, _name);
273 else if (pOrgVar->is_constructor_type()) {
278 else if (pOrgVar->is_simple_type()) {
285 NCMLUtil::setVariableNameProperly(pOrgVar, _name);
289void VariableElement::processRenameVariable(NCMLParser& p)
292 "VariableElement::processRenameVariable() called on '" + toString() <<
"' at scope='" << p.getTypedScopeString() <<
"'"<< endl);
295 NCML_ASSERT_MSG(!_name.empty(),
"Can't have an empty variable@name if variable@orgName is specified!");
296 NCML_ASSERT(!_orgName.empty());
299 BESDEBUG(
"ncml",
"Looking up the existence of a variable with name=" << _orgName <<
"..." <<endl);
300 BaseType* pOrgVar = p.getVariableInCurrentVariableContainer(_orgName);
302 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
303 "Renaming variable failed for element='" + toString() +
"' since no variable with orgName='" + _orgName
304 +
"' exists at current parser scope='" + p.getScopeString()+
"'");
306 BESDEBUG(
"ncml",
"Found variable with name=" << _orgName << endl);
309 BESDEBUG(
"ncml",
"Making sure new name=" << _name <<
" does not exist at this scope already..." << endl);
310 BaseType* pExisting = p.getVariableInCurrentVariableContainer(_name);
312 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
313 "Renaming variable failed for element='" + toString() +
"' since a variable with name='" + _name
314 +
"' already exists at current parser scope='" + p.getScopeString()+
"'");
316 BESDEBUG(
"ncml",
"Success, new variable name is open at this scope." << endl);
319 BESDEBUG(
"ncml",
"Renaming variable '" << _orgName <<
"' to '" << _name <<
"'" << endl);
335 if (p.parsingDataRequest()) {
339 bool send_p_was_set =
false;
340 if (!pOrgVar->send_p()) {
344 pOrgVar->set_send_p(
true);
346 send_p_was_set =
true;
351 if (send_p_was_set) {
359 pOrgVar->set_send_p(
false);
363 unique_ptr<BaseType> pCopy = unique_ptr<BaseType>(pOrgVar->ptr_duplicate());
364 pCopy->set_name(_name);
365 if (pCopy->type() == libdap::dods_grid_c)
dynamic_cast<libdap::Grid*
>(pCopy.get())->array_var()->set_name(_name);
368 p.deleteVariableAtCurrentScope(pOrgVar->name());
371 NetcdfElement* pCdf =
dynamic_cast<NetcdfElement*
>(p.getCurrentDataset());
372 if (pCdf->getChildAggregation()) {
373 AggregationElement* pAgg = pCdf->getChildAggregation();
374 pAgg->addAggregationVariable(_name);
377 p.addCopyOfVariableAtCurrentScope(*pCopy);
380 unique_ptr<BaseType> pCopy = unique_ptr<BaseType>(pOrgVar->ptr_duplicate());
381 pCopy->set_name(_name);
382 if (pCopy->type() == libdap::dods_grid_c)
dynamic_cast<libdap::Grid*
>(pCopy.get())->array_var()->set_name(_name);
385 p.deleteVariableAtCurrentScope(pOrgVar->name());
388 NetcdfElement* pCdf =
dynamic_cast<NetcdfElement*
>(p.getCurrentDataset());
389 if (pCdf->getChildAggregation()) {
390 AggregationElement* pAgg = pCdf->getChildAggregation();
391 pAgg->addAggregationVariable(_name);
394 p.addCopyOfVariableAtCurrentScope(*pCopy);
401 unique_ptr<BaseType> pCopy = unique_ptr<BaseType>(pOrgVar->ptr_duplicate());
403 pCopy->set_name(_name);
404 if (pCopy->type() == libdap::dods_grid_c)
405 dynamic_cast<libdap::Grid*
>(pCopy.get())->array_var()->set_name(_name);
408 p.deleteVariableAtCurrentScope(pOrgVar->name());
411 NetcdfElement* pCdf =
dynamic_cast<NetcdfElement*
>(p.getCurrentDataset());
412 if (pCdf->getChildAggregation()) {
413 AggregationElement* pAgg = pCdf->getChildAggregation();
414 pAgg->addAggregationVariable(_name);
417 p.addCopyOfVariableAtCurrentScope(*pCopy);
426 if (p.parsingDataRequest()) {
428 if (pOrgVar->type() != libdap::dods_array_c ) {
430 bool send_p_was_set =
false;
431 if (!pOrgVar->send_p()) {
435 pOrgVar->set_send_p(
true);
437 send_p_was_set =
true;
442 if (send_p_was_set) {
450 pOrgVar->set_send_p(
false);
457 pOrgVar = replaceArrayIfNeeded(p, pOrgVar, _name);
460 pOrgVar->set_name(_name);
468 if (pOrgVar->type() == libdap::dods_grid_c)
dynamic_cast<libdap::Grid*
>(pOrgVar)->array_var()->set_name(_name);
473 pOrgVar = replaceArrayIfNeeded(p, pOrgVar, _name);
489 unique_ptr<BaseType> pCopy = unique_ptr<BaseType>(pOrgVar->ptr_duplicate());
491 NCMLUtil::setVariableNameProperly(pCopy.get(), _name);
493 pCopy->set_name(_name);
494 if (pCopy->type() == libdap::dods_grid_c)
dynamic_cast<libdap::Grid*
>(pCopy.get())->array_var()->set_name(_name);
497 p.deleteVariableAtCurrentScope(pOrgVar->name());
500 NetcdfElement* pCdf =
dynamic_cast<NetcdfElement*
>(p.getCurrentDataset());
501 if (pCdf->getChildAggregation()) {
502 AggregationElement* pAgg = pCdf->getChildAggregation();
503 pAgg->addAggregationVariable(_name);
506 p.addCopyOfVariableAtCurrentScope(*pCopy);
512 BaseType* pRenamedVar = p.getVariableInCurrentVariableContainer(_name);
513 NCML_ASSERT_MSG(pRenamedVar,
"Renamed variable not found! Logic error!");
516 enterScope(p, pRenamedVar);
517 BESDEBUG(
"ncml",
"Entering scope of the renamed variable. Scope is now: '" << p.getTypedScopeString() <<
"'" << endl);
520void VariableElement::processNewVariable(NCMLParser& p)
522 BESDEBUG(
"ncml",
"Entered VariableElement::processNewVariable..." << endl);
528 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
529 "Must have non-empty variable@type when creating new variable='" + toString() +
"'");
533 string type = p.convertNcmlTypeToCanonicalType(_type);
535 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
"Unknown type for new variable='" + toString()+
"'");
539 NCMLUtil::tokenize(_shape, _shapeTokens, NCMLUtil::WHITESPACE);
545 if (_type == p.STRUCTURE_TYPE) {
546 processNewStructure(p);
548 else if (_shape.empty())
550 processNewScalar(p, type);
552 else if (!_shape.empty()) {
553 processNewArray(p, type);
556 THROW_NCML_INTERNAL_ERROR(
"UNIMPLEMENTED METHOD: Cannot create non-scalar Array types yet.");
562 _pNewlyCreatedVar = _parser->getCurrentVariable();
565void VariableElement::processNewStructure(NCMLParser& p)
568 if (!(p.isScopeCompositeVariable() || p.isScopeGlobal())) {
569 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
"Cannot add a new Structure variable at current scope! "
570 "TypedScope='" + p.getTypedScopeString()+
"'");
573 unique_ptr<BaseType> pNewVar = MyBaseTypeFactory::makeVariable(
"Structure", _name);
574 NCML_ASSERT_MSG(pNewVar.get(),
575 "VariableElement::processNewStructure: factory failed to make a new Structure variable for name='" + _name +
"'");
578 p.addCopyOfVariableAtCurrentScope(*pNewVar);
581 BaseType* pActualVar = p.getVariableInCurrentVariableContainer(_name);
582 VALID_PTR(pActualVar);
584 NCML_ASSERT(pActualVar != pNewVar.get());
587 enterScope(p, pActualVar);
593void VariableElement::processNewScalar(NCMLParser&p,
const string& dapType)
595 addNewVariableAndEnterScope(p, dapType);
598void VariableElement::processNewArray(NCMLParser& p,
const std::string& dapType)
602 addNewVariableAndEnterScope(p,
"Array<" + dapType +
">");
606 Array* pNewVar =
dynamic_cast<Array*
>(p.getCurrentVariable());
607 NCML_ASSERT_MSG(pNewVar,
608 "VariableElement::processNewArray: Expected non-null getCurrentVariable() in parser but got NULL!");
611 unique_ptr<BaseType> pTemplateVar = MyBaseTypeFactory::makeVariable(dapType, _name);
613 pNewVar->add_var(pTemplateVar.get());
616 pNewVar->add_var_nocopy(pTemplateVar.release());
619 for (
unsigned int i = 0; i < _shapeTokens.size(); ++i) {
620 unsigned int dim = getSizeForDimension(p, _shapeTokens.at(i));
621 string dimName = ((isDimensionNumericConstant(_shapeTokens.at(i))) ? (
"") : (_shapeTokens.at(i)));
623 "Appending dimension name=\"" << dimName <<
"\" of size=" << dim <<
" to the Array name=" << pNewVar->name() << endl);
624 pNewVar->append_dim(dim, dimName);
628 if (getProductOfDimensionSizes(p) >
static_cast<unsigned int>(DODS_MAX_ARRAY))
630 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
631 "Product of dimension sizes for Array must be < (2^31-1).");
637VariableElement::replaceArrayIfNeeded(NCMLParser& p, libdap::BaseType* pOrgVar,
const string& name)
640 Array* pOrgArray =
dynamic_cast<libdap::Array*
>(pOrgVar);
646 BESDEBUG(
"ncml",
"VariableElement::replaceArray if needed. Renaming an Array means we need to convert it to NCMLArray." << endl);
649 unique_ptr<NCMLBaseArray> pNewArray = NCMLBaseArray::createFromArray(*pOrgArray);
650 VALID_PTR(pNewArray.get());
653 p.deleteVariableAtCurrentScope(pOrgArray->name());
656 NCMLUtil::setVariableNameProperly(pNewArray.get(), name);
660 p.addCopyOfVariableAtCurrentScope(*(pNewArray.get()));
662 return p.getVariableInCurrentVariableContainer(name);
667VariableElement::replaceArrayIfNeeded(NCMLParser& p, libdap::BaseType* pOrgVar,
const string& name)
670 Array* pOrgArray =
dynamic_cast<libdap::Array*
>(pOrgVar);
676 "VariableElement::replaceArray if needed. Renaming an Array means we need to wrap it with RenamedArrayWrapper!" << endl);
679 unique_ptr<RenamedArrayWrapper> pNewArray = unique_ptr<RenamedArrayWrapper>(
680 new RenamedArrayWrapper(
static_cast<Array*
>(pOrgArray->ptr_duplicate())));
681 p.deleteVariableAtCurrentScope(pOrgArray->name());
685 NCMLUtil::setVariableNameProperly(pNewArray.get(), name);
687 pNewArray->set_name(name);
688 if (pNewArray->type() == libdap::dods_grid_c)
689 dynamic_cast<libdap::Grid*
>(pNewArray.get())->array_var()->set_name(name);
693 p.addCopyOfVariableAtCurrentScope(*(pNewArray.get()));
695 return p.getVariableInCurrentVariableContainer(name);
698void VariableElement::addNewVariableAndEnterScope(NCMLParser& p,
const std::string& dapType)
701 if (!(p.isScopeCompositeVariable() || p.isScopeGlobal())) {
702 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
703 "Cannot add a new scalar variable at current scope! TypedScope='" + p.getTypedScopeString()+
"'");
707 unique_ptr<BaseType> pNewVar = MyBaseTypeFactory::makeVariable(dapType, _name);
708 NCML_ASSERT_MSG(pNewVar.get(),
709 "VariableElement::addNewVariable: factory failed to make a new variable of type: '" + dapType +
"' for element: '"
714 p.addCopyOfVariableAtCurrentScope(*pNewVar);
717 BaseType* pActualVar = p.getVariableInCurrentVariableContainer(_name);
718 VALID_PTR(pActualVar);
720 NCML_ASSERT(pActualVar != pNewVar.get());
723 enterScope(p, pActualVar);
727void VariableElement::enterScope(NCMLParser& p, BaseType* pVar)
732 if (pVar->is_constructor_type()) {
733 p.enterScope(_name, ScopeStack::VARIABLE_CONSTRUCTOR);
736 p.enterScope(_name, ScopeStack::VARIABLE_ATOMIC);
740 p.setCurrentVariable(pVar);
743void VariableElement::exitScope(NCMLParser& p)
747 p.setCurrentVariable(p.getCurrentVariable()->get_parent());
752bool VariableElement::isDimensionNumericConstant(
const std::string& dimToken)
const
755 return isdigit(dimToken.at(0));
758unsigned int VariableElement::getSizeForDimension(NCMLParser& p,
const std::string& dimToken)
const
760 unsigned int dim = 0;
762 if (isDimensionNumericConstant(dimToken)) {
767 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
768 "Trying to get the dimension size in shape=" + _shape +
" for token " + dimToken
769 +
" failed to parse the unsigned int!");
773 const DimensionElement* pDim = p.getDimensionAtLexicalScope(dimToken);
775 return pDim->getLengthNumeric();
778 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
779 "Failed to find a dimension with name=" + dimToken +
" for variable=" + toString()
780 +
" with dimension table= " + p.printAllDimensionsAtLexicalScope() +
" at scope='"
781 + p.getScopeString()+
"'");
787unsigned int VariableElement::getProductOfDimensionSizes(NCMLParser& p)
const
790 if (_shape.empty()) {
795 unsigned int product = 1;
796 vector<string>::const_iterator endIt = _shapeTokens.end();
797 vector<string>::const_iterator it;
798 for (it = _shapeTokens.begin(); it != endIt; ++it) {
799 const string& dimName = *it;
800 unsigned int dimSize = getSizeForDimension(p, dimName);
803 if (product == 0 || dimSize > (DODS_MAX_ARRAY / product)) {
804 THROW_NCML_PARSE_ERROR(_parser->getParseLineNumber(),
805 "Product of dimension sizes exceeds the maximum DAP2 size of 2147483647 (2^31-1)!");
813vector<string> VariableElement::getValidAttributes()
815 vector<string> validAttrs;
816 validAttrs.reserve(4);
817 validAttrs.push_back(
"name");
818 validAttrs.push_back(
"type");
819 validAttrs.push_back(
"shape");
820 validAttrs.push_back(
"orgName");
Concrete class for NcML <variable> element.
const std::string getValueForLocalNameOrDefault(const std::string &localname, const std::string &defVal="") const
NcML Parser for adding/modifying/removing metadata (attributes) to existing local datasets using NcML...