KickJava   Java API By Example, From Geeks To Geeks.

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


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.Collections JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.HashSet JavaDoc;
18 import java.util.LinkedHashSet JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.Set JavaDoc;
21
22 import org.eclipse.core.resources.IFile;
23 import org.eclipse.core.resources.IMarker;
24 import org.eclipse.core.resources.IProject;
25 import org.eclipse.core.resources.IResource;
26 import org.eclipse.core.resources.IResourceChangeEvent;
27 import org.eclipse.core.resources.IResourceChangeListener;
28 import org.eclipse.core.resources.ResourcesPlugin;
29 import org.eclipse.core.runtime.CoreException;
30 import org.eclipse.jdt.apt.core.internal.util.FactoryPath;
31 import org.eclipse.jdt.apt.core.util.AptConfig;
32 import org.eclipse.jdt.core.ICompilationUnit;
33 import org.eclipse.jdt.core.IJavaProject;
34 import org.eclipse.jdt.core.JavaCore;
35 import org.eclipse.jdt.core.compiler.BuildContext;
36 import org.eclipse.jdt.core.compiler.CategorizedProblem;
37 import org.eclipse.jdt.core.compiler.CompilationParticipant;
38 import org.eclipse.jdt.core.compiler.ReconcileContext;
39
40 import com.sun.mirror.apt.AnnotationProcessorFactory;
41
42 /**
43  * A singleton object, created by callback through the
44  * org.eclipse.jdt.core.compilationParticipants extension point.
45  */

