Skip to content

Commit 70b003d

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 8164d79 commit 70b003d

6 files changed

Lines changed: 108 additions & 17 deletions

File tree

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+
new BuildTestParticipant() {
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.34
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[] annotations) {
147168
// called prior to processAnnotations
148-
this.hasAnnotations = detectedAnnotations;
169+
this.annotations = annotations;
149170
this.addedFiles = null;
150171
this.deletedFiles = null;
151172
this.problems = null;

0 commit comments

Comments
 (0)