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