EnvironmentNode.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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 "EnvironmentNode.hpp"
  14. #include "Exceptions.hpp"
  15. #include "Utils.hpp"
  16. void EnvironmentNode::addChild(std::shared_ptr<EnvironmentNode> pNode)
  17. {
  18. m_children.insert(std::make_pair(pNode->getName(), pNode));
  19. }
  20. bool EnvironmentNode::removeChild(const std::shared_ptr<EnvironmentNode> pNode)
  21. {
  22. bool removed = false;
  23. for (auto it=m_children.begin(); it!= m_children.end() && !removed; ++it)
  24. {
  25. if (pNode == it->second)
  26. {
  27. m_children.erase(it);
  28. removed = true;
  29. }
  30. }
  31. return removed;
  32. }
  33. bool EnvironmentNode::addAttribute(const std::string &name, std::shared_ptr<EnvironmentValue> pValue)
  34. {
  35. auto retValue = m_attributes.insert(std::make_pair(name, pValue));
  36. return retValue.second;
  37. }
  38. void EnvironmentNode::getChildren(std::vector<std::shared_ptr<EnvironmentNode>> &childNodes, const std::string &name) const
  39. {
  40. if (name.empty())
  41. {
  42. for (auto nodeIt = m_children.begin(); nodeIt != m_children.end(); ++nodeIt)
  43. {
  44. childNodes.push_back(nodeIt->second);
  45. }
  46. }
  47. else
  48. {
  49. auto rangeIt = m_children.equal_range(name);
  50. for (auto it = rangeIt.first; it != rangeIt.second; ++it)
  51. {
  52. childNodes.push_back(it->second);
  53. }
  54. }
  55. }
  56. std::shared_ptr<EnvironmentNode> EnvironmentNode::getParent() const
  57. {
  58. std::shared_ptr<EnvironmentNode> pParent;
  59. if (!m_pParent.expired())
  60. {
  61. pParent = m_pParent.lock();
  62. }
  63. return pParent;
  64. }
  65. void EnvironmentNode::getAttributes(std::vector<std::shared_ptr<EnvironmentValue>> &attrs) const
  66. {
  67. for (auto attrIt = m_attributes.begin(); attrIt != m_attributes.end(); ++attrIt)
  68. {
  69. attrs.push_back(attrIt->second);
  70. }
  71. }
  72. void EnvironmentNode::addMissingAttributesFromConfig()
  73. {
  74. std::vector<std::shared_ptr<SchemaValue>> configuredAttributes;
  75. m_pSchemaItem->getAttributes(configuredAttributes);
  76. //
  77. // go through all the configured attrubutes and for each that is not present in our list, add it
  78. for (auto it = configuredAttributes.begin(); it != configuredAttributes.end(); ++it)
  79. {
  80. auto attrIt = m_attributes.find((*it)->getName());
  81. if (attrIt == m_attributes.end())
  82. {
  83. std::shared_ptr<SchemaValue> pCfgValue = *it;
  84. std::shared_ptr<EnvironmentValue> pEnvValue = std::make_shared<EnvironmentValue>(shared_from_this(), pCfgValue, pCfgValue->getName());
  85. pCfgValue->addEnvironmentValue(pEnvValue);
  86. addAttribute(pCfgValue->getName(), pEnvValue);
  87. }
  88. }
  89. }
  90. void EnvironmentNode::setAttributeValues(const std::vector<NameValue> &values, Status &status, bool allowInvalid, bool forceCreate)
  91. {
  92. for (auto it = values.begin(); it != values.end(); ++it)
  93. {
  94. setAttributeValue((*it).name, (*it).value, status, allowInvalid, forceCreate);
  95. }
  96. }
  97. void EnvironmentNode::setAttributeValue(const std::string &attrName, const std::string &value, Status &status, bool allowInvalid, bool forceCreate)
  98. {
  99. std::shared_ptr<EnvironmentValue> pEnvValue;
  100. auto it = m_attributes.find(attrName);
  101. if (it != m_attributes.end())
  102. {
  103. pEnvValue = it->second;
  104. }
  105. //
  106. // Not found on this node. See if the configuration defines the attribute. If so, set the value and move on.
  107. // If not and the forceCreate flag is set, create it.
  108. else if (forceCreate)
  109. {
  110. std::shared_ptr<SchemaValue> pCfgValue = m_pSchemaItem->getAttribute(attrName);
  111. pEnvValue = std::make_shared<EnvironmentValue>(shared_from_this(), pCfgValue, attrName);
  112. addAttribute(attrName, pEnvValue);
  113. if (!pCfgValue->isDefined())
  114. {
  115. status.addMsg(statusMsg::warning, getId(), attrName, "Undefined attribute did not exist in configuration, was created");
  116. }
  117. }
  118. if (pEnvValue)
  119. {
  120. pEnvValue->setValue(value, &status, allowInvalid);
  121. }
  122. else
  123. {
  124. status.addMsg(statusMsg::error, getId(), attrName, "The attribute does not exist and was not created");
  125. }
  126. }
  127. std::string EnvironmentNode::getAttributeValue(const std::string &name) const
  128. {
  129. std::string value;
  130. std::shared_ptr<EnvironmentValue> pAttribute = getAttribute(name);
  131. if (pAttribute)
  132. value = pAttribute->getValue();
  133. return value;
  134. }
  135. bool EnvironmentNode::setLocalValue(const std::string &newValue, Status &status, bool force)
  136. {
  137. bool rc = false;
  138. //
  139. // If no environment value is present, create one first
  140. if (!m_pLocalValue)
  141. {
  142. std::shared_ptr<SchemaValue> pCfgValue = m_pSchemaItem->getItemSchemaValue();
  143. m_pLocalValue = std::make_shared<EnvironmentValue>(shared_from_this(), pCfgValue, ""); // node's value has no name
  144. }
  145. rc = m_pLocalValue->setValue(newValue, &status, force);
  146. return rc;
  147. }
  148. std::string EnvironmentNode::getLocalValue() const
  149. {
  150. std::string value;
  151. if (m_pLocalValue)
  152. {
  153. value = m_pLocalValue->getValue();
  154. }
  155. return value;
  156. }
  157. void EnvironmentNode::validate(Status &status, bool includeChildren, bool includeHiddenNodes) const
  158. {
  159. if (!m_pSchemaItem->isHidden() || includeHiddenNodes)
  160. {
  161. //
  162. // Check node value
  163. if (m_pLocalValue)
  164. {
  165. m_pLocalValue->validate(status, m_id);
  166. }
  167. //
  168. // Check any attributes
  169. for (auto attrIt = m_attributes.begin(); attrIt != m_attributes.end(); ++attrIt)
  170. {
  171. attrIt->second->validate(status, m_id);
  172. //
  173. // If this value must be unique, make sure it is
  174. if (attrIt->second->getSchemaValue()->isUniqueValue())
  175. {
  176. bool found = false;
  177. std::vector<std::string> allValues;
  178. attrIt->second->getAllValuesForSiblings(allValues);
  179. std::set<std::string> unquieValues;
  180. for (auto it = allValues.begin(); it != allValues.end() && !found; ++it)
  181. {
  182. auto ret = unquieValues.insert(*it);
  183. found = !ret.second;
  184. }
  185. if (found)
  186. {
  187. status.addUniqueMsg(statusMsg::error, m_id, attrIt->second->getName(), "Attribute value must be unique");
  188. }
  189. }
  190. //
  191. // Does this value need to be from another set of values?
  192. if (attrIt->second->getSchemaValue()->isFromUniqueValueSet())
  193. {
  194. bool found = false;
  195. std::vector<std::string> allValues;
  196. attrIt->second->getSchemaValue()->getAllKeyRefValues(allValues);
  197. for (auto it = allValues.begin(); it != allValues.end() && !found; ++it)
  198. found = *it == attrIt->second->getValue();
  199. if (!found)
  200. {
  201. status.addMsg(statusMsg::error, m_id, attrIt->second->getName(), "Attribute value must be from a unique set");
  202. }
  203. }
  204. }
  205. //
  206. // Now check all children
  207. if (includeChildren)
  208. {
  209. for (auto childIt = m_children.begin(); childIt != m_children.end(); ++childIt)
  210. {
  211. childIt->second->validate(status, includeChildren, includeHiddenNodes);
  212. }
  213. }
  214. }
  215. }
  216. void EnvironmentNode::getAttributeValueForAllSiblings(const std::string &attrName, std::vector<std::string> &result) const
  217. {
  218. std::shared_ptr<EnvironmentNode> pParentNode = m_pParent.lock();
  219. if (pParentNode)
  220. {
  221. std::vector<std::shared_ptr<EnvironmentNode>> nodes;
  222. pParentNode->getChildren(nodes, m_name);
  223. for (auto it = nodes.begin(); it != nodes.end(); ++it)
  224. {
  225. result.push_back((*it)->getAttributeValue(attrName));
  226. }
  227. }
  228. }
  229. const std::shared_ptr<EnvironmentValue> EnvironmentNode::getAttribute(const std::string &name) const
  230. {
  231. std::shared_ptr<EnvironmentValue> pValue;
  232. auto it = m_attributes.find(name);
  233. if (it != m_attributes.end())
  234. {
  235. pValue = it->second;
  236. }
  237. return pValue;
  238. }
  239. void EnvironmentNode::getInsertableItems(std::vector<InsertableItem> &insertableItems) const
  240. {
  241. std::map<std::string, unsigned> childCounts;
  242. //
  243. // Iterate over the children and for each, create a childCount entry based on the
  244. // child node's configuration type
  245. for (auto childIt = m_children.begin(); childIt != m_children.end(); ++childIt)
  246. {
  247. std::string itemType = childIt->second->getSchemaItem()->getItemType();
  248. auto findIt = childCounts.find(itemType);
  249. if (findIt != childCounts.end())
  250. {
  251. ++findIt->second; // increment the number of instances of this item type.
  252. }
  253. else
  254. {
  255. childCounts.insert({ itemType, 1 });
  256. }
  257. }
  258. //
  259. // Now get the full list of configurable items, then resolve it against the child counts from
  260. // above to build a vector of insertable items
  261. std::vector<std::shared_ptr<SchemaItem>> configChildren;
  262. m_pSchemaItem->getChildren(configChildren);
  263. for (auto cfgIt = configChildren.begin(); cfgIt != configChildren.end(); ++cfgIt)
  264. {
  265. auto findIt = childCounts.find((*cfgIt)->getItemType());
  266. if (findIt != childCounts.end())
  267. {
  268. if (findIt->second < (*cfgIt)->getMaxInstances())
  269. {
  270. insertableItems.push_back(InsertableItem(shared_from_this(), *cfgIt));
  271. }
  272. }
  273. else
  274. {
  275. insertableItems.push_back(InsertableItem(shared_from_this(), *cfgIt));
  276. }
  277. }
  278. }
  279. //
  280. // Called to initialize a newly added node to the environment (not just read from the environment)
  281. void EnvironmentNode::initialize()
  282. {
  283. //
  284. // Add missing attributes
  285. addMissingAttributesFromConfig();
  286. //
  287. // If we are a component and there is a buildSet attribute, set the value to the configItem's type
  288. if (!(m_pSchemaItem->getProperty("componentName").empty()) && hasAttribute("buildSet"))
  289. {
  290. Status status;
  291. setAttributeValue("buildSet", m_pSchemaItem->getProperty("componentName"), status);
  292. }
  293. //
  294. // Initilize each attribute
  295. for (auto attrIt = m_attributes.begin(); attrIt != m_attributes.end(); ++attrIt)
  296. {
  297. attrIt->second->initialize();
  298. }
  299. }
  300. void EnvironmentNode::fetchNodes(const std::string &path, std::vector<std::shared_ptr<EnvironmentNode>> &nodes) const
  301. {
  302. //
  303. // If path starts with / and we are not the root, get the root and do the find
  304. if (path[0] == '/')
  305. {
  306. std::string remainingPath = path.substr(1);
  307. std::string rootName = remainingPath;
  308. std::shared_ptr<const EnvironmentNode> pRoot = getRoot();
  309. size_t slashPos = path.find_first_of('/', 1);
  310. if (slashPos != std::string::npos)
  311. {
  312. rootName = path.substr(1, slashPos-1);
  313. remainingPath = path.substr(slashPos + 1);
  314. size_t atPos = rootName.find_first_of('@');
  315. if (atPos != std::string::npos)
  316. {
  317. rootName.erase(atPos, std::string::npos);
  318. }
  319. }
  320. if (pRoot->getName() == rootName)
  321. {
  322. pRoot->fetchNodes(remainingPath, nodes);
  323. }
  324. }
  325. else if (path[0] == '.')
  326. {
  327. //
  328. // Parent ?
  329. if (path[1] == '.')
  330. {
  331. //
  332. // Path must be at least 4 characters in length to support the leading ../<remaining path>
  333. if (!m_pParent.expired() && path.length() >= 4)
  334. {
  335. m_pParent.lock()->fetchNodes(path.substr(3), nodes); // note skipping over '..'
  336. }
  337. else
  338. {
  339. throw new ParseException("Attempt to navigate to parent with no parent or path is incomplete");
  340. }
  341. }
  342. else
  343. {
  344. fetchNodes(path.substr(1), nodes); // do the find from here stripping the '.' indicator
  345. }
  346. }
  347. //
  348. // Otherwise, start searching
  349. else
  350. {
  351. std::string nodeName = path;
  352. std::string remainingPath, searchAttrName, searchAttrValue;
  353. //
  354. // Get our portion of the path which is up to the next / or the remaining string and
  355. // set the remaining portion of the path to search
  356. size_t slashPos = nodeName.find_first_of('/');
  357. if (slashPos != std::string::npos)
  358. {
  359. remainingPath = nodeName.substr(slashPos + 1);
  360. nodeName.erase(slashPos, std::string::npos); // truncate
  361. }
  362. //
  363. // Any attributes we need to look for?
  364. size_t atPos = nodeName.find_first_of('@');
  365. if (atPos != std::string::npos)
  366. {
  367. searchAttrName = nodeName.substr(atPos + 1);
  368. nodeName.erase(atPos, std::string::npos);
  369. size_t equalPos = searchAttrName.find_first_of('=');
  370. if (equalPos != std::string::npos)
  371. {
  372. searchAttrValue = searchAttrName.substr(equalPos + 1);
  373. searchAttrName.erase(equalPos, std::string::npos);
  374. }
  375. }
  376. //
  377. // Now search the children for nodes matching the node name
  378. std::vector<std::shared_ptr<EnvironmentNode>> childNodes;
  379. getChildren(childNodes, nodeName);
  380. //
  381. // If there is an attribute specified, dig deeper
  382. if (!searchAttrName.empty())
  383. {
  384. auto childNodeIt = childNodes.begin();
  385. while (childNodeIt != childNodes.end())
  386. {
  387. std::shared_ptr<EnvironmentValue> pValue = (*childNodeIt)->getAttribute(searchAttrName);
  388. if (pValue)
  389. {
  390. //
  391. // The attribute value must be present and, if necessary, must match the search value
  392. std::string curAttrValue = pValue->getValue();
  393. if (!curAttrValue.empty() && (searchAttrValue.empty() || (searchAttrValue == curAttrValue)))
  394. {
  395. ++childNodeIt; // keep it
  396. }
  397. else
  398. {
  399. childNodeIt = childNodes.erase(childNodeIt);
  400. }
  401. }
  402. else
  403. {
  404. childNodeIt = childNodes.erase(childNodeIt);
  405. }
  406. }
  407. }
  408. //
  409. // If there is path remaining, call the children in childNodes with the remaining path, otherwise we've reached
  410. // the end. Whatever nodes are in childNodes are appended to the input nodes vector for return
  411. if (!remainingPath.empty())
  412. {
  413. for (auto childNodeIt = childNodes.begin(); childNodeIt != childNodes.end(); ++childNodeIt)
  414. {
  415. (*childNodeIt)->fetchNodes(remainingPath, nodes);
  416. }
  417. }
  418. else
  419. {
  420. nodes.insert(nodes.end(), childNodes.begin(), childNodes.end());
  421. }
  422. }
  423. }
  424. std::shared_ptr<const EnvironmentNode> EnvironmentNode::getRoot() const
  425. {
  426. if (!m_pParent.expired())
  427. {
  428. return m_pParent.lock()->getRoot();
  429. }
  430. std::shared_ptr <const EnvironmentNode> ptr = shared_from_this();
  431. return ptr;
  432. }