View Javadoc

1   /*
2    * Copyright (C) The MetaClass Group. All rights reserved.
3    *
4    * This software is published under the terms of the Spice
5    * Software License version 1.1, a copy of which has been included
6    * with this distribution in the LICENSE.txt file.
7    */
8   package org.codehaus.metaclass.io;
9   
10  import java.util.ArrayList;
11  import java.util.Properties;
12  import org.codehaus.metaclass.model.Attribute;
13  import org.codehaus.metaclass.model.ClassDescriptor;
14  import org.codehaus.metaclass.model.FieldDescriptor;
15  import org.codehaus.metaclass.model.MethodDescriptor;
16  import org.codehaus.metaclass.model.ParameterDescriptor;
17  import org.w3c.dom.Element;
18  import org.w3c.dom.Node;
19  import org.w3c.dom.NodeList;
20  import org.w3c.dom.Attr;
21  import org.w3c.dom.Document;
22  
23  /***
24   * Utility class to build a ClassDescriptor from a DOM
25   * representation Element.
26   *
27   * @author Peter Donald
28   * @version $Revision: 1.13 $ $Date: 2003/11/27 08:09:53 $
29   */
30  public final class DOMMetaClassDeserializer
31  {
32      /***
33       * Build a ClassDescriptor from a Document.
34       *
35       * @param document the document
36       * @return the ClassDescriptor
37       * @throws Exception if document malformed
38       */
39      public ClassDescriptor buildClassDescriptor( final Document document )
40          throws Exception
41      {
42          return buildClassDescriptor( document.getDocumentElement() );
43      }
44  
45      /***
46       * Build a ClassDescriptor from element.
47       *
48       * @param element the element
49       * @return the ClassDescriptor
50       * @throws Exception if element malformed
51       */
52      public ClassDescriptor buildClassDescriptor( final Element element )
53          throws Exception
54      {
55          expectElement( element, MetaClassIOXml.CLASS_ELEMENT );
56          final String type =
57              expectAttribute( element, MetaClassIOXml.TYPE_ATTRIBUTE );
58          Attribute[] attributes = Attribute.EMPTY_SET;
59          MethodDescriptor[] methods = MethodDescriptor.EMPTY_SET;
60          FieldDescriptor[] fields = FieldDescriptor.EMPTY_SET;
61          final NodeList nodes = element.getChildNodes();
62          final int length = nodes.getLength();
63          for( int i = 0; i < length; i++ )
64          {
65              final Node node = nodes.item( i );
66              final short nodeType = node.getNodeType();
67              if( nodeType == Node.ELEMENT_NODE )
68              {
69                  final Element child = (Element)node;
70                  final String childName = child.getNodeName();
71                  if( childName.equals( MetaClassIOXml.METHODS_ELEMENT ) )
72                  {
73                      methods = buildMethods( child );
74                  }
75                  else if( childName.equals( MetaClassIOXml.FIELDS_ELEMENT ) )
76                  {
77                      fields = buildFields( child );
78                  }
79                  else
80                  {
81                      attributes = buildAttributes( child );
82                  }
83              }
84          }
85          return new ClassDescriptor( type,
86                                      attributes,
87                                      attributes,
88                                      fields,
89                                      methods );
90      }
91  
92      /***
93       * Build a set of methods from element.
94       *
95       * @param element the element
96       * @return the methods
97       * @throws Exception if element malformed
98       */
99      MethodDescriptor[] buildMethods( final Element element )
100         throws Exception
101     {
102         expectElement( element, MetaClassIOXml.METHODS_ELEMENT );
103 
104         final ArrayList methods = new ArrayList();
105         final NodeList nodes = element.getChildNodes();
106         final int length = nodes.getLength();
107         for( int i = 0; i < length; i++ )
108         {
109             final Node node = nodes.item( i );
110             final short nodeType = node.getNodeType();
111             if( nodeType == Node.ELEMENT_NODE )
112             {
113                 final MethodDescriptor field = buildMethod( (Element)node );
114                 methods.add( field );
115             }
116         }
117 
118         return (MethodDescriptor[])methods.
119             toArray( new MethodDescriptor[ methods.size() ] );
120     }
121 
122     /***
123      * Build a method from element.
124      *
125      * @param element the element
126      * @return the method
127      * @throws Exception if element malformed
128      */
129     MethodDescriptor buildMethod( final Element element )
130         throws Exception
131     {
132         expectElement( element, MetaClassIOXml.METHOD_ELEMENT );
133         final String name =
134             expectAttribute( element, MetaClassIOXml.NAME_ATTRIBUTE );
135         final String type =
136             expectAttribute( element, MetaClassIOXml.TYPE_ATTRIBUTE );
137         Attribute[] attributes = Attribute.EMPTY_SET;
138         ParameterDescriptor[] parameters = ParameterDescriptor.EMPTY_SET;
139         final NodeList nodes = element.getChildNodes();
140         final int length = nodes.getLength();
141         for( int i = 0; i < length; i++ )
142         {
143             final Node node = nodes.item( i );
144             final short nodeType = node.getNodeType();
145             if( nodeType == Node.ELEMENT_NODE )
146             {
147                 final Element child = (Element)node;
148                 final String childName = child.getNodeName();
149                 if( childName.equals( MetaClassIOXml.PARAMETERS_ELEMENT ) )
150                 {
151                     parameters = buildParameters( child );
152                 }
153                 else
154                 {
155                     attributes = buildAttributes( child );
156                 }
157             }
158         }
159         return new MethodDescriptor( name, type, parameters, attributes, attributes );
160     }
161 
162     /***
163      * Build a set of method parameters from element.
164      *
165      * @param element the element
166      * @return the method parameters
167      * @throws Exception if element malformed
168      */
169     ParameterDescriptor[] buildParameters( final Element element )
170         throws Exception
171     {
172         expectElement( element, MetaClassIOXml.PARAMETERS_ELEMENT );
173 
174         final ArrayList parameters = new ArrayList();
175         final NodeList nodes = element.getChildNodes();
176         final int length = nodes.getLength();
177         for( int i = 0; i < length; i++ )
178         {
179             final Node node = nodes.item( i );
180             final short nodeType = node.getNodeType();
181             if( nodeType == Node.ELEMENT_NODE )
182             {
183                 final ParameterDescriptor parameter =
184                     buildParameter( (Element)node );
185                 parameters.add( parameter );
186             }
187         }
188 
189         return (ParameterDescriptor[])parameters.
190             toArray( new ParameterDescriptor[ parameters.size() ] );
191     }
192 
193     /***
194      * Build a method parameter from element.
195      *
196      * @param element the element
197      * @return the method parameter
198      * @throws Exception if element malformed
199      */
200     ParameterDescriptor buildParameter( final Element element )
201         throws Exception
202     {
203         expectElement( element, MetaClassIOXml.PARAMETER_ELEMENT );
204         final String name =
205             expectAttribute( element, MetaClassIOXml.NAME_ATTRIBUTE );
206         final String type =
207             expectAttribute( element, MetaClassIOXml.TYPE_ATTRIBUTE );
208 
209         return new ParameterDescriptor( name, type );
210     }
211 
212     /***
213      * Build a set of fields from element.
214      *
215      * @param element the element
216      * @return the fields
217      * @throws Exception if element malformed
218      */
219     FieldDescriptor[] buildFields( final Element element )
220         throws Exception
221     {
222         expectElement( element, MetaClassIOXml.FIELDS_ELEMENT );
223 
224         final ArrayList fields = new ArrayList();
225         final NodeList nodes = element.getChildNodes();
226         final int length = nodes.getLength();
227         for( int i = 0; i < length; i++ )
228         {
229             final Node node = nodes.item( i );
230             final short nodeType = node.getNodeType();
231             if( nodeType == Node.ELEMENT_NODE )
232             {
233                 final FieldDescriptor field = buildField( (Element)node );
234                 fields.add( field );
235             }
236         }
237 
238         return (FieldDescriptor[])fields.
239             toArray( new FieldDescriptor[ fields.size() ] );
240     }
241 
242     /***
243      * Build a field from element.
244      *
245      * @param element the element
246      * @return the field
247      * @throws Exception if element malformed
248      */
249     FieldDescriptor buildField( final Element element )
250         throws Exception
251     {
252         expectElement( element, MetaClassIOXml.FIELD_ELEMENT );
253         final String name =
254             expectAttribute( element, MetaClassIOXml.NAME_ATTRIBUTE );
255         final String type =
256             expectAttribute( element, MetaClassIOXml.TYPE_ATTRIBUTE );
257         Attribute[] attributes = Attribute.EMPTY_SET;
258         final NodeList nodes = element.getChildNodes();
259         final int length = nodes.getLength();
260         for( int i = 0; i < length; i++ )
261         {
262             final Node node = nodes.item( i );
263             final short nodeType = node.getNodeType();
264             if( nodeType == Node.ELEMENT_NODE )
265             {
266                 attributes = buildAttributes( (Element)node );
267             }
268         }
269         return new FieldDescriptor( name, type, attributes, attributes );
270     }
271 
272     /***
273      * Build a set of attributes from element.
274      *
275      * @param element the element
276      * @return the attributes
277      * @throws Exception if element malformed
278      */
279     Attribute[] buildAttributes( final Element element )
280         throws Exception
281     {
282         expectElement( element, MetaClassIOXml.ATTRIBUTES_ELEMENT );
283 
284         final ArrayList attributes = new ArrayList();
285         final NodeList nodes = element.getChildNodes();
286         final int length = nodes.getLength();
287         for( int i = 0; i < length; i++ )
288         {
289             final Node node = nodes.item( i );
290             final short nodeType = node.getNodeType();
291             if( nodeType == Node.ELEMENT_NODE )
292             {
293                 final Attribute attribute = buildAttribute( (Element)node );
294                 attributes.add( attribute );
295             }
296         }
297 
298         return (Attribute[])attributes.
299             toArray( new Attribute[ attributes.size() ] );
300     }
301 
302     /***
303      * Build attribute from specified element.
304      *
305      * @param element the element
306      * @return the attribute
307      * @throws Exception if element malformed
308      */
309     Attribute buildAttribute( final Element element )
310         throws Exception
311     {
312         expectElement( element, MetaClassIOXml.ATTRIBUTE_ELEMENT );
313         final String name =
314             expectAttribute( element, MetaClassIOXml.NAME_ATTRIBUTE );
315 
316         final StringBuffer sb = new StringBuffer();
317         final Properties parameters = new Properties();
318         final NodeList nodes = element.getChildNodes();
319         final int length = nodes.getLength();
320         for( int i = 0; i < length; i++ )
321         {
322             final Node node = nodes.item( i );
323             final short nodeType = node.getNodeType();
324             if( nodeType == Node.ELEMENT_NODE )
325             {
326                 buildParam( (Element)node, parameters );
327             }
328             else if( nodeType == Node.TEXT_NODE ||
329                 nodeType == Node.CDATA_SECTION_NODE )
330             {
331                 final String value = node.getNodeValue();
332                 sb.append( value );
333             }
334         }
335 
336         final String value = sb.toString().trim();
337         if( 0 != value.length() &&
338             0 < parameters.size() )
339         {
340             final String message =
341                 "Attribute named " + name +
342                 " specified both a value (" + value + ") " +
343                 "and parameters (" + parameters + ").";
344             throw new Exception( message );
345         }
346         if( 0 == value.length() )
347         {
348             return new Attribute( name, parameters );
349         }
350         else
351         {
352             return new Attribute( name, value );
353         }
354     }
355 
356     /***
357      * Build a parameter from element and add to specified parameters.
358      *
359      * @param element the element
360      * @param parameters the parameters
361      * @throws Exception if element malformed
362      */
363     void buildParam( final Element element,
364                      final Properties parameters )
365         throws Exception
366     {
367         expectElement( element, MetaClassIOXml.PARAM_ELEMENT );
368         final String name =
369             expectAttribute( element, MetaClassIOXml.NAME_ATTRIBUTE );
370         final String value =
371             expectAttribute( element, MetaClassIOXml.VALUE_ATTRIBUTE );
372         parameters.setProperty( name, value );
373     }
374 
375     /***
376      * Expect that specified element has specified name else
377      * throw an exception.
378      *
379      * @param element the element
380      * @param name the name
381      * @throws Exception if element does not have name
382      */
383     void expectElement( final Element element,
384                         final String name )
385         throws Exception
386     {
387         final String actual = element.getTagName();
388         if( !actual.equals( name ) )
389         {
390             final String message = "Unexpected element. " +
391                 "Expected: " + name + ". Actual: " + actual +
392                 " @ " + getPathDescription( element ) + ".";
393             throw new Exception( message );
394         }
395     }
396 
397     /***
398      * Expect that specified element has attribute with specified
399      * name and return value. If attribute can not be located then
400      * throw an exception.
401      *
402      * @param element the element
403      * @param name the attributes name
404      * @return the attributes value
405      * @throws Exception if unable to locate attribute
406      */
407     String expectAttribute( final Element element,
408                             final String name )
409         throws Exception
410     {
411         final Attr actual = element.getAttributeNode( name );
412         if( null == actual )
413         {
414             final String message =
415                 "Element named " + element.getTagName() +
416                 " missing attribute named " + name +
417                 " @ " + getPathDescription( element ) + ".";
418             throw new Exception( message );
419         }
420         return actual.getValue();
421     }
422 
423     /***
424      * Return a description of path to specified element.
425      * The path is separate by "/" and starts with root
426      * element descending to specified element.
427      *
428      * @param cause the element
429      * @return the path description
430      */
431     String getPathDescription( final Element cause )
432     {
433         final StringBuffer sb = new StringBuffer();
434 
435         Element element = cause;
436         while( true )
437         {
438             if( sb.length() > 0 )
439             {
440                 sb.insert( 0, "/" );
441             }
442             sb.insert( 0, element.getNodeName() );
443             final Node parentNode = element.getParentNode();
444             if( parentNode instanceof Element )
445             {
446                 element = (Element)parentNode;
447             }
448             else
449             {
450                 break;
451             }
452         }
453 
454         return sb.toString();
455     }
456 }