001: /*
002: * Copyright 1998-2004 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.javadoc;
027:
028: import com.sun.javadoc.*;
029:
030: import static com.sun.javadoc.LanguageVersion.*;
031:
032: import com.sun.tools.javac.util.List;
033:
034: import java.net.*;
035: import java.lang.OutOfMemoryError;
036: import java.lang.reflect.Method;
037: import java.lang.reflect.Modifier;
038: import java.lang.reflect.InvocationTargetException;
039:
040: import java.io.File;
041: import java.io.IOException;
042: import java.util.StringTokenizer;
043:
044: /**
045: * Class creates, controls and invokes doclets.
046: * @author Neal Gafter (rewrite)
047: */
048: public class DocletInvoker {
049:
050: private final Class docletClass;
051:
052: private final String docletClassName;
053:
054: private final ClassLoader appClassLoader;
055:
056: private final Messager messager;
057:
058: private static class DocletInvokeException extends Exception {
059: private static final long serialVersionUID = 0;
060: }
061:
062: private String appendPath(String path1, String path2) {
063: if (path1 == null || path1.length() == 0) {
064: return path2 == null ? "." : path2;
065: } else if (path2 == null || path2.length() == 0) {
066: return path1;
067: } else {
068: return path1 + File.pathSeparator + path2;
069: }
070: }
071:
072: public DocletInvoker(Messager messager, String docletClassName,
073: String docletPath) {
074: this .messager = messager;
075: this .docletClassName = docletClassName;
076:
077: // construct class loader
078: String cpString = null; // make sure env.class.path defaults to dot
079:
080: // do prepends to get correct ordering
081: cpString = appendPath(System.getProperty("env.class.path"),
082: cpString);
083: cpString = appendPath(System.getProperty("java.class.path"),
084: cpString);
085: cpString = appendPath(docletPath, cpString);
086: URL[] urls = pathToURLs(cpString);
087: appClassLoader = new URLClassLoader(urls);
088:
089: // attempt to find doclet
090: Class dc = null;
091: try {
092: dc = appClassLoader.loadClass(docletClassName);
093: } catch (ClassNotFoundException exc) {
094: messager.error(null, "main.doclet_class_not_found",
095: docletClassName);
096: messager.exit();
097: }
098: docletClass = dc;
099: }
100:
101: /**
102: * Generate documentation here. Return true on success.
103: */
104: public boolean start(RootDoc root) {
105: Object retVal;
106: String methodName = "start";
107: Class[] paramTypes = new Class[1];
108: Object[] params = new Object[1];
109: paramTypes[0] = RootDoc.class;
110: params[0] = root;
111: try {
112: retVal = invoke(methodName, null, paramTypes, params);
113: } catch (DocletInvokeException exc) {
114: return false;
115: }
116: if (retVal instanceof Boolean) {
117: return ((Boolean) retVal).booleanValue();
118: } else {
119: messager.error(null, "main.must_return_boolean",
120: docletClassName, methodName);
121: return false;
122: }
123: }
124:
125: /**
126: * Check for doclet added options here. Zero return means
127: * option not known. Positive value indicates number of
128: * arguments to option. Negative value means error occurred.
129: */
130: public int optionLength(String option) {
131: Object retVal;
132: String methodName = "optionLength";
133: Class[] paramTypes = new Class[1];
134: Object[] params = new Object[1];
135: paramTypes[0] = option.getClass();
136: params[0] = option;
137: try {
138: retVal = invoke(methodName, new Integer(0), paramTypes,
139: params);
140: } catch (DocletInvokeException exc) {
141: return -1;
142: }
143: if (retVal instanceof Integer) {
144: return ((Integer) retVal).intValue();
145: } else {
146: messager.error(null, "main.must_return_int",
147: docletClassName, methodName);
148: return -1;
149: }
150: }
151:
152: /**
153: * Let doclet check that all options are OK. Returning true means
154: * options are OK. If method does not exist, assume true.
155: */
156: public boolean validOptions(List<String[]> optlist) {
157: Object retVal;
158: String options[][] = optlist.toArray(new String[optlist
159: .length()][]);
160: String methodName = "validOptions";
161: DocErrorReporter reporter = messager;
162: Class[] paramTypes = new Class[2];
163: Object[] params = new Object[2];
164: paramTypes[0] = options.getClass();
165: paramTypes[1] = DocErrorReporter.class;
166: params[0] = options;
167: params[1] = reporter;
168: try {
169: retVal = invoke(methodName, Boolean.TRUE, paramTypes,
170: params);
171: } catch (DocletInvokeException exc) {
172: return false;
173: }
174: if (retVal instanceof Boolean) {
175: return ((Boolean) retVal).booleanValue();
176: } else {
177: messager.error(null, "main.must_return_boolean",
178: docletClassName, methodName);
179: return false;
180: }
181: }
182:
183: /**
184: * Return the language version supported by this doclet.
185: * If the method does not exist in the doclet, assume version 1.1.
186: */
187: public LanguageVersion languageVersion() {
188: try {
189: Object retVal;
190: String methodName = "languageVersion";
191: Class[] paramTypes = new Class[0];
192: Object[] params = new Object[0];
193: try {
194: retVal = invoke(methodName, JAVA_1_1, paramTypes,
195: params);
196: } catch (DocletInvokeException exc) {
197: return JAVA_1_1;
198: }
199: if (retVal instanceof LanguageVersion) {
200: return (LanguageVersion) retVal;
201: } else {
202: messager.error(null,
203: "main.must_return_languageversion",
204: docletClassName, methodName);
205: return JAVA_1_1;
206: }
207: } catch (NoClassDefFoundError ex) { // for boostrapping, no Enum class.
208: return null;
209: }
210: }
211:
212: /**
213: * Utility method for calling doclet functionality
214: */
215: private Object invoke(String methodName,
216: Object returnValueIfNonExistent, Class[] paramTypes,
217: Object[] params) throws DocletInvokeException {
218: Method meth;
219: try {
220: meth = docletClass.getMethod(methodName, paramTypes);
221: } catch (NoSuchMethodException exc) {
222: if (returnValueIfNonExistent == null) {
223: messager.error(null, "main.doclet_method_not_found",
224: docletClassName, methodName);
225: throw new DocletInvokeException();
226: } else {
227: return returnValueIfNonExistent;
228: }
229: } catch (SecurityException exc) {
230: messager.error(null, "main.doclet_method_not_accessible",
231: docletClassName, methodName);
232: throw new DocletInvokeException();
233: }
234: if (!Modifier.isStatic(meth.getModifiers())) {
235: messager.error(null, "main.doclet_method_must_be_static",
236: docletClassName, methodName);
237: throw new DocletInvokeException();
238: }
239: try {
240: Thread.currentThread()
241: .setContextClassLoader(appClassLoader);
242: return meth.invoke(null, params);
243: } catch (IllegalArgumentException exc) {
244: messager.error(null,
245: "main.internal_error_exception_thrown",
246: docletClassName, methodName, exc.toString());
247: throw new DocletInvokeException();
248: } catch (IllegalAccessException exc) {
249: messager.error(null, "main.doclet_method_not_accessible",
250: docletClassName, methodName);
251: throw new DocletInvokeException();
252: } catch (NullPointerException exc) {
253: messager.error(null,
254: "main.internal_error_exception_thrown",
255: docletClassName, methodName, exc.toString());
256: throw new DocletInvokeException();
257: } catch (InvocationTargetException exc) {
258: Throwable err = exc.getTargetException();
259: if (err instanceof java.lang.OutOfMemoryError) {
260: messager.error(null, "main.out.of.memory");
261: } else {
262: messager.error(null, "main.exception_thrown",
263: docletClassName, methodName, exc.toString());
264: exc.getTargetException().printStackTrace();
265: }
266: throw new DocletInvokeException();
267: }
268: }
269:
270: /**
271: * Utility method for converting a search path string to an array
272: * of directory and JAR file URLs.
273: *
274: * @param path the search path string
275: * @return the resulting array of directory and JAR file URLs
276: */
277: static URL[] pathToURLs(String path) {
278: StringTokenizer st = new StringTokenizer(path,
279: File.pathSeparator);
280: URL[] urls = new URL[st.countTokens()];
281: int count = 0;
282: while (st.hasMoreTokens()) {
283: URL url = fileToURL(new File(st.nextToken()));
284: if (url != null) {
285: urls[count++] = url;
286: }
287: }
288: if (urls.length != count) {
289: URL[] tmp = new URL[count];
290: System.arraycopy(urls, 0, tmp, 0, count);
291: urls = tmp;
292: }
293: return urls;
294: }
295:
296: /**
297: * Returns the directory or JAR file URL corresponding to the specified
298: * local file name.
299: *
300: * @param file the File object
301: * @return the resulting directory or JAR file URL, or null if unknown
302: */
303: static URL fileToURL(File file) {
304: String name;
305: try {
306: name = file.getCanonicalPath();
307: } catch (IOException e) {
308: name = file.getAbsolutePath();
309: }
310: name = name.replace(File.separatorChar, '/');
311: if (!name.startsWith("/")) {
312: name = "/" + name;
313: }
314: // If the file does not exist, then assume that it's a directory
315: if (!file.isFile()) {
316: name = name + "/";
317: }
318: try {
319: return new URL("file", "", name);
320: } catch (MalformedURLException e) {
321: throw new IllegalArgumentException("file");
322: }
323: }
324: }
|