KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > apt > core > internal > APTDispatchRunnable


1  /*******************************************************************************
2  * Copyright (c) 2005, 2007 BEA Systems, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * mkaufman@bea.com - initial API and implementation
10  *******************************************************************************/

11
12
13 package org.eclipse.jdt.apt.core.internal;
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.Collection JavaDoc;
17 import java.util.Collections JavaDoc;
18 import java.util.HashMap JavaDoc;
19 import java.util.HashSet JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.LinkedHashSet JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.Set JavaDoc;
25
26 import org.eclipse.core.resources.IFile;
27 import org.eclipse.core.resources.IWorkspace;
28 import org.eclipse.core.resources.IWorkspaceRunnable;
29 import org.eclipse.core.resources.ResourcesPlugin;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.core.runtime.IProgressMonitor;
32 import org.eclipse.jdt.apt.core.env.Phase;
33 import org.eclipse.jdt.apt.core.internal.env.AbstractCompilationEnv;
34 import org.eclipse.jdt.apt.core.internal.env.BuildEnv;
35 import org.eclipse.jdt.apt.core.internal.env.EclipseRoundCompleteEvent;
36 import org.eclipse.jdt.apt.core.internal.env.ReconcileEnv;
37 import org.eclipse.jdt.apt.core.internal.env.AbstractCompilationEnv.EnvCallback;
38 import org.eclipse.jdt.apt.core.internal.generatedfile.GeneratedFileManager;
39 import org.eclipse.jdt.apt.core.internal.util.FactoryPath;
40 import org.eclipse.jdt.core.ICompilationUnit;
41 import org.eclipse.jdt.core.IJavaProject;
42 import org.eclipse.jdt.core.compiler.BuildContext;
43 import org.eclipse.jdt.core.compiler.CategorizedProblem;
44 import org.eclipse.jdt.core.compiler.ReconcileContext;
45
46 import com.sun.mirror.apt.AnnotationProcessor;
47 import com.sun.mirror.apt.AnnotationProcessorFactory;
48 import com.sun.mirror.apt.AnnotationProcessorListener;
49 import com.sun.mirror.apt.RoundCompleteListener;
50 import com.sun.mirror.declaration.AnnotationTypeDeclaration;
51
52 public class APTDispatchRunnable implements IWorkspaceRunnable
53 {
54     /**
55      * This callback method is passed to a ReconcileEnv to be called within
56      * an AST pipeline in order to process a type during reconcile.
57      * Reconciles involve only one type at a time, but can recurse, so
58      * that multiple instances of this class are on the stack at one time.
59      */

60     private final class ReconcileEnvCallback implements EnvCallback {
61         private final ReconcileContext _context;
62         private final GeneratedFileManager _gfm;
63
64         private ReconcileEnvCallback(ReconcileContext context,
65                 GeneratedFileManager gfm) {
66             _context = context;
67             _gfm = gfm;
68         }
69
70         public void run(AbstractCompilationEnv env) {
71             // This is a ReconcileEnvCallback, so we better be dealing with a ReconcileEnv!
72
ReconcileEnv reconcileEnv = (ReconcileEnv)env;
73             
74             // Dispatch the annotation processors. Env will keep track of problems and generated types.
75
try {
76                 dispatchToFileBasedProcessor(reconcileEnv);
77             } catch (Throwable JavaDoc t) {
78                 AptPlugin.log(t, "Processor failure during reconcile"); //$NON-NLS-1$
79
}
80             
81             // "Remove" any types that were generated in the past but not on this round.
82
// Because this is a reconcile, if a file exists on disk we can't really remove
83
// it, we can only create a blank WorkingCopy that hides it; thus, we can only
84
// remove Java source files, not arbitrary files.
85
ICompilationUnit parentWC = _context.getWorkingCopy();
86             Set JavaDoc<IFile> newlyGeneratedFiles = reconcileEnv.getAllGeneratedFiles();
87             _gfm.deleteObsoleteTypesAfterReconcile(parentWC, newlyGeneratedFiles);
88             
89             // Report problems to the ReconcileContext.
90
final List JavaDoc<? extends CategorizedProblem> problemList = reconcileEnv.getProblems();
91             final int numProblems = problemList.size();
92             if (numProblems > 0) {
93                 final CategorizedProblem[] aptCatProblems = new CategorizedProblem[numProblems];
94                 _context.putProblems(
95                 AptPlugin.APT_COMPILATION_PROBLEM_MARKER, problemList
96                         .toArray(aptCatProblems));
97             }
98             
99             // Tell the Env that the round is complete.
100
// This also calls resetAST() on the context.
101
reconcileEnv.close();
102         }
103     }
104     
105     private static final BuildContext[] NO_FILES_TO_PROCESS = new BuildContext[0];
106     private /*final*/ BuildContext[] _filesWithAnnotation = null;
107     private /*final*/ BuildContext[] _filesWithoutAnnotation = null;
108     private /*final*/ Map JavaDoc<IFile, CategorizedProblem[]> _problemRecorder = null;
109     private final AptProject _aptProject;
110     private final Map JavaDoc<AnnotationProcessorFactory, FactoryPath.Attributes> _factories;
111     /** Batch processor dispatched in the previous rounds */
112     private final Set JavaDoc<AnnotationProcessorFactory> _dispatchedBatchFactories;
113     /** Batch processor dispatched in the current round */
114     private Set JavaDoc<AnnotationProcessorFactory> _currentDispatchBatchFactories = Collections.emptySet();
115     private final boolean _isFullBuild;
116     
117     
118     public static Set JavaDoc<AnnotationProcessorFactory> runAPTDuringBuild(
119             BuildContext[] filesWithAnnotations,
120             BuildContext[] filesWithoutAnnotations,
121             Map JavaDoc<IFile, CategorizedProblem[]> problemRecorder,
122             AptProject aptProject,
123             Map JavaDoc<AnnotationProcessorFactory, FactoryPath.Attributes> factories,
124             Set JavaDoc<AnnotationProcessorFactory> dispatchedBatchFactories,
125             boolean isFullBuild){
126         
127          if( filesWithAnnotations == null ){
128              filesWithAnnotations = NO_FILES_TO_PROCESS;
129          }
130         // If we're building, types can be generated, so we
131
// want to run this as an atomic workspace operation
132
APTDispatchRunnable runnable =
133              new APTDispatchRunnable(
134                      filesWithAnnotations,
135                      filesWithoutAnnotations,
136                      problemRecorder,
137                      aptProject, factories,
138                      dispatchedBatchFactories, isFullBuild );
139          IWorkspace workspace = ResourcesPlugin.getWorkspace();
140          try {
141              workspace.run(runnable, aptProject.getJavaProject().getResource(), IWorkspace.AVOID_UPDATE, null);
142          }
143          catch (CoreException ce) {
144              AptPlugin.log(ce, "Could not run APT"); //$NON-NLS-1$
145
}
146          return runnable._currentDispatchBatchFactories;
147     }
148     
149     public static void runAPTDuringReconcile(
150             ReconcileContext reconcileContext,
151             AptProject aptProject,
152             Map JavaDoc<AnnotationProcessorFactory, FactoryPath.Attributes> factories)
153     {
154         // Reconciling, so we do not want to run this as an atomic workspace
155
// operation. If we do, it is easy to have locking issues when someone
156
// calls a reconcile from within a workspace lock
157
APTDispatchRunnable runnable = new APTDispatchRunnable( aptProject, factories );
158         runnable.reconcile(reconcileContext, aptProject.getJavaProject());
159     }
160     
161     /** create a runnable used during build */
162     private APTDispatchRunnable(
163             BuildContext[] filesWithAnnotation,
164             BuildContext[] filesWithoutAnnotation,
165             Map JavaDoc<IFile, CategorizedProblem[]> problemRecorder,
166             AptProject aptProject,
167             Map JavaDoc<AnnotationProcessorFactory, FactoryPath.Attributes> factories,
168             Set JavaDoc<AnnotationProcessorFactory> dispatchedBatchFactories,
169             boolean isFullBuild)
170     {
171         assert filesWithAnnotation != null : "missing files"; //$NON-NLS-1$
172
_filesWithAnnotation = filesWithAnnotation;
173         _filesWithoutAnnotation = filesWithoutAnnotation;
174         _problemRecorder = problemRecorder;
175         _aptProject = aptProject;
176         _factories = factories;
177         _dispatchedBatchFactories = dispatchedBatchFactories;
178         _isFullBuild = isFullBuild;
179     }
180     /** create a runnable used during reconcile */
181     private APTDispatchRunnable(
182             AptProject aptProject,
183             Map JavaDoc<AnnotationProcessorFactory, FactoryPath.Attributes> factories)
184     {
185         _aptProject = aptProject;
186         _factories = factories;
187         _isFullBuild = false;
188         // does not apply in reconcile case. we don't generate file during
189
// reconcile and no apt rounding ever occur as a result.
190
_dispatchedBatchFactories = Collections.emptySet();
191     }
192     
193     private void reconcile(final ReconcileContext reconcileContext,
194                IJavaProject javaProject)
195     {
196         if (_factories.size() == 0) {
197             if (AptPlugin.DEBUG)
198                 trace("apt leaving project " + javaProject.getProject() + //$NON-NLS-1$
199
" early because there are no factories", //$NON-NLS-1$
200
null);
201             //TODO: clean up generated working copies here? I think not necessary. - WSH 10/06
202
return;
203         }
204         
205         // Construct a reconcile time environment. This will invoke
206
// dispatch from inside the callback.
207
GeneratedFileManager gfm = _aptProject.getGeneratedFileManager();
208         gfm.reconcileStarted();
209         EnvCallback callback = new ReconcileEnvCallback(reconcileContext, gfm);
210         AbstractCompilationEnv.newReconcileEnv(reconcileContext, callback);
211
212     }
213     
214     public void run(IProgressMonitor monitor)
215     {
216         build();
217     }
218     
219     /**
220      * Determine whether there are files to be processed.
221      * @return <code>true</code> iff APT processing should occur, return <code>false</code>
222      * otherwise.
223      *
224      * APT should should run one of the following is true
225      * 1) There are files with annotations
226      * 2) There are factories dispatched in an earlier round
227      */

228     private boolean shouldBuild()
229     {
230         if( (_factories == null || _factories.size() == 0) && _dispatchedBatchFactories.isEmpty() )
231             return false;
232     
233         int totalFiles = _filesWithAnnotation == null ? 0 : _filesWithAnnotation.length;
234         // We are required to dispatch even though there are no files with annotations.
235
// This is a documented behavior in the mirror spec.
236
return totalFiles > 0 || !_dispatchedBatchFactories.isEmpty();
237     }
238     
239     private void build(){
240     
241         if ( !shouldBuild() )
242         {
243             // tracing
244
if ( AptPlugin.DEBUG )
245             {
246                 String JavaDoc msg;
247                 if ( (_factories == null || _factories.size() == 0) && _dispatchedBatchFactories.isEmpty() )
248                     msg = "no AnnotationProcessoryFactory instances registered."; //$NON-NLS-1$
249
else
250                     msg = "no files to dispatch to."; //$NON-NLS-1$
251
trace( "run(): leaving project " + _aptProject.getJavaProject().getProject() + //$NON-NLS-1$
252
" early because there are " + msg, //$NON-NLS-1$
253
null);
254             }
255             cleanupAllGeneratedFiles();
256         }
257         else
258         {
259             assert _filesWithAnnotation != null :
260                    "should never be invoked unless we are in build mode!"; //$NON-NLS-1$
261

262             EnvCallback buildCallback = new EnvCallback() {
263                 public void run(AbstractCompilationEnv env) {
264                     build((BuildEnv)env);
265                 }
266             };
267             
268             // Construct build environment, this invokes the build inside a callback
269
// in order to keep open the DOM AST pipeline
270
BuildEnv.newBuildEnv(
271                     _filesWithAnnotation,
272                     _filesWithoutAnnotation,
273                     _aptProject.getJavaProject(),
274                     buildCallback);
275         }
276     }
277     
278     /**
279      * @param factories
280      * @return <code>true</code> iff there are factories that can only be run in batch mode.
281      */

282     private boolean hasBatchFactory()
283     {
284         for( FactoryPath.Attributes attr : _factories.values() ){
285             if( attr.runInBatchMode() )
286                 return true;
287         }
288         return false;
289         
290     }
291     
292     /**
293      * Batch processor should only be invoked during a clean build.
294      * @param factories
295      * @param processorEnv
296      * @return <code>true</code> iff batch processors should be dispatched.
297      * Return <code>false</code> otherwise. Return <code>false</code> if
298      * there are no batch processors.
299      */

300     private boolean shouldDispatchToBatchProcessor(final AbstractCompilationEnv processorEnv )
301     {
302         return ( _isFullBuild && processorEnv.getPhase() == Phase.BUILD && hasBatchFactory() );
303     }
304     
305     private void runAPTInFileBasedMode(final BuildEnv processorEnv)
306     {
307         final BuildContext[] cpResults = processorEnv.getFilesWithAnnotation();
308         final GeneratedFileManager gfm = _aptProject.getGeneratedFileManager();
309         for (BuildContext curResult : cpResults ) {
310             processorEnv.beginFileProcessing(curResult);
311             dispatchToFileBasedProcessor(processorEnv);
312             reportResult(
313                     curResult,
314                     processorEnv.getAllGeneratedFiles(),
315                     processorEnv.getModifiedGeneratedFiles(),
316                     processorEnv.getProblems(),
317                     processorEnv.getTypeDependencies(),
318                     gfm,
319                     processorEnv);
320             processorEnv.completedFileProcessing();
321         }
322     }
323     
324     /**
325      * @param curResult
326      * @param lastGeneratedFiles files generated from previous apt run.
327      * @param generatedFiles all files generated from current apt run.
328      * @param modifiedGeneratedFiles new generated files or files differs from those from
329      * previous run.
330      * @param problems problems from current apt run.
331      * @param deps
332      * @param gfm
333      * @param processorEnv
334      */

335     private void reportResult(
336             BuildContext curResult,
337             Set JavaDoc<IFile> java5GeneratedFiles,
338             Set JavaDoc<IFile> modifiedGeneratedFiles,
339             List JavaDoc<? extends CategorizedProblem> problems,
340             Set JavaDoc<String JavaDoc> deps,
341             GeneratedFileManager gfm,
342             BuildEnv processorEnv)
343     {
344         // Combine files generated by Java 5 and Java 6 processing phases
345
Set JavaDoc<IFile> allGeneratedFiles = null;
346         Set JavaDoc<IFile> java6GeneratedFiles = AptCompilationParticipant.getInstance().getJava6GeneratedFiles();
347         if (java5GeneratedFiles == null || java5GeneratedFiles.isEmpty()) {
348             if (java6GeneratedFiles.isEmpty()) {
349                 allGeneratedFiles = Collections.emptySet();
350             }
351             else {
352                 allGeneratedFiles = java6GeneratedFiles;
353             }
354         }
355         else {
356             if (java6GeneratedFiles.isEmpty()) {
357                 allGeneratedFiles = java5GeneratedFiles;
358             }
359             else {
360                 allGeneratedFiles = new HashSet JavaDoc<IFile>(java6GeneratedFiles);
361                 allGeneratedFiles.addAll(java5GeneratedFiles);
362             }
363         }
364         
365         // figure out exactly what got deleted
366
final List JavaDoc<IFile> deletedFiles = new ArrayList JavaDoc<IFile>();
367         IFile parentFile = curResult.getFile();
368         cleanupNoLongerGeneratedFiles(
369                 parentFile,
370                 allGeneratedFiles,
371                 gfm,
372                 processorEnv,
373                 deletedFiles);
374         // report newly created or modified generated files
375
int numNewFiles = modifiedGeneratedFiles.size();
376         if( numNewFiles > 0 ){
377             final IFile[] newFilesArray = new IFile[numNewFiles];
378             curResult.recordAddedGeneratedFiles(modifiedGeneratedFiles.toArray(newFilesArray));
379         }
380         
381         // report deleted file.
382
int numDeletedFiles = deletedFiles.size();
383         if(numDeletedFiles > 0){
384             final IFile[] deletedFilesArray = new IFile[numDeletedFiles];
385             curResult.recordDeletedGeneratedFiles(deletedFiles.toArray(deletedFilesArray));
386         }
387         
388         // report problems
389
final int numProblems = problems.size();
390         if( numProblems > 0 ){
391             final CategorizedProblem[] catProblemsArray = new CategorizedProblem[numProblems];
392             curResult.recordNewProblems(problems.toArray(catProblemsArray));
393             // Tell compilation participant about the problems, so it can report them
394
// again without reprocessing if a file is resubmitted.
395
_problemRecorder.put(curResult.getFile(), catProblemsArray);
396         }
397         
398         // report dependency
399
final int numDeps = deps.size();
400         if( numDeps > 0 ){
401             final String JavaDoc[] depsArray = new String JavaDoc[numDeps];
402             curResult.recordDependencies(deps.toArray(depsArray));
403         }
404     }
405                               
406     
407     /**
408      * mixed mode - allow batch processor to be run as well as filed based ones.
409      * @param processorEnv
410      * @param currentRoundDispatchedBatchFactories output parameter. At return contains the
411      * set of batch factories that has been dispatched.
412      */

413     private void runAPTInMixedMode(final BuildEnv processorEnv)
414     {
415         final BuildContext[] cpResults = processorEnv.getFilesWithAnnotation();
416         final Map JavaDoc<BuildContext, Set JavaDoc<AnnotationTypeDeclaration>> file2AnnotationDecls =
417             new HashMap JavaDoc<BuildContext, Set JavaDoc<AnnotationTypeDeclaration>>(cpResults.length * 4/3 + 1);
418         final Map JavaDoc<String JavaDoc, AnnotationTypeDeclaration> annotationDecls =
419             processorEnv.getAllAnnotationTypes(file2AnnotationDecls);
420         
421         if (annotationDecls.isEmpty() && _dispatchedBatchFactories.isEmpty() )
422         {
423             if ( AptPlugin.DEBUG )
424                 trace( "runAPT: leaving early because annotationDecls is empty", //$NON-NLS-1$
425
processorEnv);
426             return;
427         }
428         
429         if( AptPlugin.DEBUG )
430             trace( "annotations found " + annotationDecls.keySet(), processorEnv); //$NON-NLS-1$
431

432         // file based processing factory to the set of annotations that it 'claims'
433
final Map JavaDoc<AnnotationProcessorFactory, Set JavaDoc<AnnotationTypeDeclaration>> fileFactory2Annos =
434             new HashMap JavaDoc<AnnotationProcessorFactory, Set JavaDoc<AnnotationTypeDeclaration>>( _factories.size() * 4/3 + 1 );
435         
436         // batch processing factory to the set of annotations that it 'claims'
437
final Map JavaDoc<AnnotationProcessorFactory, Set JavaDoc<AnnotationTypeDeclaration>> batchFactory2Annos =
438             new HashMap JavaDoc<AnnotationProcessorFactory, Set JavaDoc<AnnotationTypeDeclaration>>( _factories.size() * 4/3 + 1 );
439         
440         for( Map.Entry JavaDoc<AnnotationProcessorFactory, FactoryPath.Attributes> entry : _factories.entrySet() ){
441             AnnotationProcessorFactory factory = entry.getKey();
442             Set JavaDoc<AnnotationTypeDeclaration> annotationTypes = getFactorySupportedAnnotations(factory, annotationDecls);
443             if( annotationTypes != null ){
444                 
445                 boolean batch = entry.getValue().runInBatchMode();
446                 Map JavaDoc<AnnotationProcessorFactory, Set JavaDoc<AnnotationTypeDeclaration> > factory2Annos =
447                     batch ? batchFactory2Annos : fileFactory2Annos;
448                 if( annotationTypes.size() == 0 ){
449                     // this factory is claiming all (remaining) annotations.
450
annotationTypes = new HashSet JavaDoc<AnnotationTypeDeclaration>(annotationDecls.values());
451                     factory2Annos.put(factory, annotationTypes);
452                     annotationDecls.clear();
453                     break;
454                 }
455                 else{
456                     factory2Annos.put(factory, annotationTypes);
457                 }
458             }
459             if( annotationDecls.isEmpty() )
460                 break;
461         }
462         
463         if( ! annotationDecls.isEmpty() ){
464             // TODO: (theodora) log unclaimed annotations?
465
}
466         
467         // Dispatch to the batch process factories first.
468
// Batch processors only get executed on a full/clean build
469
if( !batchFactory2Annos.isEmpty() ||
470             (_dispatchedBatchFactories != null && !_dispatchedBatchFactories.isEmpty()) ){
471                 
472             processorEnv.beginBatchProcessing();
473             if( !batchFactory2Annos.isEmpty()){
474                 // Once we figure out which factory claims what annotation,
475
// the order of the factory doesn't matter.
476
// But in order to make things consists between runs, will
477
// dispatch base on factory order.
478
_currentDispatchBatchFactories = new LinkedHashSet JavaDoc<AnnotationProcessorFactory>();
479                 for(AnnotationProcessorFactory factory : _factories.keySet() ){
480                     final Set JavaDoc<AnnotationTypeDeclaration> annotationTypes = batchFactory2Annos.get(factory);
481                     if( annotationTypes == null ) continue;
482                     final AnnotationProcessor processor =
483                         factory.getProcessorFor(annotationTypes, processorEnv);
484                     if( processor != null ){
485                         if ( AptPlugin.DEBUG )
486                             trace( "runAPT: invoking batch processor " + processor.getClass().getName(), //$NON-NLS-1$
487
processorEnv);
488                         _currentDispatchBatchFactories.add(factory);
489                         processorEnv.setCurrentProcessorFactory(factory);
490                         processor.process();
491                         processorEnv.setCurrentProcessorFactory(null);
492                     }
493                 }
494             }
495             // We have to dispatch to factories even though we may not have discovered any annotations.
496
// This is a documented APT behavior that we have to observe.
497
for( AnnotationProcessorFactory prevRoundFactory : _dispatchedBatchFactories ){
498                 if(_currentDispatchBatchFactories.contains(prevRoundFactory))
499                     continue;
500                 final AnnotationProcessor processor =
501                     prevRoundFactory.getProcessorFor(Collections.<AnnotationTypeDeclaration>emptySet(), processorEnv);
502                 if( processor != null ){
503                     if ( AptPlugin.DEBUG )
504                         trace( "runAPT: invoking batch processor " + processor.getClass().getName(), //$NON-NLS-1$
505
processorEnv);
506                     processorEnv.setCurrentProcessorFactory(prevRoundFactory);
507                     processor.process();
508                     processorEnv.setCurrentProcessorFactory(null);
509                 }
510             }
511             
512             // Currently, we are putting everything in the first file annotations.
513
// TODO: Is this correct?
514
// Why is it ok (today):
515
// 1) Problems are reported as IMarkers and not IProblem thru the
516
// BuildContext API.
517
// 2) jdt is currently not doing anything about the parent->generated file relation
518
// so it doesn't matter which BuildContext we attach the
519
// creation/modification/deletion of generated files. -theodora
520
BuildContext firstResult = null;
521             if( cpResults.length > 0 )
522                 firstResult = cpResults[0];
523             else{
524                 final BuildContext[] others = processorEnv.getFilesWithoutAnnotation();
525                 if(others != null && others.length > 0 )
526                     firstResult = others[0];
527             }
528             
529             // If there are no files to be built, apt will not be involved.
530
assert firstResult != null : "don't know where to report results"; //$NON-NLS-1$
531
if(firstResult != null ){
532                 final GeneratedFileManager gfm = _aptProject.getGeneratedFileManager();
533                 reportResult(
534                         firstResult, // just put it all in
535
processorEnv.getAllGeneratedFiles(),
536                         processorEnv.getModifiedGeneratedFiles(),
537                         processorEnv.getProblems(), // this is empty in batch mode.
538
processorEnv.getTypeDependencies(), // this is empty in batch mode.
539
gfm,
540                         processorEnv);
541             }
542             processorEnv.completedBatchProcessing();
543         }
544         
545         // Now, do the file based dispatch
546
if( !fileFactory2Annos.isEmpty() ){
547             for(BuildContext curResult : cpResults ){
548                 final Set JavaDoc<AnnotationTypeDeclaration> annotationTypesInFile = file2AnnotationDecls.get(curResult);
549                 if( annotationTypesInFile == null || annotationTypesInFile.isEmpty() )
550                     continue;
551                 for(AnnotationProcessorFactory factory : _factories.keySet() ){
552                     final Set JavaDoc<AnnotationTypeDeclaration> annotationTypesForFactory = fileFactory2Annos.get(factory);
553                     if( annotationTypesForFactory == null || annotationTypesForFactory.isEmpty() )
554                         continue;
555                     final Set JavaDoc<AnnotationTypeDeclaration> intersect = setIntersect(annotationTypesInFile, annotationTypesForFactory);
556                     if( intersect != null && !intersect.isEmpty() ){
557                         processorEnv.beginFileProcessing(curResult);
558                         final AnnotationProcessor processor =
559                             factory.getProcessorFor(intersect, processorEnv);
560                         if( processor != null ){
561                             if ( AptPlugin.DEBUG )
562                                 trace( "runAPT: invoking file-based processor " + processor.getClass().getName(), //$NON-NLS-1$
563
processorEnv );
564                             processorEnv.setCurrentProcessorFactory(factory);
565                             processor.process();
566                             processorEnv.setCurrentProcessorFactory(null);
567                         }
568                     }
569                 }
570                 
571                 final GeneratedFileManager gfm = _aptProject.getGeneratedFileManager();
572                 reportResult(
573                         curResult,
574                         processorEnv.getAllGeneratedFiles(),
575                         processorEnv.getModifiedGeneratedFiles(),
576                         processorEnv.getProblems(),
577                         processorEnv.getTypeDependencies(),
578                         gfm,
579                         processorEnv);
580                 processorEnv.completedFileProcessing();
581             }
582         }
583     }
584     
585     private void dispatchToFileBasedProcessor(
586             final AbstractCompilationEnv processorEnv){
587         
588         Map JavaDoc<String JavaDoc, AnnotationTypeDeclaration> annotationDecls = processorEnv.getAnnotationTypes();
589         for( Map.Entry JavaDoc<AnnotationProcessorFactory, FactoryPath.Attributes> entry : _factories.entrySet() ){
590             if( entry.getValue().runInBatchMode() ) continue;
591             AnnotationProcessorFactory factory = entry.getKey();
592             Set JavaDoc<AnnotationTypeDeclaration> factoryDecls = getFactorySupportedAnnotations(factory, annotationDecls);
593             if( factoryDecls != null ){
594                 if(factoryDecls.size() == 0 ){
595                     factoryDecls = new HashSet JavaDoc<AnnotationTypeDeclaration>(annotationDecls.values());
596                     annotationDecls.clear();
597                 }
598             }
599             if (factoryDecls != null && factoryDecls.size() > 0) {
600                 final AnnotationProcessor processor = factory
601                         .getProcessorFor(factoryDecls, processorEnv);
602                 if (processor != null)
603                 {
604                     if ( AptPlugin.DEBUG ) {
605                         trace( "runAPT: invoking file-based processor " + processor.getClass().getName() + " on " + processorEnv.getFile(), //$NON-NLS-1$ //$NON-NLS-2$
606
processorEnv);
607                     }
608                     processorEnv.setCurrentProcessorFactory(factory);
609                     processor.process();
610                     processorEnv.setCurrentProcessorFactory(null);
611                 }
612             }
613
614             if (annotationDecls.isEmpty())
615                 break;
616         }
617         if( ! annotationDecls.isEmpty() ){
618             // TODO: (theodora) log unclaimed annotations.
619
}
620     }
621     
622     /**
623      * @param processorEnv
624      * @param filesWithMissingType
625      * @param internalRound
626      * @param result output parameter
627      */

628     private Set JavaDoc<AnnotationProcessorFactory> build(final BuildEnv processorEnv)
629     {
630         try {
631             boolean mixedModeDispatch = shouldDispatchToBatchProcessor(processorEnv);
632             if( mixedModeDispatch ){
633                 runAPTInMixedMode(processorEnv);
634             }
635             else{
636                 runAPTInFileBasedMode(processorEnv);
637             }
638
639             // notify the processor listeners
640
final Set JavaDoc<AnnotationProcessorListener> listeners = processorEnv
641                     .getProcessorListeners();
642             EclipseRoundCompleteEvent event = null;
643             for (AnnotationProcessorListener listener : listeners) {
644                 if (listener instanceof RoundCompleteListener) {
645                     if (event == null)
646                         event = new EclipseRoundCompleteEvent(processorEnv);
647                     final RoundCompleteListener rcListener = (RoundCompleteListener) listener;
648                     rcListener.roundComplete(event);
649                 }
650             }
651             if( _filesWithoutAnnotation != null ){
652                 cleanupAllGeneratedFilesFrom(_filesWithoutAnnotation);
653             }
654             
655
656             // log unclaimed annotations.
657
}
658         catch (Error JavaDoc t) {
659             // Don't catch junit exceptions. This prevents one from unit
660
// testing a processor
661
if (t.getClass().getName().startsWith("junit.framework")) //$NON-NLS-1$
662
throw t;
663             AptPlugin.logWarning(t, "Unexpected failure running APT on the file(s): " + getFileNamesForPrinting(processorEnv)); //$NON-NLS-1$
664
}
665         catch (Throwable JavaDoc t) {
666             AptPlugin.logWarning(t, "Unexpected failure running APT on the file(s): " + getFileNamesForPrinting(processorEnv)); //$NON-NLS-1$
667
}
668         finally {
669             processorEnv.close();
670             _aptProject.getGeneratedFileManager().writeState();
671         }
672         
673         return Collections.emptySet();
674     }
675     
676     /**
677      * @param one
678      * @param two
679      * @return the set intersect of the two given sets
680      */

681     private Set JavaDoc<AnnotationTypeDeclaration> setIntersect(Set JavaDoc<AnnotationTypeDeclaration> one, Set JavaDoc<AnnotationTypeDeclaration> two ){
682         Set JavaDoc<AnnotationTypeDeclaration> intersect = null;
683         for( AnnotationTypeDeclaration obj : one ){
684             if( two.contains(obj) ){
685                 if( intersect == null )
686                     intersect = new HashSet JavaDoc<AnnotationTypeDeclaration>();
687                 intersect.add(obj);
688             }
689         }
690         return intersect;
691     }
692     
693     private void cleanupAllGeneratedFiles(){
694         cleanupAllGeneratedFilesFrom(_filesWithAnnotation);
695         cleanupAllGeneratedFilesFrom(_filesWithoutAnnotation);
696     }
697     
698     private void cleanupAllGeneratedFilesFrom(BuildContext[] cpResults){
699         if (cpResults == null) {
700             return;
701         }
702         final Set JavaDoc<IFile> deleted = new HashSet JavaDoc<IFile>();
703         GeneratedFileManager gfm = _aptProject.getGeneratedFileManager();
704         Set JavaDoc<IFile> java6GeneratedFiles = AptCompilationParticipant.getInstance().getJava6GeneratedFiles();
705         for( BuildContext cpResult : cpResults){
706             final IFile parentFile = cpResult.getFile();
707             cleanupNoLongerGeneratedFiles(
708                     parentFile,
709                     java6GeneratedFiles,
710                     gfm,
711                     null,
712                     deleted);
713             
714             if( deleted.size() > 0 ){
715                 final IFile[] deletedFilesArray = new IFile[deleted.size()];
716                 cpResult.recordDeletedGeneratedFiles(deleted.toArray(deletedFilesArray));
717             }
718         }
719     }
720     
721     /**
722      * Remove all the files that were previously generated
723      * from a particular parent file, but that were not generated
724      * in the most recent build pass.
725      * <p>
726      * Must be called during build phase, not reconcile
727      *
728      * @param parent the BuildContext associated with a single
729      * compiled parent file
730      * @param lastGeneratedFiles the files generated from parent
731      * on the previous build; typically obtained from the GFM just
732      * prior to beginning the current build.
733      * @param newGeneratedFiles the files generated from parent
734      * on the current build; typically stored in the BuildEnv, but
735      * an empty set can be passed in to remove all generated files
736      * of this parent.
737      * @param gfm
738      * @param processorEnv
739      * @param deleted
740      */

741     private void cleanupNoLongerGeneratedFiles(
742             IFile parentFile,
743             Set JavaDoc<IFile> newGeneratedFiles,
744             GeneratedFileManager gfm,
745             BuildEnv processorEnv,
746             Collection JavaDoc<IFile> deleted)
747     {
748         deleted.addAll(gfm.deleteObsoleteFilesAfterBuild(parentFile, newGeneratedFiles));
749     }
750
751     /**
752      * @return the set of {@link AnnotationTypeDeclaration} that {@link #factory} supports or null
753      * if the factory doesn't support any of the declarations.
754      * If the factory supports "*", then the empty set will be returned
755      *
756      * This method will destructively modify {@link #declarations}. Entries will be removed from
757      * {@link #declarations} as the declarations are being added into the returned set.
758      */

759     private static Set JavaDoc<AnnotationTypeDeclaration> getFactorySupportedAnnotations(
760             final AnnotationProcessorFactory factory,
761             final Map JavaDoc<String JavaDoc, AnnotationTypeDeclaration> declarations)
762
763     {
764         final Collection JavaDoc<String JavaDoc> supportedTypes = factory
765                 .supportedAnnotationTypes();
766
767         if (supportedTypes == null || supportedTypes.size() == 0)
768             return Collections.emptySet();
769
770         final Set JavaDoc<AnnotationTypeDeclaration> fDecls = new HashSet JavaDoc<AnnotationTypeDeclaration>();
771
772         for (Iterator JavaDoc<String JavaDoc> it = supportedTypes.iterator(); it.hasNext();) {
773             final String JavaDoc typeName = it.next();
774             if (typeName.equals("*")) { //$NON-NLS-1$
775
fDecls.addAll(declarations.values());
776                 declarations.clear();
777                 
778                 // Warn that * was claimed, which is non-optimal
779
AptPlugin.logWarning(null, "Processor Factory " + factory + //$NON-NLS-1$
780
" claimed all annotations (*), which prevents any following factories from being dispatched."); //$NON-NLS-1$
781

782             } else if (typeName.endsWith("*")) { //$NON-NLS-1$
783
final String JavaDoc prefix = typeName.substring(0,
784                         typeName.length() - 2);
785                 for (Iterator JavaDoc<Map.Entry JavaDoc<String JavaDoc, AnnotationTypeDeclaration>> entries = declarations
786                         .entrySet().iterator(); entries.hasNext();) {
787                     final Map.Entry JavaDoc<String JavaDoc, AnnotationTypeDeclaration> entry = entries
788                             .next();
789                     final String JavaDoc key = entry.getKey();
790                     if (key.startsWith(prefix)) {
791                         fDecls.add(entry.getValue());
792                         entries.remove();
793                     }
794                 }
795             } else {
796                 final AnnotationTypeDeclaration decl = declarations
797                         .get(typeName);
798                 if (decl != null) {
799                     fDecls.add(decl);
800                     declarations.remove(typeName);
801                 }
802             }
803         }
804         return fDecls.isEmpty() ? null : fDecls;
805     }
806     
807     private static void trace( String JavaDoc s, AbstractCompilationEnv processorEnv )
808     {
809         if (AptPlugin.DEBUG)
810         {
811             if (processorEnv != null) {
812                 s = "[ phase = " + processorEnv.getPhase() + ", file = " + getFileNamesForPrinting(processorEnv) +" ] " + s; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
813
}
814             AptPlugin.trace( s );
815         }
816     }
817     
818     private static String JavaDoc getFileNamesForPrinting(final AbstractCompilationEnv env){
819         if( env instanceof ReconcileEnv ){
820             return env.getFile().getName();
821         }
822         else{
823             return getFileNamesForPrinting((BuildEnv)env);
824         }
825     }
826     
827     /**
828      * For debugging statements only!!
829      * @return the names of the files that we are currently processing.
830      */

831     private static String JavaDoc getFileNamesForPrinting(final BuildEnv processorEnv){
832         final IFile file = processorEnv.getFile();
833         if( file != null )
834             return file.getName();
835         final BuildContext[] results = processorEnv.getFilesWithAnnotation();
836         final int len = results.length;
837         switch( len )
838         {
839         case 0:
840             return "no file(s)"; //$NON-NLS-1$
841
case 1:
842             return results[0].getFile().getName();
843         default:
844             StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
845             boolean firstItem = true;
846             for (BuildContext curResult : results) {
847                 if (firstItem) {
848                     firstItem = false;
849                 }
850                 else {
851                     sb.append(", "); //$NON-NLS-1$
852
}
853                 sb.append(curResult.getFile().getName());
854             }
855             return sb.toString();
856         }
857     }
858 }
859
Popular Tags