46 public class AptCompilationParticipant extends CompilationParticipant
47 {
48     /**
49      * Batch factories that claimed some annotation in a previous round of APT processing.
50      * This currently only apply to the build case since are only generating types during build
51      * and hence cause APT rounding.
52      * The set is an order preserving. The order is determined by their first invocation.
53      */

54     private Set JavaDoc<AnnotationProcessorFactory> _previousRoundsBatchFactories = new LinkedHashSet JavaDoc<AnnotationProcessorFactory>();
55     private int _buildRound = 0;
56     private boolean _isBatch = false;
57     private static AptCompilationParticipant INSTANCE;
58     /**
59      * Files that has been processed by apt during the current build.
60      * Files that has been compiled may need re-compilation (from jdt's perspective)
61      * because of newly generated types. APT only process each file once during a build and
62      * this set will prevent unnecessary/incorrect compilation of already processed files.
63      */

64     private Map JavaDoc<IFile, CategorizedProblem[]> _processedFiles = null;
65     
66     /**
67      * Files generated by Java 6 annotation processing during the current build.
68      * These must be stored here in order to communicate between the Java 6 annotation
69      * processing phase, which runs during Java compilation, and the Java 5 phase,
70      * which runs afterwards. This member is reinitialized during aboutToBuild()
71      * and cleared in buildComplete().
72      *
73      * Doing it this way implies that files can only be generated by Java 6 processing
74      * during a build. That is true as of 8/07. If that changes, then this may need
75      * to be stored as a thread local, or elsewhere entirely.
76      */

77     private HashSet JavaDoc<IFile> _java6GeneratedFiles = null;
78     
79     public static AptCompilationParticipant getInstance() {
80         return INSTANCE;
81     }
82     
83     /**
84      * This class is constructed indirectly, by registering an extension to the
85      * org.eclipse.jdt.core.compilationParticipants extension point. Other
86      * clients should NOT construct this object.
87      */

88     public AptCompilationParticipant()
89     {
90         INSTANCE = this;
91
92         // Bug 180107: there is no CompilationParticipant.buildComplete() method,
93
// so we have to use a resource change listener instead.
94
IResourceChangeListener listener = new IResourceChangeListener() {
95             public void resourceChanged(IResourceChangeEvent event) {
96                 buildComplete();
97             }
98         };
99         ResourcesPlugin.getWorkspace().addResourceChangeListener(listener, IResourceChangeEvent.POST_BUILD);
100     }
101     
102     public boolean isAnnotationProcessor(){
103         return true;
104     }
105     
106     public void buildStarting(BuildContext[] files, boolean isBatch){
107         // this gets called multiple times during a build.
108
// This gets called:
109
// 1) after "aboutToBuild" is called.
110
// 2) everytime an incremental build occur because of newly generated files
111
// this gets called.
112
if( _buildRound == 0 )
113             _isBatch = isBatch;
114     }
115     
116     public void processAnnotations(BuildContext[] allfiles) {
117         // This should not happen. There should always be file that that needs
118
// building when
119
final int total = allfiles == null ? 0 : allfiles.length;
120         if( total == 0 )
121             return;
122
123         final IProject project = allfiles[0].getFile().getProject();
124         final IJavaProject javaProject = JavaCore.create(project);
125         // Don't dispatch on pre-1.5 project. They cannot legally have annotations
126
String JavaDoc javaVersion = javaProject.getOption("org.eclipse.jdt.core.compiler.source", true); //$NON-NLS-1$
127
// Check for 1.3 or 1.4, as we don't want this to break in the future when 1.6
128
// is a possibility
129
if ("1.3".equals(javaVersion) || "1.4".equals(javaVersion)) { //$NON-NLS-1$ //$NON-NLS-2$
130
return;
131         }
132         
133         if ( _isBatch && _buildRound == 0 ) {
134             AnnotationProcessorFactoryLoader.getLoader().resetBatchProcessors(javaProject);
135             _previousRoundsBatchFactories.clear();
136         }
137         
138         try {
139         
140             // split up the list of files with annotations from those that don't
141
// also exclude files that has already been processed.
142
int annoFileCount = 0;
143             int noAnnoFileCount = 0;
144             for( int i=0; i<total; i++ ){
145                 BuildContext bc = allfiles[i];
146                 if( _buildRound > 0 && _processedFiles.containsKey( bc.getFile() )){
147                     // We've already processed this file; we'll skip reprocessing it, on
148
// the assumption that nothing would change, but we need to re-report
149
// any problems we reported earlier because JDT will have cleared them.
150
CategorizedProblem[] problems = _processedFiles.get(bc.getFile());
151                     if (null != problems && problems.length > 0) {
152                         bc.recordNewProblems(problems);
153                     }
154                     continue;
155                 }
156                 if( bc.hasAnnotations() )
157                     annoFileCount ++;
158                 else
159                     noAnnoFileCount ++;
160             }
161             // apt has already processed all files
162
// files that are reported at this point is triggered by
163
// dependencies introduced by type creation.
164
if( annoFileCount == 0 && noAnnoFileCount == 0 )
165                 return;
166             
167             BuildContext[] withAnnotation = null;
168             BuildContext[] withoutAnnotation = null;
169             
170             if( annoFileCount != 0 )
171                 withAnnotation = new BuildContext[annoFileCount];
172             if(noAnnoFileCount != 0 )
173                 withoutAnnotation = new BuildContext[noAnnoFileCount];
174             int wIndex = 0; // index for 'withAnnotation' array
175
int woIndex = 0; // index of 'withoutAnnotation' array
176
for( int i=0; i<total; i++ ){
177                 if( _processedFiles.containsKey( allfiles[i].getFile() ) )
178                     continue;
179                 if( allfiles[i].hasAnnotations() )
180                     withAnnotation[wIndex ++] = allfiles[i];
181                 else
182                     withoutAnnotation[woIndex ++] = allfiles[i];
183             }
184             
185             for( BuildContext file : allfiles )
186                 _processedFiles.put(file.getFile(), null);
187         
188             Map JavaDoc<AnnotationProcessorFactory, FactoryPath.Attributes> factories =
189                 AnnotationProcessorFactoryLoader.getLoader().getJava5FactoriesAndAttributesForProject(javaProject);
190             
191             AptProject aptProject = AptPlugin.getAptProject(javaProject);
192             Set JavaDoc<AnnotationProcessorFactory> dispatchedBatchFactories =
193                 APTDispatchRunnable.runAPTDuringBuild(
194                         withAnnotation,
195                         withoutAnnotation,
196                         _processedFiles,
197                         aptProject,
198                         factories,
199                         _previousRoundsBatchFactories,
200                         _isBatch);
201             _previousRoundsBatchFactories.addAll(dispatchedBatchFactories);
202         }
203         finally {
204             _buildRound ++;
205         }
206     }
207     
208     public void reconcile(ReconcileContext context){
209         final ICompilationUnit workingCopy = context.getWorkingCopy();
210         if( workingCopy == null )
211             return;
212         IJavaProject javaProject = workingCopy.getJavaProject();
213         if( javaProject == null )
214             return;
215         if (!AptConfig.shouldProcessDuringReconcile(javaProject)) {
216             AptPlugin.trace("Reconcile-time processing is disabled for project: " + javaProject.getElementName()); //$NON-NLS-1$
217
return;
218         }
219         AptProject aptProject = AptPlugin.getAptProject(javaProject);
220         
221         Map JavaDoc<AnnotationProcessorFactory, FactoryPath.Attributes> factories =
222             AnnotationProcessorFactoryLoader.getLoader().getJava5FactoriesAndAttributesForProject( javaProject );
223         APTDispatchRunnable.runAPTDuringReconcile(context, aptProject, factories);
224     }
225     
226     public void cleanStarting(IJavaProject javaProject){
227         IProject p = javaProject.getProject();
228         
229         AptPlugin.getAptProject(javaProject).projectClean( true );
230         try{
231             // clear out all markers during a clean.
232
IMarker[] markers = p.findMarkers(AptPlugin.APT_BATCH_PROCESSOR_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
233             if( markers != null ){
234                 for( IMarker marker : markers )
235                     marker.delete();
236             }
237         }
238         catch(CoreException e){
239             AptPlugin.log(e, "Unable to delete batch annotation processor markers"); //$NON-NLS-1$
240
}
241     }
242     
243     /**
244      * Does APT have anything to do for this project?
245      * Even if there are no processors on the factory path, apt may still
246      * be involved during a clean.
247      */

248     public boolean isActive(IJavaProject project){
249         return AptConfig.isEnabled(project);
250     }
251     
252     public int aboutToBuild(IJavaProject project) {
253         if (AptConfig.isEnabled(project)) {
254             // setup the classpath and make sure the generated source folder is on disk.
255
AptPlugin.getAptProject(project).compilationStarted();
256         }
257         _buildRound = 0; // reset
258
// Note that for each project build, we blow away the last project's processed files.
259
_processedFiles = new HashMap JavaDoc<IFile, CategorizedProblem[]>();
260         _java6GeneratedFiles = new HashSet JavaDoc<IFile>();
261         // TODO: (wharley) if the factory path is different we need a full build
262
return CompilationParticipant.READY_FOR_BUILD;
263     }
264     
265     /**
266      * Called during Java 6 annotation processing phase to register newly-generated files.
267      * This information is then used in the Java 5 (post-compilation) phase when
268      * determining no-longer-generated files to delete. The list of files is discarded
269      * at the end of each build.
270      */

271     public void addJava6GeneratedFile(IFile file) {
272         _java6GeneratedFiles.add(file);
273     }
274     
275     /**
276      * Get the files generated during this build by Java 6 processors.
277      * This is only meaningful in the context of a build, not a reconcile.
278      * @return an unmodifiable, non-null but possibly empty, set of IFiles.
279      */

280     public Set JavaDoc<IFile> getJava6GeneratedFiles() {
281         if (null == _java6GeneratedFiles) {
282             return Collections.emptySet();
283         }
284         return Collections.unmodifiableSet(_java6GeneratedFiles);
285     }
286     
287     private void buildComplete() {
288         _processedFiles = null;
289         _java6GeneratedFiles = null;
290     }
291 }
292
Popular Tags