0N/A/*
911N/A * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
553N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
553N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
553N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
553N/A * or visit www.oracle.com if you need additional information or have any
553N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.tools.javadoc;
0N/A
0N/Aimport com.sun.javadoc.*;
0N/A
0N/Aimport static com.sun.javadoc.LanguageVersion.*;
0N/A
0N/Aimport com.sun.tools.javac.util.List;
0N/A
911N/Aimport java.io.File;
0N/Aimport java.lang.reflect.Method;
0N/Aimport java.lang.reflect.Modifier;
0N/Aimport java.lang.reflect.InvocationTargetException;
911N/Aimport java.net.URL;
911N/Aimport java.net.URLClassLoader;
0N/A
0N/A
0N/A/**
0N/A * Class creates, controls and invokes doclets.
0N/A * @author Neal Gafter (rewrite)
0N/A */
0N/Apublic class DocletInvoker {
0N/A
73N/A private final Class<?> docletClass;
0N/A
0N/A private final String docletClassName;
0N/A
0N/A private final ClassLoader appClassLoader;
0N/A
0N/A private final Messager messager;
0N/A
0N/A private static class DocletInvokeException extends Exception {
0N/A private static final long serialVersionUID = 0;
0N/A }
0N/A
0N/A private String appendPath(String path1, String path2) {
0N/A if (path1 == null || path1.length() == 0) {
0N/A return path2 == null ? "." : path2;
0N/A } else if (path2 == null || path2.length() == 0) {
0N/A return path1;
0N/A } else {
0N/A return path1 + File.pathSeparator + path2;
0N/A }
0N/A }
0N/A
0N/A public DocletInvoker(Messager messager,
128N/A String docletClassName, String docletPath,
128N/A ClassLoader docletParentClassLoader) {
0N/A this.messager = messager;
0N/A this.docletClassName = docletClassName;
0N/A
0N/A // construct class loader
0N/A String cpString = null; // make sure env.class.path defaults to dot
0N/A
0N/A // do prepends to get correct ordering
0N/A cpString = appendPath(System.getProperty("env.class.path"), cpString);
0N/A cpString = appendPath(System.getProperty("java.class.path"), cpString);
0N/A cpString = appendPath(docletPath, cpString);
496N/A URL[] urls = com.sun.tools.javac.file.Paths.pathToURLs(cpString);
128N/A if (docletParentClassLoader == null)
208N/A appClassLoader = new URLClassLoader(urls, getDelegationClassLoader(docletClassName));
128N/A else
128N/A appClassLoader = new URLClassLoader(urls, docletParentClassLoader);
0N/A
0N/A // attempt to find doclet
183N/A Class<?> dc = null;
0N/A try {
0N/A dc = appClassLoader.loadClass(docletClassName);
0N/A } catch (ClassNotFoundException exc) {
0N/A messager.error(null, "main.doclet_class_not_found", docletClassName);
0N/A messager.exit();
0N/A }
0N/A docletClass = dc;
0N/A }
0N/A
208N/A /*
208N/A * Returns the delegation class loader to use when creating
208N/A * appClassLoader (used to load the doclet). The context class
208N/A * loader is the best choice, but legacy behavior was to use the
208N/A * default delegation class loader (aka system class loader).
208N/A *
208N/A * Here we favor using the context class loader. To ensure
208N/A * compatibility with existing apps, we revert to legacy
208N/A * behavior if either or both of the following conditions hold:
208N/A *
208N/A * 1) the doclet is loadable from the system class loader but not
208N/A * from the context class loader,
208N/A *
208N/A * 2) this.getClass() is loadable from the system class loader but not
208N/A * from the context class loader.
208N/A */
208N/A private ClassLoader getDelegationClassLoader(String docletClassName) {
208N/A ClassLoader ctxCL = Thread.currentThread().getContextClassLoader();
208N/A ClassLoader sysCL = ClassLoader.getSystemClassLoader();
208N/A if (sysCL == null)
208N/A return ctxCL;
208N/A if (ctxCL == null)
208N/A return sysCL;
208N/A
208N/A // Condition 1.
208N/A try {
208N/A sysCL.loadClass(docletClassName);
208N/A try {
208N/A ctxCL.loadClass(docletClassName);
208N/A } catch (ClassNotFoundException e) {
208N/A return sysCL;
208N/A }
208N/A } catch (ClassNotFoundException e) {
208N/A }
208N/A
208N/A // Condition 2.
208N/A try {
208N/A if (getClass() == sysCL.loadClass(getClass().getName())) {
208N/A try {
208N/A if (getClass() != ctxCL.loadClass(getClass().getName()))
208N/A return sysCL;
208N/A } catch (ClassNotFoundException e) {
208N/A return sysCL;
208N/A }
208N/A }
208N/A } catch (ClassNotFoundException e) {
208N/A }
208N/A
208N/A return ctxCL;
208N/A }
208N/A
0N/A /**
0N/A * Generate documentation here. Return true on success.
0N/A */
0N/A public boolean start(RootDoc root) {
0N/A Object retVal;
0N/A String methodName = "start";
583N/A Class<?>[] paramTypes = { RootDoc.class };
583N/A Object[] params = { root };
0N/A try {
0N/A retVal = invoke(methodName, null, paramTypes, params);
0N/A } catch (DocletInvokeException exc) {
0N/A return false;
0N/A }
0N/A if (retVal instanceof Boolean) {
0N/A return ((Boolean)retVal).booleanValue();
0N/A } else {
0N/A messager.error(null, "main.must_return_boolean",
0N/A docletClassName, methodName);
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Check for doclet added options here. Zero return means
0N/A * option not known. Positive value indicates number of
0N/A * arguments to option. Negative value means error occurred.
0N/A */
0N/A public int optionLength(String option) {
0N/A Object retVal;
0N/A String methodName = "optionLength";
583N/A Class<?>[] paramTypes = { String.class };
583N/A Object[] params = { option };
0N/A try {
0N/A retVal = invoke(methodName, new Integer(0), paramTypes, params);
0N/A } catch (DocletInvokeException exc) {
0N/A return -1;
0N/A }
0N/A if (retVal instanceof Integer) {
0N/A return ((Integer)retVal).intValue();
0N/A } else {
0N/A messager.error(null, "main.must_return_int",
0N/A docletClassName, methodName);
0N/A return -1;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Let doclet check that all options are OK. Returning true means
0N/A * options are OK. If method does not exist, assume true.
0N/A */
0N/A public boolean validOptions(List<String[]> optlist) {
0N/A Object retVal;
0N/A String options[][] = optlist.toArray(new String[optlist.length()][]);
0N/A String methodName = "validOptions";
0N/A DocErrorReporter reporter = messager;
583N/A Class<?>[] paramTypes = { String[][].class, DocErrorReporter.class };
583N/A Object[] params = { options, reporter };
0N/A try {
0N/A retVal = invoke(methodName, Boolean.TRUE, paramTypes, params);
0N/A } catch (DocletInvokeException exc) {
0N/A return false;
0N/A }
0N/A if (retVal instanceof Boolean) {
0N/A return ((Boolean)retVal).booleanValue();
0N/A } else {
0N/A messager.error(null, "main.must_return_boolean",
0N/A docletClassName, methodName);
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Return the language version supported by this doclet.
0N/A * If the method does not exist in the doclet, assume version 1.1.
0N/A */
0N/A public LanguageVersion languageVersion() {
0N/A try {
0N/A Object retVal;
0N/A String methodName = "languageVersion";
183N/A Class<?>[] paramTypes = new Class<?>[0];
0N/A Object[] params = new Object[0];
0N/A try {
0N/A retVal = invoke(methodName, JAVA_1_1, paramTypes, params);
0N/A } catch (DocletInvokeException exc) {
0N/A return JAVA_1_1;
0N/A }
0N/A if (retVal instanceof LanguageVersion) {
0N/A return (LanguageVersion)retVal;
0N/A } else {
0N/A messager.error(null, "main.must_return_languageversion",
0N/A docletClassName, methodName);
0N/A return JAVA_1_1;
0N/A }
0N/A } catch (NoClassDefFoundError ex) { // for boostrapping, no Enum class.
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Utility method for calling doclet functionality
0N/A */
0N/A private Object invoke(String methodName, Object returnValueIfNonExistent,
183N/A Class<?>[] paramTypes, Object[] params)
0N/A throws DocletInvokeException {
0N/A Method meth;
0N/A try {
0N/A meth = docletClass.getMethod(methodName, paramTypes);
0N/A } catch (NoSuchMethodException exc) {
0N/A if (returnValueIfNonExistent == null) {
0N/A messager.error(null, "main.doclet_method_not_found",
0N/A docletClassName, methodName);
0N/A throw new DocletInvokeException();
0N/A } else {
0N/A return returnValueIfNonExistent;
0N/A }
0N/A } catch (SecurityException exc) {
0N/A messager.error(null, "main.doclet_method_not_accessible",
0N/A docletClassName, methodName);
0N/A throw new DocletInvokeException();
0N/A }
0N/A if (!Modifier.isStatic(meth.getModifiers())) {
0N/A messager.error(null, "main.doclet_method_must_be_static",
0N/A docletClassName, methodName);
0N/A throw new DocletInvokeException();
0N/A }
208N/A ClassLoader savedCCL =
208N/A Thread.currentThread().getContextClassLoader();
0N/A try {
0N/A Thread.currentThread().setContextClassLoader(appClassLoader);
0N/A return meth.invoke(null , params);
0N/A } catch (IllegalArgumentException exc) {
0N/A messager.error(null, "main.internal_error_exception_thrown",
0N/A docletClassName, methodName, exc.toString());
0N/A throw new DocletInvokeException();
0N/A } catch (IllegalAccessException exc) {
0N/A messager.error(null, "main.doclet_method_not_accessible",
0N/A docletClassName, methodName);
0N/A throw new DocletInvokeException();
0N/A } catch (NullPointerException exc) {
0N/A messager.error(null, "main.internal_error_exception_thrown",
0N/A docletClassName, methodName, exc.toString());
0N/A throw new DocletInvokeException();
0N/A } catch (InvocationTargetException exc) {
0N/A Throwable err = exc.getTargetException();
0N/A if (err instanceof java.lang.OutOfMemoryError) {
0N/A messager.error(null, "main.out.of.memory");
0N/A } else {
0N/A messager.error(null, "main.exception_thrown",
0N/A docletClassName, methodName, exc.toString());
0N/A exc.getTargetException().printStackTrace();
0N/A }
0N/A throw new DocletInvokeException();
208N/A } finally {
208N/A Thread.currentThread().setContextClassLoader(savedCCL);
0N/A }
0N/A }
0N/A}