XSDSchemaParser.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2017 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include <exception>
  14. #include <algorithm>
  15. #include "XSDSchemaParser.hpp"
  16. #include "Exceptions.hpp"
  17. #include "SchemaValue.hpp"
  18. #include "XSDComponentParser.hpp"
  19. #include "XSDValueSetParser.hpp"
  20. #include "SchemaTypeStringLimits.hpp"
  21. #include "SchemaTypeIntegerLimits.hpp"
  22. #include "EnvironmentEventHandlers.hpp"
  23. #include "Utils.hpp"
  24. #include "jfile.hpp"
  25. namespace pt = boost::property_tree;
  26. bool XSDSchemaParser::doParse(const std::string &configPath, const std::string &masterConfigFile, const std::map<std::string, std::string> &cfgParms)
  27. {
  28. bool rc = true;
  29. //
  30. // Add some default types to the config. Note changing values for limits
  31. std::shared_ptr<SchemaTypeStringLimits> pStringLimits;
  32. std::shared_ptr<SchemaTypeIntegerLimits> pIntLimits;
  33. std::shared_ptr<SchemaType> pType = std::make_shared<SchemaType>("xs:string");
  34. pType->setBaseType("string");
  35. pStringLimits = std::make_shared<SchemaTypeStringLimits>();
  36. pType->setLimits(pStringLimits);
  37. m_pSchemaItem->addSchemaValueType(pType);
  38. pType = std::make_shared<SchemaType>("xs:token");
  39. pType->setBaseType("string");
  40. pStringLimits = std::make_shared<SchemaTypeStringLimits>();
  41. pType->setLimits(pStringLimits);
  42. m_pSchemaItem->addSchemaValueType(pType);
  43. pType = std::make_shared<SchemaType>("xs:boolean");
  44. pType->setBaseType("boolean");
  45. std::shared_ptr<SchemaTypeLimits> pBoolLimits = std::make_shared<SchemaTypeStringLimits>();
  46. pBoolLimits->addAllowedValue("true");
  47. pBoolLimits->addAllowedValue("false");
  48. pType->setLimits(pBoolLimits);
  49. m_pSchemaItem->addSchemaValueType(pType);
  50. pType = std::make_shared<SchemaType>("xs:integer");
  51. pType->setBaseType("integer");
  52. pIntLimits = std::make_shared<SchemaTypeIntegerLimits>();
  53. pType->setLimits(pIntLimits);
  54. m_pSchemaItem->addSchemaValueType(pType);
  55. pType = std::make_shared<SchemaType>("xs:nonNegativeInteger");
  56. pType->setBaseType("integer");
  57. pIntLimits = std::make_shared<SchemaTypeIntegerLimits>();
  58. pIntLimits->setMinInclusive(0);
  59. pType->setLimits(pIntLimits);
  60. m_pSchemaItem->addSchemaValueType(pType);
  61. pType = std::make_shared<SchemaType>("xs:positiveInteger");
  62. pType->setBaseType("integer");
  63. pIntLimits = std::make_shared<SchemaTypeIntegerLimits>();
  64. pIntLimits->setMinInclusive(1);
  65. pType->setLimits(pIntLimits);
  66. m_pSchemaItem->addSchemaValueType(pType);
  67. pType = std::make_shared<SchemaType>("xs:unsignedInt");
  68. pType->setBaseType("integer");
  69. pIntLimits = std::make_shared<SchemaTypeIntegerLimits>();
  70. pIntLimits->setMinInclusive(0);
  71. pType->setLimits(pIntLimits);
  72. m_pSchemaItem->addSchemaValueType(pType);
  73. //
  74. // Parse the master XSD
  75. m_basePath = configPath;
  76. m_masterXSDFilename = masterConfigFile;
  77. parseXSD(m_basePath + m_masterXSDFilename);
  78. //
  79. // Parse the rest of the XSDs in the config path skipping the master
  80. processXSDFiles(m_basePath, m_masterXSDFilename);
  81. //
  82. // Now plugins
  83. for (auto &pluginPath: m_pluginPaths)
  84. {
  85. processXSDFiles(pluginPath, "");
  86. }
  87. return rc;
  88. }
  89. void XSDSchemaParser::parseXSD(const std::string &fullyQualifiedPath)
  90. {
  91. pt::ptree xsdTree;
  92. try
  93. {
  94. pt::read_xml(fullyQualifiedPath, xsdTree, pt::xml_parser::trim_whitespace | pt::xml_parser::no_comments);
  95. }
  96. catch (const std::exception &e)
  97. {
  98. std::string xmlError = e.what();
  99. ParseException pe("Unable to read/parse file. Check that file is formatted correctly. Error = " + xmlError);
  100. pe.addFilename(fullyQualifiedPath);
  101. throw(pe);
  102. }
  103. try
  104. {
  105. auto schemaIt = xsdTree.find("xs:schema");
  106. pt::ptree emptyTree;
  107. const pt::ptree &keys = schemaIt->second.get_child("", emptyTree);
  108. parseXSD(keys);
  109. }
  110. catch (ParseException &pe)
  111. {
  112. pe.addFilename(fullyQualifiedPath);
  113. throw(pe);
  114. }
  115. }
  116. void XSDSchemaParser::parseXSD(const pt::ptree &keys)
  117. {
  118. for (auto it = keys.begin(); it != keys.end(); ++it)
  119. {
  120. //
  121. // Element parent (a type in realilty) and the element name help figure out how to process the XSD schema element
  122. std::string elemType = it->first;
  123. if (elemType == "xs:include")
  124. {
  125. std::string schemaFile = getXSDAttributeValue(it->second, "<xmlattr>.schemaLocation");
  126. if (m_pSchemaItem->addUniqueName(schemaFile))
  127. {
  128. parseXSD(m_basePath + schemaFile);
  129. }
  130. }
  131. else if (elemType == "xs:simpleType")
  132. {
  133. parseSimpleType(it->second);
  134. }
  135. else if (elemType == "xs:complexType")
  136. {
  137. parseComplexType(it->second);
  138. }
  139. else if (elemType == "xs:attributeGroup")
  140. {
  141. parseAttributeGroup(it->second);
  142. }
  143. else if (elemType == "xs:attribute")
  144. {
  145. parseAttribute(it->second);
  146. }
  147. else if (elemType == "xs:sequence")
  148. {
  149. parseXSD(it->second.get_child("", pt::ptree()));
  150. }
  151. else if (elemType == "xs:element")
  152. {
  153. parseElement(it->second);
  154. }
  155. else if (elemType == "xs:annotation")
  156. {
  157. parseAnnotation(it->second);
  158. }
  159. else if (elemType == "hpcc:insert")
  160. {
  161. processSchemaInsert(it->second);
  162. }
  163. }
  164. }
  165. std::string XSDSchemaParser::getXSDAttributeValue(const pt::ptree &tree, const std::string &attrName, bool throwIfNotPresent, const std::string &defaultVal) const
  166. {
  167. std::string value = defaultVal;
  168. try
  169. {
  170. value = tree.get<std::string>(attrName);
  171. }
  172. catch (std::exception &e)
  173. {
  174. if (throwIfNotPresent)
  175. throw(ParseException("Missing attribute " + attrName + "."));
  176. }
  177. return value;
  178. }
  179. void XSDSchemaParser::parseSimpleType(const pt::ptree &typeTree)
  180. {
  181. std::shared_ptr<SchemaType> pCfgType = getType(typeTree, true);
  182. m_pSchemaItem->addSchemaValueType(pCfgType);
  183. }
  184. std::shared_ptr<SchemaValue> XSDSchemaParser::parseAttribute(const pt::ptree &attr)
  185. {
  186. std::shared_ptr<SchemaValue> pCfgValue = getSchemaValue(attr);
  187. m_pSchemaItem->addAttribute(pCfgValue);
  188. return pCfgValue;
  189. }
  190. void XSDSchemaParser::parseAttributeGroup(const pt::ptree &attributeTree)
  191. {
  192. std::string groupName = getXSDAttributeValue(attributeTree, "<xmlattr>.name", false, ""); // only a named attributeGroup is supported
  193. //
  194. // 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
  195. // schema type that can be reused (usually with a ref= reference in another attribute group schema item)
  196. if (!groupName.empty())
  197. {
  198. std::shared_ptr<SchemaItem> pValueSet = std::make_shared<SchemaItem>(groupName, "valueset", m_pSchemaItem);
  199. std::shared_ptr<XSDValueSetParser> pXSDValueSetParaser = std::make_shared<XSDValueSetParser>(pValueSet);
  200. std::string groupByName = getXSDAttributeValue(attributeTree, "<xmlattr>.hpcc:groupByName", false, "");
  201. pXSDValueSetParaser->setGroupByName(groupByName);
  202. pXSDValueSetParaser->parseXSD(attributeTree.get_child("", pt::ptree()));
  203. m_pSchemaItem->addSchemaType(pValueSet, groupName);
  204. m_pSchemaItem->setProperty("attribute_group_default_overrides", getXSDAttributeValue(attributeTree, "<xmlattr>.hpcc:presetValue", false, ""));
  205. }
  206. //
  207. // Is it a reference to a named attribute group previously saved? If so, grab the defined attributes and add them.
  208. else
  209. {
  210. std::string refName = getXSDAttributeValue(attributeTree, "<xmlattr>.ref", false, ""); // only a named attributeGroup is supported
  211. if (!refName.empty())
  212. {
  213. std::shared_ptr<SchemaItem> pValueSet = m_pSchemaItem->getSchemaType(refName, true);
  214. if (pValueSet)
  215. {
  216. std::vector<std::shared_ptr<SchemaValue>> attributes;
  217. pValueSet->getAttributes(attributes);
  218. std::string groupByName = getXSDAttributeValue(attributeTree, "<xmlattr>.groupByName", false, "");
  219. if (!groupByName.empty())
  220. {
  221. for (auto &attr: attributes)
  222. {
  223. attr->setGroupByName(groupByName);
  224. }
  225. }
  226. m_pSchemaItem->addAttribute(attributes);
  227. //
  228. // Add any unique valueset references
  229. m_pSchemaItem->addUniqueAttrValueSetDefsAndRefs(pValueSet);
  230. //
  231. // See if there are any overrides for default in code values
  232. std::string dfltOverrides = pValueSet->getProperty("attribute_group_default_overrides");
  233. if (!dfltOverrides.empty())
  234. {
  235. std::vector<std::string> overrides = splitString(dfltOverrides, ",");
  236. for (auto &info: overrides)
  237. {
  238. std::vector<std::string> vals = splitString(info, "=");
  239. if (vals.size() != 2)
  240. {
  241. throw (ParseException("Invalid default value override in attribute group (" + refName + ")"));
  242. }
  243. std::shared_ptr<SchemaValue> pAttr = m_pSchemaItem->getAttribute(vals[0], false);
  244. if (pAttr)
  245. {
  246. pAttr->setPresetValue(vals[1]);
  247. }
  248. }
  249. }
  250. }
  251. }
  252. }
  253. }
  254. void XSDSchemaParser::parseComplexType(const pt::ptree &typeTree)
  255. {
  256. std::string complexTypeName = getXSDAttributeValue(typeTree, "<xmlattr>.name", false, "");
  257. if (!complexTypeName.empty())
  258. {
  259. std::shared_ptr<SchemaItem> pComplexType = std::make_shared<SchemaItem>(complexTypeName, "component", m_pSchemaItem);
  260. pComplexType->setProperty("itemType", complexTypeName);
  261. pt::ptree childTree = typeTree.get_child("", pt::ptree());
  262. if (!childTree.empty())
  263. {
  264. std::shared_ptr<XSDSchemaParser> pXSDParaser = std::make_shared<XSDSchemaParser>(pComplexType);
  265. pXSDParaser->parseXSD(childTree);
  266. m_pSchemaItem->addSchemaType(pComplexType, complexTypeName);
  267. }
  268. else
  269. {
  270. throw(ParseException("Complex type definition empty: " + complexTypeName));
  271. }
  272. }
  273. //
  274. // Just a complexType delimiter, ignore and parse the children
  275. else
  276. {
  277. parseXSD(typeTree.get_child("", pt::ptree()));
  278. }
  279. }
  280. void XSDSchemaParser::parseElement(const pt::ptree &elemTree)
  281. {
  282. //
  283. // Get schema attribute necessary to figure out what to do
  284. std::string elementName = elemTree.get("<xmlattr>.name", "");
  285. std::string itemType = elemTree.get("<xmlattr>.hpcc:itemType", "");
  286. //
  287. // Get child tree for use below
  288. pt::ptree emptyTree;
  289. pt::ptree childTree = elemTree.get_child("", emptyTree);
  290. //
  291. // Get existing element(s)
  292. std::shared_ptr<SchemaItem> pNewSchemaItem;
  293. std::vector<std::shared_ptr<SchemaItem>> children;
  294. m_pSchemaItem->getChildren(children, elementName, itemType);
  295. //
  296. // There should be no children since we are expecting to create a new element
  297. if (!children.empty())
  298. {
  299. std::string msg = "Attempt to insert duplicate element, element = '" + elementName + "' itemType='" + itemType + "'";
  300. throw(ParseException(msg));
  301. }
  302. //
  303. // Get the rest of the possible attributes for the new element
  304. std::string category = elemTree.get("<xmlattr>.hpcc:category", "");
  305. std::string typeName = elemTree.get("<xmlattr>.type", "");
  306. std::string className = elemTree.get("<xmlattr>.hpcc:class", "");
  307. std::string displayName = elemTree.get("<xmlattr>.hpcc:displayName", elementName);
  308. std::string tooltip = elemTree.get("<xmlattr>.hpcc:tooltip", "");
  309. std::string insertLimitType = elemTree.get("<xmlattr>.hpcc:insertLimitType", "");
  310. std::string insertLimitData = elemTree.get("<xmlattr>.hpcc:insertLimitData", "");
  311. unsigned minOccurs = elemTree.get<unsigned>("<xmlattr>.minOccurs", 1);
  312. std::string maxOccursStr = elemTree.get("<xmlattr>.maxOccurs", "1");
  313. unsigned maxOccurs;
  314. try
  315. {
  316. maxOccurs = (maxOccursStr != "unbounded") ? stoi(maxOccursStr) : UINT_MAX;
  317. }
  318. catch(...)
  319. {
  320. std::string msg = "Invalid maxOccurs value: '" + maxOccursStr + "', Unable to convert to a number in element = '" + elementName;
  321. throw(ParseException(msg));
  322. }
  323. if (category == "root")
  324. {
  325. m_pSchemaItem->setProperty("name", elementName);
  326. parseXSD(childTree);
  327. }
  328. else
  329. {
  330. //
  331. // Create the new element and set properties
  332. pNewSchemaItem = std::make_shared<SchemaItem>(elementName, className, m_pSchemaItem);
  333. if (!className.empty()) pNewSchemaItem->setProperty("className", className);
  334. if (!displayName.empty()) pNewSchemaItem->setProperty("displayName", displayName);
  335. if (!tooltip.empty()) pNewSchemaItem->setProperty("tooltip", tooltip);
  336. if (!insertLimitType.empty()) pNewSchemaItem->setProperty("insertLimitType", insertLimitType);
  337. if (!insertLimitData.empty()) pNewSchemaItem->setProperty("insertLimitData", insertLimitData);
  338. pNewSchemaItem->setProperty("itemType", itemType);
  339. pNewSchemaItem->setProperty("category", category.empty() ? displayName : category );
  340. pNewSchemaItem->setMinInstances(minOccurs);
  341. pNewSchemaItem->setMaxInstances(maxOccurs);
  342. pNewSchemaItem->setHidden(elemTree.get("<xmlattr>.hpcc:hidden", "false") == "true");
  343. pNewSchemaItem->setRequiredInstanceComponents(elemTree.get("<xmlattr>.hpcc:requiredInstanceComponents", ""));
  344. //
  345. // If a typeName was set, see if simple or complex and handle accordingly.
  346. if (!typeName.empty())
  347. {
  348. const std::shared_ptr<SchemaType> pSimpleType = m_pSchemaItem->getSchemaValueType(typeName, false);
  349. if (pSimpleType != nullptr)
  350. {
  351. std::shared_ptr<SchemaValue> pCfgValue = std::make_shared<SchemaValue>(""); // no name value since it's the element's value
  352. pCfgValue->setType(pSimpleType); // will throw if type is not defined
  353. pNewSchemaItem->setItemSchemaValue(pCfgValue);
  354. }
  355. else
  356. {
  357. std::shared_ptr<SchemaItem> pConfigType = m_pSchemaItem->getSchemaType(typeName, false);
  358. if (pConfigType != nullptr)
  359. {
  360. pNewSchemaItem->insertSchemaType(pConfigType);
  361. }
  362. }
  363. }
  364. //
  365. // Now parse the element child tree (note if complex was inserted, this is probably empty)
  366. std::shared_ptr<XSDSchemaParser> pXSDParaser = std::make_shared<XSDSchemaParser>(pNewSchemaItem);
  367. pXSDParaser->parseXSD(childTree);
  368. m_pSchemaItem->addChild(pNewSchemaItem);
  369. }
  370. }
  371. void XSDSchemaParser::processSchemaInsert(const pt::ptree &elemTree)
  372. {
  373. std::string path = elemTree.get("<xmlattr>.hpcc:schemaPath", "");
  374. //
  375. // Make sure path is present and well formed
  376. if (path.empty() || path[0] != '/' || path.back() == '/')
  377. {
  378. std::string msg = "Insert schema path is missing, empty or not welformed";
  379. throw(ParseException(msg));
  380. }
  381. std::string itemType = elemTree.get("<xmlattr>.hpcc:itemType", "");
  382. std::vector<std::shared_ptr<SchemaItem>> insertChildren;
  383. std::vector<std::string> pathParts = splitString(path, "/");
  384. std::shared_ptr<SchemaItem> pInsertItem = m_pSchemaItem->getSchemaRoot();
  385. size_t numPasses = pathParts.size() - 1;
  386. for (size_t i=0; i<numPasses; ++i)
  387. {
  388. bool isLast = i == (numPasses - 1);
  389. if (pInsertItem->getProperty("name") == pathParts[i])
  390. {
  391. insertChildren.clear();
  392. pInsertItem->getChildren(insertChildren, pathParts[i+1], isLast ? itemType : "");
  393. if (insertChildren.empty())
  394. {
  395. std::string msg = "Unable to find insert location, path not found: " + path;
  396. throw(ParseException(msg));
  397. }
  398. if (!isLast)
  399. {
  400. pInsertItem = insertChildren[0];
  401. }
  402. }
  403. else
  404. {
  405. std::string msg = "Unable to find insert location, path not found: " + path;
  406. throw(ParseException(msg));
  407. }
  408. }
  409. //
  410. // Get the child tree
  411. pt::ptree emptyTree;
  412. pt::ptree childTree = elemTree.get_child("", emptyTree);
  413. for (auto &child: insertChildren)
  414. {
  415. std::shared_ptr<XSDSchemaParser> pXSDParaser = std::make_shared<XSDSchemaParser>(child);
  416. pXSDParaser->parseXSD(childTree); // will extend this child schema item
  417. }
  418. }
  419. void XSDSchemaParser::parseAnnotation(const pt::ptree &elemTree)
  420. {
  421. pt::ptree emptyTree;
  422. const pt::ptree &keys = elemTree.get_child("", emptyTree);
  423. //
  424. // Parse app info sections
  425. for (auto it = keys.begin(); it != keys.end(); ++it)
  426. {
  427. if (it->first == "xs:appinfo")
  428. {
  429. parseAppInfo(it->second);
  430. }
  431. }
  432. }
  433. void XSDSchemaParser::parseAppInfo(const pt::ptree &elemTree)
  434. {
  435. std::string appInfoType = elemTree.get("<xmlattr>.hpcc:infoType", "");
  436. pt::ptree emptyTree, childTree;
  437. //
  438. // Process the app info based on its type
  439. if (appInfoType == "event")
  440. {
  441. childTree = elemTree.get_child("", emptyTree);
  442. std::string eventType = getXSDAttributeValue(childTree, "eventType");
  443. //
  444. // For a create event type, get the eventAction attrbute to decide what to do
  445. if (eventType == "create")
  446. {
  447. std::string eventAction = getXSDAttributeValue(childTree, "eventAction");
  448. //
  449. // addAttributeDependencies is used to set dependent values for an attribute based on the value of another attribute.
  450. if (eventAction == "addAttributeDependencies")
  451. {
  452. std::shared_ptr<AttributeDependencyCreateEventHandler> pDep = std::make_shared<AttributeDependencyCreateEventHandler>();
  453. pt::ptree dataTree = childTree.get_child("eventData", emptyTree);
  454. for (auto it = dataTree.begin(); it != dataTree.end(); ++it)
  455. {
  456. if (it->first == "itemType")
  457. {
  458. pDep->setItemType(it->second.data());
  459. }
  460. else if (it->first == "attribute")
  461. {
  462. std::string attrName = getXSDAttributeValue(it->second, "<xmlattr>.attributeName");
  463. std::string attrVal = getXSDAttributeValue(it->second, "<xmlattr>.attributeValue");
  464. std::string depAttr = getXSDAttributeValue(it->second, "<xmlattr>.dependentAttribute");
  465. std::string depVal = getXSDAttributeValue(it->second, "<xmlattr>.dependentValue");
  466. pDep->addDependency(attrName, attrVal, depAttr, depVal);
  467. }
  468. else if (it->first == "match")
  469. {
  470. std::string attrName = it->second.get("eventNodeAttribute", "").data();
  471. pDep->setEventNodeAttributeName(attrName);
  472. std::string matchAttrName = it->second.get("targetAttribute", "");
  473. if (!matchAttrName.empty())
  474. {
  475. pDep->setTargetAttributeName(matchAttrName);
  476. }
  477. std::string path = it->second.get("targetPath", "");
  478. pDep->setTargetPath(path);
  479. }
  480. }
  481. m_pSchemaItem->addEventHandler(pDep);
  482. }
  483. //
  484. // Insert XML is ued to insert XML ino the environment based on what's in the eventData section
  485. else if (eventAction == "insertXML")
  486. {
  487. std::shared_ptr<InsertEnvironmentDataCreateEventHandler> pInsert = std::make_shared<InsertEnvironmentDataCreateEventHandler>();
  488. pt::ptree dataTree = childTree.get_child("eventData", emptyTree);
  489. for (auto it = dataTree.begin(); it != dataTree.end(); ++it)
  490. {
  491. //
  492. // itemTye is the type of the item that was created for which the create event shall be sent.
  493. if (it->first == "itemType")
  494. {
  495. pInsert->setItemType(it->second.data());
  496. }
  497. //
  498. // The match section is optional. It is used to further qualify the conditions when XML is inserted. If missing,
  499. // the XML is inserted whenever a new node of "itemType" is inserted. When present, the following fields further
  500. // qualify when the XML is inserted.
  501. //
  502. // matchItemAttribute - name of attribute from created node whose value is compared with the value of an attribute in
  503. // another node (defined by matchLocalAttribte and matchPath)
  504. // matchPath - XPath to select the node for comparing attribute values
  505. // matchLocalAttribute - name of attribute from node selected by matchPath for value comparison. This option is
  506. // optional. If not present, the name of the attribute for comparison in the selected node is
  507. // matchItemAttribute
  508. else if (it->first == "match")
  509. {
  510. std::string attrName = it->second.get("eventNodeAttribute", "");
  511. pInsert->setEventNodeAttributeName(attrName);
  512. std::string matchAttrName = it->second.get("targetAttribute", "");
  513. if (!matchAttrName.empty())
  514. {
  515. pInsert->setTargetAttributeName(matchAttrName);
  516. }
  517. std::string path = it->second.get("targetPath", "");
  518. pInsert->setTargetPath(path);
  519. }
  520. //
  521. // the XML to be inserted. It is inserted based on the match section
  522. else if (it->first == "xml")
  523. {
  524. pt::ptree emptyTree;
  525. const pt::ptree &insertXMLTree = it->second.get_child("", emptyTree);
  526. std::ostringstream out;
  527. pt::write_xml(out, insertXMLTree);
  528. pInsert->setEnvironmentInsertData(out.str());
  529. }
  530. }
  531. m_pSchemaItem->addEventHandler(pInsert);
  532. }
  533. //
  534. // addAttributeDependencies is used to set dependent values for an attribute based on the value of another attribute.
  535. else if (eventAction == "setAttributeValue")
  536. {
  537. std::shared_ptr<AttributeSetValueCreateEventHandler> pSetAttrValue = std::make_shared<AttributeSetValueCreateEventHandler>();
  538. pt::ptree dataTree = childTree.get_child("eventData", emptyTree);
  539. for (auto it = dataTree.begin(); it != dataTree.end(); ++it)
  540. {
  541. if (it->first == "itemType")
  542. {
  543. pSetAttrValue->setItemType(it->second.data());
  544. }
  545. else if (it->first == "attribute")
  546. {
  547. std::string attrName = getXSDAttributeValue(it->second, "<xmlattr>.attributeName");
  548. std::string attrVal = getXSDAttributeValue(it->second, "<xmlattr>.attributeValue");
  549. pSetAttrValue->addAttributeValue(attrName, attrVal);
  550. }
  551. else if (it->first == "match")
  552. {
  553. std::string attrName = it->second.get("eventNodeAttribute", "");
  554. pSetAttrValue->setEventNodeAttributeName(attrName);
  555. std::string matchAttrName = it->second.get("targetAttribute", "");
  556. if (!matchAttrName.empty())
  557. {
  558. pSetAttrValue->setTargetAttributeName(matchAttrName);
  559. }
  560. std::string path = it->second.get("targetPath", "");
  561. pSetAttrValue->setTargetPath(path);
  562. }
  563. }
  564. m_pSchemaItem->addEventHandler(pSetAttrValue);
  565. }
  566. }
  567. }
  568. }
  569. void XSDSchemaParser::processXSDFiles(const std::string &path, const std::string &ignore)
  570. {
  571. Owned<IFile> pDir = createIFile(path.c_str());
  572. if (pDir->exists())
  573. {
  574. Owned<IDirectoryIterator> it = pDir->directoryFiles(nullptr, false, false);
  575. ForEach(*it)
  576. {
  577. StringBuffer fname;
  578. std::string filename = it->getName(fname).str();
  579. if (filename != ignore)
  580. {
  581. std::size_t dotPos = filename.find_last_of('.');
  582. if (dotPos != std::string::npos)
  583. {
  584. //
  585. // If the file has an XSD extension and not previously processed, build the fully
  586. // qualified name and parse it.
  587. std::string ext = filename.substr(dotPos + 1);
  588. std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
  589. if (ext == "xsd" && m_pSchemaItem->addUniqueName(filename))
  590. {
  591. std::string fullyQualifiedFilePath = path;
  592. if (std::string(1, fullyQualifiedFilePath.back()) != PATHSEPSTR)
  593. fullyQualifiedFilePath += PATHSEPSTR;
  594. fullyQualifiedFilePath += filename;
  595. parseXSD(fullyQualifiedFilePath);
  596. }
  597. }
  598. }
  599. }
  600. }
  601. }
  602. std::shared_ptr<SchemaType> XSDSchemaParser::getType(const pt::ptree &typeTree, bool nameRequired)
  603. {
  604. std::string typeName = getXSDAttributeValue(typeTree, "<xmlattr>.name", nameRequired, "");
  605. if (!nameRequired && !typeName.empty())
  606. {
  607. std::string msg = "Name (" + typeName + ") not allowed in local xs:simpleType definition";
  608. throw(ParseException(msg));
  609. }
  610. std::shared_ptr<SchemaType> pCfgType = std::make_shared<SchemaType>(typeName);
  611. std::shared_ptr<SchemaTypeLimits> pLimits;
  612. auto restriction = typeTree.find("xs:restriction");
  613. if (restriction != typeTree.not_found())
  614. {
  615. std::string xsdBaseType = getXSDAttributeValue(restriction->second, "<xmlattr>.base");
  616. std::shared_ptr<SchemaType> pBaseType = m_pSchemaItem->getSchemaValueType(xsdBaseType);
  617. pCfgType->setBaseType(pBaseType->getBaseType());
  618. if (typeName != pBaseType->getBaseType())
  619. {
  620. pCfgType->setSubType(typeName);
  621. }
  622. pLimits = pBaseType->getLimits();
  623. if (!restriction->second.empty())
  624. {
  625. pt::ptree restrictTree = restriction->second.get_child("", pt::ptree());
  626. if (std::dynamic_pointer_cast<SchemaTypeIntegerLimits>(pLimits) != nullptr)
  627. {
  628. std::shared_ptr<SchemaTypeIntegerLimits> pBaseIntLimits = std::dynamic_pointer_cast<SchemaTypeIntegerLimits>(pLimits);
  629. std::shared_ptr<SchemaTypeIntegerLimits> pIntLimits = std::make_shared<SchemaTypeIntegerLimits>(*pBaseIntLimits);
  630. parseIntegerTypeLimits(restrictTree, pIntLimits);
  631. pLimits = pIntLimits;
  632. }
  633. else if (std::dynamic_pointer_cast<SchemaTypeStringLimits>(pLimits) != nullptr)
  634. {
  635. std::shared_ptr<SchemaTypeStringLimits> pBaseStringimits = std::dynamic_pointer_cast<SchemaTypeStringLimits>(pLimits);
  636. std::shared_ptr<SchemaTypeStringLimits> pStringimits = std::make_shared<SchemaTypeStringLimits>(*pBaseStringimits);
  637. parseStringTypeLimits(restrictTree, pStringimits);
  638. pLimits = pStringimits;
  639. }
  640. else
  641. {
  642. std::string msg = "Unsupported base type(" + xsdBaseType + ")";
  643. throw(ParseException(msg));
  644. }
  645. }
  646. }
  647. pCfgType->setLimits(pLimits);
  648. return pCfgType;
  649. }
  650. void XSDSchemaParser::parseIntegerTypeLimits(const pt::ptree &restrictTree, std::shared_ptr<SchemaTypeIntegerLimits> &pIntegerLimits)
  651. {
  652. for (auto it = restrictTree.begin(); it != restrictTree.end(); ++it)
  653. {
  654. std::string restrictionType = it->first;
  655. if (restrictionType == "xs:minInclusive")
  656. pIntegerLimits->setMinInclusive(it->second.get<int>("<xmlattr>.value"));
  657. else if (restrictionType == "xs:maxInclusive")
  658. pIntegerLimits->setMaxInclusive(it->second.get<int>("<xmlattr>.value"));
  659. else if (restrictionType == "xs:minExclusive")
  660. pIntegerLimits->setMinExclusive(it->second.get<int>("<xmlattr>.value"));
  661. else if (restrictionType == "xs:maxExclusive")
  662. pIntegerLimits->setMaxExclusive(it->second.get<int>("<xmlattr>.value"));
  663. else if (restrictionType == "xs:enumeration")
  664. {
  665. parseAllowedValue(it->second, &(*pIntegerLimits));
  666. }
  667. else if (restrictionType != "<xmlattr>")
  668. {
  669. std::string msg = "Invalid restriction(" + it->first + ") found while parsing type";
  670. throw(ParseException(msg));
  671. }
  672. }
  673. }
  674. void XSDSchemaParser::parseStringTypeLimits(const pt::ptree &restrictTree, std::shared_ptr<SchemaTypeStringLimits> &pStringLimits)
  675. {
  676. for (auto it = restrictTree.begin(); it != restrictTree.end(); ++it)
  677. {
  678. std::string restrictionType = it->first;
  679. if (restrictionType == "xs:minLength")
  680. pStringLimits->setMinLength(it->second.get<int>("<xmlattr>.value"));
  681. else if (restrictionType == "xs:maxLength")
  682. pStringLimits->setMaxLength(it->second.get<int>("<xmlattr>.value"));
  683. else if (restrictionType == "xs:length")
  684. pStringLimits->setLength(it->second.get<int>("<xmlattr>.value"));
  685. else if (restrictionType == "xs:pattern")
  686. pStringLimits->addPattern(it->second.get("<xmlattr>.value", "0"));
  687. else if (restrictionType == "xs:enumeration")
  688. {
  689. parseAllowedValue(it->second, &(*pStringLimits));
  690. }
  691. else if (restrictionType != "<xmlattr>")
  692. {
  693. std::string msg = "Invalid restriction(" + it->first + ") found while parsing type";
  694. throw(ParseException(msg));
  695. }
  696. }
  697. }
  698. void XSDSchemaParser::parseAllowedValue(const pt::ptree &allowedValueTree, SchemaTypeLimits *pTypeLimits)
  699. {
  700. AllowedValue allowedValue;
  701. //
  702. // Parse the value for the enumeration, the add to the allowed values for the limits for this type. Note that enumerations
  703. // are enhanced with additional information for the UI.
  704. allowedValue.m_value = allowedValueTree.get("<xmlattr>.value", "XXXmissingYYY");
  705. allowedValue.m_displayName = allowedValueTree.get("<xmlattr>.hpcc:displayName", allowedValue.m_value);
  706. allowedValue.m_description = allowedValueTree.get("<xmlattr>.hpcc:description", "");
  707. allowedValue.m_userMessage = allowedValueTree.get("<xmlattr>.hpcc:userMessage", "");
  708. allowedValue.m_userMessageType = allowedValueTree.get("<xmlattr>.hpcc:userMessageType", allowedValue.m_userMessage.empty() ? "" : "info");
  709. //
  710. // Parse any attribute lists
  711. std::string attrList = allowedValueTree.get("<xmlattr>.hpcc:optionalAttributes", "");
  712. if (attrList.length())
  713. {
  714. allowedValue.m_optionalAttributes = splitString(attrList, ",");
  715. }
  716. attrList = allowedValueTree.get("<xmlattr>.hpcc:requiredAttributes", "");
  717. if (attrList.length())
  718. {
  719. allowedValue.m_requiredAttributes = splitString(attrList, ",");
  720. }
  721. //
  722. // Value is required. Throw an exception if not found
  723. if (allowedValue.m_value == "XXXmissingYYY")
  724. {
  725. std::string msg = "Missing value attribute for enumeration";
  726. throw(ParseException(msg));
  727. }
  728. pTypeLimits->addAllowedValue(allowedValue);
  729. }
  730. std::shared_ptr<SchemaValue> XSDSchemaParser::getSchemaValue(const pt::ptree &attr)
  731. {
  732. std::string attrName = getXSDAttributeValue(attr, "<xmlattr>.name");
  733. if (!attr.get("<xmlattr>.default", "").empty())
  734. {
  735. throw(ParseException( "Attribute " + m_pSchemaItem->getProperty("name") + "[@" + attrName + "], XSD default is not supported, use hpcc:presetValue or hpcc:forcedConfigValue instead"));
  736. }
  737. std::shared_ptr<SchemaValue> pCfgValue = std::make_shared<SchemaValue>(attrName);
  738. pCfgValue->setDisplayName(attr.get("<xmlattr>.hpcc:displayName", attrName));
  739. pCfgValue->setRequired(attr.get("<xmlattr>.use", "optional") == "required");
  740. pCfgValue->setTooltip(attr.get("<xmlattr>.hpcc:tooltip", ""));
  741. pCfgValue->setReadOnly(attr.get("<xmlattr>.hpcc:readOnly", "false") == "true");
  742. pCfgValue->setDeprecated(attr.get("<xmlattr>.hpcc:deprecated", "false") == "true");
  743. pCfgValue->setNoOutput(attr.get("<xmlattr>.hpcc:noOutput", "false") == "true");
  744. pCfgValue->setMirrorFromPath(attr.get("<xmlattr>.hpcc:mirrorFrom", ""));
  745. pCfgValue->setAutoGenerateType(attr.get("<xmlattr>.hpcc:autoGenerateType", ""));
  746. pCfgValue->setAutoGenerateValue(attr.get("<xmlattr>.hpcc:autoGenerateValue", ""));
  747. pCfgValue->setValueLimitRuleType(attr.get("<xmlattr>.hpcc:valueLimitRuleType", ""));
  748. pCfgValue->setValueLimitRuleData(attr.get("<xmlattr>.hpcc:valueLimitRuleData", ""));
  749. pCfgValue->setRequiredIf(attr.get("<xmlattr>.hpcc:requiredIf", ""));
  750. //
  751. // Process the various hidden/visible flags and ensure no conflicts
  752. std::string hidden = attr.get("<xmlattr>.hpcc:hidden", "");
  753. std::string hiddenIf = attr.get("<xmlattr>.hpcc:hiddenIf", "");
  754. std::string visibleIf = attr.get("<xmlattr>.hpcc:visibleIf", "");
  755. unsigned countAttrs = (hidden.empty() ? 0 : 1) + (hiddenIf.empty() ? 0 : 1) + (visibleIf.empty() ? 0 : 1);
  756. if (countAttrs > 1)
  757. {
  758. throw(ParseException( "Attribute " + m_pSchemaItem->getProperty("name") + "[@" + attrName + "] Only one of hpcc:hidden, hpcc:hiddenIf or hpcc:visibleIf may be specified"));
  759. }
  760. if (!hidden.empty())
  761. {
  762. pCfgValue->setHidden(hidden == "true");
  763. }
  764. else
  765. {
  766. pCfgValue->setHiddenIf(!hidden.empty() ? hiddenIf : visibleIf);
  767. pCfgValue->setInvertHiddenIf(!visibleIf.empty());
  768. }
  769. //
  770. // Defaults
  771. std::string preset = attr.get("<xmlattr>.hpcc:presetValue", ""); // This is the value used by code, preset, if no value provided (informational only)
  772. std::string forcedConfigValue = attr.get("<xmlattr>.hpcc:forcedConfigValue", ""); // this value is written to the environment if non is provided
  773. if (!preset.empty() && !forcedConfigValue.empty())
  774. {
  775. throw(ParseException( "Attribute " + m_pSchemaItem->getProperty("name") + "[@" + attrName + "] Only one of hpcc:presetValue or hpcc:forcedConfigValue may be specified"));
  776. }
  777. else if (!preset.empty())
  778. {
  779. pCfgValue->setPresetValue(preset);
  780. }
  781. else
  782. {
  783. pCfgValue->setForcedValue(forcedConfigValue);
  784. }
  785. std::string modList = attr.get("<xmlattr>.hpcc:modifiers", "");
  786. if (modList.length())
  787. {
  788. pCfgValue->setModifiers(splitString(modList, ","));
  789. }
  790. std::string typeName = attr.get("<xmlattr>.type", "");
  791. if (!typeName.empty())
  792. {
  793. pCfgValue->setType(m_pSchemaItem->getSchemaValueType(typeName));
  794. }
  795. else
  796. {
  797. std::shared_ptr<SchemaType> pType = getType(attr.get_child("xs:simpleType", pt::ptree()), false);
  798. if (!pType->isValid())
  799. {
  800. throw(ParseException("Attribute " + m_pSchemaItem->getProperty("name") + "[@" + attrName + "] does not have a valid type"));
  801. }
  802. pCfgValue->setType(pType);
  803. }
  804. //
  805. // Keyed value or from a keyed set?
  806. std::string uniqueKey = attr.get("<xmlattr>.hpcc:uniqueKey", "");
  807. std::string sourceKey = attr.get("<xmlattr>.hpcc:sourceKey", "");
  808. //
  809. // Make sure both aren't specified
  810. if (!uniqueKey.empty() && !sourceKey.empty())
  811. {
  812. throw(ParseException("Attribute " + m_pSchemaItem->getProperty("name") + "[@" + attrName + "] cannot be both unique and from a source path"));
  813. }
  814. //
  815. // If value must be unique, add a unique value set definition
  816. if (!uniqueKey.empty())
  817. {
  818. std::string elementPath = "./";
  819. m_pSchemaItem->addUniqueAttributeValueSetDefinition(uniqueKey, elementPath, attrName, true);
  820. }
  821. //
  822. // If the value must be from an existing attribute, add reference to such
  823. else if (!sourceKey.empty())
  824. {
  825. std::string elementPath = "./";
  826. m_pSchemaItem->addReferenceToUniqueAttributeValueSet(sourceKey, elementPath, attrName);
  827. }
  828. return pCfgValue;
  829. }