designdocument.class.inc.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <?php
  2. // Copyright (C) 2016 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. /**
  19. * Design document and associated nodes
  20. * @package Core
  21. */
  22. namespace Combodo\iTop;
  23. /**
  24. * Class \Combodo\iTop\DesignDocument
  25. *
  26. * A design document is the DOM tree that modelize behaviors. One of its
  27. * characteristics is that it can be altered by the mean of the same kind of document.
  28. *
  29. */
  30. class DesignDocument extends \DOMDocument
  31. {
  32. /**
  33. * @throws \Exception
  34. */
  35. public function __construct()
  36. {
  37. parent::__construct('1.0', 'UTF-8');
  38. $this->Init();
  39. }
  40. /**
  41. * Overloadable. Called prior to data loading.
  42. */
  43. protected function Init()
  44. {
  45. $this->registerNodeClass('DOMElement', '\Combodo\iTop\DesignElement');
  46. $this->formatOutput = true; // indent (must be loaded with option LIBXML_NOBLANKS)
  47. $this->preserveWhiteSpace = true; // otherwise the formatOutput option would have no effect
  48. }
  49. /**
  50. * Overload of the standard API
  51. */
  52. public function load($filename, $options = 0)
  53. {
  54. parent::load($filename, LIBXML_NOBLANKS);
  55. }
  56. /**
  57. * Overload of the standard API
  58. */
  59. public function save($filename, $options = 0)
  60. {
  61. $this->documentElement->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
  62. return parent::save($filename, LIBXML_NOBLANKS);
  63. }
  64. /**
  65. * Create an HTML representation of the DOM, for debugging purposes
  66. * @param bool|false $bReturnRes Echoes or returns the HTML representation
  67. * @return mixed void or the HTML representation of the DOM
  68. */
  69. public function Dump($bReturnRes = false)
  70. {
  71. $sXml = $this->saveXML();
  72. if ($bReturnRes)
  73. {
  74. return $sXml;
  75. }
  76. else
  77. {
  78. echo "<pre>\n";
  79. echo htmlentities($sXml);
  80. echo "</pre>\n";
  81. }
  82. }
  83. /**
  84. * Quote and escape strings for use within an XPath expression
  85. * Usage: DesignDocument::GetNodes('class[@id='.DesignDocument::XPathQuote($sId).']');
  86. * @param $sValue The value to be quoted
  87. * @return string to be used within an XPath
  88. */
  89. public static function XPathQuote($sValue)
  90. {
  91. if (strpos($sValue, '"') !== false)
  92. {
  93. $aParts = explode('"', $sValue);
  94. $sRet = 'concat("'.implode('", \'"\', "', $aParts).'")';
  95. }
  96. else
  97. {
  98. $sRet = '"'.$sValue.'"';
  99. }
  100. return $sRet;
  101. }
  102. /**
  103. * Extracts some nodes from the DOM
  104. * @param string $sXPath A XPath expression
  105. * @param DesignNode|null $oContextNode The node to start the search from
  106. * @return \DOMNodeList
  107. */
  108. public function GetNodes($sXPath, $oContextNode = null)
  109. {
  110. $oXPath = new \DOMXPath($this);
  111. if (is_null($oContextNode))
  112. {
  113. $oResult = $oXPath->query($sXPath);
  114. }
  115. else
  116. {
  117. $oResult = $oXPath->query($sXPath, $oContextNode);
  118. }
  119. return $oResult;
  120. }
  121. /**
  122. * An alternative to getNodePath, that gives the id of nodes instead of the position within the children
  123. * @param $oNode The node to describe
  124. * @return string
  125. */
  126. public static function GetItopNodePath($oNode)
  127. {
  128. if ($oNode instanceof \DOMDocument) return '';
  129. if (is_null($oNode)) return '';
  130. $sId = $oNode->getAttribute('id');
  131. $sNodeDesc = ($sId != '') ? $oNode->nodeName.'['.$sId.']' : $oNode->nodeName;
  132. return self::GetItopNodePath($oNode->parentNode).'/'.$sNodeDesc;
  133. }
  134. }
  135. /**
  136. * DesignElement: helper to read/change the DOM
  137. * @package ModelFactory
  138. */
  139. class DesignElement extends \DOMElement
  140. {
  141. /**
  142. * Extracts some nodes from the DOM
  143. * @param string $sXPath A XPath expression
  144. * @return \DOMNodeList
  145. */
  146. public function GetNodes($sXPath)
  147. {
  148. return $this->ownerDocument->GetNodes($sXPath, $this);
  149. }
  150. /**
  151. * Create an HTML representation of the DOM, for debugging purposes
  152. * @param bool|false $bReturnRes Echoes or returns the HTML representation
  153. * @return mixed void or the HTML representation of the DOM
  154. */
  155. public function Dump($bReturnRes = false)
  156. {
  157. $oDoc = new DesignDocument();
  158. $oClone = $oDoc->importNode($this->cloneNode(true), true);
  159. $oDoc->appendChild($oClone);
  160. $sXml = $oDoc->saveXML($oClone);
  161. if ($bReturnRes)
  162. {
  163. return $sXml;
  164. }
  165. else
  166. {
  167. echo "<pre>\n";
  168. echo htmlentities($sXml);
  169. echo "</pre>\n";
  170. }
  171. }
  172. /**
  173. * Returns the node directly under the given node
  174. * @param $sTagName
  175. * @param bool|true $bMustExist
  176. * @return null
  177. * @throws DOMFormatException
  178. */
  179. public function GetUniqueElement($sTagName, $bMustExist = true)
  180. {
  181. $oNode = null;
  182. foreach($this->childNodes as $oChildNode)
  183. {
  184. if ($oChildNode->nodeName == $sTagName)
  185. {
  186. $oNode = $oChildNode;
  187. break;
  188. }
  189. }
  190. if ($bMustExist && is_null($oNode))
  191. {
  192. throw new DOMFormatException('Missing unique tag: '.$sTagName);
  193. }
  194. return $oNode;
  195. }
  196. /**
  197. * Returns the node directly under the current node, or null if missing
  198. * @param $sTagName
  199. * @return null
  200. * @throws DOMFormatException
  201. */
  202. public function GetOptionalElement($sTagName)
  203. {
  204. return $this->GetUniqueElement($sTagName, false);
  205. }
  206. /**
  207. * Returns the TEXT of the current node (possibly from several child nodes)
  208. * @param null $sDefault
  209. * @return null|string
  210. */
  211. public function GetText($sDefault = null)
  212. {
  213. $sText = null;
  214. foreach($this->childNodes as $oChildNode)
  215. {
  216. if ($oChildNode instanceof \DOMText)
  217. {
  218. if (is_null($sText)) $sText = '';
  219. $sText .= $oChildNode->wholeText;
  220. }
  221. }
  222. if (is_null($sText))
  223. {
  224. return $sDefault;
  225. }
  226. else
  227. {
  228. return $sText;
  229. }
  230. }
  231. /**
  232. * Get the TEXT value from a child node
  233. * @param string $sTagName
  234. * @param string|null $sDefault
  235. * @return string
  236. */
  237. public function GetChildText($sTagName, $sDefault = null)
  238. {
  239. $sRet = $sDefault;
  240. if ($oChild = $this->GetOptionalElement($sTagName))
  241. {
  242. $sRet = $oChild->GetText($sDefault);
  243. }
  244. return $sRet;
  245. }
  246. }