KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > apt > core > internal > env > BuildEnv


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  * tyeung@bea.com - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jdt.apt.core.internal.env;
13
14 import java.io.BufferedInputStream JavaDoc;
15 import java.io.IOException JavaDoc;
16 import java.io.InputStream JavaDoc;
17 import java.io.InputStreamReader JavaDoc;
18 import java.util.*;
19
20 import org.eclipse.core.resources.IFile;
21 import org.eclipse.core.resources.IMarker;
22 import org.eclipse.core.resources.IWorkspace;
23 import org.eclipse.core.resources.IWorkspaceRunnable;
24 import org.eclipse.core.runtime.CoreException;
25 import org.eclipse.core.runtime.IProgressMonitor;
26 import org.eclipse.core.runtime.IStatus;
27 import org.eclipse.jdt.apt.core.env.Phase;
28 import org.eclipse.jdt.apt.core.internal.AptPlugin;
29 import org.eclipse.jdt.apt.core.internal.declaration.EclipseMirrorObject;
30 import org.eclipse.jdt.apt.core.internal.declaration.TypeDeclarationImpl;
31 import org.eclipse.jdt.apt.core.internal.env.MessagerImpl.Severity;
32 import org.eclipse.jdt.apt.core.internal.util.Factory;
33 import org.eclipse.jdt.apt.core.internal.util.Visitors.AnnotatedNodeVisitor;
34 import org.eclipse.jdt.apt.core.internal.util.Visitors.AnnotationVisitor;
35 import org.eclipse.jdt.core.ICompilationUnit;
36 import org.eclipse.jdt.core.IJavaProject;
37 import org.eclipse.jdt.core.JavaCore;
38 import org.eclipse.jdt.core.compiler.CategorizedProblem;
39 import org.eclipse.jdt.core.compiler.BuildContext;
40 import org.eclipse.jdt.core.dom.*;
41 import com.sun.mirror.apt.Filer;
42 import com.sun.mirror.declaration.AnnotationTypeDeclaration;
43 import com.sun.mirror.declaration.PackageDeclaration;
44 import com.sun.mirror.declaration.TypeDeclaration;
45
46 public class BuildEnv extends AbstractCompilationEnv
47 {
48     private boolean _hasRaisedErrors = false;
49
50     private final BuildFilerImpl _filer;
51
52     /**
53      * Set of strings that indicate new type dependencies introduced on the file
54      * each string is a fully-qualified type name.
55      */

56     private Set<String JavaDoc> _typeDependencies = new HashSet<String JavaDoc>();
57     
58     /**
59      * Indicates whether we are in batch mode or not. This gets flipped only
60      * during build and could be flipped back and forth.
61      */

62     private boolean _batchMode = false; // off by default.
63

64     /**
65      * Holds all the files that contains annotation that are to be processed during build.
66      * If we are not in batch mode, <code>super._file</code> holds the file
67      * being processed at the time.
68      */

69     private BuildContext[] _filesWithAnnotation = null;
70     
71     /**
72      * These are files that are part of a build but does not have annotations on it.
73      * During batch mode processing, these files still also need to be included.
74      */

75     private BuildContext[] _additionFiles = null;
76     /**
77      * This is intialized when <code>_batchMode</code> is set to be <code>true</code> or
78      * when batch processing is expected. <p>
79      * It is also set in build mode for perf reason rather than parsing and resolving
80      * each file individually.
81      * @see #getAllAnnotationTypes(Map)
82      */

83     private CompilationUnit[] _astRoots = null;
84     private List<MarkerInfo> _markerInfos = null;
85     
86     /**
87      * Constructor for creating a processor environment used during build.
88      * @param filesWithAnnotations
89      * @param additionalFiles
90      * @param units
91      * @param javaProj
92      * @param phase
93      */

94     BuildEnv(
95             final BuildContext[] filesWithAnnotations,
96             final BuildContext[] additionalFiles,
97             final IJavaProject javaProj) {
98         
99         super(null, null, javaProj, Phase.BUILD);
100         _filer = new BuildFilerImpl(this);
101         _filesWithAnnotation = filesWithAnnotations;
102         _additionFiles = additionalFiles;
103         _problems = new ArrayList<APTProblem>();
104         _markerInfos = new ArrayList<MarkerInfo>();
105         
106         if (AptPlugin.DEBUG_COMPILATION_ENV) AptPlugin.trace(
107                 "constructed " + this + " for " + _filesWithAnnotation.length + " files"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
108
}
109
110     public Filer getFiler()
111     {
112         checkValid();
113         return _filer;
114     }
115     public PackageDeclaration getPackage(String JavaDoc name)
116     {
117         checkValid();
118         return super.getPackage(name);
119     }
120
121     public TypeDeclaration getTypeDeclaration(String JavaDoc name)
122     {
123         checkValid();
124         TypeDeclaration decl = super.getTypeDeclaration(name);
125         
126         if (!_batchMode)
127             addTypeDependency(name);
128             
129         return decl;
130     }
131
132     /**
133      * @return true iff errors (MessagerImpl.Severity.Error) has been posted
134      * Always return false when this environment is closed.
135      */

136     public boolean hasRaisedErrors(){
137         return _hasRaisedErrors;
138     }
139
140     public static InputStreamReader JavaDoc getFileReader( final IFile file ) throws IOException JavaDoc, CoreException {
141         return new InputStreamReader JavaDoc(getInputStream(file), file.getCharset());
142     }
143
144     public static InputStream JavaDoc getInputStream( final IFile file ) throws IOException JavaDoc, CoreException {
145         return new BufferedInputStream JavaDoc(file.getContents());
146     }
147
148     /**
149      * @return true iff class files has been generated.
150      * Always return false when this environment is closed.
151      */

152     public boolean hasGeneratedClassFiles(){ return _filer.hasGeneratedClassFile(); }
153
154     /* (non-Javadoc)
155      * Once the environment is closed the following is not allowed
156      * 1) posting messge
157      * 2) generating file
158      * 3) retrieving type or package by name
159      * 4) add or remove listeners
160      */

161     public void close(){
162         if( isClosed() )
163             return;
164         _markerInfos = null;
165         _astRoot = null;
166         _file = null;
167         _astRoots = null;
168         _filesWithAnnotation = null;
169         _problems = null;
170         _modelCompUnit2astCompUnit.clear();
171         _hasRaisedErrors = false;
172         super.close();
173     }
174     
175     /**
176      *
177      * @param resource null to indicate current resource
178      * @param start the starting offset of the marker
179      * @param end -1 to indicate unknow ending offset.
180      * @param severity the severity of the marker
181      * @param msg the message on the marker
182      * @param line the line number of where the marker should be
183      */

184     void addMessage(IFile resource,
185                     int start,
186                     int end,
187                     Severity severity,
188                     String JavaDoc msg,
189                     int line,
190                     String JavaDoc[] arguments)
191     {
192         checkValid();
193         
194         if( resource == null )
195             resource = getFile();
196         
197         _hasRaisedErrors |= severity == MessagerImpl.Severity.ERROR;
198         
199         // Eclipse doesn't support INFO-level IProblems, so we send them to the log instead.
200
if ( severity == Severity.INFO) {
201             StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
202             sb.append("Informational message reported by annotation processor:\n"); //$NON-NLS-1$
203
sb.append(msg);
204             sb.append("\n"); //$NON-NLS-1$
205
if (resource != null) {
206                 sb.append("Resource="); //$NON-NLS-1$
207
sb.append(resource.getName());
208                 sb.append("; "); //$NON-NLS-1$
209
}
210             sb.append("starting offset="); //$NON-NLS-1$
211
sb.append(start);
212             sb.append("; ending offset="); //$NON-NLS-1$
213
sb.append(end);
214             sb.append("; line="); //$NON-NLS-1$
215
sb.append(line);
216             if (arguments != null) {
217                 sb.append("; arguments:"); //$NON-NLS-1$
218
for (String JavaDoc s : arguments) {
219                     sb.append("\n"); //$NON-NLS-1$
220
sb.append(s);
221                 }
222             }
223             else {
224                 sb.append("\n"); //$NON-NLS-1$
225
}
226             IStatus status = AptPlugin.createInfoStatus(null, sb.toString());
227             AptPlugin.log(status);
228             return;
229         }
230         
231         if( resource == null ){
232             assert _batchMode : "not in batch mode but don't know about current resource"; //$NON-NLS-1$
233
addMarker(start, end, severity, msg, line, arguments);
234         }
235         else
236             addProblem(resource, start, end, severity, msg, line, arguments);
237     }
238     
239     private void addProblem(
240             IFile resource,
241             int start,
242             int end,
243             Severity severity,
244             String JavaDoc msg,
245             int line,
246             String JavaDoc[] arguments)
247     {
248             
249         APTProblem problem = createProblem(resource, start, end, severity, msg, line, arguments);
250         _problems.add(problem);
251     }
252     
253     private void addMarker(
254             int start,
255             int end,
256             Severity severity,
257             String JavaDoc msg,
258             int line,
259             String JavaDoc[] arguments)
260     {
261         
262         // Note that the arguments are ignored -- no quick-fix for markers.
263
_markerInfos.add(new MarkerInfo(start, end, severity, msg, line));
264     }
265     
266     public Map<String JavaDoc, AnnotationTypeDeclaration> getAnnotationTypes()
267     {
268         checkValid();
269         assert _astRoot != null && _file != null && !_batchMode :
270             "operation not available under batch mode."; //$NON-NLS-1$
271
return super.getAnnotationTypes();
272     }
273     
274     /**
275      * Return all annotations at declaration level within all compilation unit(s)
276      * associated with this environment. All the files associated with this environment will
277      * be parsed and resolved for all declaration level elements at the return of this call.
278      *
279      * @param file2Annotations populated by this method to map files to the annotation types
280      * if contains. May be null.
281      * @return the map containing all annotation types found within this environment.
282      */

283     public Map<String JavaDoc, AnnotationTypeDeclaration> getAllAnnotationTypes(
284             final Map<BuildContext, Set<AnnotationTypeDeclaration>> file2Annotations) {
285         
286         checkValid();
287         if( _filesWithAnnotation == null )
288             return getAnnotationTypes();
289         
290         final List<Annotation> instances = new ArrayList<Annotation>();
291         final Map<String JavaDoc, AnnotationTypeDeclaration> decls =
292             new HashMap<String JavaDoc, AnnotationTypeDeclaration>();
293         final AnnotationVisitor visitor = new AnnotationVisitor(instances);
294         for( int astIndex=0, len=_astRoots.length; astIndex<len; astIndex++ ){
295             if( _astRoots == null || _astRoots[astIndex] == null )
296                 System.err.println();
297             _astRoots[astIndex].accept(visitor);
298             final Set<AnnotationTypeDeclaration> perFileAnnos = new HashSet<AnnotationTypeDeclaration>();
299             
300             for (int instanceIndex=0, size = instances.size(); instanceIndex < size; instanceIndex++) {
301                 final Annotation instance = instances.get(instanceIndex);
302                 final ITypeBinding annoType = instance.resolveTypeBinding();
303                 if (annoType == null)
304                     continue;
305                 final TypeDeclarationImpl decl =
306                     Factory.createReferenceType(annoType, this);
307                 if (decl.kind() == EclipseMirrorObject.MirrorKind.TYPE_ANNOTATION){
308                     final AnnotationTypeDeclaration annoDecl = (AnnotationTypeDeclaration)decl;
309                     decls.put(annoDecl.getQualifiedName(), annoDecl);
310                     perFileAnnos.add(annoDecl);
311                 }
312             }
313             if( file2Annotations != null && !perFileAnnos.isEmpty() )
314                 file2Annotations.put(_filesWithAnnotation[astIndex], perFileAnnos);
315             visitor.reset();
316         }
317         
318         return decls;
319     }
320
321     /**
322      * @return - the extra type dependencies for the files under compilation
323      */

324     public Set<String JavaDoc> getTypeDependencies() { return _typeDependencies; }
325     
326     /**
327      * Switch to batch processing mode.
328      * Note: Call to this method will cause all files associated with this environment to be
329      * read and parsed.
330      */

331     public void beginBatchProcessing(){
332         if( _phase != Phase.BUILD )
333             throw new IllegalStateException JavaDoc("No batch processing outside build."); //$NON-NLS-1$
334

335         if( _batchMode ) return;
336         checkValid();
337         
338         _batchMode = true;
339         _file = null;
340         _astRoot = null;
341     }
342     
343     public void completedBatchProcessing(){
344         postMarkers();
345         completedProcessing();
346     }
347     
348     void createASTs(BuildContext[] cpResults){
349         final int len = cpResults.length;
350         final ICompilationUnit[] units = new ICompilationUnit[len];
351         for( int i=0; i<len; i++ ){
352             // may return null if creation failed. this may occur if
353
// the file does not exists.
354
units[i] = JavaCore.createCompilationUnitFrom(cpResults[i].getFile());
355         }
356         createASTs(_javaProject, units, _requestor = new CallbackRequestor(units));
357     }
358
359     public void beginFileProcessing(BuildContext result){
360         if( result == null )
361             throw new IllegalStateException JavaDoc("missing compilation result"); //$NON-NLS-1$
362
_batchMode = false;
363         final IFile file = result.getFile();
364         if( file.equals(_file) ) // this is a no-op
365
return;
366         
367         _astRoot = null;
368         _file = null;
369         
370         // need to match up the file with the ast.
371
if( _filesWithAnnotation != null ){
372             for( int i=0, len=_filesWithAnnotation.length; i<len; i++ ){
373                 if( file.equals(_filesWithAnnotation[i].getFile()) ){
374                     _file = file;
375                     _astRoot = _astRoots[i];
376                 }
377             }
378         }
379         
380         if( _file == null || _astRoot == null)
381             throw new IllegalStateException JavaDoc(
382                     "file " + //$NON-NLS-1$
383
file.getName() +
384                     " is not in the list to be processed."); //$NON-NLS-1$
385
}
386     
387     public void completedFileProcessing(){
388         completedProcessing();
389     }
390     
391     @Override JavaDoc
392     protected void completedProcessing(){
393         _problems.clear();
394         _typeDependencies.clear();
395         super.completedProcessing();
396     }
397     
398     public List<? extends CategorizedProblem> getProblems(){
399         if( !_problems.isEmpty() )
400             EnvUtil.updateProblemLength(_problems, getAstCompilationUnit());
401         return _problems;
402     }
403     
404     // Implementation for EclipseAnnotationProcessorEnvironment
405
public CompilationUnit getAST()
406     {
407         if( _batchMode )
408             return null;
409         return _astRoot;
410     }
411
412     public void addTypeDependency(final String JavaDoc fullyQualifiedTypeName )
413     {
414         if(!_batchMode){
415             _typeDependencies.add( fullyQualifiedTypeName );
416         }
417     }
418     // End of implementation for EclipseAnnotationProcessorEnvironment
419

420     /**
421      * Include all the types from all files, files with and without annotations on it
422      * if we are in batch mode. Otherwise, just the types from the file that's currently
423      * being processed.
424      */

425     @SuppressWarnings JavaDoc("unchecked")
426     protected List<AbstractTypeDeclaration> searchLocallyForTypeDeclarations()
427     {
428         if( !_batchMode )
429             return super.searchLocallyForTypeDeclarations();
430         final List<AbstractTypeDeclaration> typeDecls = new ArrayList<AbstractTypeDeclaration>();
431         for( int i=0, len=_astRoots.length; i<len; i++ )
432             typeDecls.addAll( _astRoots[i].types() );
433         
434         getTypeDeclarationsFromAdditionFiles(typeDecls);
435         
436         return typeDecls;
437     }
438     
439     @SuppressWarnings JavaDoc("unchecked")
440     private void getTypeDeclarationsFromAdditionFiles(List<AbstractTypeDeclaration> typeDecls){
441         if( _additionFiles == null || _additionFiles.length == 0 ) return;
442     
443         final int len = _additionFiles.length;
444         final ICompilationUnit[] units = new ICompilationUnit[len];
445         for( int i=0; i<len; i++ ){
446             // may return null if creation failed. this may occur if
447
// the file does not exists.
448
units[i] = JavaCore.createCompilationUnitFrom(_additionFiles[i].getFile());
449         }
450         BaseRequestor r = new BaseRequestor(units);
451         createASTs(_javaProject, units, r);
452         
453         CompilationUnit[] asts = r.asts;
454         for( CompilationUnit ast : asts ){
455             if( ast != null ){
456                 typeDecls.addAll( ast.types() );
457             }
458         }
459     }
460     
461     protected Map<ASTNode, List<Annotation>> getASTNodesWithAnnotations()
462     {
463         if( !_batchMode )
464             return super.getASTNodesWithAnnotations();
465         final Map<ASTNode, List<Annotation>> astNode2Anno = new HashMap<ASTNode, List<Annotation>>();
466         final AnnotatedNodeVisitor visitor = new AnnotatedNodeVisitor(astNode2Anno);
467         for( int i=0, len=_astRoots.length; i<len; i++ )
468             _astRoots[i].accept( visitor );
469         return astNode2Anno;
470     }
471     
472     protected IFile getFileForNode(final ASTNode node)
473     {
474         if( !_batchMode )
475             return super.getFileForNode(node);
476         final CompilationUnit curAST = (CompilationUnit)node.getRoot();
477         for( int i=0, len=_astRoots.length; i<len; i++ ){
478             if( _astRoots[i] == curAST )
479                 return _filesWithAnnotation[i].getFile();
480         }
481         throw new IllegalStateException JavaDoc();
482     }
483     
484     /**
485      * Go through the list of compilation unit in this environment and looking for
486      * the declaration node of the given binding.
487      * @param binding
488      * @return the compilation unit that defines the given binding or null if no
489      * match is found.
490      */

491     protected CompilationUnit searchLocallyForBinding(final IBinding binding)
492     {
493         if( !_batchMode )
494             return super.searchLocallyForBinding(binding);
495         
496         for( int i=0, len=_astRoots.length; i<len; i++ ){
497             ASTNode node = _astRoots[i].findDeclaringNode(binding);
498             if( node != null)
499                 return _astRoots[i];
500         }
501         return null;
502     }
503     
504     /**
505      * Go through the list of compilation unit in this environment and looking for
506      * the declaration node of the given binding.
507      * @param binding
508      * @return the compilation unit that defines the given binding or null if no
509      * match is found.
510      */

511     protected IFile searchLocallyForIFile(final IBinding binding)
512     {
513         if( !_batchMode )
514             return super.searchLocallyForIFile(binding);
515         
516         for( int i=0, len=_astRoots.length; i<len; i++ ){
517             ASTNode node = _astRoots[i].findDeclaringNode(binding);
518             if( node != null)
519                 return _filesWithAnnotation[i].getFile();
520         }
521         return null;
522     }
523     
524     /**
525      * @param file
526      * @return the compilation unit associated with the given file.
527      * If the file is not one of those that this environment is currently processing,
528      * return null;
529      */

530     public CompilationUnit getASTFrom(final IFile file)
531     {
532         if( file == null )
533             return null;
534         else if( file.equals(_file) )
535             return _astRoot;
536         else if( _astRoots != null ){
537             for( int i=0, len=_filesWithAnnotation.length; i<len; i++ ){
538                 if( file.equals(_filesWithAnnotation[i].getFile()) )
539                     return _astRoots[i];
540             }
541         }
542         return null;
543     }
544     
545     /**
546      * @return the current ast being processed if in per-file mode.
547      * If in batch mode, one of the asts being processed (no guarantee which
548      * one will be returned.
549      */

550     protected AST getCurrentDietAST(){
551         
552         if( _astRoot != null )
553             return _astRoot.getAST();
554         else{
555             if( _astRoots == null )
556                 throw new IllegalStateException JavaDoc("no AST is available"); //$NON-NLS-1$
557
return _astRoots[0].getAST();
558         }
559     }
560     
561     void postMarkers()
562     {
563         if( _markerInfos == null || _markerInfos.size() == 0 )
564             return;
565         // Posting all the markers to the workspace. Doing this in a batch process
566
// to minimize the amount of notification.
567
try{
568             final IWorkspaceRunnable runnable = new IWorkspaceRunnable(){
569                 public void run(IProgressMonitor monitor)
570                 {
571                     for( MarkerInfo markerInfo : _markerInfos ){
572                         try{
573                             final IMarker marker = _javaProject.getProject().createMarker(AptPlugin.APT_BATCH_PROCESSOR_PROBLEM_MARKER);
574                             markerInfo.copyIntoMarker(marker);
575                         }
576                         catch(CoreException e){
577                             AptPlugin.log(e, "Failure posting markers"); //$NON-NLS-1$
578
}
579                     }
580                 }
581             };
582             IWorkspace ws = _javaProject.getProject().getWorkspace();
583             ws.run(runnable, null);
584         }
585         catch(CoreException e){
586             AptPlugin.log(e, "Failed to post markers"); //$NON-NLS-1$
587
}
588         finally{
589             _markerInfos.clear();
590         }
591     }
592     
593     public BuildContext[] getFilesWithAnnotation()
594     {
595         return _filesWithAnnotation;
596     }
597     
598     public BuildContext[] getFilesWithoutAnnotation()
599     {
600         return _additionFiles;
601     }
602     
603     private class CallbackRequestor extends BaseRequestor {
604         CallbackRequestor(ICompilationUnit[] parseUnits) {
605             super(parseUnits);
606         }
607         public void acceptBinding(String JavaDoc bindingKey, IBinding binding) {
608             // If we have recieved the last ast we have requested,
609
// then assign the asts, then begin dispatch
610
_astRoots = asts;
611             _callback.run(BuildEnv.this);
612         }
613     }
614     
615 }
616
Popular Tags