duke@1: /* jjg@1357: * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this duke@1: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javadoc; duke@1: jjg@912: import java.io.File; jjg@1357: import java.lang.reflect.InvocationTargetException; duke@1: import java.lang.reflect.Method; duke@1: import java.lang.reflect.Modifier; jjg@912: import java.net.URL; jjg@912: import java.net.URLClassLoader; duke@1: jjg@1413: import javax.tools.DocumentationTool; jjg@1413: import javax.tools.JavaFileManager; jjg@1413: jjg@1357: import com.sun.javadoc.*; jjg@1413: import com.sun.tools.javac.file.Locations; jjg@1413: import com.sun.tools.javac.util.ClientCodeException; jjg@1357: import com.sun.tools.javac.util.List; jjg@1357: import static com.sun.javadoc.LanguageVersion.*; jjg@1357: duke@1: duke@1: /** duke@1: * Class creates, controls and invokes doclets. jjg@1359: * jjg@1359: *
This is NOT part of any supported API.
jjg@1359: * If you write code that depends on this, you do so at your own risk.
jjg@1359: * This code and its internal interfaces are subject to change or
jjg@1359: * deletion without notice.
jjg@1359: *
duke@1: * @author Neal Gafter (rewrite)
duke@1: */
duke@1: public class DocletInvoker {
duke@1:
jjg@74: private final Class> docletClass;
duke@1:
duke@1: private final String docletClassName;
duke@1:
duke@1: private final ClassLoader appClassLoader;
duke@1:
duke@1: private final Messager messager;
duke@1:
jjg@1413: /**
jjg@1413: * In API mode, exceptions thrown while calling the doclet are
jjg@1413: * propagated using ClientCodeException.
jjg@1413: */
jjg@1413: private final boolean apiMode;
jjg@1413:
duke@1: private static class DocletInvokeException extends Exception {
duke@1: private static final long serialVersionUID = 0;
duke@1: }
duke@1:
duke@1: private String appendPath(String path1, String path2) {
duke@1: if (path1 == null || path1.length() == 0) {
duke@1: return path2 == null ? "." : path2;
duke@1: } else if (path2 == null || path2.length() == 0) {
duke@1: return path1;
duke@1: } else {
duke@1: return path1 + File.pathSeparator + path2;
duke@1: }
duke@1: }
duke@1:
jjg@1413: public DocletInvoker(Messager messager, Class> docletClass, boolean apiMode) {
jjg@1413: this.messager = messager;
jjg@1413: this.docletClass = docletClass;
jjg@1413: docletClassName = docletClass.getName();
jjg@1413: appClassLoader = null;
jjg@1413: this.apiMode = apiMode;
jjg@1413: }
jjg@1413:
jjg@1413: public DocletInvoker(Messager messager, JavaFileManager fileManager,
jjg@129: String docletClassName, String docletPath,
jjg@1413: ClassLoader docletParentClassLoader,
jjg@1413: boolean apiMode) {
duke@1: this.messager = messager;
duke@1: this.docletClassName = docletClassName;
jjg@1413: this.apiMode = apiMode;
duke@1:
jjg@1413: if (fileManager != null && fileManager.hasLocation(DocumentationTool.Location.DOCLET_PATH)) {
jjg@1413: appClassLoader = fileManager.getClassLoader(DocumentationTool.Location.DOCLET_PATH);
jjg@1413: } else {
jjg@1413: // construct class loader
jjg@1413: String cpString = null; // make sure env.class.path defaults to dot
duke@1:
jjg@1413: // do prepends to get correct ordering
jjg@1413: cpString = appendPath(System.getProperty("env.class.path"), cpString);
jjg@1413: cpString = appendPath(System.getProperty("java.class.path"), cpString);
jjg@1413: cpString = appendPath(docletPath, cpString);
jjg@1413: URL[] urls = Locations.pathToURLs(cpString);
jjg@1413: if (docletParentClassLoader == null)
jjg@1413: appClassLoader = new URLClassLoader(urls, getDelegationClassLoader(docletClassName));
jjg@1413: else
jjg@1413: appClassLoader = new URLClassLoader(urls, docletParentClassLoader);
jjg@1413: }
duke@1:
duke@1: // attempt to find doclet
mcimadamore@184: Class> dc = null;
duke@1: try {
duke@1: dc = appClassLoader.loadClass(docletClassName);
duke@1: } catch (ClassNotFoundException exc) {
jjg@1411: messager.error(Messager.NOPOS, "main.doclet_class_not_found", docletClassName);
duke@1: messager.exit();
duke@1: }
duke@1: docletClass = dc;
duke@1: }
duke@1:
jjg@209: /*
jjg@209: * Returns the delegation class loader to use when creating
jjg@209: * appClassLoader (used to load the doclet). The context class
jjg@209: * loader is the best choice, but legacy behavior was to use the
jjg@209: * default delegation class loader (aka system class loader).
jjg@209: *
jjg@209: * Here we favor using the context class loader. To ensure
jjg@209: * compatibility with existing apps, we revert to legacy
jjg@209: * behavior if either or both of the following conditions hold:
jjg@209: *
jjg@209: * 1) the doclet is loadable from the system class loader but not
jjg@209: * from the context class loader,
jjg@209: *
jjg@209: * 2) this.getClass() is loadable from the system class loader but not
jjg@209: * from the context class loader.
jjg@209: */
jjg@209: private ClassLoader getDelegationClassLoader(String docletClassName) {
jjg@209: ClassLoader ctxCL = Thread.currentThread().getContextClassLoader();
jjg@209: ClassLoader sysCL = ClassLoader.getSystemClassLoader();
jjg@209: if (sysCL == null)
jjg@209: return ctxCL;
jjg@209: if (ctxCL == null)
jjg@209: return sysCL;
jjg@209:
jjg@209: // Condition 1.
jjg@209: try {
jjg@209: sysCL.loadClass(docletClassName);
jjg@209: try {
jjg@209: ctxCL.loadClass(docletClassName);
jjg@209: } catch (ClassNotFoundException e) {
jjg@209: return sysCL;
jjg@209: }
jjg@209: } catch (ClassNotFoundException e) {
jjg@209: }
jjg@209:
jjg@209: // Condition 2.
jjg@209: try {
jjg@209: if (getClass() == sysCL.loadClass(getClass().getName())) {
jjg@209: try {
jjg@209: if (getClass() != ctxCL.loadClass(getClass().getName()))
jjg@209: return sysCL;
jjg@209: } catch (ClassNotFoundException e) {
jjg@209: return sysCL;
jjg@209: }
jjg@209: }
jjg@209: } catch (ClassNotFoundException e) {
jjg@209: }
jjg@209:
jjg@209: return ctxCL;
jjg@209: }
jjg@209:
duke@1: /**
duke@1: * Generate documentation here. Return true on success.
duke@1: */
duke@1: public boolean start(RootDoc root) {
duke@1: Object retVal;
duke@1: String methodName = "start";
jjg@584: Class>[] paramTypes = { RootDoc.class };
jjg@584: Object[] params = { root };
duke@1: try {
duke@1: retVal = invoke(methodName, null, paramTypes, params);
duke@1: } catch (DocletInvokeException exc) {
duke@1: return false;
duke@1: }
duke@1: if (retVal instanceof Boolean) {
duke@1: return ((Boolean)retVal).booleanValue();
duke@1: } else {
jjg@1411: messager.error(Messager.NOPOS, "main.must_return_boolean",
duke@1: docletClassName, methodName);
duke@1: return false;
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Check for doclet added options here. Zero return means
duke@1: * option not known. Positive value indicates number of
duke@1: * arguments to option. Negative value means error occurred.
duke@1: */
duke@1: public int optionLength(String option) {
duke@1: Object retVal;
duke@1: String methodName = "optionLength";
jjg@584: Class>[] paramTypes = { String.class };
jjg@584: Object[] params = { option };
duke@1: try {
duke@1: retVal = invoke(methodName, new Integer(0), paramTypes, params);
duke@1: } catch (DocletInvokeException exc) {
duke@1: return -1;
duke@1: }
duke@1: if (retVal instanceof Integer) {
duke@1: return ((Integer)retVal).intValue();
duke@1: } else {
jjg@1411: messager.error(Messager.NOPOS, "main.must_return_int",
duke@1: docletClassName, methodName);
duke@1: return -1;
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Let doclet check that all options are OK. Returning true means
duke@1: * options are OK. If method does not exist, assume true.
duke@1: */
duke@1: public boolean validOptions(List