src/share/classes/com/sun/tools/javadoc/DocletInvoker.java

Thu, 15 Nov 2012 14:41:31 -0800

author
jjg
date
Thu, 15 Nov 2012 14:41:31 -0800
changeset 1411
467f4f754368
parent 1359
25e14ad23cef
child 1413
bdcef2ef52d2
permissions
-rw-r--r--

8003257: refactor javadoc tool option handling
Reviewed-by: darcy

     1 /*
     2  * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.tools.javadoc;
    28 import java.io.File;
    29 import java.lang.reflect.InvocationTargetException;
    30 import java.lang.reflect.Method;
    31 import java.lang.reflect.Modifier;
    32 import java.net.URL;
    33 import java.net.URLClassLoader;
    35 import com.sun.javadoc.*;
    36 import com.sun.tools.javac.util.List;
    37 import static com.sun.javadoc.LanguageVersion.*;
    40 /**
    41  * Class creates, controls and invokes doclets.
    42  *
    43  *  <p><b>This is NOT part of any supported API.
    44  *  If you write code that depends on this, you do so at your own risk.
    45  *  This code and its internal interfaces are subject to change or
    46  *  deletion without notice.</b>
    47  *
    48  * @author Neal Gafter (rewrite)
    49  */
    50 public class DocletInvoker {
    52     private final Class<?> docletClass;
    54     private final String docletClassName;
    56     private final ClassLoader appClassLoader;
    58     private final Messager messager;
    60     private static class DocletInvokeException extends Exception {
    61         private static final long serialVersionUID = 0;
    62     }
    64     private String appendPath(String path1, String path2) {
    65         if (path1 == null || path1.length() == 0) {
    66             return path2 == null ? "." : path2;
    67         } else if (path2 == null || path2.length() == 0) {
    68             return path1;
    69         } else {
    70             return path1  + File.pathSeparator + path2;
    71         }
    72     }
    74     public DocletInvoker(Messager messager,
    75                          String docletClassName, String docletPath,
    76                          ClassLoader docletParentClassLoader) {
    77         this.messager = messager;
    78         this.docletClassName = docletClassName;
    80         // construct class loader
    81         String cpString = null;   // make sure env.class.path defaults to dot
    83         // do prepends to get correct ordering
    84         cpString = appendPath(System.getProperty("env.class.path"), cpString);
    85         cpString = appendPath(System.getProperty("java.class.path"), cpString);
    86         cpString = appendPath(docletPath, cpString);
    87         URL[] urls = com.sun.tools.javac.file.Locations.pathToURLs(cpString);
    88         if (docletParentClassLoader == null)
    89             appClassLoader = new URLClassLoader(urls, getDelegationClassLoader(docletClassName));
    90         else
    91             appClassLoader = new URLClassLoader(urls, docletParentClassLoader);
    93         // attempt to find doclet
    94         Class<?> dc = null;
    95         try {
    96             dc = appClassLoader.loadClass(docletClassName);
    97         } catch (ClassNotFoundException exc) {
    98             messager.error(Messager.NOPOS, "main.doclet_class_not_found", docletClassName);
    99             messager.exit();
   100         }
   101         docletClass = dc;
   102     }
   104     /*
   105      * Returns the delegation class loader to use when creating
   106      * appClassLoader (used to load the doclet).  The context class
   107      * loader is the best choice, but legacy behavior was to use the
   108      * default delegation class loader (aka system class loader).
   109      *
   110      * Here we favor using the context class loader.  To ensure
   111      * compatibility with existing apps, we revert to legacy
   112      * behavior if either or both of the following conditions hold:
   113      *
   114      * 1) the doclet is loadable from the system class loader but not
   115      *    from the context class loader,
   116      *
   117      * 2) this.getClass() is loadable from the system class loader but not
   118      *    from the context class loader.
   119      */
   120     private ClassLoader getDelegationClassLoader(String docletClassName) {
   121         ClassLoader ctxCL = Thread.currentThread().getContextClassLoader();
   122         ClassLoader sysCL = ClassLoader.getSystemClassLoader();
   123         if (sysCL == null)
   124             return ctxCL;
   125         if (ctxCL == null)
   126             return sysCL;
   128         // Condition 1.
   129         try {
   130             sysCL.loadClass(docletClassName);
   131             try {
   132                 ctxCL.loadClass(docletClassName);
   133             } catch (ClassNotFoundException e) {
   134                 return sysCL;
   135             }
   136         } catch (ClassNotFoundException e) {
   137         }
   139         // Condition 2.
   140         try {
   141             if (getClass() == sysCL.loadClass(getClass().getName())) {
   142                 try {
   143                     if (getClass() != ctxCL.loadClass(getClass().getName()))
   144                         return sysCL;
   145                 } catch (ClassNotFoundException e) {
   146                     return sysCL;
   147                 }
   148             }
   149         } catch (ClassNotFoundException e) {
   150         }
   152         return ctxCL;
   153     }
   155     /**
   156      * Generate documentation here.  Return true on success.
   157      */
   158     public boolean start(RootDoc root) {
   159         Object retVal;
   160         String methodName = "start";
   161         Class<?>[] paramTypes = { RootDoc.class };
   162         Object[] params = { root };
   163         try {
   164             retVal = invoke(methodName, null, paramTypes, params);
   165         } catch (DocletInvokeException exc) {
   166             return false;
   167         }
   168         if (retVal instanceof Boolean) {
   169             return ((Boolean)retVal).booleanValue();
   170         } else {
   171             messager.error(Messager.NOPOS, "main.must_return_boolean",
   172                            docletClassName, methodName);
   173             return false;
   174         }
   175     }
   177     /**
   178      * Check for doclet added options here. Zero return means
   179      * option not known.  Positive value indicates number of
   180      * arguments to option.  Negative value means error occurred.
   181      */
   182     public int optionLength(String option) {
   183         Object retVal;
   184         String methodName = "optionLength";
   185         Class<?>[] paramTypes = { String.class };
   186         Object[] params = { option };
   187         try {
   188             retVal = invoke(methodName, new Integer(0), paramTypes, params);
   189         } catch (DocletInvokeException exc) {
   190             return -1;
   191         }
   192         if (retVal instanceof Integer) {
   193             return ((Integer)retVal).intValue();
   194         } else {
   195             messager.error(Messager.NOPOS, "main.must_return_int",
   196                            docletClassName, methodName);
   197             return -1;
   198         }
   199     }
   201     /**
   202      * Let doclet check that all options are OK. Returning true means
   203      * options are OK.  If method does not exist, assume true.
   204      */
   205     public boolean validOptions(List<String[]> optlist) {
   206         Object retVal;
   207         String options[][] = optlist.toArray(new String[optlist.length()][]);
   208         String methodName = "validOptions";
   209         DocErrorReporter reporter = messager;
   210         Class<?>[] paramTypes = { String[][].class, DocErrorReporter.class };
   211         Object[] params = { options, reporter };
   212         try {
   213             retVal = invoke(methodName, Boolean.TRUE, paramTypes, params);
   214         } catch (DocletInvokeException exc) {
   215             return false;
   216         }
   217         if (retVal instanceof Boolean) {
   218             return ((Boolean)retVal).booleanValue();
   219         } else {
   220             messager.error(Messager.NOPOS, "main.must_return_boolean",
   221                            docletClassName, methodName);
   222             return false;
   223         }
   224     }
   226     /**
   227      * Return the language version supported by this doclet.
   228      * If the method does not exist in the doclet, assume version 1.1.
   229      */
   230     public LanguageVersion languageVersion() {
   231         try {
   232             Object retVal;
   233             String methodName = "languageVersion";
   234             Class<?>[] paramTypes = new Class<?>[0];
   235             Object[] params = new Object[0];
   236             try {
   237                 retVal = invoke(methodName, JAVA_1_1, paramTypes, params);
   238             } catch (DocletInvokeException exc) {
   239                 return JAVA_1_1;
   240             }
   241             if (retVal instanceof LanguageVersion) {
   242                 return (LanguageVersion)retVal;
   243             } else {
   244                 messager.error(Messager.NOPOS, "main.must_return_languageversion",
   245                                docletClassName, methodName);
   246                 return JAVA_1_1;
   247             }
   248         } catch (NoClassDefFoundError ex) { // for boostrapping, no Enum class.
   249             return null;
   250         }
   251     }
   253     /**
   254      * Utility method for calling doclet functionality
   255      */
   256     private Object invoke(String methodName, Object returnValueIfNonExistent,
   257                           Class<?>[] paramTypes, Object[] params)
   258         throws DocletInvokeException {
   259             Method meth;
   260             try {
   261                 meth = docletClass.getMethod(methodName, paramTypes);
   262             } catch (NoSuchMethodException exc) {
   263                 if (returnValueIfNonExistent == null) {
   264                     messager.error(Messager.NOPOS, "main.doclet_method_not_found",
   265                                    docletClassName, methodName);
   266                     throw new DocletInvokeException();
   267                 } else {
   268                     return returnValueIfNonExistent;
   269                 }
   270             } catch (SecurityException exc) {
   271                 messager.error(Messager.NOPOS, "main.doclet_method_not_accessible",
   272                                docletClassName, methodName);
   273                 throw new DocletInvokeException();
   274             }
   275             if (!Modifier.isStatic(meth.getModifiers())) {
   276                 messager.error(Messager.NOPOS, "main.doclet_method_must_be_static",
   277                                docletClassName, methodName);
   278                 throw new DocletInvokeException();
   279             }
   280             ClassLoader savedCCL =
   281                 Thread.currentThread().getContextClassLoader();
   282             try {
   283                 Thread.currentThread().setContextClassLoader(appClassLoader);
   284                 return meth.invoke(null , params);
   285             } catch (IllegalArgumentException exc) {
   286                 messager.error(Messager.NOPOS, "main.internal_error_exception_thrown",
   287                                docletClassName, methodName, exc.toString());
   288                 throw new DocletInvokeException();
   289             } catch (IllegalAccessException exc) {
   290                 messager.error(Messager.NOPOS, "main.doclet_method_not_accessible",
   291                                docletClassName, methodName);
   292                 throw new DocletInvokeException();
   293             } catch (NullPointerException exc) {
   294                 messager.error(Messager.NOPOS, "main.internal_error_exception_thrown",
   295                                docletClassName, methodName, exc.toString());
   296                 throw new DocletInvokeException();
   297             } catch (InvocationTargetException exc) {
   298                 Throwable err = exc.getTargetException();
   299                 if (err instanceof java.lang.OutOfMemoryError) {
   300                     messager.error(Messager.NOPOS, "main.out.of.memory");
   301                 } else {
   302                 messager.error(Messager.NOPOS, "main.exception_thrown",
   303                                docletClassName, methodName, exc.toString());
   304                     exc.getTargetException().printStackTrace();
   305                 }
   306                 throw new DocletInvokeException();
   307             } finally {
   308                 Thread.currentThread().setContextClassLoader(savedCCL);
   309             }
   310     }
   311 }

mercurial