SchemaItem.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  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 "SchemaItem.hpp"
  14. #include "Exceptions.hpp"
  15. #include "SchemaTypeStringLimits.hpp"
  16. #include "SchemaTypeIntegerLimits.hpp"
  17. #include <algorithm>
  18. // static class variables
  19. //std::map<std::string, std::vector<std::shared_ptr<SchemaValue>>> SchemaItem::m_uniqueAttributeValueSets;
  20. SchemaItem::SchemaItem(const std::string &name, const std::string &className, const std::shared_ptr<SchemaItem> &pParent) :
  21. m_pParent(pParent),
  22. m_hidden(false),
  23. m_minInstances(1),
  24. m_maxInstances(1)
  25. {
  26. //
  27. // Set property defaults
  28. m_properties["name"] = name;
  29. m_properties["displayName"] = name;
  30. m_properties["className"] = className;
  31. //
  32. // If this is a root node (no parent), then do some additional init
  33. if (m_pParent.expired())
  34. {
  35. //
  36. // Create a default type so that all values have a type
  37. std::shared_ptr<SchemaType> pDefaultType = std::make_shared<SchemaType>("default");
  38. std::shared_ptr<SchemaTypeLimits> pDefaultLimits = std::make_shared<SchemaTypeLimits>();
  39. pDefaultType->setLimits(pDefaultLimits);
  40. addSchemaValueType(pDefaultType);
  41. }
  42. }
  43. SchemaItem::SchemaItem(const SchemaItem &item)
  44. {
  45. //
  46. // Copy stuff that doesn't have to be unique
  47. m_hidden = item.m_hidden;
  48. m_maxInstances = item.m_maxInstances;
  49. m_minInstances = item.m_minInstances;
  50. //m_nodeInsertData = item.m_nodeInsertData;
  51. m_properties = item.m_properties;
  52. m_types = item.m_types;
  53. m_schemaTypes = item.m_schemaTypes;
  54. m_pParent = item.m_pParent;
  55. if (m_pItemValue)
  56. m_pItemValue = std::make_shared<SchemaValue>(*(item.m_pItemValue)); // copy constructed
  57. //
  58. // Make a copy of the children now
  59. for (auto childIt = item.m_children.begin(); childIt != item.m_children.end(); ++childIt)
  60. {
  61. addChild(std::make_shared<SchemaItem>(*(childIt->second)));
  62. }
  63. //
  64. // Copy the attributes
  65. for (auto attrIt = item.m_attributes.begin(); attrIt != item.m_attributes.end(); ++attrIt)
  66. {
  67. addAttribute(std::make_shared<SchemaValue>(*(attrIt->second)));
  68. }
  69. //
  70. // Event handlers
  71. m_eventHandlers = item.m_eventHandlers;
  72. m_uniqueAttributeValueSetReferences = item.m_uniqueAttributeValueSetReferences;
  73. m_uniqueAttributeValueSetDefs = item.m_uniqueAttributeValueSetDefs;
  74. }
  75. void SchemaItem::addSchemaValueType(const std::shared_ptr<SchemaType> &pType)
  76. {
  77. m_types[pType->getName()] = pType;
  78. }
  79. std::shared_ptr<SchemaType> SchemaItem::getSchemaValueType(const std::string &typeName, bool throwIfNotPresent) const
  80. {
  81. std::shared_ptr<SchemaType> pType;
  82. auto it = m_types.find(typeName);
  83. if (it != m_types.end())
  84. {
  85. return it->second;
  86. }
  87. else
  88. {
  89. std::shared_ptr<SchemaItem> pParent = m_pParent.lock();
  90. if (pParent)
  91. {
  92. return pParent->getSchemaValueType(typeName, throwIfNotPresent);
  93. }
  94. }
  95. //
  96. // Did not find the type
  97. if (throwIfNotPresent)
  98. {
  99. std::string msg = "Unable to find type: " + typeName;
  100. throw(ParseException(msg));
  101. }
  102. return pType;
  103. }
  104. bool SchemaItem::addUniqueName(const std::string keyName)
  105. {
  106. auto result = m_keys.insert(keyName);
  107. return result.second; // true if keyName did not already exist
  108. }
  109. void SchemaItem::addSchemaType(const std::shared_ptr<SchemaItem> &pItem, const std::string &typeName)
  110. {
  111. auto it = m_schemaTypes.find(typeName);
  112. if (it == m_schemaTypes.end())
  113. {
  114. m_schemaTypes[typeName] = pItem;
  115. }
  116. else
  117. {
  118. throw(ParseException("Duplicate config type found: " + m_properties["name"]));
  119. }
  120. }
  121. std::shared_ptr<SchemaItem> SchemaItem::getSchemaType(const std::string &name, bool throwIfNotPresent) const
  122. {
  123. std::shared_ptr<SchemaItem> pItem;
  124. auto it = m_schemaTypes.find(name);
  125. if (it != m_schemaTypes.end())
  126. {
  127. return it->second;
  128. }
  129. else
  130. {
  131. std::shared_ptr<SchemaItem> pParent = m_pParent.lock();
  132. if (pParent)
  133. {
  134. return pParent->getSchemaType(name, throwIfNotPresent);
  135. }
  136. }
  137. //
  138. // Did not find the type
  139. if (throwIfNotPresent)
  140. {
  141. std::string msg = "Unable to find config type: " + name;
  142. throw(ParseException(msg));
  143. }
  144. return pItem;
  145. }
  146. //
  147. // This method is used to insert a named type into the current schema item. This is done by making copies
  148. // of the relevant members and inserting them into this instance
  149. void SchemaItem::insertSchemaType(const std::shared_ptr<SchemaItem> pTypeItem)
  150. {
  151. //
  152. // Attributes
  153. std::vector< std::shared_ptr<SchemaValue>> typeAttributes;
  154. pTypeItem->getAttributes(typeAttributes);
  155. for (auto attrIt = typeAttributes.begin(); attrIt != typeAttributes.end(); ++attrIt)
  156. {
  157. std::shared_ptr<SchemaValue> pNewAttr = std::make_shared<SchemaValue>(*(*attrIt));
  158. addAttribute(pNewAttr);
  159. }
  160. //
  161. // Type main value
  162. if (pTypeItem->getItemSchemaValue() != nullptr)
  163. {
  164. std::shared_ptr<SchemaValue> pNewItemCfgValue = std::make_shared<SchemaValue>(*(pTypeItem->getItemSchemaValue()));
  165. setItemSchemaValue(pNewItemCfgValue);
  166. }
  167. //
  168. // Unique attribute sets
  169. for (auto setIt = pTypeItem->m_uniqueAttributeValueSetDefs.begin(); setIt != pTypeItem->m_uniqueAttributeValueSetDefs.end(); ++setIt)
  170. {
  171. m_uniqueAttributeValueSetDefs.insert({ setIt->first, setIt->second });
  172. }
  173. //
  174. // Unique attribute reference sets
  175. for (auto it = pTypeItem->m_uniqueAttributeValueSetReferences.begin(); it != pTypeItem->m_uniqueAttributeValueSetReferences.end(); ++it)
  176. {
  177. m_uniqueAttributeValueSetReferences.insert({ it->first, it->second });
  178. }
  179. //
  180. // Children
  181. std::vector<std::shared_ptr<SchemaItem>> typeChildren;
  182. pTypeItem->getChildren(typeChildren);
  183. for (auto childIt = typeChildren.begin(); childIt != typeChildren.end(); ++childIt)
  184. {
  185. std::shared_ptr<SchemaItem> pNewItem = std::make_shared<SchemaItem>(*(*childIt));
  186. addChild(pNewItem);
  187. }
  188. }
  189. void SchemaItem::addAttribute(const std::shared_ptr<SchemaValue> &pCfgValue)
  190. {
  191. auto retVal = m_attributes.insert({ pCfgValue->getName(), pCfgValue });
  192. if (!retVal.second)
  193. {
  194. throw(ParseException("Duplicate attribute (" + pCfgValue->getName() + ") found for element " + m_properties["name"]));
  195. }
  196. }
  197. void SchemaItem::addAttribute(const std::map<std::string, std::shared_ptr<SchemaValue>> &attributes)
  198. {
  199. for (auto it = attributes.begin(); it != attributes.end(); ++it)
  200. addAttribute(it->second);
  201. }
  202. void SchemaItem::addAttribute(const std::vector<std::shared_ptr<SchemaValue>> &attributes)
  203. {
  204. for (auto it=attributes.begin(); it!=attributes.end(); ++it)
  205. addAttribute(*it);
  206. }
  207. void SchemaItem::getAttributes(std::vector<std::shared_ptr<SchemaValue>> &attributes) const
  208. {
  209. for (auto it = m_attributes.begin(); it != m_attributes.end(); ++it)
  210. attributes.push_back(it->second);
  211. }
  212. std::shared_ptr<SchemaValue> SchemaItem::getAttribute(const std::string &name, bool createIfDoesNotExist) const
  213. {
  214. std::shared_ptr<SchemaValue> pCfgValue;
  215. auto it = m_attributes.find(name);
  216. if (it != m_attributes.end())
  217. {
  218. pCfgValue = it->second;
  219. }
  220. else if (createIfDoesNotExist)
  221. {
  222. // not found, build a default cfg value for the undefined attribute
  223. pCfgValue = std::make_shared<SchemaValue>(name, false);
  224. pCfgValue->setType(getSchemaValueType("default"));
  225. }
  226. return pCfgValue;
  227. }
  228. void SchemaItem::addUniqueAttributeValueSetDefinition(const std::string &setName, const std::string &elementPath, const std::string &attributeName, bool duplicateOk)
  229. {
  230. m_uniqueAttributeValueSetDefs.insert({ setName, SetInfo(setName, elementPath, attributeName, duplicateOk) }); // these are processed later
  231. }
  232. void SchemaItem::addReferenceToUniqueAttributeValueSet(const std::string &setName, const std::string &elementPath, const std::string &attributeName)
  233. {
  234. m_uniqueAttributeValueSetReferences.insert({ setName, SetInfo(setName, elementPath, attributeName) }); // these are processed later
  235. }
  236. void SchemaItem::processUniqueAttributeValueSetReferences(const std::map<std::string, std::vector<std::shared_ptr<SchemaValue>>> &uniqueAttributeValueSets)
  237. {
  238. for (auto setRefIt = m_uniqueAttributeValueSetReferences.begin(); setRefIt != m_uniqueAttributeValueSetReferences.end(); ++setRefIt)
  239. {
  240. auto keyIt = uniqueAttributeValueSets.find(setRefIt->second.m_setName);
  241. if (keyIt != uniqueAttributeValueSets.end())
  242. {
  243. for (auto cfgIt = keyIt->second.begin(); cfgIt != keyIt->second.end(); ++cfgIt)
  244. {
  245. std::shared_ptr<SchemaValue> pKeyRefAttribute = *cfgIt; // this is the reference attribute from which attributeName must be a member
  246. std::string cfgValuePath = ((setRefIt->second.m_elementPath != ".") ? setRefIt->second.m_elementPath : "") + "@" + setRefIt->second.m_attributeName;
  247. std::vector<std::shared_ptr<SchemaValue>> cfgValues;
  248. fetchSchemaValues(cfgValuePath, cfgValues);
  249. if (!cfgValues.empty())
  250. {
  251. for (auto attrIt = cfgValues.begin(); attrIt != cfgValues.end(); ++attrIt)
  252. {
  253. (*attrIt)->setUniqueValueSetRef(pKeyRefAttribute);
  254. }
  255. }
  256. else
  257. {
  258. throw(ParseException("Attribute " + (setRefIt->second.m_attributeName + " not found when adding keyRef for key " + (setRefIt->second.m_setName))));
  259. }
  260. }
  261. }
  262. else
  263. {
  264. throw(ParseException("Keyref to key '" + (setRefIt->second.m_setName + "' was not found")));
  265. }
  266. }
  267. }
  268. void SchemaItem::getChildren(std::vector<std::shared_ptr<SchemaItem>> &children) const
  269. {
  270. for (auto it = m_children.begin(); it != m_children.end(); ++it)
  271. {
  272. children.push_back(it->second);
  273. }
  274. }
  275. std::shared_ptr<SchemaItem> SchemaItem::getChild(const std::string &name)
  276. {
  277. std::shared_ptr<SchemaItem> pItem = std::make_shared<SchemaItem>(name, "default", shared_from_this());
  278. auto it = m_children.find(name); // only return the first one
  279. if (it != m_children.end())
  280. {
  281. pItem = it->second;
  282. }
  283. return pItem;
  284. }
  285. std::shared_ptr<SchemaItem> SchemaItem::getChildByComponent(const std::string &name, std::string &componentName)
  286. {
  287. std::shared_ptr<SchemaItem> pItem = std::make_shared<SchemaItem>(name, "default", shared_from_this());
  288. auto childItRange = m_children.equal_range(name);
  289. for (auto childIt = childItRange.first; childIt != childItRange.second; ++childIt)
  290. {
  291. if (childIt->second->getProperty("componentName") == componentName)
  292. {
  293. pItem = childIt->second;
  294. break;
  295. }
  296. }
  297. return pItem;
  298. }
  299. void SchemaItem::resetEnvironment()
  300. {
  301. for (auto it = m_attributes.begin(); it != m_attributes.end(); ++it)
  302. {
  303. it->second->resetEnvironment();
  304. }
  305. }
  306. void SchemaItem::fetchSchemaValues(const std::string &path, std::vector<std::shared_ptr<SchemaValue>> &schemaValues)
  307. {
  308. bool rootPath = path[0] == '/'; //todo: convert this to use the findSchemaRoot below
  309. //
  310. // If path is from the root, and we aren't the root, pass the request to our parent
  311. if (rootPath && !m_pParent.expired())
  312. {
  313. std::shared_ptr<SchemaItem> pParent = m_pParent.lock();
  314. if (pParent)
  315. {
  316. return pParent->fetchSchemaValues(path, schemaValues);
  317. }
  318. }
  319. //
  320. // Break the path down and process it
  321. size_t start = rootPath ? 1 : 0; // skip leading slash if we are at the root
  322. size_t end = path.find_first_of("/@", start);
  323. if (end != std::string::npos)
  324. {
  325. std::string elem = path.substr(start, end - start);
  326. if (rootPath)
  327. {
  328. if (m_properties["name"] == elem)
  329. {
  330. return fetchSchemaValues(path.substr(end + 1), schemaValues);
  331. }
  332. else
  333. {
  334. throw(ParseException("Unable to find root element '" + elem + "' when searching path '" + path + "'"));
  335. }
  336. }
  337. if (path[0] == '@')
  338. {
  339. std::string attrName = path.substr(1);
  340. auto rangeIt = m_attributes.equal_range(attrName);
  341. for (auto it = rangeIt.first; it != rangeIt.second; ++it)
  342. {
  343. schemaValues.push_back(it->second);
  344. }
  345. }
  346. else
  347. {
  348. auto rangeIt = m_children.equal_range(elem);
  349. for (auto it = rangeIt.first; it != rangeIt.second; ++it)
  350. {
  351. it->second->fetchSchemaValues(path.substr(end + ((path[end] == '/') ? 1 : 0)), schemaValues);
  352. }
  353. }
  354. }
  355. return;
  356. }
  357. std::shared_ptr<const SchemaItem> SchemaItem::getSchemaRoot() const
  358. {
  359. if (!m_pParent.expired())
  360. {
  361. return m_pParent.lock()->getSchemaRoot();
  362. }
  363. std::shared_ptr<const SchemaItem> ptr = shared_from_this();
  364. return ptr;
  365. }
  366. void SchemaItem::processDefinedUniqueAttributeValueSets(std::map<std::string, std::vector<std::shared_ptr<SchemaValue>>> &uniqueAttributeValueSets)
  367. {
  368. for (auto setIt = m_uniqueAttributeValueSetDefs.begin(); setIt != m_uniqueAttributeValueSetDefs.end(); ++setIt)
  369. {
  370. auto it = uniqueAttributeValueSets.find(setIt->first);
  371. bool keyDefExists = it != uniqueAttributeValueSets.end();
  372. if (!keyDefExists || setIt->second.m_duplicateOk)
  373. {
  374. std::string cfgValuePath = ((setIt->second.m_elementPath != ".") ? setIt->second.m_elementPath : "") + "@" + setIt->second.m_attributeName;
  375. std::vector<std::shared_ptr<SchemaValue>> cfgValues;
  376. fetchSchemaValues(cfgValuePath, cfgValues);
  377. if (!cfgValues.empty())
  378. {
  379. //
  380. // For each attribute, if it does not already exist in the list of attributes making up this
  381. // key value, add it.
  382. for (auto attrIt = cfgValues.begin(); attrIt != cfgValues.end(); ++attrIt)
  383. {
  384. (*attrIt)->setUniqueValue(true);
  385. if (!keyDefExists)
  386. {
  387. std::vector<std::shared_ptr<SchemaValue>> values;
  388. values.push_back(*attrIt);
  389. it = uniqueAttributeValueSets.insert({ setIt->second.m_setName, values }).first; // so the else condition will work
  390. keyDefExists = true; // Now, it does exist
  391. }
  392. else
  393. {
  394. std::vector<std::shared_ptr<SchemaValue>> &values = it->second;
  395. bool found = false;
  396. for (auto cfgIt = values.begin(); cfgIt != values.end() && !found; ++cfgIt)
  397. {
  398. found = (*cfgIt) == (*attrIt);
  399. }
  400. if (!found)
  401. values.push_back(*attrIt);
  402. }
  403. }
  404. }
  405. else
  406. {
  407. throw(ParseException("Attribute " + setIt->second.m_attributeName + " not found for key " + setIt->second.m_setName));
  408. }
  409. }
  410. else
  411. {
  412. throw(ParseException("Duplicate key (" + setIt->second.m_setName + ") found for element " + m_properties["name"]));
  413. }
  414. }
  415. //
  416. // Post process all of our children now
  417. for (auto it = m_children.begin(); it != m_children.end(); ++it)
  418. {
  419. it->second->processDefinedUniqueAttributeValueSets(uniqueAttributeValueSets);
  420. }
  421. }
  422. void SchemaItem::postProcessConfig(const std::map<std::string, std::vector<std::shared_ptr<SchemaValue>>> &uniqueAttributeValueSets)
  423. {
  424. //
  425. // Make sure that the item type value for all children that are insertable (minRequired = 0 or maxAllowed > minRequired)
  426. std::set<std::string> itemTypes;
  427. for (auto it = m_children.begin(); it != m_children.end(); ++it)
  428. {
  429. if (it->second->isInsertable())
  430. {
  431. auto rc = itemTypes.insert(it->second->getItemType());
  432. if (!rc.second)
  433. {
  434. throw(ParseException("Duplicate itemType(" + it->second->getItemType() + ") found for element " + m_properties["name"]));
  435. }
  436. }
  437. }
  438. processUniqueAttributeValueSetReferences(uniqueAttributeValueSets);
  439. //
  440. // Post process the attributes
  441. for (auto it = m_attributes.begin(); it != m_attributes.end(); ++it)
  442. {
  443. //
  444. // If this is a mirroed value, go find the source and attach ourselves so that if that value changes,
  445. // it is replicated to us.
  446. if (it->second->isMirroredValue())
  447. {
  448. std::vector<std::shared_ptr<SchemaValue>> cfgValues;
  449. fetchSchemaValues(it->second->getMirrorFromPath(), cfgValues);
  450. if (!cfgValues.empty() && cfgValues.size() == 1)
  451. {
  452. if (cfgValues.size() == 1)
  453. {
  454. it->second->addMirroredSchemaValue(cfgValues[0]);
  455. }
  456. else
  457. {
  458. throw(ParseException("Multiple sources found for mirror from path for attribute " + it->second->getName() + " (path=" + it->second->getMirrorFromPath()));
  459. }
  460. }
  461. else
  462. {
  463. throw(ParseException("Mirrored from source not found for attribute " + it->second->getName() + " path=" + it->second->getMirrorFromPath()));
  464. }
  465. }
  466. }
  467. //
  468. // Post process all of our children now
  469. for (auto it = m_children.begin(); it!= m_children.end(); ++it)
  470. {
  471. it->second->postProcessConfig(uniqueAttributeValueSets);
  472. }
  473. }
  474. std::string SchemaItem::getItemType() const
  475. {
  476. //
  477. // Return itemType based on this set of rules
  478. if (!getProperty("itemType").empty())
  479. return getProperty("itemType");
  480. else if (!getProperty("componentName").empty())
  481. return getProperty("componentName");
  482. return getProperty("name");
  483. }
  484. std::string SchemaItem::getProperty(const std::string &name, const std::string &dflt) const
  485. {
  486. std::string retVal = dflt;
  487. auto it = m_properties.find(name);
  488. if (it != m_properties.end())
  489. {
  490. retVal = it->second;
  491. }
  492. return retVal;
  493. }
  494. void SchemaItem::processEvent(const std::string &eventType, const std::shared_ptr<EnvironmentNode> &pEventSourceNode) const
  495. {
  496. //
  497. // Loop through any event handlers we may have
  498. for (auto eventIt = m_eventHandlers.begin(); eventIt != m_eventHandlers.end(); ++eventIt)
  499. {
  500. (*eventIt)->processEvent(eventType, pEventSourceNode);
  501. }
  502. //
  503. // Pass the event on because events are broadcast
  504. std::vector<std::shared_ptr<SchemaItem>> children;
  505. getChildren(children);
  506. for (auto childIt = children.begin(); childIt != children.end(); ++childIt)
  507. {
  508. (*childIt)->processEvent(eventType, pEventSourceNode);
  509. }
  510. }