1
2
3
4
5
6
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
400 }
401 }
402 }
403 }