123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2017 HPCC Systems®.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ############################################################################## */
- #include <exception>
- #include "XSDSchemaParser.hpp"
- #include "Exceptions.hpp"
- #include "SchemaValue.hpp"
- #include "XSDComponentParser.hpp"
- #include "XSDValueSetParser.hpp"
- #include "SchemaTypeStringLimits.hpp"
- #include "SchemaTypeIntegerLimits.hpp"
- namespace pt = boost::property_tree;
- bool XSDSchemaParser::doParse(const std::string &configPath, const std::string &masterConfigFile, const std::vector<std::string> &cfgParms)
- {
- bool rc = true;
- //
- // Add some default types to the config. Note changing values for limits
- std::shared_ptr<SchemaTypeStringLimits> pStringLimits;
- std::shared_ptr<SchemaTypeIntegerLimits> pIntLimits;
- std::shared_ptr<SchemaType> pType = std::make_shared<SchemaType>("xs:string");
- pStringLimits = std::make_shared<SchemaTypeStringLimits>();
- pType->setLimits(pStringLimits);
- m_pSchemaItem->addSchemaValueType(pType);
- pType = std::make_shared<SchemaType>("xs:token");
- pStringLimits = std::make_shared<SchemaTypeStringLimits>();
- pType->setLimits(pStringLimits);
- m_pSchemaItem->addSchemaValueType(pType);
- pType = std::make_shared<SchemaType>("xs:boolean");
- std::shared_ptr<SchemaTypeLimits> pBoolLimits = std::make_shared<SchemaTypeStringLimits>();
- pBoolLimits->addAllowedValue("true");
- pBoolLimits->addAllowedValue("false");
- pType->setLimits(pBoolLimits);
- m_pSchemaItem->addSchemaValueType(pType);
- pType = std::make_shared<SchemaType>("xs:integer");
- pIntLimits = std::make_shared<SchemaTypeIntegerLimits>();
- pType->setLimits(pIntLimits);
- m_pSchemaItem->addSchemaValueType(pType);
- pType = std::make_shared<SchemaType>("xs:int");
- pIntLimits = std::make_shared<SchemaTypeIntegerLimits>();
- pType->setLimits(pIntLimits);
- m_pSchemaItem->addSchemaValueType(pType);
- pType = std::make_shared<SchemaType>("xs:nonNegativeInteger");
- pIntLimits = std::make_shared<SchemaTypeIntegerLimits>();
- pIntLimits->setMinInclusive(0);
- pType->setLimits(pIntLimits);
- m_pSchemaItem->addSchemaValueType(pType);
- pType = std::make_shared<SchemaType>("xs:positiveInteger");
- pIntLimits = std::make_shared<SchemaTypeIntegerLimits>();
- pIntLimits->setMinInclusive(1);
- pType->setLimits(pIntLimits);
- m_pSchemaItem->addSchemaValueType(pType);
- pType = std::make_shared<SchemaType>("xs:unsignedInt");
- pIntLimits = std::make_shared<SchemaTypeIntegerLimits>();
- pIntLimits->setMinInclusive(0);
- pType->setLimits(pIntLimits);
- m_pSchemaItem->addSchemaValueType(pType);
- //
- // Get our specific XSD parameters from the input
- m_basePath = configPath;
- m_masterXSDFilename = masterConfigFile;
- //m_buildsetFilename = cfgParms[2];
- parseXSD(m_masterXSDFilename);
- return rc;
- }
- void XSDSchemaParser::parseXSD(const std::string &filename)
- {
- pt::ptree xsdTree;
- std::string fpath = m_basePath + filename;
- try
- {
- pt::read_xml(fpath, xsdTree, pt::xml_parser::trim_whitespace | pt::xml_parser::no_comments);
- }
- catch (const std::exception &e)
- {
- std::string xmlError = e.what();
- ParseException pe("Unable to read/parse file. Check that file is formatted correctly. Error = " + xmlError);
- pe.addFilename(filename);
- throw(pe);
- }
- try
- {
- auto schemaIt = xsdTree.find("xs:schema");
- pt::ptree emptyTree;
- const pt::ptree &keys = schemaIt->second.get_child("", emptyTree);
- parseXSD(keys);
- }
- catch (ParseException &pe)
- {
- pe.addFilename(filename);
- throw(pe);
- }
- }
- void XSDSchemaParser::parseXSD(const pt::ptree &keys)
- {
- for (auto it = keys.begin(); it != keys.end(); ++it)
- {
- //
- // Element parent (a type in realilty) and the element name help figure out how to process the XSD schema element
- std::string elemType = it->first;
- if (elemType == "xs:include")
- {
- std::string schemaFile = getXSDAttributeValue(it->second, "<xmlattr>.schemaLocation");
- if (m_pSchemaItem->addUniqueName(schemaFile))
- {
- parseXSD(schemaFile);
- }
- }
- else if (elemType == "xs:simpleType")
- {
- parseSimpleType(it->second);
- }
- else if (elemType == "xs:complexType")
- {
- parseComplexType(it->second);
- }
- else if (elemType == "xs:attributeGroup")
- {
- parseAttributeGroup(it->second);
- }
- else if (elemType == "xs:attribute")
- {
- parseAttribute(it->second);
- }
- else if (elemType == "xs:sequence")
- {
- parseXSD(it->second.get_child("", pt::ptree()));
- }
- else if (elemType == "xs:element")
- {
- parseElement(it->second);
- }
- else if (elemType == "xs:key")
- {
- parseKey(it->second);
- }
- else if (elemType == "xs:keyref")
- {
- parseKeyRef(it->second);
- }
- }
- }
- std::string XSDSchemaParser::getXSDAttributeValue(const pt::ptree &tree, const std::string &attrName, bool throwIfNotPresent, const std::string &defaultVal) const
- {
- std::string value = defaultVal;
- try
- {
- value = tree.get<std::string>(attrName);
- }
- catch (std::exception &e)
- {
- if (throwIfNotPresent)
- throw(ParseException("Missing attribute " + attrName + "."));
- }
- return value;
- }
- void XSDSchemaParser::parseSimpleType(const pt::ptree &typeTree)
- {
- std::shared_ptr<SchemaType> pCfgType = getSchemaType(typeTree, true);
- m_pSchemaItem->addSchemaValueType(pCfgType);
- }
- void XSDSchemaParser::parseAttribute(const pt::ptree &attr)
- {
- std::shared_ptr<SchemaValue> pCfgValue = getSchemaValue(attr);
- m_pSchemaItem->addAttribute(pCfgValue);
- }
- void XSDSchemaParser::parseAttributeGroup(const pt::ptree &attributeTree)
- {
- std::string groupName = getXSDAttributeValue(attributeTree, "<xmlattr>.name", false, ""); // only a named attributeGroup is supported
- //
- // If there is a name (for the attribute group) then a group of attributes is being defined. Create the group, parese it, and add it as a
- // schema type that can be reused (usually with a ref= reference in another attribute group schema item)
- if (!groupName.empty())
- {
- std::shared_ptr<SchemaItem> pValueSet = std::make_shared<SchemaItem>(groupName, "valueset", m_pSchemaItem);
- std::shared_ptr<XSDValueSetParser> pXSDValueSetParaser = std::make_shared<XSDValueSetParser>(pValueSet);
- pXSDValueSetParaser->parseXSD(attributeTree.get_child("", pt::ptree()));
- m_pSchemaItem->addSchemaType(pValueSet, groupName);
- }
- //
- // Is it a reference to a named attribute group previously saved? If so, grab the defined attributes and add them.
- else
- {
- std::string refName = getXSDAttributeValue(attributeTree, "<xmlattr>.ref", false, ""); // only a named attributeGroup is supported
- if (!refName.empty())
- {
- std::shared_ptr<SchemaItem> pValueSet = m_pSchemaItem->getSchemaType(refName, true);
- if (pValueSet)
- {
- std::vector<std::shared_ptr<SchemaValue>> attributes;
- pValueSet->getAttributes(attributes);
- m_pSchemaItem->addAttribute(attributes);
- }
- }
- }
- }
- void XSDSchemaParser::parseComplexType(const pt::ptree &typeTree)
- {
- std::string complexTypeName = getXSDAttributeValue(typeTree, "<xmlattr>.name", false, "");
- std::string className = typeTree.get("<xmlattr>.hpcc:class", "");
- std::string catName = typeTree.get("<xmlattr>.hpcc:category", "");
- std::string componentName = typeTree.get("<xmlattr>.hpcc:componentName", "");
- std::string displayName = typeTree.get("<xmlattr>.hpcc:displayName", "");
- bool hidden = typeTree.get("<xmlattr>.hpcc:hidden", "false") == "true";
- if (!complexTypeName.empty())
- {
- if (!className.empty())
- {
- if (className == "component")
- {
- std::shared_ptr<SchemaItem> pComponent = std::make_shared<SchemaItem>(complexTypeName, "component", m_pSchemaItem);
- pComponent->setProperty("category", catName);
- pComponent->setProperty("componentName", componentName);
- pComponent->setProperty("displayName", displayName);
- pComponent->setHidden(hidden);
- pt::ptree componentTree = typeTree.get_child("", pt::ptree());
- if (!componentTree.empty())
- {
- std::shared_ptr<XSDComponentParser> pComponentXSDParaser = std::make_shared<XSDComponentParser>(std::dynamic_pointer_cast<SchemaItem>(pComponent));
- pComponentXSDParaser->parseXSD(typeTree);
- m_pSchemaItem->addSchemaType(pComponent, complexTypeName);
- }
- else
- {
- throw(ParseException("Component definition empty: " + displayName));
- }
- }
- else
- {
- throw(ParseException("Unrecognized class name for complex type: " + className));
- }
- }
- //
- // This is a complex type definition of just regular XSD statements, no special format. Create a parser and parse it
- // and add it to the
- else
- {
- std::shared_ptr<SchemaItem> pTypeItem = std::make_shared<SchemaItem>(complexTypeName, "", m_pSchemaItem);
- pt::ptree childTree = typeTree.get_child("", pt::ptree());
- if (!childTree.empty())
- {
- std::shared_ptr<XSDSchemaParser> pXSDParaser = std::make_shared<XSDSchemaParser>(pTypeItem);
- pXSDParaser->parseXSD(childTree);
- m_pSchemaItem->addSchemaType(pTypeItem, complexTypeName);
- }
- else
- {
- throw(ParseException("Complex type definition empty: " + displayName));
- }
- }
- }
- //
- // Just a complexType delimiter, ignore and parse the children
- else
- {
- parseXSD(typeTree.get_child("", pt::ptree()));
- }
- }
- void XSDSchemaParser::parseElement(const pt::ptree &elemTree)
- {
- std::string elementName = elemTree.get("<xmlattr>.name", "");
- std::string className = elemTree.get("<xmlattr>.hpcc:class", "");
- std::string category = elemTree.get("<xmlattr>.hpcc:category", "");
- std::string displayName = elemTree.get("<xmlattr>.hpcc:displayName", "");
- std::string tooltip = elemTree.get("<xmlattr>.hpcc:tooltip", "");
- std::string typeName = elemTree.get("<xmlattr>.type", "");
- unsigned minOccurs = elemTree.get("<xmlattr>.minOccurs", 1);
- std::string maxOccursStr = elemTree.get("<xmlattr>.maxOccurs", "1");
- unsigned maxOccurs = (maxOccursStr != "unbounded") ? stoi(maxOccursStr) : UINT_MAX;
- std::shared_ptr<SchemaItem> pNewSchemaItem = std::make_shared<SchemaItem>(elementName, className, m_pSchemaItem);
- pNewSchemaItem->setProperty("displayName", displayName);
- pNewSchemaItem->setMinInstances(minOccurs);
- pNewSchemaItem->setMaxInstances(maxOccurs);
- pNewSchemaItem->setProperty("category", category);
- pNewSchemaItem->setProperty("tooltip", tooltip);
- pNewSchemaItem->setHidden(elemTree.get("<xmlattr>.hpcc:hidden", "false") == "true");
- pt::ptree childTree = elemTree.get_child("", pt::ptree());
- // special case to set the root since the top level schema can't specify it
- if (category == "root") // special case to set the root since the top level schema can't specify it
- {
- m_pSchemaItem->setProperty("name", elementName);
- parseXSD(childTree);
- }
- else
- {
- //
- // If a type is specified, then either it's a simple value type (which could be previously defined) for this element, or a named complex type.
- if (!typeName.empty())
- {
- const std::shared_ptr<SchemaType> pSimpleType = m_pSchemaItem->getSchemaValueType(typeName, false);
- if (pSimpleType != nullptr)
- {
- std::shared_ptr<SchemaValue> pCfgValue = std::make_shared<SchemaValue>(""); // no name value since it's the element's value
- pCfgValue->setType(pSimpleType); // will throw if type is not defined
- pNewSchemaItem->setItemSchemaValue(pCfgValue);
- }
- else
- {
- std::shared_ptr<SchemaItem> pConfigType = m_pSchemaItem->getSchemaType(typeName, false);
- if (pConfigType != nullptr)
- {
- //
- // Insert into this config element the component defined data (attributes, references, etc.)
- pNewSchemaItem->insertSchemaType(pConfigType);
- //
- // Set element min/max instances to that defined by the component type def (ignore values parsed above)
- pNewSchemaItem->setMinInstances(pConfigType->getMinInstances());
- pNewSchemaItem->setMaxInstances(pConfigType->getMaxInstances());
- //
- // If a component, then set element data (allow overriding with locally parsed values)
- if (pConfigType->getProperty("className") == "component")
- {
- pNewSchemaItem->setProperty("name", (!elementName.empty()) ? elementName : pConfigType->getProperty("name"));
- pNewSchemaItem->setProperty("className", (!className.empty()) ? className : pConfigType->getProperty("className"));
- pNewSchemaItem->setProperty("category", (!category.empty()) ? category : pConfigType->getProperty("category"));
- pNewSchemaItem->setProperty("displayName", (!displayName.empty()) ? displayName : pConfigType->getProperty("displayName"));
- pNewSchemaItem->setProperty("componentName", pConfigType->getProperty("componentName"));
- }
- }
- else
- {
- std::string msg = "Unable to find type " + typeName + " when parsing element " + elementName;
- throw(ParseException(msg));
- }
- }
- }
- //
- // Now, if there are children, create a parser and have at it
- if (!childTree.empty())
- {
- std::shared_ptr<XSDSchemaParser> pXSDParaser = std::make_shared<XSDSchemaParser>(pNewSchemaItem);
- pXSDParaser->parseXSD(childTree);
- }
- //
- // Add the element
- m_pSchemaItem->addChild(pNewSchemaItem);
- }
- }
- std::shared_ptr<SchemaType> XSDSchemaParser::getSchemaType(const pt::ptree &typeTree, bool nameRequired)
- {
- std::string typeName = getXSDAttributeValue(typeTree, "<xmlattr>.name", nameRequired, "");
- std::shared_ptr<SchemaType> pCfgType = std::make_shared<SchemaType>(typeName);
- std::shared_ptr<SchemaTypeLimits> pLimits;
- auto restriction = typeTree.find("xs:restriction");
- if (restriction != typeTree.not_found())
- {
- std::string baseType = getXSDAttributeValue(restriction->second, "<xmlattr>.base");
- std::shared_ptr<SchemaType> pType = std::make_shared<SchemaType>(*(m_pSchemaItem->getSchemaValueType(baseType)));
- pLimits = pType->getLimits();
- if (!restriction->second.empty())
- {
- pt::ptree restrictTree = restriction->second.get_child("", pt::ptree());
- if (std::dynamic_pointer_cast<SchemaTypeIntegerLimits>(pLimits) != nullptr)
- {
- std::shared_ptr<SchemaTypeIntegerLimits> pBaseIntLimits = std::dynamic_pointer_cast<SchemaTypeIntegerLimits>(pLimits);
- std::shared_ptr<SchemaTypeIntegerLimits> pIntLimits = std::make_shared<SchemaTypeIntegerLimits>(*pBaseIntLimits);
- parseIntegerTypeLimits(restrictTree, pIntLimits);
- pLimits = pIntLimits;
- }
- else if (std::dynamic_pointer_cast<SchemaTypeStringLimits>(pLimits) != nullptr)
- {
- std::shared_ptr<SchemaTypeStringLimits> pBaseStringimits = std::dynamic_pointer_cast<SchemaTypeStringLimits>(pLimits);
- std::shared_ptr<SchemaTypeStringLimits> pStringimits = std::make_shared<SchemaTypeStringLimits>(*pBaseStringimits);
- parseStringTypeLimits(restrictTree, pStringimits);
- pLimits = pStringimits;
- }
- else
- {
- std::string msg = "Unsupported base type(" + baseType + ")";
- throw(ParseException(msg));
- }
- }
- }
- pCfgType->setLimits(pLimits);
- return pCfgType;
- }
- void XSDSchemaParser::parseIntegerTypeLimits(const pt::ptree &restrictTree, std::shared_ptr<SchemaTypeIntegerLimits> &pIntegerLimits)
- {
- for (auto it = restrictTree.begin(); it != restrictTree.end(); ++it)
- {
- std::string restrictionType = it->first;
- if (restrictionType == "xs:minInclusive")
- pIntegerLimits->setMinInclusive(it->second.get<int>("<xmlattr>.value"));
- else if (restrictionType == "xs:maxInclusive")
- pIntegerLimits->setMaxInclusive(it->second.get<int>("<xmlattr>.value"));
- else if (restrictionType == "xs:minExclusive")
- pIntegerLimits->setMinExclusive(it->second.get<int>("<xmlattr>.value"));
- else if (restrictionType == "xs:maxExclusive")
- pIntegerLimits->setMaxExclusive(it->second.get<int>("<xmlattr>.value"));
- else if (restrictionType == "xs:enumeration")
- {
- pIntegerLimits->addAllowedValue(it->second.get("<xmlattr>.value", "badbadbad"), it->second.get("<xmlattr>.hpcc:description", ""));
- }
- else if (restrictionType != "<xmlattr>")
- {
- std::string msg = "Invalid restriction(" + it->first + ") found while parsing type";
- throw(ParseException(msg));
- }
- }
- }
- void XSDSchemaParser::parseStringTypeLimits(const pt::ptree &restrictTree, std::shared_ptr<SchemaTypeStringLimits> &pStringLimits)
- {
- for (auto it = restrictTree.begin(); it != restrictTree.end(); ++it)
- {
- std::string restrictionType = it->first;
- if (restrictionType == "xs:minLength")
- pStringLimits->setMinLength(it->second.get<int>("<xmlattr>.value"));
- else if (restrictionType == "xs:maxLength")
- pStringLimits->setMaxLength(it->second.get<int>("<xmlattr>.value"));
- else if (restrictionType == "xs:length")
- pStringLimits->setLength(it->second.get<int>("<xmlattr>.value"));
- else if (restrictionType == "xs:pattern")
- pStringLimits->addPattern(it->second.get("<xmlattr>.value", "0"));
- else if (restrictionType == "xs:enumeration")
- {
- pStringLimits->addAllowedValue(it->second.get("<xmlattr>.value", "badbadbad"), it->second.get("<xmlattr>.hpcc:description", ""));
- }
- else if (restrictionType != "<xmlattr>")
- {
- std::string msg = "Invalid restriction(" + it->first + ") found while parsing type";
- throw(ParseException(msg));
- }
- }
- }
- std::shared_ptr<SchemaValue> XSDSchemaParser::getSchemaValue(const pt::ptree &attr)
- {
- std::string attrName = getXSDAttributeValue(attr, "<xmlattr>.name");
- std::shared_ptr<SchemaValue> pCfgValue = std::make_shared<SchemaValue>(attrName);
- pCfgValue->setDisplayName(attr.get("<xmlattr>.hpcc:displayName", attrName));
- pCfgValue->setRequired(attr.get("<xmlattr>.use", "optional") == "required");
- pCfgValue->setTooltip(attr.get("<xmlattr>.hpcc:tooltip", ""));
- pCfgValue->setReadOnly(attr.get("<xmlattr>.hpcc:readOnly", "false") == "true");
- pCfgValue->setHidden(attr.get("<xmlattr>.hpcc:hidden", "false") == "true");
- pCfgValue->setDeprecated(attr.get("<xmlattr>.hpcc:deprecated", "false") == "true");
- pCfgValue->setMirrorFromPath(attr.get("<xmlattr>.hpcc:mirrorFrom", ""));
- pCfgValue->setAutoGenerateType(attr.get("<xmlattr>.hpcc:autoGenerateType", ""));
- pCfgValue->setAutoGenerateValue(attr.get("<xmlattr>.hpcc:autoGenerateValue", ""));
- pCfgValue->setDefaultValue(attr.get("<xmlattr>.default", ""));
- std::string modList = attr.get("<xmlattr>.hpcc:modifiers", "");
- if (modList.length())
- {
- pCfgValue->setModifiers(split(modList, ","));
- }
- std::string typeName = attr.get("<xmlattr>.type", "");
- if (!typeName.empty())
- {
- pCfgValue->setType(m_pSchemaItem->getSchemaValueType(typeName));
- }
- else
- {
- std::shared_ptr<SchemaType> pType = getSchemaType(attr.get_child("xs:simpleType", pt::ptree()), false);
- if (!pType->isValid())
- {
- throw(ParseException("Attribute " + attrName + " does not have a valid type"));
- }
- pCfgValue->setType(pType);
- }
- return pCfgValue;
- }
- void XSDSchemaParser::parseKey(const pt::ptree &keyTree)
- {
- std::string keyName = getXSDAttributeValue(keyTree, "<xmlattr>.name");
- bool duplicateOk = keyTree.get("<xmlattr>.hpcc:allowDuplicate", "false") == "true";
- std::string elementName = getXSDAttributeValue(keyTree, "xs:selector.<xmlattr>.xpath", false, "");
- std::string attrName = getXSDAttributeValue(keyTree, "xs:field.<xmlattr>.xpath", false, "");
- std::string attributeName;
- if (attrName.find_first_of('@') != std::string::npos)
- {
- attributeName = attrName.substr(attrName.find_first_of('@') + 1);
- }
- else
- {
- attributeName = attrName;
- }
- m_pSchemaItem->addUniqueAttributeValueSetDefinition(keyName, elementName, attributeName, duplicateOk);
- }
- void XSDSchemaParser::parseKeyRef(const pt::ptree &keyTree)
- {
- std::string keyName = getXSDAttributeValue(keyTree, "<xmlattr>.refer");
- std::string elementName = getXSDAttributeValue(keyTree, "xs:selector.<xmlattr>.xpath", false, "");
- std::string attrName = getXSDAttributeValue(keyTree, "xs:field.<xmlattr>.xpath", false, "");
- std::string attributeName;
- if (attrName.find_first_of('@') != std::string::npos)
- {
- attributeName = attrName.substr(attrName.find_first_of('@') + 1);
- }
- else
- {
- attributeName = attrName;
- }
- m_pSchemaItem->addReferenceToUniqueAttributeValueSet(keyName, elementName, attributeName);
- }
|