root/tags/RELEASE_0_1_2/XJConf/DefinitionParser.php

Revision 46, 8.2 kB (checked in by mikey, 2 years ago)

added possibility to use class loaders

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      * hashmap of class loaders where the key is the namespace the class loader
75      *  should be used for
76      *
77      * @var  array<String,XJConfClassLoader>
78      */
79     private $classLoaders    = array();
80
81     /**
82      * constructor
83      *
84      * Sets the node types depending on your PHP version using the constants
85      * defined by the XMLReader PHP extension.
86      *
87      * @param  array<String,XJConfClassLoader>  $classLoaders  optional
88      */
89     public function __construct($classLoaders = array())
90     {
91         $this->defs             = new NamespaceDefinitions();
92         $this->currentNamespace = self::DEFAULT_NAMESPACE;
93         $this->classLoaders     = $classLoaders;
94
95         if (!defined('XMLREADER_ELEMENT')) {
96             $this->nodeTypes = array('startTag' => XMLReader::ELEMENT,
97                                      'text'     => XMLReader::TEXT,
98                                      'endTag'   => XMLReader::END_ELEMENT
99                                );
100         } else {
101             $this->nodeTypes = array('startTag' => XMLREADER_ELEMENT,
102                                      'text'     => XMLREADER_TEXT,
103                                      'endTag'   => XMLREADER_END_ELEMENT
104                                );
105         }
106     }
107
108     /**
109      * returns the current namespace
110      *
111      * @return  string
112      */
113     public function getCurrentNamespace()
114     {
115         return $this->currentNamespace;
116     }
117
118     /**
119      * returns the list of created namespace definitions
120      *
121      * @return  NamespaceDefinitions
122      */
123     public function getNamespaceDefinitions()
124     {
125         return $this->defs;
126     }
127     
128     /**
129      * check whether a class loader exists for given namespace
130      *
131      * @param   string  $namespace
132      * @return  bool
133      */
134     public function hasClassLoader($namespace)
135     {
136         return (isset($this->classLoaders[$namespace]));
137     }
138     
139     /**
140      * return the class loader for the given namespace
141      *
142      * @param   string             $namespace
143      * @return  XJConfClassLoader
144      */
145     public function getClassLoader($namespace)
146     {
147         
148         if ($this->hasClassLoader($namespace) == true) {
149             return $this->classLoaders[$namespace];
150         }
151         
152         return null;
153     }
154
155     /**
156      * returns the definition stack
157      *
158      * @return  array<Definition>
159      */
160     public function &getDefStack()
161     {
162         return $this->defStack;
163     }
164
165     /**
166      * initializes the parser
167      */
168     private function initParser()
169     {
170         if (null == $this->reader) {
171             $this->reader = new XMLReader();
172         }
173     }
174
175     /**
176      * parse a tag definitions file and return
177      * an instance of NamespaceDefinition
178      *
179      * @param   string               $filename  filename of the defintions file
180      * @return  NamespaceDefinition
181      * @throws  InvalidNamespaceDefinitionException
182      */
183     public function parse($filename)
184     {
185         $this->initParser();
186         $this->reader->open($filename);
187         while ($this->reader->read()) {
188             switch ($this->reader->nodeType) {
189                 case $this->nodeTypes['startTag']:
190                     $nameSpaceURI = $this->reader->namespaceURI;
191                     $elementName  = $this->reader->localName;
192                     $attributes   = array();
193                     $empty = $this->reader->isEmptyElement;
194                     if (TRUE == $this->reader->hasAttributes) {
195                         // go to first attribute
196                         $attribute = $this->reader->moveToFirstAttribute();
197                         // save data of all attributes
198                         while (TRUE == $attribute) {
199                             $attributes[$this->reader->localName] = $this->reader->value;
200                             $attribute = $this->reader->moveToNextAttribute();
201                         }
202                     }
203                     $this->startElement($nameSpaceURI, $elementName, $attributes);
204                     if (true === $empty) {
205                         $this->endElement($nameSpaceURI, $elementName);
206                     }
207                     break;
208
209                 case $this->nodeTypes['text']:
210                     $this->characters($this->reader->value);
211                     break;
212
213                 case $this->nodeTypes['endTag']:
214                     $this->endElement($this->reader->namespaceURI, $this->reader->localName);
215                     break;
216             }
217         }
218
219         $this->reader->close($filename);
220
221         return $this->defs;
222     }
223
224     /**
225      * Start Element handler
226      *
227      * Creates the TagDefinition object and places it on
228      * the stack.
229      *
230      * @param   string  $namespaceURI  namespace of start tag
231      * @param   string  $sName         name of start tag
232      * @param   array   $atts          attributes of tag
233      * @throws  InvalidNamespaceDefinitionException
234      */
235     private function startElement($namespaceURI, $sName, $atts)
236     {
237         // a new namespace
238         if (self::TAG_NAMESPACE  == $sName) {
239             if (isset($atts['uri']) == false) {
240                 throw new InvalidNamespaceDefinitionException('The <' . self::TAG_NAMESPACE . '> tag is missing the uri attribute.');
241             }
242
243             // change current namespace to new namespace
244             $this->currentNamespace = $atts['uri'];
245             return;
246         }
247         
248         // create the appropriate definition handler and use this
249         // to create the required definition
250         $defHandler = DefinitionHandlerFactory::create($sName, $this);
251         $def        = $defHandler->startElement($namespaceURI, $sName, $atts);
252         if (null != $def) {
253             array_push($this->defStack, $def);
254         }
255
256         array_push($this->defHandlerStack, $defHandler);
257     }
258
259     /**
260      * End Element handler
261      *
262      * Fetches the TagDefinition from the stack and
263      * adds it to the NamespaceDefinition object.
264      *
265      * @param   string  $namespaceURI  namespace of end tag
266      * @param   string  $sName         name of end tag
267      */
268     private function endElement($namespaceURI, $sName)
269     {
270         // namespace definition ends, switch back to default namespace
271         if (self::TAG_NAMESPACE  == $sName) {
272             $this->currentNamespace = self::DEFAULT_NAMESPACE;
273             return;
274         }
275
276         // use definition handler to finalize the definition of the current tag
277         $defHandler = array_pop($this->defHandlerStack);
278         $defHandler->endElement($namespaceURI, $sName);
279     }
280 }
281 ?>
Note: See TracBrowser for help on using the browser.