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