Package org.jdesktop.wonderland.utils.doclet

Source Code of org.jdesktop.wonderland.utils.doclet.WonderlandDoclet

/**
* Project Wonderland
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., All Rights Reserved
*
* Redistributions in source code form must reproduce the above
* copyright and this condition.
*
* The contents of this file are subject to the GNU General Public
* License, Version 2 (the "License"); you may not use this file
* except in compliance with the License. A copy of the License is
* available at http://www.opensource.org/licenses/gpl-license.php.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied
* this code.
*/
package org.jdesktop.wonderland.utils.doclet;

import com.sun.javadoc.AnnotationDesc;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.DocErrorReporter;
import com.sun.javadoc.Doclet;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.RootDoc;
import com.sun.tools.doclets.formats.html.HtmlDoclet;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* A doclet that processes based on Wonderland annotations.  This doclet
* wraps the core RootDoc and PackageDoc elements to add a new level of
* filter based on the Wonderland API stability level.  The desired
* stability level is specified with the <code>-wonderlandAPI</code> argument
* to javadoc.
* <p>
* The <code>-wonderlandAPI</code> argument must be followed by a list of
* stability levels separated by commas.  The valid stability levels are
* <ul><li>stable
*     <li>experimental
*     <li>private
*     <li>internal
* </ul>
* These correspond to classes tagged with the corresponding annotation from
* the <code>org.jdesktop.wonderland</code> package.  So calling the
* doclet with the arguments <code>-wonderlandAPI experimental,private</code>
* will generate javadoc for all classes tagged with the
* <code>ExperimentalAPI</code> and <code>PrivateAPI</code> annotations.
*
* @author jkaplan
*/
public class WonderlandDoclet extends Doclet {
    /** map from argument name to API name */;
    private static final Map<String, String> API_MAP =
            new HashMap<String, String>();
   
    static {
        API_MAP.put("stable",       "StableAPI");
        API_MAP.put("experimental", "ExperimentalAPI");
        API_MAP.put("private",      "PrivateAPI");
        API_MAP.put("internal",     "InternalAPI");
    }
   
    /**
     * Start parsing using the standard HTML doclet
     * @param root the root of the document tree
     * @return true if the parsing is successful, or false if there is an error
     */
    public static boolean start(RootDoc root) {
        HtmlDoclet doclet = new HtmlDoclet();
       
        // get the APIs from the arguments
        String[][] options = root.options();
        String[] types = null;
        for (String[] option : options) {
            if (option[0].toLowerCase().equals("-wonderlandapi")) {
                types = option[1].split(",");
               
                // convert to internal names
                for (int i = 0; i < types.length; i++) {
                    types[i] = API_MAP.get(types[i].toLowerCase());
                }
            }
        }
       
        try {
            // wrap the root object
            ClassLoader loader = WonderlandDoclet.class.getClassLoader();
            Class<?>[] interfaces = new Class<?>[] { RootDoc.class };
            RootDocWrapper handler = new RootDocWrapper(root, types);
            RootDoc wrapper =
                    (RootDoc) Proxy.newProxyInstance(loader, interfaces, handler);
       
            // overwrite the configuration used by the standard doclet to
            // return our wrapped classes
            doclet.configuration.root = wrapper;
            doclet.configuration.packages = handler.wrappedPackages;
           
            // parse using the standard HTML doclet
            doclet.start(doclet, wrapper);
        } catch (Exception exc) {
            exc.printStackTrace();
            return false;
        }
        return true;
    }
   
    /**
     * Validate standard HTML doclet arguments and also the
     * <code>-wonderlandAPI</code> argument.
     * @param arg the option that was passes on the command line
     * @return the number of arguments required
     */
    public static int optionLength(String arg) {
        if (arg.toLowerCase().equals("-wonderlandapi")) {
            return 2;
        }
       
        return HtmlDoclet.optionLength(arg);
    }
   
    /**
     * Validate standard HTML doclet arguments and also the
     * <code>-wonderlandAPI</code> argument.
     * @param options the options to validate
     * @param errorReporter how to report an error
     * @return true if the options are valid, or false if not
     */
    public static boolean validOptions(String[][] options,
                                       DocErrorReporter errorReporter)
    {
        if (!HtmlDoclet.validOptions(options, errorReporter)) {
            return false;
        }
   
        // check that the -wonderlandAPI option exists
        boolean wonderlandAPI = false;
        boolean validOptions = true;
        for (int i = 0; i < options.length; i++) {
            String[] option = options[i];
            if (option[0].toLowerCase().equals("-wonderlandapi")) {
                wonderlandAPI = true;
           
                String[] apis = option[1].split(",");
                for (String api : apis) {
                    if (!API_MAP.containsKey(api.toLowerCase())) {
                        validOptions = false;
                        break;
                    }
                }
            }
        }
       
        String usage = "Usage: -wonderlandAPI stable,experimental,internal,private";
       
        // if the -wonderlandAPI argument was not present, throw an error
        if (!wonderlandAPI) {
            errorReporter.printError("No -wonderlandAPI specified.\n" + usage);
            return false;
        }
       
        // if specified APIS are invaliud
        if (!validOptions) {
            errorReporter.printError("Invalid wonderland API.\n" + usage);
            return false;
        }
       
        return true;
    }
   
