The craftsman may be late, but he is never absent. Finally, he decided to share his cloud notes

The reason

You’ve probably used the Javadoc command or IDE wrapper command to generate Java API doc documents, but have you ever thought about how the Javadoc command parses file generation? Javadoc is actually just an executable in the JDK directory, but the executable is a wrapper based on the JDK’s Tools.jar, which means that javadoc is implemented in Tools.jar.

Many times we may have some bizarre requirements, such as obtaining Java document comments for processing. How do we parse Java files to obtain these comment information? You may have initially thought about using regular matching, but there are compatibility issues with this scheme. Or, you might consider using third-party libraries to parse Java source files, but many of these libraries are for The Java source, not the comments in the source code. So a great solution is to customize the Doclet and use Javadoc operations.

Program verification

Since this solution relies on Javadoc and Doclet, let’s take a look at the documentation for a technical evaluation. For details, see the Oracle documentation:

  • javadoc doclet
  • javadoc tools

We can find that, in fact, we only need to customize a Doclet class. As for how to define the Doclet class, the document has been written in detail, and the specific code fragment is given, we can directly move over for verification, the code is as follows:

public class CustomerDoclet extends Doclet {
  public static boolean start(RootDoc root) {
    ClassDoc[] classes = root.classes();
    // Annotate the document information, you can parse the organization as you like, depending on your needs
    return true; }}public static void main(String[] args) {
     String[] docArgs =
         new String[] {
           "-doclet", CustomerDoclet.class.getName(), "/home/yan/test/cn/test/JavaSource.java"
         };
     com.sun.tools.javadoc.Main.execute(docArgs);
  }
Copy the code

Simply run the above code snippet to customize the Javadoc output parsing. Run around and find it’s okay, so let’s get started.

Implement a Gradle plug-in for javadoc customization

For the sake of simplicity and simplicity, we are going to implement a plugin that checks whether the Android, androidLibrary, Java, and javaLibrary code source files contain Javadoc@author. Gradle-javadoc-checker. For details, see github.com/yanbober/gr… To obtain.

Note: This section requires you to be familiar with Gradle plugin development, so it is recommended that you read this section first.

Add the dependent

dependencies {
    compile gradleApi()
    compile 'com. Android. Tools. Build: gradle: 3.1.0'
    / / tools. The dependence on the jar
    compile files(org.gradle.internal.jvm.Jvm.current().toolsJar)
}
Copy the code

Write a custom Javadoc judge @author tool

public class JavaDocReader {
    private static RootDoc root;
	// Customize the doclet
    public static class CustomerDoclet {
        public static boolean start(RootDoc root) {
            JavaDocReader.root = root;
            return true; }}// Javadoc encapsulation in tools.jar
    public static RootDoc process(String[] extraArges) {
        List<String> argsOrderList = new ArrayList<>();
        argsOrderList.add("-doclet");
        argsOrderList.add(CustomerDoclet.class.getName());
        argsOrderList.addAll(Arrays.asList(extraArges));
        String[] args = argsOrderList.toArray(new String[argsOrderList.size()]);
        System.out.println(args);
        Main.execute(args);
        return root;
    }

    // Javadoc encapsulation in tools.jar
    public static void process(List
       
         sourcePaths, List
        
          javapackages, List
         
           excludePackages, String outputDir)
         
        
        throws Exception {
        String paths = list2formatString(sourcePaths, ";");
        String includes = list2formatString(javapackages, ":");
        String excludes = list2formatString(excludePackages, ":");

        List<String> argsOrderList = new ArrayList<>();
        argsOrderList.add("-doclet");
        argsOrderList.add(CustomerDoclet.class.getName());

        if(paths ! =null && paths.length() > 0) {
            argsOrderList.add("-sourcepath");
            argsOrderList.add(paths);
        }

        argsOrderList.add("-encoding");
        argsOrderList.add("utf-8");
        argsOrderList.add("-verbose");

        if(includes ! =null && includes.length() > 0) {
            argsOrderList.add("-subpackages");
            argsOrderList.add(includes);
        }

        if(excludes ! =null && excludes.length() > 0) {
            argsOrderList.add("-exclude");
            argsOrderList.add(excludes);
        }

        String[] args = argsOrderList.toArray(new String[argsOrderList.size()]);
        System.out.println(Arrays.toString(args));
		// Run the javadoc command in tools.jar
        Main.execute(args);

        File file = new File(outputDir);
        if(! file.exists()) { file.mkdirs(); } file =new File(file, new Date().toString() + ".txt");
        FileOutputStream outputStream = new FileOutputStream(file);
		// Determine if each top-level Java class has an @author writer, and write a file record if it does not
        ClassDoc[] classes = root.classes();
        if(classes ! =null) {
            for (int i = 0; i < classes.length; ++i) {
                if (classes[i].containingClass() == null && classes[i].isPublic()) {
                    Tag[] authorTags = classes[i].tags("author");
                    if (authorTags == null || authorTags.length == 0) {
                        String filename = classes[i].position().file().getAbsolutePath();
                        outputStream.write((filename+"\r\n").getBytes());
                    }
                }
            }
        }
        root = null;
        outputStream.flush();
        outputStream.close();
    }

