Skip to content

Commit 097d1ae

Browse files
committed
Enhance the BuildContext with the discovered annotations
Currently one can only get the information that annotations are present on a file in a BuildContext / CompilationParticipant but sometimes it is much more interesting to know if a specific annotation is present so one don't need to scann files unnecsaryly. This captures the found low-level annotations and adds a new method based on this BuildContext.hasAnnotations(String) so that CompilationParticipant can check fi the file is of any interest for them. Fix #674
1 parent d9438d4 commit 097d1ae

8 files changed

Lines changed: 127 additions & 27 deletions

File tree

org.eclipse.jdt.apt.tests/src-annotations/org/eclipse/jdt/apt/tests/annotations/generic/AbstractGenericProcessor.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212

1313
import java.util.Collection;
1414

15-
import junit.framework.AssertionFailedError;
15+
import com.sun.mirror.apt.AnnotationProcessor;
16+
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
17+
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
18+
import com.sun.mirror.declaration.Declaration;
1619

17-
import com.sun.mirror.apt.*;
18-
import com.sun.mirror.declaration.*;
20+
import junit.framework.AssertionFailedError;
1921

2022
public abstract class AbstractGenericProcessor implements AnnotationProcessor {
2123
protected AnnotationProcessorEnvironment env;
@@ -28,20 +30,27 @@ public void setEnv(AnnotationProcessorEnvironment env) {
2830
decls = env.getDeclarationsAnnotatedWith(genericAnnotation);
2931
}
3032

31-
public abstract void _process();
32-
3333
/**
3434
* This method is abstract, so that subclasses need to implement
3535
* _process. We'll handle catching any errant throwables
3636
* and fail any junit tests.
3737
*/
38+
public abstract void _process();
39+
40+
@Override
3841
public final void process() {
3942
try {
4043
_process();
4144
}
4245
catch (Throwable t) {
46+
if (t instanceof AssertionFailedError) {
47+
throw t;
48+
}
4349
t.printStackTrace();
44-
throw new AssertionFailedError("Processor threw an exception during processing");
50+
AssertionFailedError assertionFailedError = new AssertionFailedError(
51+
"Processor threw an exception during processing: " + t);
52+
assertionFailedError.initCause(t);
53+
throw assertionFailedError;
4554
}
4655
}
4756

org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/MirrorDeclarationTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@
1818
import java.util.Collection;
1919
import java.util.Map;
2020

21-
import junit.framework.Test;
22-
import junit.framework.TestSuite;
23-
2421
import org.eclipse.core.resources.IProject;
2522
import org.eclipse.core.runtime.IPath;
2623
import org.eclipse.jdt.apt.core.env.EnvironmentFactory;
@@ -43,6 +40,9 @@
4340
import com.sun.mirror.declaration.TypeDeclaration;
4441
import com.sun.mirror.util.SourcePosition;
4542

43+
import junit.framework.Test;
44+
import junit.framework.TestSuite;
45+
4646
public class MirrorDeclarationTests extends APTTestBase {
4747

4848
public MirrorDeclarationTests(final String name)
@@ -340,7 +340,7 @@ static class PackageInfoProc extends AbstractGenericProcessor {
340340
public void _process() {
341341
called = true;
342342
AnnotationTypeDeclaration annoDecl = (AnnotationTypeDeclaration)env.getTypeDeclaration("pkg.PkgAnnotation");
343-
assertNotNull(annoDecl);
343+
assertNotNull("Type declaration 'pkg.PkgAnnotation' not found!", annoDecl);
344344
// get the annotated declarations
345345
Collection<Declaration> annotatedDecls = env.getDeclarationsAnnotatedWith(annoDecl);
346346
// don't return the package declaration - well, apt is doing that..

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/CompilationResult.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2017 IBM Corporation and others.
2+
* Copyright (c) 2000, 2023 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -10,9 +10,11 @@
1010
*
1111
* Contributors:
1212
* IBM Corporation - initial API and implementation
13+
* Christoph Läubrich - Enhance the BuildContext with the discovered annotations #674
1314
*******************************************************************************/
1415
package org.eclipse.jdt.internal.compiler;
1516

17+
import java.util.ArrayList;
1618
/**
1719
* A compilation result consists of all information returned by the compiler for
1820
* a single compiled compilation source unit. This includes:
@@ -39,6 +41,7 @@
3941
import java.util.HashSet;
4042
import java.util.Hashtable;
4143
import java.util.Iterator;
44+
import java.util.List;
4245
import java.util.Map;
4346
import java.util.Set;
4447

@@ -48,6 +51,7 @@
4851
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
4952
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
5053
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
54+
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
5155
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
5256
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
5357
import org.eclipse.jdt.internal.compiler.parser.RecoveryScannerData;
@@ -81,6 +85,7 @@ public class CompilationResult {
8185
public boolean checkSecondaryTypes = false; // check for secondary types which were created after the initial buildTypeBindings call
8286
private int numberOfErrors;
8387
private boolean hasMandatoryErrors;
88+
public List<AnnotationBinding[]> annotations = new ArrayList<>(1);
8489

8590
private static final int[] EMPTY_LINE_ENDS = Util.EMPTY_INT_ARRAY;
8691
private static final Comparator PROBLEM_COMPARATOR = new Comparator() {

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
* Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas
5151
* Sebastian Zarnekow - Contributions for
5252
* bug 544921 - [performance] Poor performance with large source files
53+
* Christoph Läubrich - Contributions for
54+
* Issue 674 - Enhance the BuildContext with the discovered annotations
5355
*******************************************************************************/
5456
package org.eclipse.jdt.internal.compiler.lookup;
5557

@@ -3338,6 +3340,12 @@ SimpleLookupTable storedAnnotations(boolean forceInitialize, boolean forceStore)
33383340
return this.storedAnnotations;
33393341
}
33403342

3343+
@Override
3344+
void storeAnnotations(Binding binding, AnnotationBinding[] annotations, boolean forceStore) {
3345+
super.storeAnnotations(binding, annotations, forceStore);
3346+
this.scope.referenceCompilationUnit().compilationResult.annotations.add(annotations);
3347+
}
3348+
33413349
@Override
33423350
public ReferenceBinding superclass() {
33433351
if (!isPrototype())

org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/ParticipantBuildTests.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,45 @@ public void processAnnotations(BuildContext[] files) {
378378
expectingNoProblems();
379379
}
380380

381+
/**
382+
* Test that a build participant can inspect the declared annotations by name
383+
* @throws JavaModelException
384+
*/
385+
public void testProcessAnnotationHasAnnotation() throws JavaModelException {
386+
IPath projectPath = env.addProject("Project", "1.5"); //$NON-NLS-1$ //$NON-NLS-2$
387+
env.addExternalJars(projectPath, Util.getJavaClassLibs());
388+
env.removePackageFragmentRoot(projectPath, ""); //$NON-NLS-1$
389+
IPath root = env.addPackageFragmentRoot(projectPath, "src"); //$NON-NLS-1$
390+
env.setOutputFolder(projectPath, "bin"); //$NON-NLS-1$
391+
392+
env.addClass(root, "p1", "Test", //$NON-NLS-1$ //$NON-NLS-2$
393+
"package p1;\n" + //$NON-NLS-1$
394+
"@GeneratedAnnotation\n" + //$NON-NLS-1$
395+
"public class Test { public void method() { } }\n" //$NON-NLS-1$
396+
);
397+
env.addClass(root, "p1", "GeneratedAnnotation", //$NON-NLS-1$ //$NON-NLS-2$
398+
"package p1;\n" + //$NON-NLS-1$
399+
"@interface GeneratedAnnotation{}\n" //$NON-NLS-1$
400+
);
401+
402+
// install compilationParticipant
403+
TestCompilationParticipant1.PARTICIPANT = new CompilationParticipant() {
404+
public boolean isAnnotationProcessor() {
405+
return true;
406+
}
407+
public void processAnnotations(BuildContext[] files) {
408+
Optional<BuildContext> testFileContext = Arrays.stream(files).filter(bc->bc.getFile().getName().equals("Test.java")).findFirst();
409+
assertTrue("Testfile not found in build context!", testFileContext.isPresent());
410+
assertTrue(testFileContext.get().hasAnnotations());
411+
assertTrue(testFileContext.get().hasAnnotations("p1.GeneratedAnnotation"));
412+
assertFalse(testFileContext.get().hasAnnotations("gibts.nicht.Hier"));
413+
}
414+
};
415+
416+
fullBuild(projectPath);
417+
expectingNoProblems();
418+
}
419+
381420
public void testProcessAnnotationReferences() throws JavaModelException {
382421
IPath projectPath = env.addProject("Project", "1.5"); //$NON-NLS-1$ //$NON-NLS-2$
383422
env.addExternalJars(projectPath, Util.getJavaClassLibs());

org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/BuildContext.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2006, 2018 IBM Corporation and others.
2+
* Copyright (c) 2006, 2023 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -10,7 +10,7 @@
1010
*
1111
* Contributors:
1212
* IBM Corporation - initial API and implementation
13-
*
13+
* Christoph Läubrich - Enhance the BuildContext with the discovered annotations #674
1414
*******************************************************************************/
1515

1616
package org.eclipse.jdt.core.compiler;
@@ -60,6 +60,18 @@ public boolean hasAnnotations() {
6060
return false; // default overridden by concrete implementation
6161
}
6262

63+
/**
64+
* Returns whether the compilation unit contained any annotations with a given type when it was compiled.
65+
*
66+
* NOTE: This is only valid during {@link CompilationParticipant#processAnnotations(BuildContext[])}.
67+
* @param fqn the fully qualified name of the annotation to check for precence
68+
* @return whether the compilation unit contained any annotations of the given type when it was compiled
69+
* @since 3.35
70+
*/
71+
public boolean hasAnnotations(String fqn) {
72+
return false; // default overridden by concrete implementation
73+
}
74+
6375
/**
6476
* Record the added/changed generated files that need to be compiled.
6577
*

org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2021 IBM Corporation and others.
2+
* Copyright (c) 2000, 2023 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
1010
*
1111
* Contributors:
1212
* IBM Corporation - initial API and implementation
13+
* Christoph Läubrich - Enhance the BuildContext with the discovered annotations #674
1314
*******************************************************************************/
1415
package org.eclipse.jdt.internal.core.builder;
1516

@@ -23,6 +24,7 @@
2324
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
2425
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
2526
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
27+
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
2628
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
2729
import org.eclipse.jdt.internal.compiler.problem.*;
2830
import org.eclipse.jdt.internal.compiler.util.SimpleSet;
@@ -60,7 +62,7 @@ public abstract class AbstractImageBuilder implements ICompilerRequestor, ICompi
6062
private boolean inCompiler;
6163

6264
protected boolean keepStoringProblemMarkers;
63-
protected Set<SourceFile> filesWithAnnotations = null;
65+
protected Map<SourceFile, AnnotationBinding[]> filesWithAnnotations = null;
6466

6567
//2000 is best compromise between space used and speed
6668
public static int MAX_AT_ONCE = Integer.getInteger(JavaModelManager.MAX_COMPILED_UNITS_AT_ONCE, 2000).intValue();
@@ -104,7 +106,7 @@ protected AbstractImageBuilder(JavaBuilder javaBuilder, boolean buildStarting, S
104106
// initialize this set so the builder knows to gather CUs that define Annotation types
105107
// each Annotation processor participant is then asked to process these files AFTER
106108
// the compile loop. The normal dependency loop will then recompile all affected types
107-
this.filesWithAnnotations = new HashSet<>(1);
109+
this.filesWithAnnotations = new HashMap<>(1);
108110
break;
109111
}
110112
}
@@ -219,8 +221,11 @@ public void acceptResult(CompilationResult result) {
219221
createProblemFor(compilationUnit.resource, null, Messages.build_inconsistentClassFile, JavaCore.ERROR);
220222
}
221223
}
222-
if (result.hasAnnotations && this.filesWithAnnotations != null) // only initialized if an annotation processor is attached
223-
this.filesWithAnnotations.add(compilationUnit);
224+
if (result.hasAnnotations && this.filesWithAnnotations != null) {
225+
// only initialized if an annotation processor is attached
226+
AnnotationBinding[] bindings = result.annotations.stream().flatMap(Arrays::stream).filter(Objects::nonNull).toArray(AnnotationBinding[]::new);
227+
this.filesWithAnnotations.put(compilationUnit, bindings);
228+
}
224229

225230
this.compiler.lookupEnvironment.releaseClassFiles(classFiles);
226231
finishedWith(typeLocator, result, compilationUnit.getMainTypeName(), definedTypeNames, duplicateTypeNames);
@@ -653,8 +658,9 @@ protected void processAnnotations(CompilationParticipantResult[] results) {
653658
if (!hasAnnotationProcessor) return;
654659

655660
boolean foundAnnotations = this.filesWithAnnotations != null && this.filesWithAnnotations.size() > 0;
656-
for (int i = results.length; --i >= 0;)
657-
results[i].reset(foundAnnotations && this.filesWithAnnotations.contains(results[i].sourceFile));
661+
for (int i = results.length; --i >= 0;) {
662+
results[i].reset(foundAnnotations ? this.filesWithAnnotations.get(results[i].sourceFile) : null);
663+
}
658664

659665
// even if no files have annotations, must still tell every annotation processor in case the file used to have them
660666
for (int i = 0, l = this.javaBuilder.participants.length; i < l; i++)

org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/CompilationParticipantResult.java

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2005, 2018 IBM Corporation and others.
2+
* Copyright (c) 2005, 2023 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -10,17 +10,19 @@
1010
*
1111
* Contributors:
1212
* IBM - rewrote spec
13-
*
13+
* Christoph Läubrich - Enhance the BuildContext with the discovered annotations #674
1414
*******************************************************************************/
1515

1616
package org.eclipse.jdt.internal.core.builder;
1717

1818
import org.eclipse.core.resources.IFile;
1919
import org.eclipse.jdt.core.compiler.*;
20+
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
21+
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
2022

2123
public class CompilationParticipantResult extends BuildContext {
2224
protected SourceFile sourceFile;
23-
protected boolean hasAnnotations; // only set during processAnnotations
25+
protected AnnotationBinding[] annotations; // only set during processAnnotations
2426
protected IFile[] addedFiles; // added/changed generated source files that need to be compiled
2527
protected IFile[] deletedFiles; // previously generated source files that should be deleted
2628
protected CategorizedProblem[] problems; // new problems to report against this compilationUnit
@@ -30,7 +32,7 @@ public class CompilationParticipantResult extends BuildContext {
3032
protected CompilationParticipantResult(SourceFile sourceFile, boolean isTestCode) {
3133
this.sourceFile = sourceFile;
3234
this.isTestCode = isTestCode;
33-
this.hasAnnotations = false;
35+
this.annotations = null;
3436
this.addedFiles = null;
3537
this.deletedFiles = null;
3638
this.problems = null;
@@ -66,7 +68,26 @@ public IFile getFile() {
6668
*/
6769
@Override
6870
public boolean hasAnnotations() {
69-
return this.hasAnnotations; // only set during processAnnotations
71+
return this.annotations != null; // only set during processAnnotations
72+
}
73+
74+
@Override
75+
public boolean hasAnnotations(String fqn) {
76+
if (this.annotations != null) {
77+
for (AnnotationBinding binding : this.annotations) {
78+
if (binding == null) {
79+
continue;
80+
}
81+
ReferenceBinding type = binding.getAnnotationType();
82+
if (type == null || type.compoundName == null) {
83+
continue;
84+
}
85+
if (fqn.equals(new String(CharOperation.concatWith(type.compoundName, '.')))) {
86+
return true;
87+
}
88+
}
89+
}
90+
return false;
7091
}
7192

7293
/**
@@ -143,9 +164,9 @@ public void recordNewProblems(CategorizedProblem[] newProblems) {
143164
this.problems = merged;
144165
}
145166

146-
void reset(boolean detectedAnnotations) {
167+
void reset(AnnotationBinding[] newAnnotations) {
147168
// called prior to processAnnotations
148-
this.hasAnnotations = detectedAnnotations;
169+
this.annotations = newAnnotations;
149170
this.addedFiles = null;
150171
this.deletedFiles = null;
151172
this.problems = null;

0 commit comments

Comments
 (0)