SchemaValue.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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 "SchemaValue.hpp"
  14. #include "EnvironmentValue.hpp"
  15. #include "EnvironmentNode.hpp"
  16. #include <algorithm>
  17. #include "ConfigPath.hpp"
  18. #include "Utils.hpp"
  19. #include "Exceptions.hpp"
  20. SchemaValue::SchemaValue(const std::string &name, bool isDefined) :
  21. m_name(name), m_displayName(name), m_invertHiddenIf(false)
  22. {
  23. bitMask.m_required = 0;
  24. bitMask.m_readOnly = 0;
  25. bitMask.m_hidden = 0;
  26. bitMask.m_deprecated = 0;
  27. bitMask.m_isUnique = 0;
  28. bitMask.m_isDefined = isDefined;
  29. }
  30. SchemaValue::SchemaValue(const SchemaValue &value)
  31. {
  32. // Primary purpose of the copy constructor is for use when a complexType is referenced. A copy is made which includes a copy
  33. // of each SchemaValue in the complexType SchemaItem.
  34. m_pType = value.m_pType;
  35. m_name = value.m_name;
  36. m_displayName = value.m_displayName;
  37. m_mirrorFromPath = value.m_mirrorFromPath;
  38. m_autoGenerateValue = value.m_autoGenerateValue;
  39. m_autoGenerateType = value.m_autoGenerateType;
  40. bitMask = value.bitMask;
  41. m_forcedValue = value.m_forcedValue;
  42. m_tooltip = value.m_tooltip;
  43. m_modifiers = value.m_modifiers;
  44. m_valueLimitRuleType = value.m_valueLimitRuleType;
  45. m_valueLimitRuleData = value.m_valueLimitRuleData;
  46. m_requiredIf = value.m_requiredIf;
  47. m_groupByName = value.m_groupByName;
  48. m_hiddenIf = value.m_hiddenIf;
  49. m_invertHiddenIf = value.m_invertHiddenIf;
  50. // special processing? Maybe after inserting?
  51. std::vector<std::shared_ptr<SchemaValue>> m_mirrorToSchemaValues;
  52. std::vector<std::weak_ptr<SchemaValue>> m_pUniqueValueSetRefs; // this value serves as the key from which values are valid
  53. }
  54. bool SchemaValue::isValueValid(const std::string &value, const EnvironmentValue *pEnvValue) const
  55. {
  56. bool isValid = true; // assume valid
  57. //
  58. // Check the type
  59. isValid = m_pType->isValueValid(value);
  60. //
  61. // Keyed ?, then value must NOT be in the current list.
  62. if (isValid && isUniqueValue() && pEnvValue != nullptr)
  63. {
  64. bool found = false;
  65. std::vector<std::string> allValues;
  66. pEnvValue->getAllValuesForSiblings(allValues);
  67. for (auto it = allValues.begin(); it != allValues.end() && !found; ++it)
  68. found = *it == value;
  69. if (found)
  70. {
  71. return false;
  72. }
  73. }
  74. //
  75. // Keyref ?, then the value must be from another set
  76. if (isValid && isFromUniqueValueSet() && pEnvValue != nullptr)
  77. {
  78. bool found = false;
  79. std::vector<std::string> allValues;
  80. getAllKeyRefValues(allValues);
  81. for (auto it = allValues.begin(); it != allValues.end() && !found; ++it)
  82. found = *it == value;
  83. isValid = found;
  84. }
  85. return isValid;
  86. }
  87. void SchemaValue::validate(Status &status, const std::string &id, const EnvironmentValue *pEnvValue) const
  88. {
  89. bool isValid = true;
  90. if (pEnvValue == nullptr)
  91. {
  92. std::string msg = "Attempt to validate schema value w/o an environment value.";
  93. throw(std::runtime_error(msg));
  94. }
  95. //
  96. // If we have an environment value, more specific information can be provided
  97. if (pEnvValue)
  98. {
  99. std::string curValue = pEnvValue->getValue();
  100. isValid = m_pType->isValueValid(curValue);
  101. //
  102. // See if there is a dependency on another value being set.
  103. if (!m_requiredIf.empty() && isValid)
  104. {
  105. //
  106. // Required if string format is an xpath. Search this environment value's owning node
  107. // for a match.
  108. std::vector<std::shared_ptr<EnvironmentNode>> nodes;
  109. pEnvValue->getEnvironmentNode()->fetchNodes(m_requiredIf, nodes);
  110. if (!nodes.empty())
  111. {
  112. //
  113. // Since here is a match for a requiredIf, this value MUST be set
  114. if (pEnvValue->getValue().empty())
  115. {
  116. isValid = false;
  117. std::string msg = "Environment value required based on requiredIf rule " + m_requiredIf + " being set.";
  118. status.addMsg(statusMsg::error, pEnvValue->getNodeId(), pEnvValue->getName(), msg);
  119. }
  120. }
  121. }
  122. //
  123. // If not valid, provide the reason
  124. if (!isValid)
  125. {
  126. std::string msg;
  127. if (pEnvValue->wasForced())
  128. msg = "Value was forced to an invalid value (" + curValue + ").";
  129. else
  130. msg = "Value is invalid (" + curValue + ").";
  131. msg += " Valid value (" + m_pType->getLimitString() + ")";
  132. status.addMsg(pEnvValue->wasForced() ? statusMsg::warning : statusMsg::error, pEnvValue->getNodeId(), pEnvValue->getName(), msg);
  133. }
  134. //
  135. // Otherwise, the value is valid, but there could be a validate message
  136. else
  137. {
  138. const std::string &validateMsg = m_pType->getValidateMsg();
  139. if (!validateMsg.empty())
  140. {
  141. status.addMsg(status.getMsgLevelFromString(m_pType->getValidateMsgType()), pEnvValue->getNodeId(), pEnvValue->getName(), validateMsg);
  142. }
  143. }
  144. }
  145. }
  146. void SchemaValue::resetEnvironment()
  147. {
  148. m_envValues.clear();
  149. }
  150. // replicates the new value throughout the environment
  151. void SchemaValue::mirrorValueToEnvironment(const std::string &oldValue, const std::string &newValue, Status *pStatus)
  152. {
  153. for (auto &pSchemaValue: m_mirrorToSchemaValues)
  154. {
  155. pSchemaValue->doMirroroToEnvironmentValues(oldValue, newValue, pStatus);
  156. }
  157. }
  158. void SchemaValue::doMirroroToEnvironmentValues(const std::string &oldValue, const std::string &newValue, Status *pStatus)
  159. {
  160. std::string msg = "Value automatically changed from " + oldValue + " to " + newValue;
  161. for (auto &envValueIt: m_envValues)
  162. {
  163. std::shared_ptr<EnvironmentValue> pEnvValue = envValueIt.lock();
  164. if (pEnvValue && pEnvValue->getValue() == oldValue)
  165. {
  166. pEnvValue->setValue(newValue, nullptr, true);
  167. if (pStatus != nullptr)
  168. {
  169. pStatus->addMsg(statusMsg::change, msg, pEnvValue->getEnvironmentNode()->getId(), pEnvValue->getSchemaValue()->getDisplayName());
  170. }
  171. }
  172. }
  173. }
  174. void SchemaValue::getAllEnvironmentValues(std::vector<std::shared_ptr<EnvironmentValue>> &envValues) const
  175. {
  176. for (auto it = m_envValues.begin(); it != m_envValues.end(); ++it)
  177. {
  178. envValues.push_back(it->lock());
  179. }
  180. }
  181. bool SchemaValue::getAllowedValues(std::vector<AllowedValue> &allowedValues, const std::shared_ptr<const EnvironmentNode> &pEnvNode) const
  182. {
  183. bool rc = false;
  184. //
  185. // If enumerated, get the allowed values
  186. if (m_pType->isEnumerated())
  187. {
  188. allowedValues = m_pType->getEnumeratedValues();
  189. rc = true;
  190. }
  191. //
  192. // Is there a specialized rule that limits the values? The order is important here. Place more restrictive rules first. Note also that
  193. // a rule could be used inside a unique value set which should remain the last entry in this if then else if block
  194. //
  195. // uniqueItemType_espBinding - value is based on a unique item type described by the data for the rule. This is version 1
  196. else if (m_valueLimitRuleType == "uniqueItemType_espBinding")
  197. {
  198. std::vector<std::string> params = splitString(m_valueLimitRuleData, ",");
  199. if (params.size() != 4)
  200. {
  201. std::string msg = "Applying rule " + m_valueLimitRuleType + ", expected 4 parameters in rule data";
  202. throw(ParseException(msg));
  203. }
  204. std::vector<std::shared_ptr<EnvironmentNode>> existingSourceNodes;
  205. pEnvNode->fetchNodes(params[0], existingSourceNodes);
  206. std::vector<std::string> existingSourceAttributeValues;
  207. for (auto &existingNodeIt: existingSourceNodes)
  208. {
  209. existingSourceAttributeValues.push_back( existingNodeIt->getAttributeValue(params[1]));
  210. }
  211. //
  212. // Get the full set of possible values using the params[1] values. From its parts, parts[0] is the path
  213. // to find the set of all possible nodes that could serve as an allowable value.
  214. std::vector<std::shared_ptr<EnvironmentNode>> allSourceNodes;
  215. std::string sourceAttributeName = params[3]; // for use below in case parts is reused later
  216. pEnvNode->fetchNodes(params[2], allSourceNodes);
  217. //
  218. // For each exising source node, using the existingSourceAttributeValues, matching the name to the value in
  219. // sourceAttributeName, and collect the itemType values found.
  220. std::vector<std::string> existingItemTypes;
  221. for (auto &existingValueIt: existingSourceAttributeValues)
  222. {
  223. std::vector<std::shared_ptr<EnvironmentNode>>::iterator sourceIt = std::find_if(allSourceNodes.begin(), allSourceNodes.end(),
  224. [&](std::shared_ptr<EnvironmentNode> &srcIt) {
  225. return srcIt->getAttributeValue(sourceAttributeName) == existingValueIt;
  226. });
  227. if (sourceIt != allSourceNodes.end())
  228. {
  229. existingItemTypes.push_back((*sourceIt)->getSchemaItem()->getItemType());
  230. }
  231. }
  232. //
  233. // Build the allowable value list by only adding itmes from the all sources list that don't hvae
  234. // an entry in the existing item type vector
  235. for (auto &sourceIt: allSourceNodes)
  236. {
  237. std::vector<std::string>::const_iterator itemTypeIt = std::find_if(existingItemTypes.begin(), existingItemTypes.end(), [&](const std::string &itemIt) {
  238. return itemIt == sourceIt->getSchemaItem()->getItemType();
  239. });
  240. if (itemTypeIt == existingItemTypes.end())
  241. {
  242. allowedValues.emplace_back(AllowedValue(sourceIt->getAttributeValue(sourceAttributeName), ""));
  243. }
  244. }
  245. rc = true;
  246. }
  247. //
  248. // Or, keyed? (note that the keyed check MUST be last since a more restrictive rule may be defined for UI purposes
  249. // while a keyed reference is present for XML schema validation)
  250. else if (isFromUniqueValueSet())
  251. {
  252. std::shared_ptr<EnvironmentValue> pEnvValue = pEnvNode->getAttribute(getName());
  253. std::vector<std::string> curValues;
  254. pEnvValue->getAllValuesForSiblings(curValues);
  255. std::vector<std::string> refValues;
  256. getAllKeyRefValues(refValues);
  257. //
  258. // For each key reference value (the full set of allowed values), remove any that are already in use (curValues)
  259. for (auto &refIt: refValues)
  260. {
  261. std::vector<std::string>::const_iterator inUseIt = std::find_if(curValues.begin(), curValues.end(), [&](const std::string &curValueIt) {
  262. return curValueIt == refIt;
  263. });
  264. if (inUseIt == curValues.end())
  265. {
  266. allowedValues.emplace_back(AllowedValue(refIt, ""));
  267. }
  268. }
  269. if (!allowedValues.empty() && m_valueLimitRuleType == "addDependencies_FromSiblingAttributeValue")
  270. {
  271. std::vector<std::string> params = splitString(m_valueLimitRuleData, ",");
  272. if (params.size() != 4)
  273. {
  274. std::string msg = "Applying rule " + m_valueLimitRuleType + ", expected 4 parameters in rule data";
  275. throw(ParseException(msg));
  276. }
  277. std::string matchPath = params[0];
  278. std::string matchAttribute = params[1];
  279. std::string depAttrSource = params[2];
  280. std::string depAttrTarget = params[3];
  281. //
  282. // Get an environment node pointer using the first entry in the env values vector. We know it's not empty because
  283. // we have at least one allowed value. Use this for fetching
  284. std::shared_ptr<EnvironmentNode> pEnvValuesNode = m_envValues[0].lock()->getEnvironmentNode();
  285. //
  286. // Loop through each allowed value and find it's environment node (by value). Then add a dependency
  287. // based on the dependent attribute source.
  288. for (auto &allowedValue: allowedValues)
  289. {
  290. std::string path = matchPath + "[@" + matchAttribute + "='" + allowedValue.m_value + "']";
  291. std::vector<std::shared_ptr<EnvironmentNode>> envNodes;
  292. pEnvValuesNode->fetchNodes(path, envNodes);
  293. if (!envNodes.empty())
  294. {
  295. std::shared_ptr<EnvironmentValue> pAttr = envNodes[0]->getAttribute(depAttrSource);
  296. if (pAttr)
  297. {
  298. allowedValue.addDependentValue(depAttrTarget, pAttr->getValue());
  299. }
  300. }
  301. }
  302. }
  303. rc = true;
  304. }
  305. return rc;
  306. }
  307. void SchemaValue::getAllKeyRefValues(std::vector<std::string> &keyRefValues) const
  308. {
  309. std::vector<std::weak_ptr<SchemaValue>> refCfgValues = getUniqueValueSetRefs();
  310. for (auto refCfgValueIt = refCfgValues.begin(); refCfgValueIt != refCfgValues.end(); ++refCfgValueIt)
  311. {
  312. std::shared_ptr<SchemaValue> pRefCfgValue = (*refCfgValueIt).lock();
  313. std::vector<std::shared_ptr<EnvironmentValue>> allEnvValues;
  314. pRefCfgValue->getAllEnvironmentValues(allEnvValues);
  315. for (auto &envIt: allEnvValues)
  316. {
  317. keyRefValues.push_back(envIt->getValue());
  318. }
  319. }
  320. }
  321. bool SchemaValue::hasModifier(const std::string &modifier) const
  322. {
  323. return std::find(m_modifiers.begin(), m_modifiers.end(), modifier) != m_modifiers.end();
  324. }
  325. bool SchemaValue::isHidden(const EnvironmentValue *pEnvValue) const
  326. {
  327. bool hidden = bitMask.m_hidden;
  328. if (!hidden && !m_hiddenIf.empty() && pEnvValue != nullptr)
  329. {
  330. //
  331. // Hidden if string format is an xpath. Search this environment value's owning node
  332. // for a match.
  333. std::vector<std::shared_ptr<EnvironmentNode>> nodes;
  334. pEnvValue->getEnvironmentNode()->fetchNodes(m_hiddenIf, nodes);
  335. hidden = !nodes.empty();
  336. hidden = m_invertHiddenIf == !hidden;
  337. }
  338. return hidden;
  339. }
  340. void SchemaValue::removeEnvironmentValue(const std::shared_ptr<EnvironmentValue> &pEnvValue)
  341. {
  342. for (auto it = m_envValues.begin(); it != m_envValues.end(); ++it)
  343. {
  344. if ((*it).lock() == pEnvValue)
  345. {
  346. m_envValues.erase(it);
  347. break;
  348. }
  349. }
  350. }