Java tutorial
/******************************************************************************* * Copyright (c) 2013, 2015 IBH SYSTEMS GmbH and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBH SYSTEMS GmbH - initial API and implementation *******************************************************************************/ package org.eclipse.tycho.extras.docbundle; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Dependency; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import org.apache.maven.toolchain.ToolchainManager; import org.codehaus.plexus.util.FileUtils; import org.eclipse.tycho.core.osgitools.BundleReader; /** * Create the javadoc based API reference for this bundle <br/> * This mojo creates the javadoc documentation by calling the javadoc application from the command * line. In addition it creates a ready to include toc-xml file for the Eclipse Help system. <br/> * The sources for creating the javadoc are generated automatically based on the dependency that * this project has. As dependency you can specify any other maven project, for example the feature * project that references you other bundles. Included features will be added to the list. * * @since 0.20.0 */ @Mojo(name = "javadoc", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, threadSafe = false) public class JavadocMojo extends AbstractMojo { /** * The directory where the javadoc content will be generated * */ @Parameter(property = "outputDirectory", defaultValue = "${project.build.directory}/reference/api", required = true) private File outputDirectory; /** * The base output directory */ @Parameter(property = "basedir", required = true, readonly = true) private File basedir; /** * The build directory where temporary build files will be placed */ @Parameter(property = "project.build.directory", required = true) private File buildDirectory; /** * An option to clean out the whole outputDirectory first. */ @Parameter(property = "cleanFirst", defaultValue = "true") private boolean cleanFirst; @Component private ToolchainManager toolchainManager; @Parameter(property = "session", required = true, readonly = true) private MavenSession session; @Parameter(property = "reactorProjects", required = true, readonly = true) protected List<MavenProject> reactorProjects; /** * The scopes that the dependencies must have in order to be included */ @Parameter(property = "scopes", defaultValue = "compile,provided") private Set<String> scopes = new HashSet<String>(); /** * Maven module types that will be used to include the source */ @Parameter(property = "sourceTypes", defaultValue = "eclipse-plugin") private Set<String> sourceTypes = new HashSet<String>(); /** * Options for calling the javadoc application. Possible options are (all options are optional): * <ul> * <li><tt>ignoreError</tt>, specifies if errors calling javadoc should be ignored</li> * <li><tt>doclet</tt>, used as javadoc <tt>-doclet</tt> parameter</li> * <li><tt>docletArtifacts</tt>, dependencies will be resovled and added as <tt>-docletpath</tt> * parameter</li> * <li><tt>encoding</tt>, used as javadoc <tt>-encoding</tt> parameter (default: * <tt>${project.build.sourceEncoding}</tt></li> * <li><tt>additionalArguments</tt>, a list of additional arguments passed to javadoc</li> * </ul> * Example configuration: * * <pre> * <configuration> * <javadocOptions> * <ignoreError>false</ignoreError> * <encoding>UTF-8</encoding> * <doclet>foo.bar.MyDoclet</doclet> * <docletArtifacts> * <docletArtifact> * <groupId>foo.bar</groupId> * <artifactId>foo.bar.mydocletartifact</artifactId> * <version>1.0</version> * </docletArtifact> * </docletArtifacts> * <additionalArguments> * <additionalArgument>-windowtitle "The Window Title"</additionalArgument> * <additionalArgument>-nosince</additionalArgument> * </additionalArguments> * </javadocOptions> * </configuration> * </pre> */ @Parameter(property = "javadocOptions") private JavadocOptions javadocOptions = new JavadocOptions(); /** * Options for creating the toc files. */ @Parameter(property = "tocOptions") private TocOptions tocOptions = new TocOptions(); /** * Set this property to true to skip the generation of the Eclipse TOC files. */ @Parameter(property = "skipTocGen", defaultValue = "false") private boolean skipTocGen = false; /** * The output location of the toc file.<br/> * This file will be overwritten. */ @Parameter(property = "tocFile", defaultValue = "${project.build.directory}/tocjavadoc.xml") private File tocFile; @Parameter(property = "project.build.sourceEncoding", readonly = true) private String projectBuildSourceEncoding; @Component private BundleReader bundleReader; @Component private DocletArtifactsResolver docletArtifactsResolver; public void setTocOptions(TocOptions tocOptions) { this.tocOptions = tocOptions; } public void setSourceTypes(final Set<String> sourceTypes) { this.sourceTypes = sourceTypes; } public void setScopes(final Set<String> scopes) { this.scopes = scopes; } public void setJavadocOptions(final JavadocOptions javadocOptions) { this.javadocOptions = javadocOptions; } public void execute() throws MojoExecutionException { getLog().info("Scopes: " + this.scopes); getLog().info("Output directory: " + this.outputDirectory); getLog().info("BaseDir: " + this.basedir); if (this.cleanFirst) { getLog().info("Cleaning up first"); cleanUp(); } // if no encoding is set, fall back to ${project.build.sourceEncoding} if (javadocOptions.getEncoding() == null) { javadocOptions.setEncoding(projectBuildSourceEncoding); } final JavadocRunner runner = new JavadocRunner(); runner.setLog(getLog()); runner.setOutput(this.outputDirectory); runner.setBuildDirectory(this.buildDirectory); runner.setToolchainManager(this.toolchainManager); runner.setSession(this.session); runner.setDocletArtifactsResolver(docletArtifactsResolver); final GatherManifestVisitor gmv = new GatherManifestVisitor(); visitProjects(this.session.getCurrentProject().getDependencies(), this.scopes, gmv); final GatherSourcesVisitor gsv = new GatherSourcesVisitor(); visitProjects(this.session.getCurrentProject().getDependencies(), this.scopes, gsv); getLog().info(String.format("%s source folders", gsv.getSourceFolders().size())); for (final File file : gsv.getSourceFolders()) { getLog().info("Source folder: " + file); } // TODO: use tycho's approach of gathering the classpath final GatherClasspathVisitor gcv = new GatherClasspathVisitor(); visitProjects(this.session.getCurrentProject().getDependencies(), this.scopes, gcv); final Collection<String> cp = gcv.getClassPath(); getLog().info(String.format("%s classpath deps", cp.size())); for (final String ele : cp) { getLog().info("CP: " + ele); } runner.setBundleReader(this.bundleReader); runner.setOptions(this.javadocOptions); runner.setManifestFiles(gmv.getManifestFiles()); runner.setSourceFolders(gsv.getSourceFolders()); runner.setClassPath(cp); // Setup toc writer final TocWriter tocWriter = new TocWriter(); tocWriter.setOptions(this.tocOptions); tocWriter.setJavadocDir(this.outputDirectory); tocWriter.setBasedir(this.basedir); tocWriter.setLog(getLog()); try { runner.run(); if (!skipTocGen) { tocWriter.writeTo(this.tocFile); } } catch (final Exception e) { throw new MojoExecutionException("Failed to run javadoc", e); } } private void cleanUp() throws MojoExecutionException { if (!this.outputDirectory.exists()) { return; } try { FileUtils.deleteDirectory(this.outputDirectory); } catch (final IOException e) { throw new MojoExecutionException("Failed to clean output directory", e); } } @SuppressWarnings("unchecked") private void visitProjects(final List<?> dependencies, final Set<String> scopes, final ProjectVisitor visitor) throws MojoExecutionException { for (final Dependency dep : (List<Dependency>) dependencies) { getLog().debug("Dependency: " + dep + " / scope=" + dep.getScope()); final String scope = dep.getScope(); if (scopes.contains(scope)) { visitDeps(dep, visitor, scopes); } } } private interface ProjectVisitor { public void visit(MavenProject project) throws MojoExecutionException; } private class GatherSourcesVisitor implements ProjectVisitor { private final Set<File> sourceFolders = new HashSet<File>(); public void visit(final MavenProject project) { if (JavadocMojo.this.sourceTypes.contains(project.getPackaging())) { for (final String root : (Collection<String>) project.getCompileSourceRoots()) { getLog().debug("\tAdding source root: " + root); final File rootFile = new File(root); if (rootFile.isDirectory()) { this.sourceFolders.add(rootFile); } } } } public Set<File> getSourceFolders() { return this.sourceFolders; } } private class GatherManifestVisitor implements ProjectVisitor { private final Set<File> manifestFiles = new HashSet<File>(); public void visit(final MavenProject project) { if (JavadocMojo.this.sourceTypes.contains(project.getPackaging())) { this.manifestFiles.add(new File(project.getBasedir(), "META-INF/MANIFEST.MF")); } } public Set<File> getManifestFiles() { return manifestFiles; } } private class GatherClasspathVisitor implements ProjectVisitor { private final Set<String> classPath = new HashSet<String>(); public void visit(final MavenProject project) throws MojoExecutionException { for (final Dependency dep : (List<Dependency>) project.getDependencies()) { if (dep.getSystemPath() != null) { this.classPath.add(dep.getSystemPath()); } } } public Set<String> getClassPath() { return this.classPath; } } private void visitDeps(final Dependency dep, final ProjectVisitor visitor, final Set<String> scopes) throws MojoExecutionException { final MavenProject project = findProject(dep.getGroupId(), dep.getArtifactId()); if (project == null) { getLog().info(String.format("Did not find project %s in reactor", dep)); return; } getLog().debug("Adding sources from: " + project); visitor.visit(project); getLog().debug("Scanning dependencies: " + project.getDependencies().size()); visitProjects(project.getDependencies(), scopes, visitor); getLog().debug("Done processing: " + project); } private MavenProject findProject(final String groupId, final String artifactId) { getLog().debug(String.format("findProject - groupId: %s, artifactId: %s", groupId, artifactId)); for (final MavenProject p : this.reactorProjects) { if (!p.getGroupId().equals(groupId)) { continue; } if (!p.getArtifactId().equals(artifactId)) { continue; } return p; } return null; } }