I have two classes with methods and i want to combine the methods of the two classes to one class.
@Service("ITestService")
public interface ITest1
{
@Export
void method1();
}
@Service("ITestService")
public interface ITest2
{
@Export
void method2();
}
Result should be:
public interface ITestService extends Remote
{
void method1();
void method2();
}
The first run of my AnnotationProcessor generates the correct output (because the RoundEnvironment contains both classes).
But if I edit one of the classes (for example adding a new method), the RoundEnviroment contains only the edited class and so the result is follwing (adding newMethod() to interface ITest1)
public interface ITestService extends Remote
{
void method1();
void newMethod();
}
Now method2 is missing. I don't know how to fix my problem. Is there a way (Enviroment), to access all classes in the project? Or is there another way to solve this?
The code to generate the class is pretty long, so here a short description how i generate the class. I iterate through the Elements with env.getElementsAnnotatedWith(Service.class)
and extract the methods and write them into the new file with:
FileObject file = null;
file = filer.createSourceFile("com/test/" + serviceName);
file.openWriter().append(serviceContent).close();
-- Option 1 - Manual compilation from command line ---
I tried to do what you want, which is access all the classes from a processor, and as people commented, javac is always compiling all classes and from RoundEnvironment I do have access to all classes that are being compiled, everytime (even when no files changed), with one small detail: as long as all classes show on the list of classes to be compiled.
I've done a few tests with two interfaces where one (A) depends on the (B) other (extends) and I have the following scenarios:
Implicitly compiled files were not subject to annotation processing.
If you set the compiler to be verbose, you'll get an explicity message showing you what classes will be processed in each round. This is what I got when I explicitly passed interface (A):
Round 1:
input files: {com.bearprogrammer.test.TestInterface}
annotations: [com.bearprogrammer.annotation.Service]
last round: false
And this is what I've got when I added both classes:
Round 1:
input files: {com.bearprogrammer.test.AnotherInterface, com.bearprogrammer.test.TestInterface}
annotations: [com.bearprogrammer.annotation.Service]
last round: false
In both cases I see that the compiler parses both classes, but in a different order. For the first case (only one interface added):
[parsing started RegularFileObject[src\main\java\com\bearprogrammer\test\TestInterface.java]]
[parsing completed 15ms]
[search path for source files: src\main\java]
[search path for class files: ...]
[loading ZipFileIndexFileObject[lib\processor.jar(com/bearprogrammer/annotation/Service.class)]]
[loading RegularFileObject[src\main\java\com\bearprogrammer\test\AnotherInterface.java]]
[parsing started RegularFileObject[src\main\java\com\bearprogrammer\test\AnotherInterface.java]]
For the second case (all interfaces added):
[parsing started RegularFileObject[src\main\java\com\bearprogrammer\test\AnotherInterface.java]]
...
[parsing started RegularFileObject[src\main\java\com\bearprogrammer\test\TestInterface.java]]
[search path for source files: src\main\java]
[search path for class files: ...]
...
The important detail here is that the compiler is loading the dependency as an implicit object for the compilation in the first case. In the second case it will load it as part of the to-be-compiled-objects (you can see this because it starts searching other paths for files after the provided classes are parsed). And it seems that implicit objects aren't included in the annotation processing list.
For more details over the compilation process, check this Compilation Overview. Which is not explicitly saying what files are picked up for processing.
The solution in this case would be to always add all classes into the command for the compiler.
--- Option 2 - Compiling from Eclipse ---
If you are compiling from Eclipse, incremental build will make your processor fail (haven't tested it). But I would think you can go around that asking for a clean build (Project > Clean..., also haven't tested it) or writing an Ant build that always clean the classes directory and setting up an Ant Builder from Eclipse.
--- Option 3 - Using build tools ---
If you are using some other build tool like Ant, Maven or Gradle, the best solution would be to have the source generation in a separate step than your compilation. You would also need to have your processor compiled in a separated previous step (or a separated subproject if using multiprojects build in Maven/Gradle). This would be the best scenario because:
-proc:only
from javac to only process the files)For this third option you could also setup an Ant build script to generate those files from Eclipse as a builder that runs before your Java builder. Generate the source files in some special folder and add that to your classpath/buildpath in Eclipse.
NetBeans @Messages annotation generates single Bundle.java file per all classes in the same package. It works correctly with incremental compilation thanks to following trick in the annotation processor:
Set<Element> toProcess = new HashSet<Element>();
for (Element e : roundEnv.getElementsAnnotatedWith(Messages.class)) {
PackageElement pkg = findPkg(e);
for (Element elem : pkg.getEnclosingElements()) {
if (elem.getAnnotation(Message.class) != null) {
toProcess.add(elem);
}
}
}
// now process all package elements in toProcess
// rather just those provided by the roundEnv
PackageElement findPkg(Element e) {
for (;;) {
if (e instanceof PackageElement) {
return (PackageElement)e;
}
e = e.getEnclosingElement();
}
}
By doing this one can be sure all (top level) elements in a package are processed together even if the compilation has only been invoked on a single source file in the package.
In case you know where to look for your annotation (top level elements in a package or even any element in a package) you should be able to always get list of all such elements.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With