designdocument.class.inc.php 6.5 KB

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