/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.gradle.plugins.app.javadoc.builder; import com.liferay.gradle.util.FileUtil; import com.liferay.gradle.util.GradleUtil; import com.liferay.gradle.util.Validator; import groovy.lang.Closure; import java.io.File; import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.Callable; import org.gradle.api.Action; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileTree; import org.gradle.api.file.SourceDirectorySet; import org.gradle.api.internal.ConventionMapping; import org.gradle.api.invocation.Gradle; import org.gradle.api.logging.Logger; import org.gradle.api.plugins.BasePlugin; import org.gradle.api.plugins.JavaBasePlugin; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.ReportingBasePlugin; import org.gradle.api.reporting.ReportingExtension; import org.gradle.api.specs.Spec; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.javadoc.Javadoc; import org.gradle.external.javadoc.CoreJavadocOptions; import org.gradle.external.javadoc.StandardJavadocDocletOptions; /** * @author Andrea Di Giorgi */ public class AppJavadocBuilderPlugin implements Plugin<Project> { public static final String APP_JAVADOC_TASK_NAME = "appJavadoc"; public static final String JAR_APP_JAVADOC_TASK_NAME = "jarAppJavadoc"; public static final String PLUGIN_NAME = "appJavadocBuilder"; @Override public void apply(Project project) { GradleUtil.applyPlugin(project, BasePlugin.class); GradleUtil.applyPlugin(project, ReportingBasePlugin.class); final AppJavadocBuilderExtension appJavadocBuilderExtension = GradleUtil.addExtension( project, PLUGIN_NAME, AppJavadocBuilderExtension.class); ReportingExtension reportingExtension = GradleUtil.getExtension( project, ReportingExtension.class); final Javadoc appJavadocTask = _addTaskAppJavadoc( project, reportingExtension); _addTaskJarAppJavadoc(appJavadocTask); Gradle gradle = project.getGradle(); gradle.afterProject( new Closure<Void>(project) { @SuppressWarnings("unused") public void doCall(Project subproject) { Set<Project> subprojects = appJavadocBuilderExtension.getSubprojects(); if (subprojects.contains(subproject)) { _configureTaskAppJavadoc( appJavadocTask, appJavadocBuilderExtension, subproject); } } }); project.afterEvaluate( new Action<Project>() { @Override public void execute(Project project) { _configureTaskAppJavadoc( appJavadocTask, appJavadocBuilderExtension); } }); } private Javadoc _addTaskAppJavadoc( Project project, final ReportingExtension reportingExtension) { final Javadoc javadoc = GradleUtil.addTask( project, APP_JAVADOC_TASK_NAME, Javadoc.class); javadoc.setDescription( "Generates Javadoc API documentation for the app."); javadoc.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP); ConventionMapping conventionMapping = javadoc.getConventionMapping(); conventionMapping.map( "destinationDir", new Callable<File>() { @Override public File call() throws Exception { Project project = javadoc.getProject(); return new File(project.getBuildDir(), "docs/javadoc"); } }); conventionMapping.map( "title", new Callable<String>() { @Override public String call() throws Exception { return reportingExtension.getApiDocTitle(); } }); StandardJavadocDocletOptions standardJavadocDocletOptions = (StandardJavadocDocletOptions)javadoc.getOptions(); standardJavadocDocletOptions.setEncoding(StandardCharsets.UTF_8.name()); // Replace default map to sort groups alphabetically standardJavadocDocletOptions.setGroups( new TreeMap<String, List<String>>()); return javadoc; } private Jar _addTaskJarAppJavadoc(Javadoc javadoc) { Jar jar = GradleUtil.addTask( javadoc.getProject(), JAR_APP_JAVADOC_TASK_NAME, Jar.class); jar.from(javadoc); jar.setClassifier("javadoc"); jar.setDescription( "Assembles a jar archive containing the Javadoc files for this " + "app."); jar.setGroup(BasePlugin.BUILD_GROUP); return jar; } private void _configureTaskAppJavadoc( Javadoc javadoc, AppJavadocBuilderExtension appJavadocBuilderExtension) { CoreJavadocOptions coreJavadocOptions = (CoreJavadocOptions)javadoc.getOptions(); if (appJavadocBuilderExtension.isDoclintDisabled()) { coreJavadocOptions.addStringOption("Xdoclint:none", "-quiet"); } } private void _configureTaskAppJavadoc( Javadoc javadoc, AppJavadocBuilderExtension appJavadocBuilderExtension, Project subproject) { Logger logger = javadoc.getLogger(); TaskContainer taskContainer = subproject.getTasks(); Task task = taskContainer.findByName(JavaPlugin.JAVADOC_TASK_NAME); if (!(task instanceof Javadoc)) { if (logger.isInfoEnabled()) { logger.info( "Excluding {} from {} because it is not a valid Java " + "project", subproject, javadoc, JavaPlugin.JAVADOC_TASK_NAME); } return; } Spec<Project> spec = appJavadocBuilderExtension.getOnlyIf(); if (!spec.isSatisfiedBy(subproject)) { if (logger.isInfoEnabled()) { logger.info( "Explicitly excluding {} from {}", subproject, javadoc); } return; } Javadoc subprojectJavadoc = (Javadoc)task; javadoc.dependsOn(subprojectJavadoc); FileCollection classpath = javadoc.getClasspath(); javadoc.setClasspath(classpath.plus(subprojectJavadoc.getClasspath())); FileTree subprojectSource = subprojectJavadoc.getSource(); javadoc.source(subprojectSource); StandardJavadocDocletOptions standardJavadocDocletOptions = (StandardJavadocDocletOptions)javadoc.getOptions(); if (appJavadocBuilderExtension.isCopyTags()) { StandardJavadocDocletOptions subprojectStandardJavadocDocletOptions = (StandardJavadocDocletOptions) subprojectJavadoc.getOptions(); standardJavadocDocletOptions.tags( subprojectStandardJavadocDocletOptions.getTags()); } if (appJavadocBuilderExtension.isGroupPackages()) { SourceSet sourceSet = GradleUtil.getSourceSet( subproject, SourceSet.MAIN_SOURCE_SET_NAME); SourceDirectorySet sourceDirectorySet = sourceSet.getAllJava(); Closure<String> closure = appJavadocBuilderExtension.getGroupNameClosure(); String groupName = closure.call(subproject); Set<String> packageNames = _getPackageNames( subprojectSource, sourceDirectorySet.getSrcDirs()); if (Validator.isNotNull(groupName) && !packageNames.isEmpty()) { standardJavadocDocletOptions.group( groupName, packageNames.toArray(new String[packageNames.size()])); } } } private String _getPackageName(File file, Set<File> srcDirs) { File dir = null; for (File srcDir : srcDirs) { if (FileUtil.isChild(file, srcDir)) { dir = srcDir; break; } } if (dir == null) { return null; } String relativePath = FileUtil.relativize(file, dir); relativePath = relativePath.substring( 0, relativePath.lastIndexOf(File.separatorChar)); return relativePath.replace(File.separatorChar, '.'); } private Set<String> _getPackageNames( Iterable<File> files, Set<File> srcDirs) { Set<String> packageNames = new HashSet<>(); for (File file : files) { String packageName = _getPackageName(file, srcDirs); if (Validator.isNotNull(packageName)) { packageNames.add(packageName); } } return packageNames; } }