Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check if the package exists from inside the annotation processor (in compile-time)?

I'm building an Annotation Processor for Android (for the sake of future explanations, let's call it TestProcessor).

The plan is for the processor to operate in two modes:

  • Mode 1: Generation of the code A

  • Mode 2: Generation of the code A and B


Mode 2 should be only selected if the additional (optional) package exists in the environment for which the processor generates code.

Is there a way to find out in the Annotation Processor's process method if the optional package exists?


EDIT 1:

Small clarification on the "(optional) package". By the additional package I mean a java package that may but does not have to exist in the project (that is using the processor). The optional package can for example represent the contents an external library. The external library can but does not have to be included in the project.

Let me just make a small example:

  • Let's assume we have a (one-module) project Sample.
  • Sample is using my TestProcessor.
  • Let's take Square's Picasso as an example of an external library
  • If Sample HAS Square's Picasso as one of its dependencies, TestProcessor operates in Mode 2 and generates code A and B. In other words: if you can use classes from com.square.picasso (in Sample) without any problems, then processor should operate in Mode 2.
  • If there is no Square's Picasso in Sample's dependencies, TestProcessor operates in Mode 1 and generates only code A.

EDIT 2:

I thought of two workarounds for this problem:

  1. Using two types of annotations for two processor Modes (example: @DoStuffModeOne, @DoStuffModeTwo)
  2. Using a second type of annotation (that should be used for example on the Application) that triggers the usage of Mode 2 (example: @TriggerModeTwo)

For my case the second workaround is much more preferable, but is still much worse than it would be if the TestProcessor itself could decide which Mode to choose.

like image 734
Bartek Lipinski Avatar asked Dec 30 '15 18:12

Bartek Lipinski


2 Answers

You could do a check for one of the ButterKnife classes in the classpath of the application. Then perform your logic based on the check's result.

Class butterKnifeClass = null;
try {
    butterKnifeClass = Class.forName("butterknife.ButterKnife");
} catch (ClassNotFoundException e) {
    // no butterknife present
}
if (butterKnifeClass != null) {
    // blah... stuff with butterknife
}
like image 127
JBirdVegas Avatar answered Oct 03 '22 22:10

JBirdVegas


You could use this code to find out if a class is present in the annotation processing environment.

TypeElement typeElement = processingEnvironment.getElementUtils().
    getTypeElement("com.squareup.picasso.Picasso");

I didn't have success using the Elements.getPackageElement() method because it returned successfully for non-existent packages (at least under Eclipse), and running the PackageElement.getEnclosedElements() method on packages created like this was empty even for non-empty packages. I'd suggest you go for a specific class in your package of interest as in my prior example because it worked as expected for existing type elements and returning null for non-existing types.

I currently didn't find an Annotation Processing API based method to decide whether the type element found in this way is based on a source code file on the source path of your project or a binary class file on the compilation classpath. There could be a method for this involving the processingEnvironment.getFiler().getResource(StandardLocation.SOURCE_PATH, ...) but, unfortunately is not portable, because StandardLocation.SOURCE_PATH is not supported in Eclipse.

I hope this helps.

like image 28
Nándor Előd Fekete Avatar answered Oct 04 '22 00:10

Nándor Előd Fekete