    private static String list2formatString(List<String> srcs, String div) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int index=0; index<srcs.size(); index++) {
            if (index > 0) {
                stringBuilder.append(div);
            }
            stringBuilder.append(srcs.get(index));
        }
        returnstringBuilder.toString(); }}Copy the code

With the Javadoc custom utility classes in place, you can write gradle custom Tasks.

Write a custom Gradle Task to check

/ / groovy
class JavaDocCheckerTask extends DefaultTask {
    // Customize input for task
    @Input
    List<String> includePackages

    @Input
    List<String> excludePackages

    @Input
    List<String> sourcePaths

    // Customize the output of the task
    @OutputDirectory
    String outputDir

    // Customize the execution logic of the task
    @TaskAction
    void checker() {
        if (sourcePaths == null || sourcePaths.size() == 0) {
            throw new GradleScriptException("JavaDocCheckerTask sourcePaths params can't be null or empty!")}if (outputDir == null || outputDir.length() == 0) {
            throw new GradleScriptException("JavaDocCheckerTask outputDir params can't be null or empty!")}//task Executes javadoc command operations based on output parameters
        JavaDocReader.process(sourcePaths, includePackages, excludePackages, outputDir)
    }
}
Copy the code

With a custom Gradle Task for Javadoc operations, it’s time to plug in the plug-in.

Add custom tasks to the build project

Define the extension parameters for the plug-in:

class CheckerExtension {
    public static final String NAME = "javadocChecker"

    List<String> includePackages

    List<String> excludePackages

    List<String> sourcePaths

    String outputDirectory
}
Copy the code

Combine extended parameters with task:

class JavaDocCheckerPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        // Add a custom extension
        project.extensions.create(CheckerExtension.NAME, CheckerExtension)
        // Add custom tasks to project
        project.tasks.create("javaDocChecker", JavaDocCheckerTask)

        // Apply to Java, AndroidLibrary and AndroidApplication
        JavaPluginConvention java = null
        BaseExtension android = null
        if (project.plugins.hasPlugin(AppPlugin)) {
            android = project.extensions.getByType(AppExtension)
        } else if(project.plugins.hasPlugin(LibraryPlugin)) {
            android = project.extensions.getByType(LibraryExtension)
        } else if (project.plugins.hasPlugin(JavaPlugin)) {
            java = project.convention.getPlugin(JavaPluginConvention)
        }

        if (java == null && android == null) {
            throw new GradleException("it's a not support plugin type!")
        }

        project.afterEvaluate {
            afterEvaluateInner(project, java, android)
        }
    }

    private void afterEvaluateInner(Project project, JavaPluginConvention java, BaseExtension android) {
        if(java ! =null) {
            // The Java plug-in does the Java sourceSets processing
            processJava(project, java)
        } else if(android ! =null) {
            // The Android plugin does the Android sourceSets processing
            processAndroid(project, android)
        }
    }

    private void processJava(Project project, JavaPluginConvention java) {
        List<String> sources = new ArrayList<>()
        // Get the SRC of Java sourceSets mainSourceSet mainSourceSet = java.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) mainSourceSet.allJava.srcDirs.each {  sources.add(it.absolutePath) } assignedTask(project, sources) }private void processAndroid(Project project, BaseExtension android) {
        List<String> sources = new ArrayList<>()
        // Check the android sourceSets main SRC
        AndroidSourceSet mainSourceSet = android.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
        mainSourceSet.java.srcDirs.each {
            sources.add(it.absolutePath)
        }

        assignedTask(project, sources)
    }

    // Assign the custom attributes of the plug-in extension to the input and output of the custom task
    private void assignedTask(Project project, List<String> sources) {
        def checker = project[CheckerExtension.NAME]
        if (checker == null) {
            return
        }

        project.getTasksByName("javaDocChecker".false).each {
            it.configure {
                includePackages = checker.includePackages == null ? [] : checker.includePackages
                excludePackages = checker.excludePackages == null ? [] : checker.excludePackages
                sourcePaths = sources
                outputDir = checker.outputDirectory
            }
        }
    }
}
Copy the code

The core body of this plug-in is developed and ready to use. This is a complete small project to solve practical problems through custom Javadoc output. You can access the project source code for research or customize your own operations. The full plugin source can be found at github.com/yanbober/gr… To obtain.

conclusion

This article gives an implementation idea, you can find that Doclet is simply a giant, for the Java Doc document operation only you can think of, nothing he can not do. I hope it inspires you.

Artisans if the water without permission is strictly prohibited to be reproduced, please respect the author’s work.