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