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

Tue, 24 Dec 2013 09:17:37 -0800

author
ksrini
date
Tue, 24 Dec 2013 09:17:37 -0800
changeset 2227
998b10c43157
parent 1413
bdcef2ef52d2
child 2525
2eb010b6cb22
permissions
-rw-r--r--

8029230: Update copyright year to match last edit in jdk8 langtools repository for 2013
Reviewed-by: ksrini
Contributed-by: steve.sides@oracle.com

     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 javax.tools.DocumentationTool;
    36 import javax.tools.JavaFileManager;
    38 import com.sun.javadoc.*;
    39 import com.sun.tools.javac.file.Locations;
    40 import com.sun.tools.javac.util.ClientCodeException;
    41 import com.sun.tools.javac.util.List;
    42 import static com.sun.javadoc.LanguageVersion.*;
    45 /**
    46  * Class creates, controls and invokes doclets.
    47  *
    48  *  <p><b>This is NOT part of any supported API.
    49  *  If you write code that depends on this, you do so at your own risk.
    50  *  This code and its internal interfaces are subject to change or
    51  *  deletion without notice.</b>
    52  *
    53  * @author Neal Gafter (rewrite)
    54  */
    55 public class DocletInvoker {
    57     private final Class<?> docletClass;
    59     private final String docletClassName;
    61     private final ClassLoader appClassLoader;
    63     private final Messager messager;
    65     /**
    66      * In API mode, exceptions thrown while calling the doclet are
    67      * propagated using ClientCodeException.
    68      */
    69     private final boolean apiMode;
    71     private static class DocletInvokeException extends Exception {
    72         private static final long serialVersionUID = 0;
    73     }
    75     private String appendPath(String path1, String path2) {
    76         if (path1 == null || path1.length() == 0) {
    77             return path2 == null ? "." : path2;
    78         } else if (path2 == null || path2.length() == 0) {
    79             return path1;
    80         } else {
    81             return path1  + File.pathSeparator + path2;
    82         }
    83     }
    85     public DocletInvoker(Messager messager, Class<?> docletClass, boolean apiMode) {
    86         this.messager = messager;
    87         this.docletClass = docletClass;
    88         docletClassName = docletClass.getName();
    89         appClassLoader = null;
    90         this.apiMode = apiMode;
    91     }
    93     public DocletInvoker(Messager messager, JavaFileManager fileManager,
    94                          String docletClassName, String docletPath,
    95                          ClassLoader docletParentClassLoader,
    96                          boolean apiMode) {
    97         this.messager = messager;
    98         this.docletClassName = docletClassName;
    99         this.apiMode = apiMode;
   101         if (fileManager != null && fileManager.hasLocation(DocumentationTool.Location.DOCLET_PATH)) {
   102             appClassLoader = fileManager.getClassLoader(DocumentationTool.Location.DOCLET_PATH);
   103         } else {
   104             // construct class loader
   105             String cpString = null;   // make sure env.class.path defaults to dot
   107             // do prepends to get correct ordering
   108             cpString = appendPath(System.getProperty("env.class.path"), cpString);
   109             cpString = appendPath(System.getProperty("java.class.path"), cpString);
   110             cpString = appendPath(docletPath, cpString);
   111             URL[] urls = Locations.pathToURLs(cpString);
   112             if (docletParentClassLoader == null)
   113                 appClassLoader = new URLClassLoader(urls, getDelegationClassLoader(docletClassName));
   114             else
   115                 appClassLoader = new URLClassLoader(urls, docletParentClassLoader);
   116         }
   118         // attempt to find doclet
   119         Class<?> dc = null;
   120         try {
   121             dc = appClassLoader.loadClass(docletClassName);
   122         } catch (ClassNotFoundException exc) {
   123             messager.error(Messager.NOPOS, "main.doclet_class_not_found", docletClassName);
   124             messager.exit();
   125         }
   126         docletClass = dc;
   127     }
   129     /*
   130      * Returns the delegation class loader to use when creating
   131      * appClassLoader (used to load the doclet).  The context class
   132      * loader is the best choice, but legacy behavior was to use the
   133      * default delegation class loader (aka system class loader).
   134      *
   135      * Here we favor using the context class loader.  To ensure
   136      * compatibility with existing apps, we revert to legacy
   137      * behavior if either or both of the following conditions hold:
   138      *
   139      * 1) the doclet is loadable from the system class loader but not
   140      *    from the context class loader,
   141      *
   142      * 2) this.getClass() is loadable from the system class loader but not
   143      *    from the context class loader.
   144      */
   145     private ClassLoader getDelegationClassLoader(String docletClassName) {
   146         ClassLoader ctxCL = Thread.currentThread().getContextClassLoader();
   147         ClassLoader sysCL = ClassLoader.getSystemClassLoader();
   148         if (sysCL == null)
   149             return ctxCL;
   150         if (ctxCL == null)
   151             return sysCL;
   153         // Condition 1.
   154         try {
   155             sysCL.loadClass(docletClassName);
   156             try {
   157                 ctxCL.loadClass(docletClassName);
   158             } catch (ClassNotFoundException e) {
   159                 return sysCL;
   160             }
   161         } catch (ClassNotFoundException e) {
   162         }
   164         // Condition 2.
   165         try {
   166             if (getClass() == sysCL.loadClass(getClass().getName())) {
   167                 try {
   168                     if (getClass() != ctxCL.loadClass(getClass().getName()))
   169                         return sysCL;
   170                 } catch (ClassNotFoundException e) {
   171                     return sysCL;
   172                 }
   173             }
   174         } catch (ClassNotFoundException e) {
   175         }
   177         return ctxCL;
   178     }
   180     /**
   181      * Generate documentation here.  Return true on success.
   182      */
   183     public boolean start(RootDoc root) {
   184         Object retVal;
   185         String methodName = "start";
   186         Class<?>[] paramTypes = { RootDoc.class };
   187         Object[] params = { root };
   188         try {
   189             retVal = invoke(methodName, null, paramTypes, params);
   190         } catch (DocletInvokeException exc) {
   191             return false;
   192         }
   193         if (retVal instanceof Boolean) {
   194             return ((Boolean)retVal).booleanValue();
   195         } else {
   196             messager.error(Messager.NOPOS, "main.must_return_boolean",
   197                            docletClassName, methodName);
   198             return false;
   199         }
   200     }
   202     /**
   203      * Check for doclet added options here. Zero return means
   204      * option not known.  Positive value indicates number of
   205      * arguments to option.  Negative value means error occurred.
   206      */
   207     public int optionLength(String option) {
   208         Object retVal;
   209         String methodName = "optionLength";
   210         Class<?>[] paramTypes = { String.class };
   211         Object[] params = { option };
   212         try {
   213             retVal = invoke(methodName, new Integer(0), paramTypes, params);
   214         } catch (DocletInvokeException exc) {
   215             return -1;
   216         }
   217         if (retVal instanceof Integer) {
   218             return ((Integer)retVal).intValue();
   219         } else {
   220             messager.error(Messager.NOPOS, "main.must_return_int",
   221                            docletClassName, methodName);
   222             return -1;
   223         }
   224     }
   226     /**
   227      * Let doclet check that all options are OK. Returning true means
   228      * options are OK.  If method does not exist, assume true.
   229      */
   230     public boolean validOptions(List<String[]> optlist) {
   231         Object retVal;
   232         String options[][] = optlist.toArray(new String[optlist.length()][]);
   233         String methodName = "validOptions";
   234         DocErrorReporter reporter = messager;
   235         Class<?>[] paramTypes = { String[][].class, DocErrorReporter.class };
   236         Object[] params = { options, reporter };
   237         try {
   238             retVal = invoke(methodName, Boolean.TRUE, paramTypes, params);
   239         } catch (DocletInvokeException exc) {
   240             return false;
   241         }
   242         if (retVal instanceof Boolean) {
   243             return ((Boolean)retVal).booleanValue();
   244         } else {
   245             messager.error(Messager.NOPOS, "main.must_return_boolean",
   246                            docletClassName, methodName);
   247             return false;
   248         }
   249     }
   251     /**
   252      * Return the language version supported by this doclet.
   253      * If the method does not exist in the doclet, assume version 1.1.
   254      */
   255     public LanguageVersion languageVersion() {
   256         try {
   257             Object retVal;
   258             String methodName = "languageVersion";
   259             Class<?>[] paramTypes = new Class<?>[0];
   260             Object[] params = new Object[0];
   261             try {
   262                 retVal = invoke(methodName, JAVA_1_1, paramTypes, params);
   263             } catch (DocletInvokeException exc) {
   264                 return JAVA_1_1;
   265             }
   266             if (retVal instanceof LanguageVersion) {
   267                 return (LanguageVersion)retVal;
   268             } else {
   269                 messager.error(Messager.NOPOS, "main.must_return_languageversion",
   270                                docletClassName, methodName);
   271                 return JAVA_1_1;
   272             }
   273         } catch (NoClassDefFoundError ex) { // for boostrapping, no Enum class.
   274             return null;
   275         }
   276     }
   278     /**
   279      * Utility method for calling doclet functionality
   280      */
   281     private Object invoke(String methodName, Object returnValueIfNonExistent,
   282                           Class<?>[] paramTypes, Object[] params)
   283         throws DocletInvokeException {
   284             Method meth;
   285             try {
   286                 meth = docletClass.getMethod(methodName, paramTypes);
   287             } catch (NoSuchMethodException exc) {
   288                 if (returnValueIfNonExistent == null) {
   289                     messager.error(Messager.NOPOS, "main.doclet_method_not_found",
   290                                    docletClassName, methodName);
   291                     throw new DocletInvokeException();
   292                 } else {
   293                     return returnValueIfNonExistent;
   294                 }
   295             } catch (SecurityException exc) {
   296                 messager.error(Messager.NOPOS, "main.doclet_method_not_accessible",
   297                                docletClassName, methodName);
   298                 throw new DocletInvokeException();
   299             }
   300             if (!Modifier.isStatic(meth.getModifiers())) {
   301                 messager.error(Messager.NOPOS, "main.doclet_method_must_be_static",
   302                                docletClassName, methodName);
   303                 throw new DocletInvokeException();
   304             }
   305             ClassLoader savedCCL =
   306                 Thread.currentThread().getContextClassLoader();
   307             try {
   308                 if (appClassLoader != null) // will be null if doclet class provided via API
   309                     Thread.currentThread().setContextClassLoader(appClassLoader);
   310                 return meth.invoke(null , params);
   311             } catch (IllegalArgumentException exc) {
   312                 messager.error(Messager.NOPOS, "main.internal_error_exception_thrown",
   313                                docletClassName, methodName, exc.toString());
   314                 throw new DocletInvokeException();
   315             } catch (IllegalAccessException exc) {
   316                 messager.error(Messager.NOPOS, "main.doclet_method_not_accessible",
   317                                docletClassName, methodName);
   318                 throw new DocletInvokeException();
   319             } catch (NullPointerException exc) {
   320                 messager.error(Messager.NOPOS, "main.internal_error_exception_thrown",
   321                                docletClassName, methodName, exc.toString());
   322                 throw new DocletInvokeException();
   323             } catch (InvocationTargetException exc) {
   324                 Throwable err = exc.getTargetException();
   325                 if (apiMode)
   326                     throw new ClientCodeException(err);
   327                 if (err instanceof java.lang.OutOfMemoryError) {
   328                     messager.error(Messager.NOPOS, "main.out.of.memory");
   329                 } else {
   330                     messager.error(Messager.NOPOS, "main.exception_thrown",
   331                                docletClassName, methodName, exc.toString());
   332                     exc.getTargetException().printStackTrace();
   333                 }
   334                 throw new DocletInvokeException();
   335             } finally {
   336                 Thread.currentThread().setContextClassLoader(savedCCL);
   337             }
   338     }
   339 }

mercurial