PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/generator/lib/builder/util/XmlToAppData.php

https://github.com/1989gaurav/Propel
PHP | 404 lines | 259 code | 70 blank | 75 comment | 25 complexity | bdedc453dc8dfad819969992ed2741c1 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Propel package.
  4. * For the full copyright and license information, please view the LICENSE
  5. * file that was distributed with this source code.
  6. *
  7. * @license MIT License
  8. */
  9. require_once dirname(__FILE__) . '/../../model/AppData.php';
  10. require_once dirname(__FILE__) . '/../../exception/SchemaException.php';
  11. /**
  12. * A class that is used to parse an input xml schema file and creates an AppData
  13. * PHP object.
  14. *
  15. * @author Hans Lellelid <hans@xmpl.org> (Propel)
  16. * @author Leon Messerschmidt <leon@opticode.co.za> (Torque)
  17. * @author Jason van Zyl <jvanzyl@apache.org> (Torque)
  18. * @author Martin Poeschl <mpoeschl@marmot.at> (Torque)
  19. * @author Daniel Rall <dlr@collab.net> (Torque)
  20. * @version $Revision$
  21. * @package propel.generator.builder.util
  22. */
  23. class XmlToAppData
  24. {
  25. /** enables debug output */
  26. const DEBUG = false;
  27. private $app;
  28. private $currDB;
  29. private $currTable;
  30. private $currColumn;
  31. private $currFK;
  32. private $currIndex;
  33. private $currUnique;
  34. private $currValidator;
  35. private $currBehavior;
  36. private $currVendorObject;
  37. private $isForReferenceOnly;
  38. private $currentPackage;
  39. private $currentXmlFile;
  40. private $defaultPackage;
  41. private $encoding;
  42. /** two-dimensional array,
  43. first dimension is for schemas(key is the path to the schema file),
  44. second is for tags within the schema */
  45. private $schemasTagsStack = array();
  46. /**
  47. * Creates a new instance for the specified database type.
  48. *
  49. * @param PropelPlatformInterface $defaultPlatform The default database platform for the application.
  50. * @param string $defaultPackage the default PHP package used for the om
  51. * @param string $encoding The database encoding.
  52. */
  53. public function __construct(PropelPlatformInterface $defaultPlatform = null, $defaultPackage = null, $encoding = 'iso-8859-1')
  54. {
  55. $this->app = new AppData($defaultPlatform);
  56. $this->defaultPackage = $defaultPackage;
  57. $this->firstPass = true;
  58. $this->encoding = $encoding;
  59. }
  60. /**
  61. * Set the AppData generator configuration
  62. *
  63. * @param GeneratorConfig $generatorConfig
  64. */
  65. public function setGeneratorConfig(GeneratorConfig $generatorConfig)
  66. {
  67. $this->app->setGeneratorConfig($generatorConfig);
  68. }
  69. /**
  70. * Parses a XML input file and returns a newly created and
  71. * populated AppData structure.
  72. *
  73. * @param string $xmlFile The input file to parse.
  74. * @return AppData populated by <code>xmlFile</code>.
  75. */
  76. public function parseFile($xmlFile)
  77. {
  78. // we don't want infinite recursion
  79. if ($this->isAlreadyParsed($xmlFile)) {
  80. return;
  81. }
  82. return $this->parseString(file_get_contents($xmlFile), $xmlFile);
  83. }
  84. /**
  85. * Parses a XML input string and returns a newly created and
  86. * populated AppData structure.
  87. *
  88. * @param string $xmlString The input string to parse.
  89. * @param string $xmlFile The input file name.
  90. * @return AppData populated by <code>xmlFile</code>.
  91. */
  92. public function parseString($xmlString, $xmlFile = null)
  93. {
  94. // we don't want infinite recursion
  95. if ($this->isAlreadyParsed($xmlFile)) {
  96. return;
  97. }
  98. // store current schema file path
  99. $this->schemasTagsStack[$xmlFile] = array();
  100. $this->currentXmlFile = $xmlFile;
  101. $parser = xml_parser_create();
  102. xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
  103. xml_set_object($parser, $this);
  104. xml_set_element_handler($parser, 'startElement', 'endElement');
  105. if (!xml_parse($parser, $xmlString)) {
  106. throw new Exception(sprintf("XML error: %s at line %d",
  107. xml_error_string(xml_get_error_code($parser)),
  108. xml_get_current_line_number($parser))
  109. );
  110. }
  111. xml_parser_free($parser);
  112. array_pop($this->schemasTagsStack);
  113. return $this->app;
  114. }
  115. /**
  116. * Handles opening elements of the xml file.
  117. *
  118. * @param string $uri
  119. * @param string $localName The local name (without prefix), or the empty string if
  120. * Namespace processing is not being performed.
  121. * @param string $rawName The qualified name (with prefix), or the empty string if
  122. * qualified names are not available.
  123. * @param string $attributes The specified or defaulted attributes
  124. */
  125. public function startElement($parser, $name, $attributes)
  126. {
  127. $parentTag = $this->peekCurrentSchemaTag();
  128. if ($parentTag === false) {
  129. switch($name) {
  130. case "database":
  131. if ($this->isExternalSchema()) {
  132. $this->currentPackage = @$attributes["package"];
  133. if ($this->currentPackage === null) {
  134. $this->currentPackage = $this->defaultPackage;
  135. }
  136. } else {
  137. $this->currDB = $this->app->addDatabase($attributes);
  138. }
  139. break;
  140. default:
  141. $this->_throwInvalidTagException($parser, $name);
  142. }
  143. } elseif ($parentTag == "database") {
  144. switch($name) {
  145. case "external-schema":
  146. $xmlFile = @$attributes["filename"];
  147. // "referenceOnly" attribute is valid in the main schema XML file only,
  148. // and it's ignored in the nested external-schemas
  149. if (!$this->isExternalSchema()) {
  150. $isForRefOnly = @$attributes["referenceOnly"];
  151. $this->isForReferenceOnly = ($isForRefOnly !== null ? (strtolower($isForRefOnly) === "true") : true); // defaults to TRUE
  152. }
  153. if ($xmlFile{0} != '/') {
  154. $xmlFile = realpath(dirname($this->currentXmlFile) . DIRECTORY_SEPARATOR . $xmlFile);
  155. if (!file_exists($xmlFile)) {
  156. throw new SchemaException(sprintf('Unknown include external "%s"', $xmlFile));
  157. }
  158. }
  159. $this->parseFile($xmlFile);
  160. break;
  161. case "domain":
  162. $this->currDB->addDomain($attributes);
  163. break;
  164. case "table":
  165. $this->currTable = $this->currDB->addTable($attributes);
  166. if ($this->isExternalSchema()) {
  167. $this->currTable->setForReferenceOnly($this->isForReferenceOnly);
  168. $this->currTable->setPackage($this->currentPackage);
  169. }
  170. break;
  171. case "vendor":
  172. $this->currVendorObject = $this->currDB->addVendorInfo($attributes);
  173. break;
  174. case "behavior":
  175. $this->currBehavior = $this->currDB->addBehavior($attributes);
  176. break;
  177. default:
  178. $this->_throwInvalidTagException($parser, $name);
  179. }
  180. } elseif ($parentTag == "table") {
  181. switch($name) {
  182. case "column":
  183. $this->currColumn = $this->currTable->addColumn($attributes);
  184. break;
  185. case "foreign-key":
  186. $this->currFK = $this->currTable->addForeignKey($attributes);
  187. break;
  188. case "index":
  189. $this->currIndex = $this->currTable->addIndex($attributes);
  190. break;
  191. case "unique":
  192. $this->currUnique = $this->currTable->addUnique($attributes);
  193. break;
  194. case "vendor":
  195. $this->currVendorObject = $this->currTable->addVendorInfo($attributes);
  196. break;
  197. case "validator":
  198. $this->currValidator = $this->currTable->addValidator($attributes);
  199. break;
  200. case "id-method-parameter":
  201. $this->currTable->addIdMethodParameter($attributes);
  202. break;
  203. case "behavior":
  204. $this->currBehavior = $this->currTable->addBehavior($attributes);
  205. break;
  206. default:
  207. $this->_throwInvalidTagException($parser, $name);
  208. }
  209. } elseif ($parentTag == "column") {
  210. switch($name) {
  211. case "inheritance":
  212. $this->currColumn->addInheritance($attributes);
  213. break;
  214. case "vendor":
  215. $this->currVendorObject = $this->currColumn->addVendorInfo($attributes);
  216. break;
  217. default:
  218. $this->_throwInvalidTagException($parser, $name);
  219. }
  220. } elseif ($parentTag == "foreign-key") {
  221. switch($name) {
  222. case "reference":
  223. $this->currFK->addReference($attributes);
  224. break;
  225. case "vendor":
  226. $this->currVendorObject = $this->currUnique->addVendorInfo($attributes);
  227. break;
  228. default:
  229. $this->_throwInvalidTagException($parser, $name);
  230. }
  231. } elseif ($parentTag == "index") {
  232. switch($name) {
  233. case "index-column":
  234. $this->currIndex->addColumn($attributes);
  235. break;
  236. case "vendor":
  237. $this->currVendorObject = $this->currIndex->addVendorInfo($attributes);
  238. break;
  239. default:
  240. $this->_throwInvalidTagException($parser, $name);
  241. }
  242. } elseif ($parentTag == "unique") {
  243. switch($name) {
  244. case "unique-column":
  245. $this->currUnique->addColumn($attributes);
  246. break;
  247. case "vendor":
  248. $this->currVendorObject = $this->currUnique->addVendorInfo($attributes);
  249. break;
  250. default:
  251. $this->_throwInvalidTagException($parser, $name);
  252. }
  253. } elseif ($parentTag == "behavior") {
  254. switch($name) {
  255. case "parameter":
  256. $this->currBehavior->addParameter($attributes);
  257. break;
  258. default:
  259. $this->_throwInvalidTagException($parser, $name);
  260. }
  261. } elseif ($parentTag == "validator") {
  262. switch($name) {
  263. case "rule":
  264. $this->currValidator->addRule($attributes);
  265. break;
  266. default:
  267. $this->_throwInvalidTagException($parser, $name);
  268. }
  269. } elseif ($parentTag == "vendor") {
  270. switch($name) {
  271. case "parameter":
  272. $this->currVendorObject->addParameter($attributes);
  273. break;
  274. default:
  275. $this->_throwInvalidTagException($parser, $name);
  276. }
  277. } else {
  278. // it must be an invalid tag
  279. $this->_throwInvalidTagException($parser, $name);
  280. }
  281. $this->pushCurrentSchemaTag($name);
  282. }
  283. function _throwInvalidTagException($parser, $tag_name)
  284. {
  285. $location = '';
  286. if ($this->currentXmlFile !== null) {
  287. $location .= sprintf('file %s,', $this->currentXmlFile);
  288. }
  289. $location .= sprintf('line %d', xml_get_current_line_number($parser));
  290. if ($col = xml_get_current_column_number($parser)) {
  291. $location .= sprintf(', column %d', $col);
  292. }
  293. throw new SchemaException(sprintf('Unexpected tag <%s> in %s', $tag_name, $location));
  294. }
  295. /**
  296. * Handles closing elements of the xml file.
  297. *
  298. * @param uri
  299. * @param localName The local name (without prefix), or the empty string if
  300. * Namespace processing is not being performed.
  301. * @param rawName The qualified name (with prefix), or the empty string if
  302. * qualified names are not available.
  303. */
  304. public function endElement($parser, $name)
  305. {
  306. if (self::DEBUG) {
  307. print("endElement(" . $name . ") called\n");
  308. }
  309. $this->popCurrentSchemaTag();
  310. }
  311. protected function peekCurrentSchemaTag()
  312. {
  313. $keys = array_keys($this->schemasTagsStack);
  314. return end($this->schemasTagsStack[end($keys)]);
  315. }
  316. protected function popCurrentSchemaTag()
  317. {
  318. $keys = array_keys($this->schemasTagsStack);
  319. array_pop($this->schemasTagsStack[end($keys)]);
  320. }
  321. protected function pushCurrentSchemaTag($tag)
  322. {
  323. $keys = array_keys($this->schemasTagsStack);
  324. $this->schemasTagsStack[end($keys)][] = $tag;
  325. }
  326. protected function isExternalSchema()
  327. {
  328. return count($this->schemasTagsStack) > 1;
  329. }
  330. protected function isAlreadyParsed($filePath)
  331. {
  332. return isset($this->schemasTagsStack[$filePath]);
  333. }
  334. }