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

Thu, 04 Feb 2010 10:14:28 -0800

author
jjg
date
Thu, 04 Feb 2010 10:14:28 -0800
changeset 489
4b4e282a3146
parent 229
03bcd66bd8e7
child 497
16b9b7f45933
permissions
-rw-r--r--

6923080: TreeScanner.visitNewClass should scan tree.typeargs
Reviewed-by: darcy

     1 /*
     2  * Copyright 1998-2009 Sun Microsystems, Inc.  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.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    26 package com.sun.tools.javadoc;
    28 import com.sun.javadoc.*;
    30 import static com.sun.javadoc.LanguageVersion.*;
    32 import com.sun.tools.javac.util.List;
    34 import java.net.*;
    35 import java.lang.reflect.Method;
    36 import java.lang.reflect.Modifier;
    37 import java.lang.reflect.InvocationTargetException;
    39 import java.io.File;
    40 import java.io.IOException;
    41 import java.util.StringTokenizer;
    43 /**
    44  * Class creates, controls and invokes doclets.
    45  * @author Neal Gafter (rewrite)
    46  */
    47 public class DocletInvoker {
    49     private final Class<?> docletClass;
    51     private final String docletClassName;
    53     private final ClassLoader appClassLoader;
    55     private final Messager messager;
    57     private static class DocletInvokeException extends Exception {
    58         private static final long serialVersionUID = 0;
    59     }
    61     private String appendPath(String path1, String path2) {
    62         if (path1 == null || path1.length() == 0) {
    63             return path2 == null ? "." : path2;
    64         } else if (path2 == null || path2.length() == 0) {
    65             return path1;
    66         } else {
    67             return path1  + File.pathSeparator + path2;
    68         }
    69     }
    71     public DocletInvoker(Messager messager,
    72                          String docletClassName, String docletPath,
    73                          ClassLoader docletParentClassLoader) {
    74         this.messager = messager;
    75         this.docletClassName = docletClassName;
    77         // construct class loader
    78         String cpString = null;   // make sure env.class.path defaults to dot
    80         // do prepends to get correct ordering
    81         cpString = appendPath(System.getProperty("env.class.path"), cpString);
    82         cpString = appendPath(System.getProperty("java.class.path"), cpString);
    83         cpString = appendPath(docletPath, cpString);
    84         URL[] urls = pathToURLs(cpString);
    85         if (docletParentClassLoader == null)
    86             appClassLoader = new URLClassLoader(urls, getDelegationClassLoader(docletClassName));
    87         else
    88             appClassLoader = new URLClassLoader(urls, docletParentClassLoader);
    90         // attempt to find doclet
    91         Class<?> dc = null;
    92         try {
    93             dc = appClassLoader.loadClass(docletClassName);
    94         } catch (ClassNotFoundException exc) {
    95             messager.error(null, "main.doclet_class_not_found", docletClassName);
    96             messager.exit();
    97         }
    98         docletClass = dc;
    99     }
   101     /*
   102      * Returns the delegation class loader to use when creating
   103      * appClassLoader (used to load the doclet).  The context class
   104      * loader is the best choice, but legacy behavior was to use the
   105      * default delegation class loader (aka system class loader).
   106      *
   107      * Here we favor using the context class loader.  To ensure
   108      * compatibility with existing apps, we revert to legacy
   109      * behavior if either or both of the following conditions hold:
   110      *
   111      * 1) the doclet is loadable from the system class loader but not
   112      *    from the context class loader,
   113      *
   114      * 2) this.getClass() is loadable from the system class loader but not
   115      *    from the context class loader.
   116      */
   117     private ClassLoader getDelegationClassLoader(String docletClassName) {
   118         ClassLoader ctxCL = Thread.currentThread().getContextClassLoader();
   119         ClassLoader sysCL = ClassLoader.getSystemClassLoader();
   120         if (sysCL == null)
   121             return ctxCL;
   122         if (ctxCL == null)
   123             return sysCL;
   125         // Condition 1.
   126         try {
   127             sysCL.loadClass(docletClassName);
   128             try {
   129                 ctxCL.loadClass(docletClassName);
   130             } catch (ClassNotFoundException e) {
   131                 return sysCL;
   132             }
   133         } catch (ClassNotFoundException e) {
   134         }
   136         // Condition 2.
   137         try {
   138             if (getClass() == sysCL.loadClass(getClass().getName())) {
   139                 try {
   140                     if (getClass() != ctxCL.loadClass(getClass().getName()))
   141                         return sysCL;
   142                 } catch (ClassNotFoundException e) {
   143                     return sysCL;
   144                 }
   145             }
   146         } catch (ClassNotFoundException e) {
   147         }
   149         return ctxCL;
   150     }
   152     /**
   153      * Generate documentation here.  Return true on success.
   154      */
   155     public boolean start(RootDoc root) {
   156         Object retVal;
   157         String methodName = "start";
   158         Class<?>[] paramTypes = new Class<?>[1];
   159         Object[] params = new Object[1];
   160         paramTypes[0] = RootDoc.class;
   161         params[0] = root;
   162         try {
   163             retVal = invoke(methodName, null, paramTypes, params);
   164         } catch (DocletInvokeException exc) {
   165             return false;
   166         }
   167         if (retVal instanceof Boolean) {
   168             return ((Boolean)retVal).booleanValue();
   169         } else {
   170             messager.error(null, "main.must_return_boolean",
   171                            docletClassName, methodName);
   172             return false;
   173         }
   174     }
   176     /**
   177      * Check for doclet added options here. Zero return means
   178      * option not known.  Positive value indicates number of
   179      * arguments to option.  Negative value means error occurred.
   180      */
   181     public int optionLength(String option) {
   182         Object retVal;
   183         String methodName = "optionLength";
   184         Class<?>[] paramTypes = new Class<?>[1];
   185         Object[] params = new Object[1];
   186         paramTypes[0] = option.getClass();
   187         params[0] = option;
   188         try {
   189             retVal = invoke(methodName, new Integer(0), paramTypes, params);
   190         } catch (DocletInvokeException exc) {
   191             return -1;
   192         }
   193         if (retVal instanceof Integer) {
   194             return ((Integer)retVal).intValue();
   195         } else {
   196             messager.error(null, "main.must_return_int",
   197                            docletClassName, methodName);
   198             return -1;
   199         }
   200     }
   202     /**
   203      * Let doclet check that all options are OK. Returning true means
   204      * options are OK.  If method does not exist, assume true.
   205      */
   206     public boolean validOptions(List<String[]> optlist) {
   207         Object retVal;
   208         String options[][] = optlist.toArray(new String[optlist.length()][]);
   209         String methodName = "validOptions";
   210         DocErrorReporter reporter = messager;
   211         Class<?>[] paramTypes = new Class<?>[2];
   212         Object[] params = new Object[2];
   213         paramTypes[0] = options.getClass();
   214         paramTypes[1] = DocErrorReporter.class;
   215         params[0] = options;
   216         params[1] = reporter;
   217         try {
   218             retVal = invoke(methodName, Boolean.TRUE, paramTypes, params);
   219         } catch (DocletInvokeException exc) {
   220             return false;
   221         }
   222         if (retVal instanceof Boolean) {
   223             return ((Boolean)retVal).booleanValue();
   224         } else {
   225             messager.error(null, "main.must_return_boolean",
   226                            docletClassName, methodName);
   227             return false;
   228         }
   229     }
   231     /**
   232      * Return the language version supported by this doclet.
   233      * If the method does not exist in the doclet, assume version 1.1.
   234      */
   235     public LanguageVersion languageVersion() {
   236         try {
   237             Object retVal;
   238             String methodName = "languageVersion";
   239             Class<?>[] paramTypes = new Class<?>[0];
   240             Object[] params = new Object[0];
   241             try {
   242                 retVal = invoke(methodName, JAVA_1_1, paramTypes, params);
   243             } catch (DocletInvokeException exc) {
   244                 return JAVA_1_1;
   245             }
   246             if (retVal instanceof LanguageVersion) {
   247                 return (LanguageVersion)retVal;
   248             } else {
   249                 messager.error(null, "main.must_return_languageversion",
   250                                docletClassName, methodName);
   251                 return JAVA_1_1;
   252             }
   253         } catch (NoClassDefFoundError ex) { // for boostrapping, no Enum class.
   254             return null;
   255         }
   256     }
   258     /**
   259      * Utility method for calling doclet functionality
   260      */
   261     private Object invoke(String methodName, Object returnValueIfNonExistent,
   262                           Class<?>[] paramTypes, Object[] params)
   263         throws DocletInvokeException {
   264             Method meth;
   265             try {
   266                 meth = docletClass.getMethod(methodName, paramTypes);
   267             } catch (NoSuchMethodException exc) {
   268                 if (returnValueIfNonExistent == null) {
   269                     messager.error(null, "main.doclet_method_not_found",
   270                                    docletClassName, methodName);
   271                     throw new DocletInvokeException();
   272                 } else {
   273                     return returnValueIfNonExistent;
   274                 }
   275             } catch (SecurityException exc) {
   276                 messager.error(null, "main.doclet_method_not_accessible",
   277                                docletClassName, methodName);
   278                 throw new DocletInvokeException();
   279             }
   280             if (!Modifier.isStatic(meth.getModifiers())) {
   281                 messager.error(null, "main.doclet_method_must_be_static",
   282                                docletClassName, methodName);
   283                 throw new DocletInvokeException();
   284             }
   285             ClassLoader savedCCL =
   286                 Thread.currentThread().getContextClassLoader();
   287             try {
   288                 Thread.currentThread().setContextClassLoader(appClassLoader);
   289                 return meth.invoke(null , params);
   290             } catch (IllegalArgumentException exc) {
   291                 messager.error(null, "main.internal_error_exception_thrown",
   292                                docletClassName, methodName, exc.toString());
   293                 throw new DocletInvokeException();
   294             } catch (IllegalAccessException exc) {
   295                 messager.error(null, "main.doclet_method_not_accessible",
   296                                docletClassName, methodName);
   297                 throw new DocletInvokeException();
   298             } catch (NullPointerException exc) {
   299                 messager.error(null, "main.internal_error_exception_thrown",
   300                                docletClassName, methodName, exc.toString());
   301                 throw new DocletInvokeException();
   302             } catch (InvocationTargetException exc) {
   303                 Throwable err = exc.getTargetException();
   304                 if (err instanceof java.lang.OutOfMemoryError) {
   305                     messager.error(null, "main.out.of.memory");
   306                 } else {
   307                 messager.error(null, "main.exception_thrown",
   308                                docletClassName, methodName, exc.toString());
   309                     exc.getTargetException().printStackTrace();
   310                 }
   311                 throw new DocletInvokeException();
   312             } finally {
   313                 Thread.currentThread().setContextClassLoader(savedCCL);
   314             }
   315     }
   317     /**
   318      * Utility method for converting a search path string to an array
   319      * of directory and JAR file URLs.
   320      *
   321      * @param path the search path string
   322      * @return the resulting array of directory and JAR file URLs
   323      */
   324     static URL[] pathToURLs(String path) {
   325         StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
   326         URL[] urls = new URL[st.countTokens()];
   327         int count = 0;
   328         while (st.hasMoreTokens()) {
   329             URL url = fileToURL(new File(st.nextToken()));
   330             if (url != null) {
   331                 urls[count++] = url;
   332             }
   333         }
   334         if (urls.length != count) {
   335             URL[] tmp = new URL[count];
   336             System.arraycopy(urls, 0, tmp, 0, count);
   337             urls = tmp;
   338         }
   339         return urls;
   340     }
   342     /**
   343      * Returns the directory or JAR file URL corresponding to the specified
   344      * local file name.
   345      *
   346      * @param file the File object
   347      * @return the resulting directory or JAR file URL, or null if unknown
   348      */
   349     static URL fileToURL(File file) {
   350         String name;
   351         try {
   352             name = file.getCanonicalPath();
   353         } catch (IOException e) {
   354             name = file.getAbsolutePath();
   355         }
   356         name = name.replace(File.separatorChar, '/');
   357         if (!name.startsWith("/")) {
   358             name = "/" + name;
   359         }
   360         // If the file does not exist, then assume that it's a directory
   361         if (!file.isFile()) {
   362             name = name + "/";
   363         }
   364         try {
   365             return new URL("file", "", name);
   366         } catch (MalformedURLException e) {
   367             throw new IllegalArgumentException("file");
   368         }
   369     }
   370 }

mercurial