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

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

mercurial