root/tags/RELEASE_0_1_2/XJConf/XmlParser.php

Revision 47, 9.8 kB (checked in by mikey, 2 years ago)

removed wrong committed stuff

Line 
1 <?php
2 /**
3  * Parser that reads xml files and generates the data structure.
4  *
5  * @author  Stephan Schmidt <stephan.schmidt@schlund.de>
6  * @author  Frank Kleine <frank.kleine@schlund.de>
7  */
8 XJConfLoader::load('DefinedTag',
9                    'GenericTag',
10                    'definitions.NamespaceDefinition',
11                    'definitions.NamespaceDefinitions',
12                    'exceptions.UnknownNamespaceException',
13                    'exceptions.UnknownTagException'
14 );
15 /**
16  * Parser that reads xml files and generates the data structure.
17  *
18  * This parser reads xml files using the tag definitions and
19  * created the data structure and objects described by tag
20  * definitions and the xml file.
21  *
22  * @package  XJConf
23  */
24 class XmlParser
25 {
26     /**
27      * the list of tags that have to be processed
28      *
29      * @var  array<Tag>
30      */
31     private $tagStack    = array();
32     /**
33      * hashmap of generated data types
34      *
35      * @var  array<String, mixed>
36      */
37     private $config      = array();
38     /**
39      * a listof defined namespaces
40      *
41      * @var  NamespaceDefinitions
42      */
43     private $tagDefs;
44     /**
45      * current depth within the parsed document
46      *
47      * @var  int
48      */
49     private $depth       = 0;
50     /**
51      * list of extensions to use for the namespace
52      *
53      * @var  array<String, Extension>
54      */
55     private $extensions  = array();
56     /**
57      * the default namespace if none is set
58      *
59      * @var  string
60      */
61     private $myNamespace = 'http://www.schst.net/XJConf';
62     /**
63      * stack of currently opened files
64      *
65      * @var  array<String>
66      */
67     private $openFiles   = array();
68
69     /**
70      * list of node types, used for compatibility between PHP 5.0 and 5.1
71      *
72      * @var  array
73      */
74     private $nodeTypes   = array();
75
76     /**
77      * constructor
78      *
79      * Sets the node types depending on your PHP version using the constants
80      * defined by the XMLReader PHP extension.
81      */
82     public function __construct()
83     {
84         if (!defined('XMLREADER_ELEMENT')) {
85             $this->nodeTypes = array('startTag' => XMLReader::ELEMENT,
86                                      'text'     => XMLReader::TEXT,
87                                      'endTag'   => XMLReader::END_ELEMENT
88                                );
89         } else {
90             $this->nodeTypes = array('startTag' => XMLREADER_ELEMENT,
91                                      'text'     => XMLREADER_TEXT,
92                                      'endTag'   => XMLREADER_END_ELEMENT
93                                );
94         }
95     }
96
97     /**
98      * set the list of namespace defintions
99      *
100      * @param  NamespaceDefinitions  $tagDefs
101      */
102     public function setTagDefinitions(NamespaceDefinitions $tagDefs)
103     {
104         $this->tagDefs = $tagDefs;
105     }
106
107     /**
108      * add some more namespace definitions
109      *
110      * @param  NamespaceDefinitions  $tagDefs
111      */
112     public function addTagDefinitions(NamespaceDefinitions $tagDefs)
113     {
114         if (null == $this->tagDefs) {
115             $this->setTagDefinitions($tagDefs);
116             return;
117         }
118
119         $this->tagDefs->appendNamespaceDefinitions($tagDefs);
120     }
121
122     /**
123      * add an extension that handles all tags in given namespace
124      *
125      * @param  string     $namespace  handle all tags in this namespace with given extension
126      * @param  Extension  $ext        use this extension to handle all tags in given namespace
127      */
128     public function addExtension(Extension $ext, $namespace = null)
129     {
130         if ($namespace == null) {
131             $namespace = $ext->getNamespace();
132         }
133         $this->extensions[$namespace] = $ext;
134     }
135
136     /**
137      * parses a given file and creates the data structure described in this file
138      *
139      * @param  string  $filename
140      */
141     public function parse($filename)
142     {
143         $reader = $this->initParser();
144         array_push($this->openFiles, $filename);
145         $reader->open($filename);
146         while ($reader->read()) {
147             switch ($reader->nodeType) {
148                 case $this->nodeTypes['startTag']:
149                     $empty = $reader->isEmptyElement;
150                     $nameSpaceURI = $reader->namespaceURI;
151                     $elementName  = $reader->localName;
152                     $attributes   = array();
153                     if (TRUE == $reader->hasAttributes) {
154                         // go to first attribute
155                         $attribute = $reader->moveToFirstAttribute();
156                         // save data of all attributes
157                         while (TRUE == $attribute) {
158                             $attributes[$reader->localName] = $reader->value;
159                             $attribute = $reader->moveToNextAttribute();
160                         }
161                     }
162
163                     $this->startElement($nameSpaceURI, $elementName, $attributes);
164                     if (true === $empty) {
165                         $this->endElement($nameSpaceURI, $elementName);
166                     }
167                     break;
168
169                 case $this->nodeTypes['text']:
170                     $this->characters($reader->value);
171                     break;
172
173                 case $this->nodeTypes['endTag']:
174                     $this->endElement($reader->namespaceURI, $reader->localName);
175                     break;
176             }
177         }
178
179         $reader->close($filename);
180         array_pop($this->openFiles);
181
182     }
183
184     /**
185      * returns the data structure associated with this name
186      *
187      * @param   string  $name
188      * @return  mixed
189      */
190     public function getConfigValue($name)
191     {
192         return $this->config[$name];
193     }
194
195     /**
196      * returns the name of the file that is currently parsed
197      *
198      * @return  string
199      */
200     public function getCurrentFile()
201     {
202         return end($this->openFiles);
203     }
204
205     /**
206      * initializes the parser
207      */
208     private function initParser()
209     {
210         $reader = new XMLReader();
211         return $reader;
212     }
213
214     /**
215      * handles the start element
216      *
217      * Creates a new Tag object and pushes it
218      * onto the stack.
219      *
220      * @param  string  $namespaceURI  namespace of start tag
221      * @param  string  $sName         name of start tag
222      * @param  array   $atts          attributes of tag
223      */
224     private function startElement($namespaceURI, $sName, $atts)
225     {
226         // do not handle stuff in our own namespace
227         if ($this->myNamespace == $namespaceURI && 0 < $this->depth) {
228             return;
229         }
230         $this->depth++;
231
232         // no namespace defined, use the default namespace
233         if (strlen($namespaceURI) == 0) {
234             $namespaceURI = '__default';
235         }
236
237         // ignore the root tag
238         if (1 == $this->depth) {
239             return;
240         }
241
242         // This tag needs to be handled by an extension
243         if (isset($this->extensions[$namespaceURI]) == true) {
244             $tag = new GenericTag($sName, $atts);
245             $this->extensions[$namespaceURI]->startElement($this, $tag);
246         // This tag has been defined internally
247         } else {
248             if ($this->tagDefs->isNamespaceDefined($namespaceURI) == false) {
249                 throw new UnknownNamespaceException('Unknown namespace ' . $namespaceURI . ' in file ' . end($this->openFiles));
250             }
251
252             if ($this->tagDefs->isTagDefined($namespaceURI, $sName) == false) {
253                 throw new UnknownTagException('Unknown tag ' . $sName . ' in namespace ' . $namespaceURI);
254             }
255
256             $tag = new DefinedTag($sName, $atts);
257             // fetch the defintion for this tag
258             $tag->setDefinition($this->tagDefs->getTagDefinition($namespaceURI, $sName));
259         }
260
261         array_push($this->tagStack, $tag);
262     }
263
264     /**
265      * handles the end element
266      *
267      * Fetches the current element from the stack and
268      * converts it to the correct type.
269      *
270      * @param  string  $namespaceURI  namespace of end tag
271      * @param  string  $sName         name of end tag
272      */
273     private function endElement($namespaceURI, $sName)
274     {
275         // do not handle stuff in our own namespace
276         if ($this->myNamespace == $namespaceURI && 0 < $this->depth) {
277             return;
278         }
279         $this->depth--;
280
281         // no namespace defined, use the default namespace
282         if (strlen($namespaceURI) == 0) {
283             $namespaceURI = '__default';
284         }
285
286         // ignore the root tag
287         if (0 == $this->depth) {
288             return;
289         }
290
291         // get the last tag from the stack
292         $tag = array_pop($this->tagStack);
293
294         // This tag needs to be handled by an extension
295         if (isset($this->extensions[$namespaceURI]) == true) {
296             $result = $this->extensions[$namespaceURI]->endElement($this, $tag);
297             if (null != $result) {
298                 if (1 == $this->depth) {
299                     $this->config[$tag->getKey()] = $result->getConvertedValue();
300                 } else {
301                     $parent = array_pop($this->tagStack);
302                     if ($result->getKey() == null && $parent->supportsIndexedChildren() == false) {
303                         $parent->setContent($result->getConvertedValue());
304                     } else {
305                         $parent->addChild($result);
306                     }
307                     array_push($this->tagStack, $parent);
308                 }
309             }
310         // last tag before returning to root
311         } elseif (1 == $this->depth) {
312             $this->config[$tag->getKey()] = $tag->getConvertedValue();
313         // add this tag to the tag before as child
314         } else {
315             $parent = array_pop($this->tagStack);
316             $parent->addChild($tag);
317             array_push($this->tagStack, $parent);
318         }
319     }
320
321     /**
322      * Character data handler
323      *
324      * Fetches the current tag from the stack and
325      * appends the data.
326      *
327      * @param  string  $buf
328      */
329     private function characters($buf)
330     {
331         if (count($this->tagStack) == 0) {
332             return;
333         }
334
335         $tag = end($this->tagStack);
336         $tag->addData($buf);
337     }
338 }
339 ?>
Note: See TracBrowser for help on using the browser.