Here's the updated Javadoc.java file. Actually it's only about 4 lines
of code :) I'll look into the offline links later.

I've `cvs update'd my Jakarta sources today, so they should be recent, I've
marked the code I've added, search for `ernst'.

Ernst


Conor MacNeill wrote:
> 
> > Hi,
> >
> > I've fixed the docletpath problem. Should I send it to anyone? Or should
> > I request commit privileges ? Hmm, I think I'll just check the web site,
> > as I should :)
> 
> I'll commit the changes if you send them to the list.
> 
> >
> > Concerning the linkoffline problem: There seems to be some supporting
> > code in Javadoc.java, like createLink(), but the code is not actually
> > called, as far as I can see. Can anyone shed some light on this?
> >
> 
> createLink is used for nested elements which would be the easiest way to set
> up an offline link.
> <javadoc ...>
>   <link offline="yes" href="blah" packagelistLoc="blahblah"/>
> </javadoc>
> 
> I updated the documentation when I committed the last patch to Javadoc. Have
> you read that?
> 
> Can you also tell me how you were using the linkoffline attribute?
> 
> Cheers
> Conor
> 
/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.tools.ant.taskdefs;

import org.apache.tools.ant.*;

import java.io.*;
import java.util.*;

/**
 * This Task makes it easy to generate javadocs for a collection of source code.
 *
 * Current known limitations are:
 *  - multiple source path breaks operation
 *  - patterns must be of the form "xxx.*", every other pattern doesn't work.
 *  - the java comment-stripper reader is horribly slow
 *  - there is no control on arguments sanity since they are left
 *    to the javadoc implementation.
 *  - argument J in javadoc1 is not supported (what is that for anyway?)
 *
 * Note: This task is run on another VM because stupid Javadoc calls
 * System.exit() that would break Ant functionality.
 *
 * @author Jon S. Stevens <a href="mailto:[EMAIL PROTECTED]">[EMAIL 
PROTECTED]</a>
 * @author Stefano Mazzocchi <a href="mailto:[EMAIL PROTECTED]">[EMAIL 
PROTECTED]</a>
 * @author Patrick Chanezon <a href="mailto:[EMAIL PROTECTED]">[EMAIL 
PROTECTED]</a>
 */

public class Javadoc extends Exec {

    private String maxmemory = null;
    private String sourcePath = null;
    private String additionalParam = null;
    private File destDir = null;
    private File overviewFile = null;
    private String sourceFiles = null;
    private String packageNames = null;
    private boolean pub = false;
    private boolean prot = false;
    private boolean pack = false;
    private boolean priv = false;
    private boolean author = true;
    private boolean version = true;
    private String doclet = null;
    private File docletpath = null;
    private boolean old = false;
    private String classpath = null;
    private String bootclasspath = null;
    private String extdirs = null;
    private boolean verbose = false;
    private String locale = null;
    private String encoding = null;
    private boolean use = false;
    private boolean splitindex = false;
    private String windowtitle = null;
    private String doctitle = null;
    private String header = null;
    private String footer = null;
    private String bottom = null;
    private String linkoffline = null;
    private String link = null;
    private String group = null;
    private boolean nodeprecated = false;
    private boolean nodeprecatedlist = false;
    private boolean notree = false;
    private boolean noindex = false;
    private boolean nohelp = false;
    private boolean nonavbar = false;
    private File stylesheetfile = null;
    private File helpfile = null;
    private String docencoding = null;
    private Vector compileList = new Vector(10);
    private String packageList = null;
    private Vector links = new Vector(2);
    private Vector groups = new Vector(2);

    public void setMaxmemory(String src){
        maxmemory = src;
    }

    public void setadditionalParam(String src){
        additionalParam = src;
    }
    
