| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.codehaus.groovy.ant; |
| |
| import org.apache.groovy.util.SystemUtil; |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.DirectoryScanner; |
| import org.apache.tools.ant.Task; |
| import org.apache.tools.ant.types.DirSet; |
| import org.apache.tools.ant.types.Path; |
| import org.apache.tools.ant.types.PatternSet; |
| import org.codehaus.groovy.runtime.ResourceGroovyMethods; |
| import org.codehaus.groovy.tools.groovydoc.ClasspathResourceManager; |
| import org.codehaus.groovy.tools.groovydoc.FileOutputTool; |
| import org.codehaus.groovy.tools.groovydoc.GroovyDocTool; |
| import org.codehaus.groovy.tools.groovydoc.LinkArgument; |
| import org.codehaus.groovy.tools.groovydoc.gstringTemplates.GroovyDocTemplateInfo; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.StringTokenizer; |
| |
| /** |
| * Access to the GroovyDoc tool from Ant. |
| */ |
| public class Groovydoc extends Task { |
| private final LoggingHelper log = new LoggingHelper(this); |
| |
| private Path sourcePath; |
| private File destDir; |
| private final List<String> packageNames; |
| private final List<String> excludePackageNames; |
| private String windowTitle = "Groovy Documentation"; |
| private String docTitle = "Groovy Documentation"; |
| private String footer = "Groovy Documentation"; |
| private String header = "Groovy Documentation"; |
| private Boolean privateScope; |
| private Boolean protectedScope; |
| private Boolean packageScope; |
| private Boolean publicScope; |
| private Boolean author; |
| private Boolean processScripts; |
| private Boolean includeMainForScripts; |
| private boolean useDefaultExcludes; |
| private boolean includeNoSourcePackages; |
| private Boolean noTimestamp; |
| private Boolean noVersionStamp; |
| private final List<DirSet> packageSets; |
| private final List<String> sourceFilesToDoc; |
| private final List<LinkArgument> links = new ArrayList<>(); |
| private File overviewFile; |
| private File styleSheetFile; |
| // dev note: update javadoc comment for #setExtensions(String) if updating below |
| private String extensions = ".java:.groovy:.gv:.gvy:.gsh"; |
| |
| private String charset; |
| private String fileEncoding; |
| |
| public Groovydoc() { |
| packageNames = new ArrayList<>(); |
| excludePackageNames = new ArrayList<>(); |
| packageSets = new ArrayList<DirSet>(); |
| sourceFilesToDoc = new ArrayList<>(); |
| privateScope = false; |
| protectedScope = false; |
| publicScope = false; |
| packageScope = false; |
| useDefaultExcludes = true; |
| includeNoSourcePackages = false; |
| author = true; |
| processScripts = true; |
| includeMainForScripts = true; |
| noTimestamp = false; |
| noVersionStamp = false; |
| } |
| |
| /** |
| * Specify where to find source file |
| * |
| * @param src a Path instance containing the various source directories. |
| */ |
| public void setSourcepath(Path src) { |
| if (sourcePath == null) { |
| sourcePath = src; |
| } else { |
| sourcePath.append(src); |
| } |
| } |
| |
| /** |
| * Set the directory where the Groovydoc output will be generated. |
| * |
| * @param dir the destination directory. |
| */ |
| public void setDestdir(File dir) { |
| destDir = dir; |
| // todo: maybe tell groovydoc to use file output |
| } |
| |
| /** |
| * If set to false, author will not be displayed. |
| * Currently not used. |
| * |
| * @param author new value |
| */ |
| public void setAuthor(boolean author) { |
| this.author = author; |
| } |
| |
| /** |
| * If set to true, hidden timestamp will not appear within generated HTML. |
| * |
| * @param noTimestamp new value |
| */ |
| public void setNoTimestamp(boolean noTimestamp) { |
| this.noTimestamp = noTimestamp; |
| } |
| |
| /** |
| * If set to true, hidden version stamp will not appear within generated HTML. |
| * |
| * @param noVersionStamp new value |
| */ |
| public void setNoVersionStamp(boolean noVersionStamp) { |
| this.noVersionStamp = noVersionStamp; |
| } |
| |
| /** |
| * If set to false, Scripts will not be processed. |
| * Defaults to true. |
| * |
| * @param processScripts new value |
| */ |
| public void setProcessScripts(boolean processScripts) { |
| this.processScripts = processScripts; |
| } |
| |
| /** |
| * If set to false, 'public static void main' method will not be displayed. |
| * Defaults to true. Ignored when not processing Scripts. |
| * |
| * @param includeMainForScripts new value |
| */ |
| public void setIncludeMainForScripts(boolean includeMainForScripts) { |
| this.includeMainForScripts = includeMainForScripts; |
| } |
| |
| /** |
| * A colon-separated list of filename extensions to look for when searching for files to process in a given directory. |
| * Default value: <code>.java:.groovy:.gv:.gvy:.gsh</code> |
| * |
| * @param extensions new value |
| */ |
| public void setExtensions(String extensions) { |
| this.extensions = extensions; |
| } |
| |
| /** |
| * Set the package names to be processed. |
| * |
| * @param packages a comma separated list of packages specs |
| * (may be wildcarded). |
| */ |
| public void setPackagenames(String packages) { |
| StringTokenizer tok = new StringTokenizer(packages, ","); |
| while (tok.hasMoreTokens()) { |
| String packageName = tok.nextToken(); |
| packageNames.add(packageName); |
| } |
| } |
| |
| public void setUse(boolean b) { |
| //ignore as 'use external file' irrelevant with groovydoc :-) |
| } |
| |
| /** |
| * Set the title to be placed in the HTML <title> tag of the |
| * generated documentation. |
| * |
| * @param title the window title to use. |
| */ |
| public void setWindowtitle(String title) { |
| windowTitle = title; |
| } |
| |
| /** |
| * Set the title for the overview page. |
| * |
| * @param htmlTitle the html to use for the title. |
| */ |
| public void setDoctitle(String htmlTitle) { |
| docTitle = htmlTitle; |
| } |
| |
| /** |
| * Specify the file containing the overview to be included in the generated documentation. |
| * |
| * @param file the overview file |
| */ |
| public void setOverview(File file) { |
| overviewFile = file; |
| } |
| |
| /** |
| * Indicates the access mode or scope of interest: one of public, protected, package, or private. |
| * Package scoped access is ignored for fields of Groovy classes where they correspond to properties. |
| * |
| * @param access one of public, protected, package, or private |
| */ |
| public void setAccess(String access) { |
| if ("public".equals(access)) publicScope = true; |
| else if ("protected".equals(access)) protectedScope = true; |
| else if ("package".equals(access)) packageScope = true; |
| else if ("private".equals(access)) privateScope = true; |
| } |
| |
| /** |
| * Indicate whether all classes and |
| * members are to be included in the scope processed. |
| * |
| * @param b true if scope is to be private level. |
| */ |
| public void setPrivate(boolean b) { |
| privateScope = b; |
| } |
| |
| /** |
| * Indicate whether only public classes and members are to be included in the scope processed. |
| * |
| * @param b true if scope only includes public level classes and members |
| */ |
| public void setPublic(boolean b) { |
| publicScope = b; |
| } |
| |
| /** |
| * Indicate whether only protected and public classes and members are to be included in the scope processed. |
| * |
| * @param b true if scope includes protected level classes and members |
| */ |
| public void setProtected(boolean b) { |
| protectedScope = b; |
| } |
| |
| /** |
| * Indicate whether only package, protected and public classes and members are to be included in the scope processed. |
| * Package scoped access is ignored for fields of Groovy classes where they correspond to properties. |
| * |
| * @param b true if scope includes package level classes and members |
| */ |
| public void setPackage(boolean b) { |
| packageScope = b; |
| } |
| |
| /** |
| * Set the footer to place at the bottom of each generated html page. |
| * |
| * @param footer the footer value |
| */ |
| public void setFooter(String footer) { |
| this.footer = footer; |
| } |
| |
| /** |
| * Specifies the header text to be placed at the top of each output file. |
| * The header will be placed to the right of the upper navigation bar. |
| * It may contain HTML tags and white space, though if it does, it must |
| * be enclosed in quotes. Any internal quotation marks within the header |
| * may have to be escaped. |
| * |
| * @param header the header value |
| */ |
| public void setHeader(String header) { |
| this.header = header; |
| } |
| |
| /** |
| * Specifies the charset to be used in the templates, i.e. the value output within: |
| * <meta http-equiv="Content-Type" content="text/html; charset=<em>charset</em>">. |
| * |
| * @param charset the charset value |
| */ |
| public void setCharset(String charset) { |
| this.charset = charset; |
| } |
| |
| /** |
| * Specifies the file encoding to be used for generated files. If <em>fileEncoding</em> is missing, |
| * the <em>charset</em> encoding will be used for writing the files. If <em>fileEncoding</em> and |
| * <em>charset</em> are missing, the file encoding will default to <em>Charset.defaultCharset()</em>. |
| * |
| * @param fileEncoding the file encoding |
| */ |
| public void setFileEncoding(String fileEncoding) { |
| this.fileEncoding = fileEncoding; |
| } |
| |
| /** |
| * Specifies a stylesheet file to use. If not specified, |
| * a default one will be generated for you. |
| * |
| * @param styleSheetFile the css stylesheet file to use |
| */ |
| public void setStyleSheetFile(File styleSheetFile) { |
| this.styleSheetFile = styleSheetFile; |
| } |
| |
| /** |
| * Add the directories matched by the nested dirsets to the resulting |
| * packages list and the base directories of the dirsets to the Path. |
| * It also handles the packages and excludepackages attributes and |
| * elements. |
| * |
| * @param resultantPackages a list to which we add the packages found |
| * @param sourcePath a path to which we add each basedir found |
| * @since 1.5 |
| */ |
| private void parsePackages(List<String> resultantPackages, Path sourcePath) { |
| List<String> addedPackages = new ArrayList<>(); |
| List<DirSet> dirSets = new ArrayList<DirSet>(packageSets); |
| |
| // for each sourcePath entry, add a directoryset with includes |
| // taken from packagenames attribute and nested package |
| // elements and excludes taken from excludepackages attribute |
| // and nested excludepackage elements |
| if (this.sourcePath != null) { |
| PatternSet ps = new PatternSet(); |
| if (!packageNames.isEmpty()) { |
| for (String pn : packageNames) { |
| String pkg = pn.replace('.', '/'); |
| if (pkg.endsWith("*")) { |
| pkg += "*"; |
| } |
| ps.createInclude().setName(pkg); |
| } |
| } else { |
| ps.createInclude().setName("**"); |
| } |
| |
| for (String epn : excludePackageNames) { |
| String pkg = epn.replace('.', '/'); |
| if (pkg.endsWith("*")) { |
| pkg += "*"; |
| } |
| ps.createExclude().setName(pkg); |
| } |
| |
| String[] pathElements = this.sourcePath.list(); |
| for (String pathElement : pathElements) { |
| File dir = new File(pathElement); |
| if (dir.isDirectory()) { |
| DirSet ds = new DirSet(); |
| ds.setDefaultexcludes(useDefaultExcludes); |
| ds.setDir(dir); |
| ds.createPatternSet().addConfiguredPatternset(ps); |
| dirSets.add(ds); |
| } else { |
| log.warn("Skipping " + pathElement + " since it is no directory."); |
| } |
| } |
| } |
| |
| for (DirSet ds : dirSets) { |
| File baseDir = ds.getDir(getProject()); |
| log.debug("scanning " + baseDir + " for packages."); |
| DirectoryScanner dsc = ds.getDirectoryScanner(getProject()); |
| String[] dirs = dsc.getIncludedDirectories(); |
| boolean containsPackages = false; |
| for (String dir : dirs) { |
| // are there any groovy or java files in this directory? |
| File pd = new File(baseDir, dir); |
| String[] files = pd.list((dir1, name) -> { |
| if (!includeNoSourcePackages |
| && name.equals("package.html")) return true; |
| final StringTokenizer tokenizer = new StringTokenizer(extensions, ":"); |
| while (tokenizer.hasMoreTokens()) { |
| String ext = tokenizer.nextToken(); |
| if (name.endsWith(ext)) return true; |
| } |
| return false; |
| }); |
| |
| if (files != null) { |
| for (String filename : files) { |
| sourceFilesToDoc.add(dir + File.separator + filename); |
| } |
| if (files.length > 0) { |
| if (dir.isEmpty()) { |
| log.warn(baseDir |
| + " contains source files in the default package," |
| + " you must specify them as source files not packages."); |
| } else { |
| containsPackages = true; |
| String pn = dir.replace(File.separatorChar, '.'); |
| if (!addedPackages.contains(pn)) { |
| addedPackages.add(pn); |
| resultantPackages.add(pn); |
| } |
| } |
| } |
| } |
| } |
| if (containsPackages) { |
| // We don't need to care for duplicates here, |
| // Path.list does it for us. |
| sourcePath.createPathElement().setLocation(baseDir); |
| } else { |
| log.verbose(baseDir + " doesn't contain any packages, dropping it."); |
| } |
| } |
| } |
| |
| @Override |
| public void execute() throws BuildException { |
| List<String> packagesToDoc = new ArrayList<>(); |
| Path sourceDirs = new Path(getProject()); |
| Properties properties = new Properties(); |
| properties.setProperty("windowTitle", windowTitle); |
| properties.setProperty("docTitle", docTitle); |
| properties.setProperty("footer", footer); |
| properties.setProperty("header", header); |
| checkScopeProperties(properties); |
| properties.setProperty("publicScope", publicScope.toString()); |
| properties.setProperty("protectedScope", protectedScope.toString()); |
| properties.setProperty("packageScope", packageScope.toString()); |
| properties.setProperty("privateScope", privateScope.toString()); |
| properties.setProperty("author", author.toString()); |
| properties.setProperty("processScripts", processScripts.toString()); |
| properties.setProperty("includeMainForScripts", includeMainForScripts.toString()); |
| properties.setProperty("overviewFile", overviewFile != null ? overviewFile.getAbsolutePath() : ""); |
| properties.setProperty("charset", charset != null ? charset : ""); |
| properties.setProperty("fileEncoding", fileEncoding != null ? fileEncoding : ""); |
| properties.setProperty("timestamp", Boolean.valueOf(!noTimestamp).toString()); |
| properties.setProperty("versionStamp", Boolean.valueOf(!noVersionStamp).toString()); |
| String phaseOverride = SystemUtil.getSystemPropertySafe("groovydoc.phase.override"); |
| if (phaseOverride != null) properties.put("phaseOverride", phaseOverride); |
| |
| if (sourcePath != null) { |
| sourceDirs.addExisting(sourcePath); |
| } |
| parsePackages(packagesToDoc, sourceDirs); |
| |
| GroovyDocTool htmlTool = new GroovyDocTool( |
| new ClasspathResourceManager(), // we're gonna get the default templates out of the dist jar file |
| sourcePath.list(), |
| getDocTemplates(), |
| getPackageTemplates(), |
| getClassTemplates(), |
| links, |
| properties |
| ); |
| |
| try { |
| htmlTool.add(sourceFilesToDoc); |
| FileOutputTool output = new FileOutputTool(); |
| htmlTool.renderToOutput(output, destDir.getCanonicalPath()); // TODO push destDir through APIs? |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| // try to override the default stylesheet with custom specified one if needed |
| if (styleSheetFile != null) { |
| try { |
| String css = ResourceGroovyMethods.getText(styleSheetFile); |
| File outfile = new File(destDir, "stylesheet.css"); |
| ResourceGroovyMethods.setText(outfile, css); |
| } catch (IOException e) { |
| System.out.println("Warning: Unable to copy specified stylesheet '" + styleSheetFile.getAbsolutePath() + |
| "'. Using default stylesheet instead. Due to: " + e.getMessage()); |
| } |
| } |
| } |
| |
| private void checkScopeProperties(Properties properties) { |
| // make protected the default scope and check for invalid duplication |
| int scopeCount = 0; |
| if (packageScope) scopeCount++; |
| if (privateScope) scopeCount++; |
| if (protectedScope) scopeCount++; |
| if (publicScope) scopeCount++; |
| if (scopeCount == 0) { |
| protectedScope = true; |
| } else if (scopeCount > 1) { |
| throw new BuildException("More than one of public, private, package, or protected scopes specified."); |
| } |
| } |
| |
| /** |
| * Create link to Javadoc/GroovyDoc output at the given URL. |
| * |
| * @return link argument to configure |
| */ |
| public LinkArgument createLink() { |
| LinkArgument result = new LinkArgument(); |
| links.add(result); |
| return result; |
| } |
| |
| /** |
| * Creates and returns an array of package template classpath entries. |
| * <p> |
| * This method is meant to be overridden by custom GroovyDoc implementations, using custom package templates. |
| * |
| * @return an array of package templates, whereas each entry is resolved as classpath entry, defaults to |
| * {@link GroovyDocTemplateInfo#DEFAULT_PACKAGE_TEMPLATES}. |
| */ |
| protected String[] getPackageTemplates() { |
| return GroovyDocTemplateInfo.DEFAULT_PACKAGE_TEMPLATES; |
| } |
| |
| /** |
| * Creates and returns an array of doc template classpath entries. |
| * <p> |
| * This method is meant to be overridden by custom GroovyDoc implementations, using custom doc templates. |
| * |
| * @return an array of doc templates, whereas each entry is resolved as classpath entry, defaults to |
| * {@link GroovyDocTemplateInfo#DEFAULT_DOC_TEMPLATES}. |
| */ |
| protected String[] getDocTemplates() { |
| return GroovyDocTemplateInfo.DEFAULT_DOC_TEMPLATES; |
| } |
| |
| /** |
| * Creates and returns an array of class template classpath entries. |
| * <p> |
| * This method is meant to be overridden by custom GroovyDoc implementations, using custom class templates. |
| * |
| * @return an array of class templates, whereas each entry is resolved as classpath entry, defaults to |
| * {@link GroovyDocTemplateInfo#DEFAULT_CLASS_TEMPLATES}. |
| */ |
| protected String[] getClassTemplates() { |
| return GroovyDocTemplateInfo.DEFAULT_CLASS_TEMPLATES; |
| } |
| } |