SchemaValue.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  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_default = value.m_default;
  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. std::string msg = "Value automatically changed from " + oldValue + " to " + newValue;
  154. for (auto mirrorCfgIt = m_mirrorToSchemaValues.begin(); mirrorCfgIt != m_mirrorToSchemaValues.end(); ++mirrorCfgIt)
  155. {
  156. for (auto &envValueIt: m_envValues)
  157. {
  158. std::shared_ptr<EnvironmentValue> pEnvValue = envValueIt.lock();
  159. if (pEnvValue && pEnvValue->getValue() == oldValue)
  160. {
  161. pEnvValue->setValue(newValue, nullptr, true);
  162. if (pStatus != nullptr)
  163. {
  164. pStatus->addMsg(statusMsg::change, msg, pEnvValue->getEnvironmentNode()->getId(), pEnvValue->getSchemaValue()->getDisplayName());
  165. }
  166. }
  167. }
  168. }
  169. }
  170. void SchemaValue::getAllEnvironmentValues(std::vector<std::shared_ptr<EnvironmentValue>> &envValues) const
  171. {
  172. for (auto it = m_envValues.begin(); it != m_envValues.end(); ++it)
  173. {
  174. envValues.push_back(it->lock());
  175. }
  176. }
  177. bool SchemaValue::getAllowedValues(std::vector<AllowedValue> &allowedValues, const std::shared_ptr<const EnvironmentNode> &pEnvNode) const
  178. {
  179. bool rc = false;
  180. //
  181. // If enumerated, get the allowed values
  182. if (m_pType->isEnumerated())
  183. {
  184. allowedValues = m_pType->getEnumeratedValues();
  185. rc = true;
  186. }
  187. //
  188. // Is there a specialized rule that limits the values? The order is important here. Place more restrictive rules first. Note also that
  189. // a rule could be used inside a unique value set which should remain the last entry in this if then else if block
  190. //
  191. // uniqueItemType_espBinding - value is based on a unique item type described by the data for the rule. This is version 1
  192. else if (m_valueLimitRuleType == "uniqueItemType_espBinding")
  193. {
  194. std::vector<std::string> params = splitString(m_valueLimitRuleData, ",");
  195. if (params.size() != 2)
  196. {
  197. std::string msg = "Applying rule " + m_valueLimitRuleType + ", expected 2 parameters in rule data";
  198. throw(ParseException(msg));
  199. }
  200. //
  201. // First parameter is the source values for an attribute search. The two parts of the parameter are the path to the
  202. // node set where the atttribute, the second part, name is found (not that there may be no entries). Find all the nodes
  203. // for the path (parts[0]), then get all of the values for the attribute (parts[1]). This serves as the list of existing
  204. // values that are eliminated from the final list of allowable values.
  205. ConfigPath sourcePath(params[0]);
  206. std::shared_ptr<ConfigPathItem> pSourcePath = sourcePath.getNextPathItem();
  207. std::vector<std::shared_ptr<EnvironmentNode>> existingSourceNodes;
  208. pEnvNode->fetchNodes(pSourcePath->getElementName(), existingSourceNodes);
  209. std::vector<std::string> existingSourceAttributeValues;
  210. for (auto &existingNodeIt: existingSourceNodes)
  211. {
  212. existingSourceAttributeValues.push_back( existingNodeIt->getAttributeValue(pSourcePath->getAttributeName()));
  213. }
  214. //
  215. // Get the full set of possible values using the params[1] values. From its parts, parts[0] is the path
  216. // to find the set of all possible nodes that could serve as an allowable value.
  217. std::vector<std::shared_ptr<EnvironmentNode>> allSourceNodes;
  218. ConfigPath valuesPath(params[1]);
  219. std::shared_ptr<ConfigPathItem> pValuesPath = valuesPath.getNextPathItem();
  220. std::string sourceAttributeName = pValuesPath->getAttributeName(); // for use below in case parts is reused later
  221. pEnvNode->fetchNodes(pValuesPath->getElementName(), allSourceNodes);
  222. //
  223. // For each exising source node, using the existingSourceAttributeValues, matching the name to the value in
  224. // sourceAttributeName, and collect the itemType values found.
  225. std::vector<std::string> existingItemTypes;
  226. for (auto &existingValueIt: existingSourceAttributeValues)
  227. {
  228. std::vector<std::shared_ptr<EnvironmentNode>>::iterator sourceIt = std::find_if(allSourceNodes.begin(), allSourceNodes.end(),
  229. [&](std::shared_ptr<EnvironmentNode> &srcIt) {
  230. return srcIt->getAttributeValue(sourceAttributeName) == existingValueIt;
  231. });
  232. if (sourceIt != allSourceNodes.end())
  233. {
  234. existingItemTypes.push_back((*sourceIt)->getSchemaItem()->getItemType());
  235. }
  236. }
  237. //
  238. // Build the allowable value list by only adding itmes from the all sources list that don't hvae
  239. // an entry in the existing item type vector
  240. for (auto &sourceIt: allSourceNodes)
  241. {
  242. std::vector<std::string>::const_iterator itemTypeIt = std::find_if(existingItemTypes.begin(), existingItemTypes.end(), [&](const std::string &itemIt) {
  243. return itemIt == sourceIt->getSchemaItem()->getItemType();
  244. });
  245. if (itemTypeIt == existingItemTypes.end())
  246. {
  247. allowedValues.push_back({ sourceIt->getAttributeValue(sourceAttributeName), "" });
  248. }
  249. }
  250. rc = true;
  251. }
  252. //
  253. // Or, keyed? (note that the keyed check MUST be last since a more restrictive rule may be defined for UI purposes
  254. // while a keyed reference is present for XML schema validation)
  255. else if (isFromUniqueValueSet())
  256. {
  257. std::vector<std::string> refValues;
  258. getAllKeyRefValues(refValues);
  259. for (auto it = refValues.begin(); it != refValues.end(); ++it)
  260. {
  261. allowedValues.push_back({ *it, "" });
  262. }
  263. if (!allowedValues.empty() && m_valueLimitRuleType == "addDependencies_FromSiblingAttributeValue")
  264. {
  265. std::vector<std::string> params = splitString(m_valueLimitRuleData, ",");
  266. if (params.size() != 2)
  267. {
  268. std::string msg = "Applying rule " + m_valueLimitRuleType + ", expected 4 parameters in rule data";
  269. throw(ParseException(msg));
  270. }
  271. std::string matchPath = params[0];
  272. std::string matchAttribute = params[1];
  273. std::string depAttrSource = params[2];
  274. std::string depAttrTarget = params[3];
  275. //
  276. // Get an environment node pointer using the first entry in the env values vector. We know it's not empty because
  277. // we have at least one allowed value. Use this for fetching
  278. std::shared_ptr<EnvironmentNode> pEnvNode = m_envValues[0].lock()->getEnvironmentNode();
  279. //
  280. // Loop through each allowed value and find it's environment node (by value). Then add a dependency
  281. // based on the dependent attribute source.
  282. for (auto &allowedValue: allowedValues)
  283. {
  284. std::string path = matchPath + "[@" + matchAttribute + "='" + allowedValue.m_value + "']";
  285. std::vector<std::shared_ptr<EnvironmentNode>> envNodes;
  286. pEnvNode->fetchNodes(path, envNodes);
  287. if (!envNodes.empty())
  288. {
  289. std::shared_ptr<EnvironmentValue> pAttr = envNodes[0]->getAttribute(depAttrSource);
  290. if (pAttr)
  291. {
  292. allowedValue.addDependentValue(depAttrTarget, pAttr->getValue());
  293. }
  294. }
  295. }
  296. }
  297. rc = true;
  298. }
  299. return rc;
  300. }
  301. void SchemaValue::getAllKeyRefValues(std::vector<std::string> &keyRefValues) const
  302. {
  303. std::vector<std::weak_ptr<SchemaValue>> refCfgValues = getUniqueValueSetRefs();
  304. for (auto refCfgValueIt = refCfgValues.begin(); refCfgValueIt != refCfgValues.end(); ++refCfgValueIt)
  305. {
  306. std::shared_ptr<SchemaValue> pRefCfgValue = (*refCfgValueIt).lock();
  307. std::vector<std::shared_ptr<EnvironmentValue>> allEnvValues;
  308. pRefCfgValue->getAllEnvironmentValues(allEnvValues);
  309. for (auto &envIt: allEnvValues)
  310. {
  311. keyRefValues.push_back(envIt->getValue());
  312. }
  313. }
  314. }
  315. bool SchemaValue::hasModifier(const std::string &modifier) const
  316. {
  317. return std::find(m_modifiers.begin(), m_modifiers.end(), modifier) != m_modifiers.end();
  318. }
  319. bool SchemaValue::isHidden(const EnvironmentValue *pEnvValue) const
  320. {
  321. bool hidden = bitMask.m_hidden;
  322. if (!hidden && !m_hiddenIf.empty() && pEnvValue != nullptr)
  323. {
  324. //
  325. // Hidden if string format is an xpath. Search this environment value's owning node
  326. // for a match.
  327. std::vector<std::shared_ptr<EnvironmentNode>> nodes;
  328. pEnvValue->getEnvironmentNode()->fetchNodes(m_hiddenIf, nodes);
  329. hidden = !nodes.empty();
  330. hidden = m_invertHiddenIf == !hidden;
  331. }
  332. return hidden;
  333. }