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.compiler;
9   
10  import com.thoughtworks.qdox.JavaDocBuilder;
11  import com.thoughtworks.qdox.model.JavaClass;
12  import com.thoughtworks.qdox.model.JavaSource;
13  import java.io.File;
14  import java.io.FileInputStream;
15  import java.io.FileNotFoundException;
16  import java.io.FileReader;
17  import java.io.IOException;
18  import java.io.InputStream;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Set;
25  import org.codehaus.metaclass.io.MetaClassIO;
26  import org.codehaus.metaclass.model.ClassDescriptor;
27  import org.codehaus.metaclass.tools.packer.ClassDescriptorPacker;
28  import org.codehaus.metaclass.tools.qdox.QDoxAttributeInterceptor;
29  import org.codehaus.metaclass.tools.qdox.QDoxDescriptorParser;
30  
31  /***
32   * A bean that can create MetaClass descriptors by processing Java Source files
33   * with qdox.
34   *
35   * @author Peter Donald
36   * @version $Revision: 1.16 $ $Date: 2003/12/11 08:41:50 $
37   */
38  public class ClassDescriptorCompiler
39  {
40      /*** The utility class used to generate MetaClass object. */
41      private static final QDoxDescriptorParser c_descriptorParser = new QDoxDescriptorParser();
42  
43      /***
44       * Flag indicating whether the compacter should methods with no attributes.
45       */
46      private boolean m_keepEmptyMethods;
47  
48      /*** Destination directory */
49      private File m_destDir;
50  
51      /*** The IO object to use when writing descriptors. */
52      private MetaClassIO m_metaClassIO;
53  
54      /*** the monitor that receives messages about operation of compiler. */
55      private CompilerMonitor m_monitor = new DefaultCompilerMonitor();
56  
57      /*** The source files to process. */
58      private final List m_sourceFiles = new ArrayList();
59  
60      /*** The interceptors used to process source files. */
61      private final List m_interceptors = new ArrayList();
62  
63      /*** The filters used to filter source files. */
64      private final List m_filters = new ArrayList();
65  
66      /***
67       * Set flag indicating whether Compacter should keep empty methods.
68       *
69       * @param keepEmptyMethods the flag
70       */
71      public void setKeepEmptyMethods( final boolean keepEmptyMethods )
72      {
73          m_keepEmptyMethods = keepEmptyMethods;
74      }
75  
76      /***
77       * Set the monitor to receive messages about compiler operation.
78       *
79       * @param monitor the monitor.
80       */
81      public void setMonitor( final CompilerMonitor monitor )
82      {
83          if( null == monitor )
84          {
85              throw new NullPointerException( "monitor" );
86          }
87          m_monitor = monitor;
88      }
89  
90      /***
91       * Add a sourceFile to process.
92       *
93       * @param sourceFile the sourceFile
94       */
95      public void addSourceFile( final File sourceFile )
96      {
97          if( null == sourceFile )
98          {
99              throw new NullPointerException( "sourceFile" );
100         }
101         m_sourceFiles.add( sourceFile );
102     }
103 
104     /***
105      * Add a filter to process metadata.
106      *
107      * @param filter the filter
108      */
109     public void addFilter( final JavaClassFilter filter )
110     {
111         if( null == filter )
112         {
113             throw new NullPointerException( "filter" );
114         }
115         m_filters.add( filter );
116     }
117 
118     /***
119      * Add an interceptor to process metadata.
120      *
121      * @param interceptor the interceptor
122      */
123     public void addInterceptor( final QDoxAttributeInterceptor interceptor )
124     {
125         if( null == interceptor )
126         {
127             throw new NullPointerException( "interceptor" );
128         }
129         m_interceptors.add( interceptor );
130     }
131 
132     /***
133      * Set the destination directory for generated files.
134      *
135      * @param destDir the destination directory for generated files.
136      */
137     public void setDestDir( final File destDir )
138     {
139         m_destDir = destDir;
140     }
141 
142     /***
143      * Set the IO object used to write descriptors.
144      *
145      * @param metaClassIO the IO object used to write descriptors.
146      */
147     public void setMetaClassIO( final MetaClassIO metaClassIO )
148     {
149         if( null == metaClassIO )
150         {
151             throw new NullPointerException( "metaClassIO" );
152         }
153         m_metaClassIO = metaClassIO;
154     }
155 
156     /***
157      * Return the MetaClassIO used to serialize descriptors.
158      *
159      * @return the MetaClassIO used to serialize descriptors.
160      */
161     public MetaClassIO getMetaClassIO()
162     {
163         return m_metaClassIO;
164     }
165 
166     /***
167      * Generate classes and output.
168      *
169      * @throws Exception if unable to compile descriptors
170      */
171     public void execute()
172         throws Exception
173     {
174         validate();
175         processSourceFiles();
176     }
177 
178     /***
179      * Output the ClassDescriptors that are not filtered out.
180      */
181     protected void processSourceFiles()
182     {
183         final List classes = collectJavaClassesToSerialize();
184         m_monitor.javaClassObjectsLoaded( classes );
185 
186         final Collection filteredClasses = filterJavaClassObjects( classes );
187         m_monitor.postFilterJavaClassList( classes );
188 
189         final Collection descriptors = buildClassDescriptors( filteredClasses );
190         m_monitor.postBuildDescriptorsList( descriptors );
191 
192         final Set output = compactClassDescriptors( descriptors );
193         m_monitor.postCompactDescriptorsList( output );
194 
195         writeClassDescriptors( output );
196     }
197 
198     /***
199      * Build class descriptors from input JavaCLass objects.
200      *
201      * @param classes the list containing JavaClass objects.
202      * @return a list containing created ClassDescriptor objects.
203      */
204     private List buildClassDescriptors( final Collection classes )
205     {
206         final QDoxAttributeInterceptor[] interceptors =
207             (QDoxAttributeInterceptor[])m_interceptors.
208             toArray( new QDoxAttributeInterceptor[ m_interceptors.size() ] );
209 
210         final ArrayList descriptors = new ArrayList();
211         final Iterator iterator = classes.iterator();
212         while( iterator.hasNext() )
213         {
214             final JavaClass javaClass = (JavaClass)iterator.next();
215             try
216             {
217                 final ClassDescriptor descriptor =
218                     c_descriptorParser.buildClassDescriptor( javaClass,
219                                                              interceptors );
220                 descriptors.add( descriptor );
221             }
222             catch( final Throwable t )
223             {
224                 final String classname = javaClass.getFullyQualifiedName();
225                 m_monitor.errorGeneratingDescriptor( classname, t );
226             }
227         }
228         return descriptors;
229     }
230 
231     /***
232      * Output the specified ClassDescriptors.
233      *
234      * @param classes the list containing ClassDescriptor objects.
235      */
236     private void writeClassDescriptors( final Collection classes )
237     {
238         final Iterator iterator = classes.iterator();
239         while( iterator.hasNext() )
240         {
241             final ClassDescriptor descriptor = (ClassDescriptor)iterator.next();
242             writeClassDescriptor( descriptor );
243         }
244     }
245 
246     /***
247      * Return the set of classes that will actually be serialized and have not
248      * been filtered out.
249      *
250      * @return list of classes to serialize
251      */
252     private List collectJavaClassesToSerialize()
253     {
254         final JavaDocBuilder builder = new JavaDocBuilder();
255         final int count = m_sourceFiles.size();
256         for( int i = 0; i < count; i++ )
257         {
258             final File file = (File)m_sourceFiles.get( i );
259             FileInputStream in = null;
260             try
261             {
262                 builder.addSource( new FileReader( file ) );
263             }
264             catch( final FileNotFoundException fnfe )
265             {
266                 m_monitor.missingSourceFile( file );
267             }
268             finally
269             {
270                 shutdownStream( in );
271             }
272         }
273         final JavaSource[] sources = builder.getSources();
274         final ArrayList classes = new ArrayList();
275         for( int i = 0; i < sources.length; i++ )
276         {
277             final JavaClass[] javaClasses = sources[ i ].getClasses();
278             for( int j = 0; j < javaClasses.length; j++ )
279             {
280                 classes.add( javaClasses[ j ] );
281             }
282         }
283         return classes;
284     }
285 
286     /***
287      * Return the set of classes that will actually be serialized and have not
288      * been filtered out.
289      *
290      * @param input the list of input classes to filter
291      * @return list of classes to serialize
292      */
293     private Set filterJavaClassObjects( final List input )
294     {
295         final JavaClassFilter[] filters = (JavaClassFilter[])m_filters.
296             toArray( new JavaClassFilter[ m_filters.size() ] );
297         final MulticastJavaClassFilter filter =
298             new MulticastJavaClassFilter( filters );
299 
300         final Set classes = new HashSet();
301         final int classCount = input.size();
302         for( int i = 0; i < classCount; i++ )
303         {
304             final JavaClass candidate = (JavaClass)input.get( i );
305             final JavaClass javaClass = filter.filterClass( candidate );
306             if( null == javaClass )
307             {
308                 continue;
309             }
310             classes.add( javaClass );
311         }
312         return classes;
313     }
314 
315     /***
316      * Return the set of ClassDescriptors after cmpaction.
317      *
318      * @param input the list of input ClassDescriptors to compact
319      * @return list of ClassDescriptors to serialize
320      */
321     private Set compactClassDescriptors( final Collection input )
322     {
323         final ClassDescriptorPacker packer =
324             new ClassDescriptorPacker( m_keepEmptyMethods );
325         final Set classes = new HashSet();
326         final Iterator iterator = input.iterator();
327         while( iterator.hasNext() )
328         {
329             final ClassDescriptor candidate = (ClassDescriptor)iterator.next();
330             final ClassDescriptor descriptor = packer.pack( candidate );
331             if( null == descriptor )
332             {
333                 continue;
334             }
335             classes.add( descriptor );
336         }
337         return classes;
338     }
339 
340     /***
341      * Validate that the parameters are valid.
342      *
343      * @throws Exception if unable to validate settings
344      */
345     private void validate()
346         throws Exception
347     {
348         if( null == m_destDir )
349         {
350             final String message = "DestDir not specified";
351             throw new Exception( message );
352         }
353         if( m_destDir.exists() && !m_destDir.isDirectory() )
354         {
355             final String message =
356                 "DestDir (" + m_destDir + ") is not a directory.";
357             throw new Exception( message );
358         }
359         if( !m_destDir.exists() && !m_destDir.mkdirs() )
360         {
361             final String message =
362                 "DestDir (" + m_destDir + ") could not be created.";
363             throw new Exception( message );
364         }
365     }
366 
367     /***
368      * Write ClassDescriptor out into a file.
369      *
370      * @param descriptor the ClassDescriptor object
371      */
372     void writeClassDescriptor( final ClassDescriptor descriptor )
373     {
374         try
375         {
376             m_metaClassIO.writeDescriptor( m_destDir, descriptor );
377         }
378         catch( final Exception e )
379         {
380             m_monitor.errorWritingDescriptor( descriptor, e );
381         }
382     }
383 
384     /***
385      * Close the specified input stream and swallow any exceptions.
386      *
387      * @param inputStream the input stream
388      */
389     void shutdownStream( final InputStream inputStream )
390     {
391         if( null != inputStream )
392         {
393             try
394             {
395                 inputStream.close();
396             }
397             catch( IOException e )
398             {
399                 //Ignored
400             }
401         }
402     }
403 }