    /**
     * Utility to filter a set of classes based on annotations.  This method
     * will filter the given list of classes based on whether any of the
     * annotations given in the types argument are present.  This looks only
     * at the class name of the annotation, not the fully-qualified name.
     * @param orig the original set of classes to filter
     * @param types the types of annotations to accept
     * @return the filtered list of classes
     */
    protected static ClassDoc[] filterClasses(ClassDoc[] orig, String... types) {
            if (types == null || types.length == 0) {
                return new ClassDoc[0];
            }
           
            List<String> in = Arrays.asList(types);
            List<ClassDoc> out = new ArrayList<ClassDoc>();
            for (ClassDoc classDoc : orig) {
                for (AnnotationDesc annotation : classDoc.annotations()) {
                    if (in.contains(annotation.annotationType().name())) {
                        out.add(classDoc);
                        break;
                    }
                }
            }
           
            return out.toArray(new ClassDoc[0]);
        }
   
    /**
     * Wrapper for the root that overrides the <code>classes</code> and
     * <code>specifiedPackages</code> methods to use the filter passed
     * in via the <code>-wonderlandAPI</code> option.
     */
    static class RootDocWrapper implements InvocationHandler
    {
        private RootDoc wrapped;
        private ClassDoc[] wrappedClasses;
        private PackageDoc[] wrappedPackages;
       
        public RootDocWrapper(RootDoc wrapped, String... types)
                throws Exception
        {
            this.wrapped = wrapped;
           
            wrappedClasses = filterClasses(wrapped.classes(), types);
            wrappedPackages = getPackages(wrapped.specifiedPackages(), types);
        }
       
        public Object invoke(Object obj, Method method, Object[] args)
                throws Throwable
        {
            if (method.getName().equals("classes")) {
                return wrappedClasses;
            } else if (method.getName().equals("specifiedPackages")) {
                return wrappedPackages;
            }
           
            return method.invoke(wrapped, args);
        }
      
        private PackageDoc[] getPackages(PackageDoc[] orig, String... types) {
            List<PackageDoc> wrapped = new ArrayList<PackageDoc>();

            for (PackageDoc p : orig) {
                // wrap the package
                ClassLoader loader = WonderlandDoclet.class.getClassLoader();
                Class<?>[] interfaces = new Class<?>[] { PackageDoc.class };
                InvocationHandler handler = new PackageDocWrapper(p, "ExperimentalAPI");
                PackageDoc pkg =
                        (PackageDoc) Proxy.newProxyInstance(loader, interfaces, handler);
                if (pkg.allClasses().length > 0) {
                    wrapped.add(pkg);
                }
            }
           
            return wrapped.toArray(new PackageDoc[0]);
        }
    }
   
    /**
     * Wrapper for an individual package that overrides all methods that
     * get the various class lists to apply the filter specified in the
     * <code>-wonderlandAPI</code> command line option.
     */
    static class PackageDocWrapper implements InvocationHandler
    {
        private PackageDoc wrapped;
        private String[] types;
       
        private ClassDoc[] allClasses;
       
        public PackageDocWrapper(PackageDoc wrapped, String... types) {
            this.wrapped = wrapped;
            this.types = types;
           
            allClasses = filterClasses(wrapped.allClasses(true), types);
        }
       
        public Object invoke(Object obj, Method method, Object[] args)
                throws Throwable
        {
            if (method.getName().equals("allClasses") &&
                (args == null || args.length == 0 ||
                    ((Boolean) args[0]).booleanValue()))
            {
                // override the allClasses method, but only when
                // the argument is either not given or TRUE
                return allClasses;
            } else if (method.getName().equals("ordinaryClasses") ||
                       method.getName().equals("exceptions") ||
                       method.getName().equals("errors") ||
                       method.getName().equals("enums") ||
                       method.getName().equals("interfaces"))
            {
                // override the various individual class type getter methods
                return filterClasses((ClassDoc[]) method.invoke(wrapped, args),
                                     types);
            } else if (method.getName().equals("compareTo") &&
                       args[0] instanceof PackageDoc)
            {
                // hack to avoid a ClassCastException in DocImpl
                return wrapped.name().compareTo(((PackageDoc) args[0]).name());
            }
           
            return method.invoke(wrapped, args);
        }
    }
}
TOP

Related Classes of org.jdesktop.wonderland.utils.doclet.WonderlandDoclet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.