root/tags/RELEASE_0_1_1/XJConf/DefinitionParser.php

Revision 19, 7.2 kB (checked in by mikey, 2 years ago)

- fix: DefinitionParser? used last attribute namespace and name for calling endElement() instead of the ones of the element
- removed DefinitionHandler::needsEnd()

Line 
1 <?php
2 /**
3  * Parse tag definitions files.
4  *
5  * @author  Stephan Schmidt <stephan.schmidt@schlund.de>
6  * @author  Frank Kleine <frank.kleine@schlund.de>
7  */
8 XJConfLoader::load('definitions.AttributeDefinition',
9                    'definitions.CDataDefinition',
10                    'definitions.ChildDefinition',
11                    'definitions.ConstructorDefinition',
12                    'definitions.FactoryMethodDefinition',
13                    'definitions.NamespaceDefinition',
14                    'definitions.NamespaceDefinitions',
15                    'definitions.TagDefinition',
16                    'definitions.handler.DefinitionHandlerFactory',
17                    'exceptions.InvalidNamespaceDefinitionException'
18 );
19 /**
20  * Parse tag definitions files.
21  *
22  * This parser reads xml files that define the tags used by other
23  * xml documents which describe a data structure.
24  *
25  * @package  XJConf
26  */
27 class DefinitionParser
28 {
29     /**
30      * this tag defines a namespace
31      */
32     const TAG_NAMESPACE      = 'namespace';
33     /**
34      * stack for currently open definitions
35      *
36      * @var  array<Definition>
37      */
38     private $defStack        = array();
39     /**
40      * stack for currently opened definition handlers
41      *
42      * @var  DefinitionHandler
43      */
44     private $defHandlerStack = array();
45     /**
46      * Constant for the default namespace
47      */
48     const DEFAULT_NAMESPACE  = '__default';
49     /**
50      * The current namespace
51      *
52      * @var  string
53      */
54     private $currentNamespace;
55     /**
56      * All extracted namespace definitions
57      *
58      * @var  NamespaceDefinitions
59      */
60     private $defs;
61     /**
62      * the real xml parser
63      *
64      * @var  XMLReader
65      */
66     private $reader;
67     /**
68      * list of node types, used for compatibility between PHP 5.0 and 5.1
69      *
70      * @var  array
71      */
72     private $nodeTypes       = array();
73
74     /**
75      * constructor
76      *
77      * Sets the node types depending on your PHP version using the constants
78      * defined by the XMLReader PHP extension.
79      */
80     public function __construct()
81     {
82         $this->defs             = new NamespaceDefinitions();
83         $this->currentNamespace = self::DEFAULT_NAMESPACE;
84
85         if (!defined('XMLREADER_ELEMENT')) {
86             $this->nodeTypes = array('startTag' => XMLReader::ELEMENT,
87                                      'text'     => XMLReader::TEXT,
88                                      'endTag'   => XMLReader::END_ELEMENT
89                                );
90         } else {
91             $this->nodeTypes = array('startTag' => XMLREADER_ELEMENT,
92                                      'text'     => XMLREADER_TEXT,
93                                      'endTag'   => XMLREADER_END_ELEMENT
94                                );
95         }
96     }
97
98     /**
99      * returns the current namespace
100      *
101      * @return  string
102      */
103     public function getCurrentNamespace()
104     {
105         return $this->currentNamespace;
106     }
107
108     /**
109      * returns the list of created namespace definitions
110      *
111      * @return  NamespaceDefinitions
112      */
113     public function getNamespaceDefinitions()
114     {
115         return $this->defs;
116     }
117
118     /**
119      * returns the definition stack
120      *
121      * @return  array<Definition>
122      */
123     public function &getDefStack()
124     {
125         return $this->defStack;
126     }
127
128     /**
129      * initializes the parser
130      */
131     private function initParser()
132     {
133         if (null == $this->reader) {
134             $this->reader = new XMLReader();
135         }
136     }
137
138     /**
139      * parse a tag definitions file and return
140      * an instance of NamespaceDefinition
141      *
142      * @param   string               $filename  filename of the defintions file
143      * @return  NamespaceDefinition
144      * @throws  InvalidNamespaceDefinitionException
145      */
146     public function parse($filename)
147     {
148         $this->initParser();
149         $this->reader->open($filename);
150         while ($this->reader->read()) {
151             switch ($this->reader->nodeType) {
152                 case $this->nodeTypes['startTag']:
153                     $nameSpaceURI = $this->reader->namespaceURI;
154                     $elementName  = $this->reader->localName;
155                     $attributes   = array();
156                     $empty = $this->reader->isEmptyElement;
157                     if (TRUE == $this->reader->hasAttributes) {
158                         // go to first attribute
159                         $attribute = $this->reader->moveToFirstAttribute();
160                         // save data of all attributes
161                         while (TRUE == $attribute) {
162                             $attributes[$this->reader->localName] = $this->reader->value;
163                             $attribute = $this->reader->moveToNextAttribute();
164                         }
165                     }
166
167                     $this->startElement($nameSpaceURI, $elementName, $attributes);
168                     if (true === $empty) {
169                         $this->endElement($nameSpaceURI, $elementName);
170                     }
171                     break;
172
173                 case $this->nodeTypes['text']:
174                     $this->characters($this->reader->value);
175                     break;
176
177                 case $this->nodeTypes['endTag']:
178                     $this->endElement($this->reader->namespaceURI, $this->reader->localName);
179                     break;
180             }
181         }
182
183         $this->reader->close($filename);
184
185         return $this->defs;
186     }
187
188     /**
189      * Start Element handler
190      *
191      * Creates the TagDefinition object and places it on
192      * the stack.
193      *
194      * @param   string  $namespaceURI  namespace of start tag
195      * @param   string  $sName         name of start tag
196      * @param   array   $atts          attributes of tag
197      * @throws  InvalidNamespaceDefinitionException
198      */
199     private function startElement($namespaceURI, $sName, $atts)
200     {
201         // a new namespace
202         if (self::TAG_NAMESPACE  == $sName) {
203             if (isset($atts['uri']) == false) {
204                 throw new InvalidNamespaceDefinitionException('The <' . self::TAG_NAMESPACE . '> tag is missing the uri attribute.');
205             }
206
207             // change current namespace to new namespace
208             $this->currentNamespace = $atts['uri'];
209             return;
210         }
211
212         // create the appropriate definition handler and use this
213         // to create the required definition
214         $defHandler = DefinitionHandlerFactory::create($sName, $this);
215         $def        = $defHandler->startElement($namespaceURI, $sName, $atts);
216         if (null != $def) {
217             array_push($this->defStack, $def);
218         }
219
220         array_push($this->defHandlerStack, $defHandler);
221     }
222
223     /**
224      * End Element handler
225      *
226      * Fetches the TagDefinition from the stack and
227      * adds it to the NamespaceDefinition object.
228      *
229      * @param   string  $namespaceURI  namespace of end tag
230      * @param   string  $sName         name of end tag
231      */
232     private function endElement($namespaceURI, $sName)
233     {
234         // namespace definition ends, switch back to default namespace
235         if (self::TAG_NAMESPACE  == $sName) {
236             $this->currentNamespace = self::DEFAULT_NAMESPACE;
237             return;
238         }
239
240         // use definition handler to finalize the definition of the current tag
241         $defHandler = array_pop($this->defHandlerStack);
242         $defHandler->endElement($namespaceURI, $sName);
243     }
244 }
245 ?>
Note: See TracBrowser for help on using the browser.