mavenmaven-plugindocumentation-generationmaven-plugin-development

How to parse a java class located in an external library?


Im currently developing a plugin which can create a markdown table from an enum. For example if i have this enum:

enum MyEnum {
  /**
   * Documentation for 1
   */
  MYCONSTANT1(1),
  MYCONSTANT2(2);
  // rest is omitted
}

The plugin will generate this table:

Code Name Description
1 MYCONSTANT1 Documentation for 1
2 MYCONSTANT2

The plugin currently requires the path of the java file to operate and it works fine.

My problem is that if the enum is located in an external artifact / library, then there is no way to make this plugin work because they are stored in a jar.

My senses tell me that there should be a reliable way to get the java file using the maven plugin api or some other library but the lack of documentation makes this really hard for me.

How should i approach this problem?

Is there a way to get the source file reliably by the class reference, for example com.example.MyEnum?

I tried reading the documentation but i couldnt find much information about the problem. Also i tried using chatgpt but it hallucinated way too much code and since this problem is very specific it couldnt make up an answer that i can work with.

Edit

I realized its not that simple just to get the compiled source file and decompile it because it doesnt contain the documentation. The best solution would be to download the sources of the artifact and then parse the class file just like i did when the source is in the project's files.


Solution

  • I dove deeper into the maven plugin development documentation and i found that you can use the RepositorySystem component to resolve artifact sources.

    ArtifactRequest artifactRequest = new ArtifactRequest(artifact, project.getRemoteProjectRepositories(), null);
    ArtifactResult artifactResult = repositorySystem.resolveArtifact(session.getRepositorySession(), artifactRequest)
    

    This way you can define the artifact in the plugin's dependencies that contains the source of the class and resolve it during runtime. From there you just need to load the jar file and read its sources.

    Here is a full example of getting a source fle by reference:

    private Optional<File> getSourceFile() {
      return execution.getPlugin()
                      .getDependencies()
                      .stream()
                      .map(dependency -> findSourceFileInDependency(sourceClass, dependency))
                      .filter(Objects::nonNull)
                      .findFirst();
    }
    
    private File findSourceFileInDependency(String sourceClass, Dependency dependency) {
      String sourceFileName = sourceClass.replace(".", "/") + ".java";
      DefaultArtifact artifact = new DefaultArtifact(dependency.getGroupId(), dependency.getArtifactId(), "sources", "jar", dependency.getVersion());
      ArtifactRequest artifactRequest = new ArtifactRequest(artifact, project.getRemoteProjectRepositories(), null);
    
      try {
        File artifactFile = repositorySystem.resolveArtifact(session.getRepositorySession(), artifactRequest)
                                            .getArtifact()
                                            .getFile();
        try (JarFile jarFile = new JarFile(artifactFile);
             InputStream sourceFileIs = jarFile.getInputStream(jarFile.getEntry(sourceFileName))) {
          File sourceFile = File.createTempFile(String.format("%s_%s", UUID.randomUUID(), sourceFileName), ".java");
          sourceFile.deleteOnExit();
    
          Files.copy(sourceFileIs, sourceFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
    
          return sourceFile;
        } catch (IOException | NullPointerException e) {
        }
      } catch (ArtifactResolutionException e) {
      }
    
      return null;
    }