root/branches/PRE_NAMESPACES/XJConf/DefinitionParser.php

Revision 140, 8.7 kB (checked in by mikey, 6 months ago)

complete doc comment

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