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.tools.qdox;
9   
10  import com.thoughtworks.qdox.model.DocletTag;
11  import com.thoughtworks.qdox.model.JavaClass;
12  import com.thoughtworks.qdox.model.JavaField;
13  import com.thoughtworks.qdox.model.JavaMethod;
14  import com.thoughtworks.qdox.model.JavaParameter;
15  import com.thoughtworks.qdox.model.Type;
16  import java.util.ArrayList;
17  import java.util.Properties;
18  import org.codehaus.metaclass.model.Attribute;
19  import org.codehaus.metaclass.model.ClassDescriptor;
20  import org.codehaus.metaclass.model.FieldDescriptor;
21  import org.codehaus.metaclass.model.MethodDescriptor;
22  import org.codehaus.metaclass.model.ParameterDescriptor;
23  
24  /***
25   * This class is responsible for parsing a JavaClass object
26   * and building a ClassDescriptor to correspond to the JavaClass
27   * object.
28   *
29   * @version $Revision: 1.20 $ $Date: 2003/10/28 13:40:52 $
30   */
31  public class QDoxDescriptorParser
32  {
33      /***
34       * Constant indicating parse state is
35       * before the start of key.
36       */
37      private static final int PARSE_KEY_START = 0;
38  
39      /***
40       * Constant indicating parse state is
41       * parsing the key.
42       */
43      private static final int PARSE_KEY = 1;
44  
45      /***
46       * Constant indicating parse state is
47       * before the start of value and expecting ".
48       */
49      private static final int PARSE_VALUE_START = 2;
50  
51      /***
52       * Constant indicating parse state is
53       * parsing value string.
54       */
55      private static final int PARSE_VALUE = 3;
56  
57      /***
58       * Constant indicating parse state is
59       * after value closed.
60       */
61      private static final int PARSE_END = 4;
62  
63      /***
64       * Build a ClassDescriptor for a JavaClass.
65       *
66       * @param javaClass the JavaClass
67       * @return the ClassDescriptor
68       */
69      public ClassDescriptor buildClassDescriptor( final JavaClass javaClass )
70      {
71          return buildClassDescriptor( javaClass, new DefaultQDoxAttributeInterceptor() );
72      }
73  
74      /***
75       * Build a ClassDescriptor for a JavaClass.
76       *
77       * @param javaClass the JavaClass
78       * @param interceptors the AttributeInterceptors
79       * @return the ClassDescriptor
80       */
81      public ClassDescriptor buildClassDescriptor( final JavaClass javaClass,
82                                                   final QDoxAttributeInterceptor[] interceptors )
83      {
84          return buildClassDescriptor( javaClass, new MulticastInterceptor( interceptors ) );
85      }
86  
87      /***
88       * Build a ClassDescriptor for a JavaClass.
89       *
90       * @param javaClass the JavaClass
91       * @param interceptor the AttributeInterceptor
92       * @return the ClassDescriptor
93       */
94      public ClassDescriptor buildClassDescriptor( final JavaClass javaClass,
95                                                   final QDoxAttributeInterceptor interceptor )
96      {
97          final String classname = javaClass.getFullyQualifiedName();
98          final Attribute[] originalAttributes = buildAttributes( javaClass, interceptor );
99          final Attribute[] attributes =
100             interceptor.processClassAttributes( javaClass, originalAttributes );
101 
102         final FieldDescriptor[] fields =
103             buildFields( javaClass.getFields(), interceptor );
104         final MethodDescriptor[] methods =
105             buildMethods( javaClass.getMethods(), interceptor );
106 
107         return new ClassDescriptor( classname,
108                                     attributes,
109                                     attributes,
110                                     fields,
111                                     methods );
112     }
113 
114     /***
115      * Build a set of MethodDescriptor instances for a JavaClass.
116      *
117      * @param methods the methods
118      * @param interceptor the AttributeInterceptor
119      * @return the MethodDescriptors
120      */
121     MethodDescriptor[] buildMethods( final JavaMethod[] methods,
122                                      final QDoxAttributeInterceptor interceptor )
123     {
124         final MethodDescriptor[] methodDescriptors = new MethodDescriptor[ methods.length ];
125         for( int i = 0; i < methods.length; i++ )
126         {
127             methodDescriptors[ i ] = buildMethod( methods[ i ], interceptor );
128         }
129         return methodDescriptors;
130     }
131 
132     /***
133      * Build a MethodDescriptor for a JavaMethod.
134      *
135      * @param method the JavaMethod
136      * @param interceptor the AttributeInterceptor
137      * @return the MethodDescriptor
138      */
139     MethodDescriptor buildMethod( final JavaMethod method,
140                                   final QDoxAttributeInterceptor interceptor )
141     {
142         final String name = method.getName();
143         final Type returns = method.getReturns();
144         final String type;
145         if( null != returns )
146         {
147             type = returns.getValue();
148         }
149         else
150         {
151             type = "";
152         }
153 
154         final Attribute[] originalAttributes = buildAttributes( method, interceptor );
155         final Attribute[] attributes =
156             interceptor.processMethodAttributes( method, originalAttributes );
157         final ParameterDescriptor[] parameters =
158             buildParameters( method.getParameters() );
159 
160         return new MethodDescriptor( name,
161                                      type,
162                                      parameters,
163                                      attributes, 
164                                      attributes );
165     }
166 
167     /***
168      * Build a set of ParameterDescriptor for JavaParameters.
169      *
170      * @param parameters the JavaParameters
171      * @return the ParameterDescriptors
172      */
173     ParameterDescriptor[] buildParameters( final JavaParameter[] parameters )
174     {
175         final ParameterDescriptor[] descriptors =
176             new ParameterDescriptor[ parameters.length ];
177         for( int i = 0; i < parameters.length; i++ )
178         {
179             descriptors[ i ] = buildParameter( parameters[ i ] );
180         }
181         return descriptors;
182     }
183 
184     /***
185      * Build a ParameterDescriptor for a JavaParameter.
186      *
187      * @param parameter the JavaParameter
188      * @return the ParameterDescriptor
189      */
190     ParameterDescriptor buildParameter( final JavaParameter parameter )
191     {
192         final String name = parameter.getName();
193         final String value = parameter.getType().getValue();
194         return new ParameterDescriptor( name, value );
195     }
196 
197     /***
198      * Build a set of FieldDescriptor instances for a JavaClass.
199      *
200      * @param fields the fields
201      * @param interceptor the AttributeInterceptor
202      * @return the FieldDescriptors
203      */
204     FieldDescriptor[] buildFields( final JavaField[] fields,
205                                    final QDoxAttributeInterceptor interceptor )
206     {
207         final FieldDescriptor[] fieldDescriptors = new FieldDescriptor[ fields.length ];
208         for( int i = 0; i < fields.length; i++ )
209         {
210             fieldDescriptors[ i ] = buildField( fields[ i ], interceptor );
211         }
212         return fieldDescriptors;
213     }
214 
215     /***
216      * Build a set of FieldDescriptor instances for a JavaField.
217      *
218      * @param field the JavaField
219      * @param interceptor the AttributeInterceptor
220      * @return the FieldDescriptor
221      */
222     FieldDescriptor buildField( final JavaField field,
223                                 final QDoxAttributeInterceptor interceptor )
224     {
225         final String name = field.getName();
226         final String type = field.getType().getValue();
227         final Attribute[] originalAttributes = buildAttributes( field, interceptor );
228         final Attribute[] attributes =
229             interceptor.processFieldAttributes( field, originalAttributes );
230         return new FieldDescriptor( name,
231                                     type,
232                                     attributes,
233                                     attributes );
234     }
235 
236     /***
237      * Build a set of Attribute instances for a JavaClass.
238      * Use Interceptor to process tags during construction.
239      *
240      * @param javaClass the JavaClass
241      * @param interceptor the AttributeInterceptor
242      * @return the Attributes
243      */
244     private Attribute[] buildAttributes( final JavaClass javaClass,
245                                          final QDoxAttributeInterceptor interceptor )
246     {
247         final ArrayList attributes = new ArrayList();
248         final DocletTag[] tags = javaClass.getTags();
249         for( int i = 0; i < tags.length; i++ )
250         {
251             final Attribute originalAttribute = buildAttribute( tags[ i ] );
252             final Attribute attribute =
253                 interceptor.processClassAttribute( javaClass, originalAttribute );
254             if( null != attribute )
255             {
256                 attributes.add( attribute );
257             }
258         }
259         return (Attribute[])attributes.toArray( new Attribute[ attributes.size() ] );
260     }
261 
262     /***
263      * Build a set of Attribute instances for a JavaMethod.
264      * Use Interceptor to process tags during construction.
265      *
266      * @param method the JavaMethod
267      * @param interceptor the AttributeInterceptor
268      * @return the Attributes
269      */
270     private Attribute[] buildAttributes( final JavaMethod method,
271                                          final QDoxAttributeInterceptor interceptor )
272     {
273         final ArrayList attributes = new ArrayList();
274         final DocletTag[] tags = method.getTags();
275         for( int i = 0; i < tags.length; i++ )
276         {
277             final Attribute originalAttribute = buildAttribute( tags[ i ] );
278             final Attribute attribute =
279                 interceptor.processMethodAttribute( method, originalAttribute );
280             if( null != attribute )
281             {
282                 attributes.add( attribute );
283             }
284         }
285         return (Attribute[])attributes.toArray( new Attribute[ attributes.size() ] );
286     }
287 
288     /***
289      * Build a set of Attribute instances for a JavaField.
290      * Use Interceptor to process tags during construction.
291      *
292      * @param field the JavaField
293      * @param interceptor the AttributeInterceptor
294      * @return the Attributes
295      */
296     private Attribute[] buildAttributes( final JavaField field,
297                                          final QDoxAttributeInterceptor interceptor )
298     {
299         final ArrayList attributes = new ArrayList();
300         final DocletTag[] tags = field.getTags();
301         for( int i = 0; i < tags.length; i++ )
302         {
303             final Attribute originalAttribute = buildAttribute( tags[ i ] );
304             final Attribute attribute =
305                 interceptor.processFieldAttribute( field, originalAttribute );
306             if( null != attribute )
307             {
308                 attributes.add( attribute );
309             }
310         }
311         return (Attribute[])attributes.toArray( new Attribute[ attributes.size() ] );
312     }
313 
314     /***
315      * Build an Attribute object from a DocletTag.
316      *
317      * @param tag the DocletTag instance.
318      * @return the Attribute
319      */
320     Attribute buildAttribute( final DocletTag tag )
321     {
322         final String name = tag.getName();
323         final String value = tag.getValue();
324         if( null == value || "".equals( value.trim() ) )
325         {
326             return new Attribute( name );
327         }
328 
329         final Properties parameters = parseValueIntoParameters( value );
330         if( null == parameters )
331         {
332             return new Attribute( name, value );
333         }
334         else
335         {
336             return new Attribute( name, parameters );
337         }
338     }
339 
340     /***
341      * Parse the value string into a set of parameters.
342      * The parameters must match the pattern
343      *
344      * <pre>
345      * ^[ \t\r\n]*([a-zA-Z\_][a-zA-Z0-9\_]*=\"[^\"]*\"[ \t\r\n]+)+
346      * </pre>
347      *
348      * <p>If the value does not match this pattern then null is returned
349      * other wise the key=value pairs are parsed out and placed in
350      * a properties object.</p>
351      *
352      * @param input the input value
353      * @return the parameters if matches patterns, else null
354      */
355     Properties parseValueIntoParameters( final String input )
356     {
357         final Properties parameters = new Properties();
358 
359         final StringBuffer key = new StringBuffer();
360         final StringBuffer value = new StringBuffer();
361 
362         int state = PARSE_KEY_START;
363         final int length = input.length();
364         for( int i = 0; i < length; i++ )
365         {
366             final char ch = input.charAt( i );
367             switch( state )
368             {
369                 case PARSE_KEY_START:
370                     if( Character.isWhitespace( ch ) )
371                     {
372                         continue;
373                     }
374                     else if( Character.isJavaIdentifierStart( ch ) )
375                     {
376                         key.append( ch );
377                         state = PARSE_KEY;
378                     }
379                     else
380                     {
381                         return null;
382                     }
383                     break;
384 
385                 case PARSE_KEY:
386                     if( '=' == ch )
387                     {
388                         state = PARSE_VALUE_START;
389                     }
390                     else if( Character.isJavaIdentifierPart( ch ) )
391                     {
392                         key.append( ch );
393                     }
394                     else
395                     {
396                         return null;
397                     }
398                     break;
399 
400                 case PARSE_VALUE_START:
401                     if( '\"' != ch )
402                     {
403                         return null;
404                     }
405                     else
406                     {
407                         state = PARSE_VALUE;
408                     }
409                     break;
410 
411                 case PARSE_VALUE:
412                     if( '\"' == ch )
413                     {
414                         state = PARSE_END;
415                         parameters.setProperty( key.toString(), value.toString() );
416                         key.setLength( 0 );
417                         value.setLength( 0 );
418                     }
419                     else
420                     {
421                         value.append( ch );
422                     }
423                     break;
424 
425                 case PARSE_END:
426                 default:
427                     if( Character.isWhitespace( ch ) )
428                     {
429                         state = PARSE_KEY_START;
430                     }
431                     else
432                     {
433                         return null;
434                     }
435                     break;
436             }
437         }
438 
439         if( PARSE_KEY_START != state &&
440             PARSE_END != state )
441         {
442             return null;
443         }
444 
445         return parameters;
446     }
447 }