Thu, 02 Oct 2008 19:58:40 -0700
6754988: Update copyright year
Summary: Update for files that have been modified starting July 2008
Reviewed-by: ohair, tbell
1 /*
2 * Copyright 1998-2008 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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.OutOfMemoryError;
36 import java.lang.reflect.Method;
37 import java.lang.reflect.Modifier;
38 import java.lang.reflect.InvocationTargetException;
40 import java.io.File;
41 import java.io.IOException;
42 import java.util.StringTokenizer;
44 /**
45 * Class creates, controls and invokes doclets.
46 * @author Neal Gafter (rewrite)
47 */
48 public class DocletInvoker {
50 private final Class<?> docletClass;
52 private final String docletClassName;
54 private final ClassLoader appClassLoader;
56 private final Messager messager;
58 private static class DocletInvokeException extends Exception {
59 private static final long serialVersionUID = 0;
60 }
62 private String appendPath(String path1, String path2) {
63 if (path1 == null || path1.length() == 0) {
64 return path2 == null ? "." : path2;
65 } else if (path2 == null || path2.length() == 0) {
66 return path1;
67 } else {
68 return path1 + File.pathSeparator + path2;
69 }
70 }
72 public DocletInvoker(Messager messager,
73 String docletClassName, String docletPath) {
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 = pathToURLs(cpString);
85 appClassLoader = new URLClassLoader(urls);
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 * Generate documentation here. Return true on success.
100 */
101 public boolean start(RootDoc root) {
102 Object retVal;
103 String methodName = "start";
104 Class[] paramTypes = new Class[1];
105 Object[] params = new Object[1];
106 paramTypes[0] = RootDoc.class;
107 params[0] = root;
108 try {
109 retVal = invoke(methodName, null, paramTypes, params);
110 } catch (DocletInvokeException exc) {
111 return false;
112 }
113 if (retVal instanceof Boolean) {
114 return ((Boolean)retVal).booleanValue();
115 } else {
116 messager.error(null, "main.must_return_boolean",
117 docletClassName, methodName);
118 return false;
119 }
120 }
122 /**
123 * Check for doclet added options here. Zero return means
124 * option not known. Positive value indicates number of
125 * arguments to option. Negative value means error occurred.
126 */
127 public int optionLength(String option) {
128 Object retVal;
129 String methodName = "optionLength";
130 Class[] paramTypes = new Class[1];
131 Object[] params = new Object[1];
132 paramTypes[0] = option.getClass();
133 params[0] = option;
134 try {
135 retVal = invoke(methodName, new Integer(0), paramTypes, params);
136 } catch (DocletInvokeException exc) {
137 return -1;
138 }
139 if (retVal instanceof Integer) {
140 return ((Integer)retVal).intValue();
141 } else {
142 messager.error(null, "main.must_return_int",
143 docletClassName, methodName);
144 return -1;
145 }
146 }
148 /**
149 * Let doclet check that all options are OK. Returning true means
150 * options are OK. If method does not exist, assume true.
151 */
152 public boolean validOptions(List<String[]> optlist) {
153 Object retVal;
154 String options[][] = optlist.toArray(new String[optlist.length()][]);
155 String methodName = "validOptions";
156 DocErrorReporter reporter = messager;
157 Class[] paramTypes = new Class[2];
158 Object[] params = new Object[2];
159 paramTypes[0] = options.getClass();
160 paramTypes[1] = DocErrorReporter.class;
161 params[0] = options;
162 params[1] = reporter;
163 try {
164 retVal = invoke(methodName, Boolean.TRUE, 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 * Return the language version supported by this doclet.
179 * If the method does not exist in the doclet, assume version 1.1.
180 */
181 public LanguageVersion languageVersion() {
182 try {
183 Object retVal;
184 String methodName = "languageVersion";
185 Class[] paramTypes = new Class[0];
186 Object[] params = new Object[0];
187 try {
188 retVal = invoke(methodName, JAVA_1_1, paramTypes, params);
189 } catch (DocletInvokeException exc) {
190 return JAVA_1_1;
191 }
192 if (retVal instanceof LanguageVersion) {
193 return (LanguageVersion)retVal;
194 } else {
195 messager.error(null, "main.must_return_languageversion",
196 docletClassName, methodName);
197 return JAVA_1_1;
198 }
199 } catch (NoClassDefFoundError ex) { // for boostrapping, no Enum class.
200 return null;
201 }
202 }
204 /**
205 * Utility method for calling doclet functionality
206 */
207 private Object invoke(String methodName, Object returnValueIfNonExistent,
208 Class[] paramTypes, Object[] params)
209 throws DocletInvokeException {
210 Method meth;
211 try {
212 meth = docletClass.getMethod(methodName, paramTypes);
213 } catch (NoSuchMethodException exc) {
214 if (returnValueIfNonExistent == null) {
215 messager.error(null, "main.doclet_method_not_found",
216 docletClassName, methodName);
217 throw new DocletInvokeException();
218 } else {
219 return returnValueIfNonExistent;
220 }
221 } catch (SecurityException exc) {
222 messager.error(null, "main.doclet_method_not_accessible",
223 docletClassName, methodName);
224 throw new DocletInvokeException();
225 }
226 if (!Modifier.isStatic(meth.getModifiers())) {
227 messager.error(null, "main.doclet_method_must_be_static",
228 docletClassName, methodName);
229 throw new DocletInvokeException();
230 }
231 try {
232 Thread.currentThread().setContextClassLoader(appClassLoader);
233 return meth.invoke(null , params);
234 } catch (IllegalArgumentException exc) {
235 messager.error(null, "main.internal_error_exception_thrown",
236 docletClassName, methodName, exc.toString());
237 throw new DocletInvokeException();
238 } catch (IllegalAccessException exc) {
239 messager.error(null, "main.doclet_method_not_accessible",
240 docletClassName, methodName);
241 throw new DocletInvokeException();
242 } catch (NullPointerException exc) {
243 messager.error(null, "main.internal_error_exception_thrown",
244 docletClassName, methodName, exc.toString());
245 throw new DocletInvokeException();
246 } catch (InvocationTargetException exc) {
247 Throwable err = exc.getTargetException();
248 if (err instanceof java.lang.OutOfMemoryError) {
249 messager.error(null, "main.out.of.memory");
250 } else {
251 messager.error(null, "main.exception_thrown",
252 docletClassName, methodName, exc.toString());
253 exc.getTargetException().printStackTrace();
254 }
255 throw new DocletInvokeException();
256 }
257 }
259 /**
260 * Utility method for converting a search path string to an array
261 * of directory and JAR file URLs.
262 *
263 * @param path the search path string
264 * @return the resulting array of directory and JAR file URLs
265 */
266 static URL[] pathToURLs(String path) {
267 StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
268 URL[] urls = new URL[st.countTokens()];
269 int count = 0;
270 while (st.hasMoreTokens()) {
271 URL url = fileToURL(new File(st.nextToken()));
272 if (url != null) {
273 urls[count++] = url;
274 }
275 }
276 if (urls.length != count) {
277 URL[] tmp = new URL[count];
278 System.arraycopy(urls, 0, tmp, 0, count);
279 urls = tmp;
280 }
281 return urls;
282 }
284 /**
285 * Returns the directory or JAR file URL corresponding to the specified
286 * local file name.
287 *
288 * @param file the File object
289 * @return the resulting directory or JAR file URL, or null if unknown
290 */
291 static URL fileToURL(File file) {
292 String name;
293 try {
294 name = file.getCanonicalPath();
295 } catch (IOException e) {
296 name = file.getAbsolutePath();
297 }
298 name = name.replace(File.separatorChar, '/');
299 if (!name.startsWith("/")) {
300 name = "/" + name;
301 }
302 // If the file does not exist, then assume that it's a directory
303 if (!file.isFile()) {
304 name = name + "/";
305 }
306 try {
307 return new URL("file", "", name);
308 } catch (MalformedURLException e) {
309 throw new IllegalArgumentException("file");
310 }
311 }
312 }