    public void setSourcepath(String src) {
        sourcePath = project.translatePath(src);
    }
    public void setDestdir(String src) {
        destDir = project.resolveFile(src);
    }
    public void setSourcefiles(String src) {
        sourceFiles = src;
    }
    public void setPackagenames(String src) {
        packageNames = src;
    }
    public void setOverview(String src) {
        overviewFile = project.resolveFile(src);
    }
    public void setPublic(String src) {
        pub = Project.toBoolean(src);
    }
    public void setProtected(String src) {
        prot = Project.toBoolean(src);
    }
    public void setPackage(String src) {
        pack = Project.toBoolean(src);
    }
    public void setPrivate(String src) {
        priv = Project.toBoolean(src);
    }
    public void setDoclet(String src) {
        doclet = src;
    }
    public void setDocletPath(String src) {
        docletpath = project.resolveFile(src);
    }
    public void setOld(String src) {
        old = Project.toBoolean(src);
    }
    public void setClasspath(String src) {
        classpath = project.translatePath(src);
    }
    public void setBootclasspath(String src) {
        bootclasspath = project.translatePath(src);
    }
    public void setExtdirs(String src) {
        extdirs = src;
    }
    public void setVerbose(String src) {
        verbose = Project.toBoolean(src);
    }
    public void setLocale(String src) {
        locale = src;
    }
    public void setEncoding(String src) {
        encoding = src;
    }
    public void setVersion(String src) {
        version = Project.toBoolean(src);
    }
    public void setUse(String src) {
        use = Project.toBoolean(src);
    }
    public void setAuthor(String src) {
        author = Project.toBoolean(src);
    }
    public void setSplitindex(String src) {
        splitindex = Project.toBoolean(src);
    }
    public void setWindowtitle(String src) {
        windowtitle = src;
    }
    public void setDoctitle(String src) {
        doctitle = src;
    }
    public void setHeader(String src) {
        header = src;
    }
    public void setFooter(String src) {
        footer = src;
    }
    public void setBottom(String src) {
        bottom = src;
    }
    public void setLinkoffline(String src) {
        linkoffline = src;
    }
    public void setGroup(String src) {
        group = src;
    }
    public void setLink(String src) {
        link = src;
    }
    public void setNodeprecated(String src) {
        nodeprecated = Project.toBoolean(src);
    }
    public void setNodeprecatedlist(String src) {
        nodeprecatedlist = Project.toBoolean(src);
    }
    public void setNotree(String src) {
        notree = Project.toBoolean(src);
    }
    public void setNoindex(String src) {
        noindex = Project.toBoolean(src);
    }
    public void setNohelp(String src) {
        nohelp = Project.toBoolean(src);
    }
    public void setNonavbar(String src) {
        nonavbar = Project.toBoolean(src);
    }
    public void setStylesheetfile(String src) {
        stylesheetfile = project.resolveFile(src);
    }
    public void setDocencoding(String src) {
        docencoding = src;
    }
    public void setPackageList(String src) {
        packageList = src;
    }
    
    public LinkArgument createLink() {
        LinkArgument la = new LinkArgument();
        links.addElement(la);
        return la;
    }
    
    public class LinkArgument {
        private String href;
        private boolean offline = false;
        private String packagelistLoc;
        
        public LinkArgument() {
        }

        public void setHref(String hr) {
            href = hr;
        }
        
        public String getHref() {
            return href;
        }
        
        public void setPackagelistLoc(String src) {
            packagelistLoc = src;
        }
        
        public String getPackagelistLoc() {
            return packagelistLoc;
        }
        
        public void setOffline(String offline) {
            this.offline = Project.toBoolean(offline);
        }
        
        public boolean isLinkOffline() {
            return offline;
        }
    }
    
    public GroupArgument createGroup() {
        GroupArgument ga = new GroupArgument();
        groups.addElement(ga);
        return ga;
    }

    public class GroupArgument {
        private String title;
        private String packages;

        public GroupArgument() {
        }

        public void setTitle(String src) {
            title = src;
        }

        public String getTitle() {
            return title;
        }

        public void setPackages(String src) {
            packages = src;
        }

        public String getPackages() {
            return packages;
        }
    }

