SchemaValue.cpp 12 KB

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