13

I have a simple annotation processor that needs to read a configuration file from the same project as the annotated classes. Example structure:

- myproject 
  - src
    - main
      - java
        - my.package.SourceFile
      - resources
        - config.json

In the annotation processor, I try to read the file:

FileObject resource = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", "config.json");

but it throws FileNotFoundException. I also tried other paths, such as ../resources/config.json, (which throws Invalid relative name: ../resources/config.json). And I tried putting the config file in src/main/java (and even src/main/java/my/package) instead, which I don't like, but that also still throws FileNotFoundException.

It would already help if I could get filer.getResource() to tell me where it's looking. To find that out, I tried generating a file:

filer.createResource(StandardLocation.SOURCE_OUTPUT, "", "dummy");

which generated in myproject/build/classes/main/dummy. Unfortunately, I can't generate in SOURCE_PATH, so that doesn't help with finding this out.

Jorn
  • 20,612
  • 18
  • 79
  • 126
  • I don't know the API that you're using, but if it takes the source path as a base, that would resolve to `main/java/my/package/config.json`, so going up once with `..` would not be enough. You could try putting the resource in the same package as the source file. i.e `main/resources/my/package/config.json`. – Jorn Vernee Sep 29 '16 at 13:56
  • I'm not sure why the source path would take the class's location as a base. There's no reference to the class location anywhere in reading the config file. It doesn't. I would expect it to take `src/main/java`, but apparently that's not what it does either. I did try putting the resource in the same location as the source file, it wasn't found there either. – Jorn Sep 29 '16 at 14:13
  • Ok, I thought maybe it took `SOURCE_PATH` as the path to the source file that was currently being processed. – Jorn Vernee Sep 29 '16 at 14:17
  • I did find [the doc](https://docs.oracle.com/javase/8/docs/api/javax/annotation/processing/Filer.html) for this that mentions _"A relative name is a non-null, non-empty sequence of path segments separated by '/'; '.' and '..' are invalid path segments"_ So that explains why the use of `..` does not work. – Jorn Vernee Sep 29 '16 at 14:21
  • I know - I only tried the relative path as a workaround. The question is, why is the contents of the resources folder not on the source path? And, why are the files that are on the source path, still not being found? – Jorn Sep 29 '16 at 14:22
  • The path should be "/config.json" but the resource must be on the class path of the annotation processor. So first the classes must be compiled (resource under target classes directory) and the classpath of the processor must be correct and then processing? Do those annotated classes need the config.json? – Joop Eggen Sep 30 '16 at 07:44

5 Answers5

7

I'd expect that the stuff from src/main/resources gets copied to target/classes during the build (prior to annotation processing). In that case you can open them like this:

ProcessingEnvironment pe = ...;
FileObject fileObject = pe.getFiler()
    .getResource( StandardLocation.CLASS_OUTPUT, "", "config.json" );
InputStream jsonStream = fileObject.openInputStream();
Gunnar
  • 18,095
  • 1
  • 53
  • 73
4

I've looked at this with one of the Project Lombok developers. If anyone knows annotation processing, it's them ;)

Our conclusion was, that the JavacFileManager that handles the request internally, does not have a path to resolve StandardLocation.SOURCE_PATH to. We're not sure, but it might be related to building with Gradle.

Jorn
  • 20,612
  • 18
  • 79
  • 126
1

I had the same problem and was searching for solution for a while and found this cool hack that does the trick for Android

And below you can see my solution from pure Java/Kotlin project

fun ProcessingEnvironment.getResourcesDirectory(): File {
val dummySourceFile = filer.createSourceFile("dummy" + System.currentTimeMillis())
var dummySourceFilePath = dummySourceFile.toUri().toString()

if (dummySourceFilePath.startsWith("file:")) {
    if (!dummySourceFilePath.startsWith("file://")) {
        dummySourceFilePath = "file://" + dummySourceFilePath.substring("file:".length)
    }
} else {
    dummySourceFilePath = "file://$dummySourceFilePath"
}

val cleanURI = URI(dummySourceFilePath)

val dummyFile = File(cleanURI)

val projectRoot = dummyFile.parentFile.parentFile.parentFile.parentFile.parentFile

return File(projectRoot.absolutePath + "/resources")
}
Andranik
  • 2,729
  • 1
  • 29
  • 45
  • The use of .parentFile.parentFile ... requires a specific package depth - and will not work for projects that are using a different number of "." in their package names. – Michael Conrad Oct 30 '20 at 12:21
0

Following function works for me with annotation processor being triggered by gradle, it's not the pretties one but works:

private fun resolveApplicationPropertiesFile(): File {
    val projectRoot = Path.of(processingEnv.filer.getResource(StandardLocation.CLASS_OUTPUT, "", "doesntmatter")
            .toUri())
            .parent
            .parent
            .parent
            .parent
            .parent
            .parent
    val properties = Path.of(projectRoot.toString(), "src", "main", "resources", "application.yml")
    return properties.toFile()
}

where processingEnv is a member of AbstractProcessor

Adrian Kapuscinski
  • 1,124
  • 12
  • 13
-2
  • If your element is instance of TypeElement,then you can use these code to find your source code

    FileObject fileObject = processingEnv.getFiler().getResource( StandardLocation.SOURCE_PATH, element.getEnclosingElement().toString(), element.getSimpleName() + ".java");

    1. element.getEnclosingElement() is your class package, eg: com.fool
    2. element.getSimpleName() is your class name, eg: Person
  • then you can print them: CharSequence content = fileObject.getCharContent(true);

xinkun
  • 27
  • 5