    public void execute() throws BuildException {
        if (sourcePath == null && destDir == null ) {
            String msg = "sourcePath and destDir attributes must be set!";
            throw new BuildException(msg);
        }

        boolean javadoc1 = (Project.getJavaVersion() == Project.JAVA_1_1);

        project.log("Generating Javadoc", project.MSG_INFO);

        Vector argList = new Vector();

// ------------------------------------------------ general javadoc arguments
        if (classpath == null)
            classpath = System.getProperty("java.class.path");


        if(maxmemory != null){
            if(javadoc1){
                argList.addElement("-J-mx" + maxmemory);
            }
            else{
                argList.addElement("-J-Xmx" + maxmemory);
            }
        }

        if ( (!javadoc1) || (sourcePath == null) ) {
            argList.addElement("-classpath");
            argList.addElement(classpath);
            if (sourcePath != null) {
                argList.addElement("-sourcepath");
                argList.addElement(sourcePath);
            }
        } else {
            argList.addElement("-classpath");
            argList.addElement(sourcePath +
                System.getProperty("path.separator") + classpath);
        }

        if (destDir != null) {
            argList.addElement("-d");
            argList.addElement(destDir.getAbsolutePath());
        }
        if (version)
            argList.addElement ("-version");
        if (nodeprecated)
            argList.addElement ("-nodeprecated");
        if (author)
            argList.addElement ("-author");
        if (noindex)
            argList.addElement ("-noindex");
        if (notree)
            argList.addElement ("-notree");
        if (pub)
            argList.addElement ("-public");
        if (prot)
            argList.addElement ("-protected");
        if (pack)
            argList.addElement ("-package");
        if (priv)
            argList.addElement ("-private");
        if (encoding != null) {
            argList.addElement("-encoding");
            argList.addElement(encoding);
        }
        if (docencoding != null) {
            argList.addElement("-docencoding");
            argList.addElement(docencoding);
        }

// --------------------------------- javadoc2 arguments for default doclet

// XXX: how do we handle a custom doclet?

        if (!javadoc1) {
            if (overviewFile != null) {
                argList.addElement("-overview");
                argList.addElement(overviewFile.getAbsolutePath());
            }
            if (old)
                argList.addElement("-1.1");
            if (verbose)
                argList.addElement("-verbose");
            if (use)
                argList.addElement("-use");
            if (splitindex)
                argList.addElement("-splitindex");
            if (nodeprecatedlist)
                argList.addElement("-nodeprecatedlist");
            if (nohelp)
                argList.addElement("-nohelp");
            if (nonavbar)
                argList.addElement("-nonavbar");
            if (doclet != null) {
                argList.addElement("-doclet");
                argList.addElement(doclet);
            }

/* Addition by [EMAIL PROTECTED] @ 2000.07.03 17:32 GMT+1 */
            if (docletpath != null) {
                argList.addElement("-docletpath");
                argList.addElement(docletpath.getAbsolutePath());
            }
/* End of addition by [EMAIL PROTECTED] */

            if (bootclasspath != null) {
                argList.addElement("-bootclasspath");
                argList.addElement(bootclasspath);
            }
            if (extdirs != null) {
                argList.addElement("-extdirs");
                argList.addElement(extdirs);
            }
            if (locale != null) {
                argList.addElement("-locale");
                argList.addElement(locale);
            }
            if (encoding != null) {
                argList.addElement("-encoding");
                argList.addElement(encoding);
            }
            if (windowtitle != null) {
                argList.addElement("-windowtitle");
                argList.addElement(windowtitle);
            }
            if (doctitle != null) {
                argList.addElement("-doctitle");
                argList.addElement(doctitle);
            }
            if (header != null) {
                argList.addElement("-header");
                argList.addElement(header);
            }
            if (footer != null) {
                argList.addElement("-footer");
                argList.addElement(footer);
            }
            if (bottom != null) {
                argList.addElement("-bottom");
                argList.addElement(bottom);
            }
            
            // add the single link arguments
            if (link != null) {
                argList.addElement("-link");
                argList.addElement(link);
            }
            
            // add the links arguments
            if (links.size() != 0) {
                for (Enumeration e = links.elements(); e.hasMoreElements(); ) {
                    LinkArgument la = (LinkArgument)e.nextElement();
                
                    if (la.getHref() == null) {
                        throw new BuildException("Links must provide the RUL to 
the external class documentation.");
                    }
                
                    if (la.isLinkOffline()) {
                        String packageListLocation = la.getPackagelistLoc();
                        if (packageListLocation == null) {
                            throw new BuildException("The package list location 
for link " + la.getHref() +
                                                     " must be provided because 
the link is offline");
                        }
                        argList.addElement("-linkoffline");
                        argList.addElement(la.getHref());
                        argList.addElement(packageListLocation);
                    }
                    else {
                        argList.addElement("-link");
                        argList.addElement(la.getHref());
                    }
                }
            }                                   
                                                
            // add the single linkoffline arguments
            if (linkoffline != null) {
                argList.addElement("-linkoffline");
                argList.addElement(linkoffline);
            }
            
            // add the single group arguments
            // Javadoc 1.2 rules:
            //   Multiple -group args allowed.
            //   Each arg includes 3 strings: -group [name] [packagelist].
            //   Elements in [packagelist] are colon-delimited.
            //   An element in [packagelist] may end with the * wildcard.

            // Ant javadoc task rules for group attribute:
            //   Args are comma-delimited.
            //   Each arg is 2 space-delimited strings.
            //   E.g., group="XSLT_Packages 
org.apache.xalan.xslt*,XPath_Packages orgapache.xalan.xpath*"
            if (group != null) {
                StringTokenizer tok = new StringTokenizer(group, ",", false);
                while (tok.hasMoreTokens()) {
                  String grp = tok.nextToken().trim();
                  int space = grp.indexOf(" ");
                  if (space > 0){
                    String name = grp.substring(0, space);
                    String pkgList = grp.substring(space + 1);
                    argList.addElement("-group");
                    argList.addElement(name);
                    argList.addElement(pkgList);
                  }
                }
            }
            
            // add the group arguments
            if (groups.size() != 0) {
                for (Enumeration e = groups.elements(); e.hasMoreElements(); ) {
                    GroupArgument ga = (GroupArgument)e.nextElement();
                    String title = ga.getTitle();
                    String packages = ga.getPackages();
                    if (title == null || packages == null) {
                        throw new BuildException("The title and packages must 
be specified for group elements.");
                    }
                    argList.addElement("-group");
                    argList.addElement(title);
                    argList.addElement(packages);
                }
            }

            if (stylesheetfile != null) {
                argList.addElement("-stylesheetfile");
                argList.addElement(stylesheetfile.getAbsolutePath());
            }
            if (helpfile != null) {
                argList.addElement("-helpfile");
                argList.addElement(helpfile.getAbsolutePath());
            }
            if (additionalParam != null) {
                argList.addElement(additionalParam);
            }
        }

        if ((packageNames != null) && (packageNames.length() > 0)) {
            Vector packages = new Vector();
            StringTokenizer tok = new StringTokenizer(packageNames, ",", false);
            while (tok.hasMoreTokens()) {
                String name = tok.nextToken().trim();
                if (name.endsWith(".*")) {
                    packages.addElement(name);
                } else {
                    argList.addElement(name);
                }
            }
            if (packages.size() > 0) {
                evaluatePackages(sourcePath, packages, argList);
            }
        }

        if ((sourceFiles != null) && (sourceFiles.length() > 0)) {
            StringTokenizer tok = new StringTokenizer(sourceFiles, ",", false);
            while (tok.hasMoreTokens()) {
                argList.addElement(tok.nextToken().trim());
            }
        }

         if (packageList != null) {
            argList.addElement("@" + packageList);
        }
        project.log("Javadoc args: " + argList.toString(), "javadoc", 
project.MSG_VERBOSE);

        project.log("Javadoc execution", project.MSG_INFO);

        StringBuffer b = new StringBuffer();
        b.append("javadoc ");

        Enumeration e = argList.elements();
        while (e.hasMoreElements()) {
            String arg = (String) e.nextElement();
            if (!arg.startsWith("-")) {
                b.append("\"");
                b.append(arg);
                b.append("\"");
            } else {
                b.append(arg);
            }
            if (e.hasMoreElements()) b.append(" ");
        }

        run(b.toString());
    }

    /**
     * Given a source path, a list of package patterns, fill the given list
     * with the packages found in that path subdirs matching one of the given
     * patterns.
     */
    private void evaluatePackages(String sourcePath, Vector packages, Vector 
argList) {
        project.log("Parsing source files for packages", project.MSG_INFO);
        project.log("Source path = " + sourcePath, project.MSG_VERBOSE);
        project.log("Packages = " + packages, project.MSG_VERBOSE);

        Vector addedPackages = new Vector();
        PathTokenizer tokenizer = new PathTokenizer(sourcePath);
        while (tokenizer.hasMoreTokens()) {
            File source = new 
File(project.translatePath(tokenizer.nextToken()));
            
            Hashtable map = mapClasses(source);

            Enumeration e = map.keys();
            while (e.hasMoreElements()) {
                String pack = (String) e.nextElement();
                for (int i = 0; i < packages.size(); i++) {
                    if (matches(pack, (String) packages.elementAt(i))) {
                        if (!addedPackages.contains(pack)) {
                            argList.addElement(pack);
                            addedPackages.addElement(pack);
                        }
                        break;
                    }
                }
            }
        }
    }

    /**
     * Implements the pattern matching. For now it's only able to
     * guarantee that "aaa.bbb.ccc" matches "aaa.*" and "aaa.bbb.*"
     * FIXME: this code needs much improvement.
     */
    private boolean matches(String string, String pattern) {
        return string.startsWith(pattern.substring(0, pattern.length() - 2));
    }

    /**
     * Returns an hashtable of packages linked to the last parsed
     * file in that package. This map is use to return a list of unique
     * packages as map keys.
     */
    private Hashtable mapClasses(File path) {
        Hashtable map = new Hashtable();

        Vector files = new Vector();
        getFiles(path, files);

        Enumeration e = files.elements();
        while (e.hasMoreElements()) {
            File file = (File) e.nextElement();
            String packageName = getPackageName(file);
            if (packageName != null) map.put(packageName, file);
        }

        return map;
    }

    /**
     * Fills the given vector with files under the given path filtered
     * by the given file filter.
     */
    private void getFiles(File path, Vector list) {
        if (!path.exists()) {
            throw new BuildException("Path " + path + " does not exist.");
        }

        String[] files = path.list();
        String cwd = path.getPath() + System.getProperty("file.separator");

        if (files != null) {
            int count = 0;
            for (int i = 0; i < files.length; i++) {
                File file = new File(cwd + files[i]);
                if (file.isDirectory()) {
                    getFiles(file, list);
                } else if (files[i].endsWith(".java")) {
                    count++;
                    list.addElement(file);
                }
            }
            if (count > 0) {
                project.log("found " + count + " source files in " + path, 
"javadoc", project.MSG_VERBOSE);
            }
        } else {
            throw new BuildException("Error occurred during " + path + " 
evaluation.");
        }
    }

    /**
     * Return the package name of the given java source file.
     * This method performs valid java parsing to figure out the package.
     */
    private String getPackageName(File file) {
        String name = null;

        try {
            // do not remove the double buffered reader, this is a _major_ 
speed up in this special case!
            BufferedReader reader = new BufferedReader(new JavaReader(new 
BufferedReader(new FileReader(file))));
            String line;
            while (true) {
                line = reader.readLine();
                if (line == null) {
                    project.log("Could not evaluate package for " + file, 
"javadoc", project.MSG_WARN);
                    return null;
                }
                if (line.trim().startsWith("package ")) {
                    name = line.substring(8, line.indexOf(";")).trim();
                    break;
                }
            }
            reader.close();
        } catch (Exception e) {
            project.log("Exception " + e + " parsing " + file, "javadoc", 
project.MSG_WARN);
            return null;
        }

        project.log(file + " --> " + name, "javadoc", project.MSG_VERBOSE);

        return name;
    }

    /**
     * This is a java comment and string stripper reader that filters
     * these lexical tokens out for purposes of simple Java parsing.
     * (if you have more complex Java parsing needs, use a real lexer).
     * Since this class heavily relies on the single char read function,
     * you are reccomended to make it work on top of a buffered reader.
     */
    class JavaReader extends FilterReader {

        public JavaReader(Reader in) {
            super(in);
        }

        public int read() throws IOException {
            int c = in.read();
            if (c == '/') {
                c = in.read();
                if (c == '/') {
                    while (c != '\n' && c != -1) c = in.read();
                } else if (c == '*') {
                    while (c != -1) {
                        c = in.read();
                        if (c == '*') {
                            c = in.read();
                            if (c == '/') {
                                c = read();
                                break;
                            }
                        }
                    }
                }
            }
            if (c == '"') {
                while (c != -1) {
                    c = in.read();
                    if (c == '\\') {
                        c = in.read();
                    } else if (c == '"') {
                        c = read();
                        break;
                    }
                }
            }
            if (c == '\'') {
                c = in.read();
                if (c == '\\') c = in.read();
                c = in.read();
                c = read();
            }
            return c;
        }

        public int read(char cbuf[], int off, int len) throws IOException {
            for (int i = 0; i < len; i++) {
                int c = read();
                if (c == -1) {
                    if (i == 0) {
                        return -1;
                    } else {
                        return i;
                    }
                }
                cbuf[off + i] = (char) c;
            }
            return len;
        }

        public long skip(long n) throws IOException {
            for (long i = 0; i < n; i++) {
                if (in.read() == -1) return i;
            }
            return n;
        }
    }
}

Reply via email to