001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.tools.javah;
019:
020: import java.io.File;
021: import java.io.FileWriter;
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.Map;
026: import java.util.Set;
027:
028: import org.apache.harmony.tools.ClassProvider;
029: import org.apache.harmony.tools.Mangler;
030:
031: /**
032: * This is a tool that allows you to create JNI-style header files.
033: */
034: public class Main {
035:
036: /**
037: * Prints the usage information.
038: */
039: public static void usage() {
040: System.out.println("JNI-style header files generator.");
041: System.out.println();
042: System.out.println("Usage: " + Main.class.getName()
043: + " [options] <class names>\n");
044: System.out.println("[options]");
045: System.out
046: .println(" -bootclasspath <path> The given path will be prepended to");
047: System.out
048: .println(" the default class path.\n");
049: System.out
050: .println(" -classpath <path> The given path will be appended to");
051: System.out
052: .println(" the default class path.\n");
053: System.out
054: .println(" -o <file> All the output will be redirected into");
055: System.out
056: .println(" the specified file.\n");
057: System.out
058: .println(" -d <directory> All the header files will be created in the specified");
059: System.out
060: .println(" directory which will be created if it is necessary.\n");
061: System.out
062: .println(" -inner Inner classes will be processed automatically.\n");
063: System.out
064: .println(" -verbose Allows running commentary.\n");
065: System.out
066: .println("<class names> A list of the fully qualified class names");
067: System.out
068: .println(" separated by a space.");
069: }
070:
071: /**
072: * A convenient way to run this tool from a command line.
073: */
074: public static void main(String args[]) {
075: if (args.length < 1) {
076: usage();
077: System.exit(1);
078: }
079:
080: String pathSep = null;
081: try {
082: pathSep = System.getProperty("path.separator");
083: } catch (SecurityException e) {
084: // ignore
085: }
086:
087: Set names = new HashSet();
088: Map options = new HashMap();
089: int i = 0;
090: while (i < args.length) {
091: if (args[i].equals("-bootclasspath")
092: || args[i].equals("-classpath")) {
093: String path = (String) args[i + 1];
094:
095: // We can concatenate several options, if there is no
096: // security restriction; otherwise, the new option replaces
097: // the old one.
098: if (pathSep != null) {
099: String prevPath = (String) options.get(args[i]);
100: if (prevPath != null) {
101: path = prevPath + pathSep + path;
102: }
103: }
104:
105: options.put(args[i], path);
106: i++;
107: } else if (args[i].equals("-d") || args[i].equals("-o")) {
108: options.put(args[i], new File(args[i + 1]));
109: i++;
110: } else if (args[i].equals("-inner")
111: || args[i].equals("-verbose")) {
112: options.put(args[i], Boolean.valueOf(true));
113: } else {
114: names.add(args[i]);
115: }
116: i++;
117: }
118: System.exit(run(options, names));
119: }
120:
121: /**
122: * Runs a tool.
123: *
124: * @param options - a <code>java.util.Map</code> of the following key-value
125: * pairs.
126: * <p> Note, "-d" can not be specified with "-o" at the same time.
127: *
128: * <li> <i>key</i> - "-d"
129: * <li> <i>value</i> - a <code>java.io.File</code> object which represents
130: * a directory where the generated header files will be created.
131: * If this directory does not exist it will be created.
132: *
133: * <li> <i>key</i> - "-o"
134: * <li> <i>value</i> - a <code>java.io.File</code> object where
135: * the output will be directed to.
136: *
137: * <li> <i>key</i> - "-bootclasspath"
138: * <li> <i>value</i> - a <code>java.lang.String</code> which is a path
139: * where bootstrap classes are located.
140: *
141: * <li> <i>key</i> - "-classpath"
142: * <li> <i>value</i> - a <code>java.lang.String</code> which is a path
143: * where classes are located.
144: *
145: * <li> <i>key</i> - "-inner"
146: * <li> <i>value</i> - a <code>java.lang.Boolean</code> which is equal to
147: * <code>true</code>, if inner classes should be processed automatically,
148: * that is, as if they are specified as elements of
149: * the <code>classNames</code> parameter.
150: *
151: * <li> <i>key</i> - "-verbose"
152: * <li> <i>value</i> - a <code>java.lang.Boolean</code> which is equal to
153: * <code>true</code>, if the verbose output is needed.
154: *
155: * @param classNames - a set of the fully qualified class names.
156: * @return <code>0</code> if there is no error; <code>1</code> otherwise.
157: */
158: public static int run(Map options, Set classNames) {
159: File outDirectory = getFile(options, "-d");
160: File outFile = getFile(options, "-o");
161:
162: if (outDirectory != null && outFile != null) {
163: System.err
164: .println("Error: You can not use both -d and -o options.");
165: System.err.println();
166: usage();
167: return 1;
168: }
169:
170: String bootClasspath = getString(options, "-bootclasspath");
171: String classpath = getString(options, "-classpath");
172:
173: boolean inner = getBoolean(options, "-inner");
174: boolean verbose = getBoolean(options, "-verbose");
175:
176: ClassProvider classProvider = new ClassProvider(bootClasspath,
177: classpath, verbose);
178: try {
179: String n = System.getProperty("line.separator");
180: String warning = "/*"
181: + n
182: + " * THE FILE HAS BEEN AUTOGENERATED BY THE IJH TOOL."
183: + n
184: + " * Please be aware that all changes made to this file manually"
185: + n
186: + " * will be overwritten by the tool if it runs again."
187: + n + " */" + n + n + "#include <jni.h>" + n + n;
188:
189: StringBuffer result = new StringBuffer();
190: Set innerNames = new HashSet();
191: File file = outFile;
192: Iterator namesIter = classNames.iterator();
193: while (namesIter.hasNext()) {
194:
195: // Parse the next class.
196: Clazz clazz = new Clazz(classProvider,
197: (String) namesIter.next());
198:
199: if (inner) {
200: // Get the inner class names and store them
201: // in a separate set.
202: String innerClassNames[] = clazz
203: .getInnerClassNames();
204: for (int i = 0; i < innerClassNames.length; i++) {
205: innerNames.add(innerClassNames[i]);
206: }
207: }
208:
209: if (outFile == null) {
210: // Each header should be written into the separate file.
211:
212: String fileName = Mangler.mangleFileName(clazz
213: .getName())
214: + ".h";
215:
216: if (outDirectory != null) {
217: if (!outDirectory.exists()) {
218: outDirectory.mkdir();
219: }
220: fileName = outDirectory.getPath()
221: + File.separator + fileName;
222: }
223:
224: // Reset the output file.
225: file = new File(fileName);
226:
227: // Reset the result buffer.
228: result = new StringBuffer();
229: }
230:
231: // Append the next header.
232: result.append(Main.getHeader(clazz));
233:
234: // Process the inner class names, if any.
235: if (!namesIter.hasNext() && !innerNames.isEmpty()) {
236: // Remove the inner class names that have already
237: // been processed.
238: innerNames.removeAll(classNames);
239:
240: // Reset the loop iterator.
241: classNames = new HashSet(innerNames);
242: namesIter = classNames.iterator();
243:
244: // Clear the set of the inner class names.
245: innerNames.clear();
246: }
247:
248: // Write the result into a file.
249: if (outFile == null || !namesIter.hasNext()) {
250: FileWriter writer = new FileWriter(file);
251: try {
252: writer.write(warning);
253: writer.write(result.toString());
254: if (verbose) {
255: System.out.println(file.getName()
256: + " created");
257: }
258: } finally {
259: writer.close();
260: }
261: }
262: }
263:
264: } catch (Exception e) {
265: System.err.println("Error:");
266: e.printStackTrace();
267: return 1;
268: }
269:
270: return 0;
271: }
272:
273: /**
274: * Returns a value of an option with the given name. The return type
275: * is <code>java.io.File</code>.
276: */
277: private static File getFile(Map options, String name) {
278: try {
279: return (File) options.get(name);
280: } catch (ClassCastException e) {
281: throw new RuntimeException("'" + name
282: + "': expected java.io.File", e);
283: }
284: }
285:
286: /**
287: * Returns a value of an option with the given name. The return type
288: * is <code>java.lang.String</code>.
289: */
290: private static String getString(Map options, String name) {
291: try {
292: return (String) options.get(name);
293: } catch (ClassCastException e) {
294: throw new RuntimeException("'" + name
295: + "': expected java.lang.String", e);
296: }
297: }
298:
299: /**
300: * Returns a value of an option with the given name. The return type
301: * is <code>java.lang.Boolean</code>.
302: */
303: private static boolean getBoolean(Map options, String name) {
304: try {
305: Object value = options.get(name);
306: if (value != null) {
307: return ((Boolean) value).booleanValue();
308: }
309: } catch (ClassCastException e) {
310: throw new RuntimeException("'" + name
311: + "': expected java.lang.Boolean", e);
312: }
313: return false;
314: }
315:
316: /**
317: * Returns a string that represents a JNI-style header file.
318: */
319: public static String getHeader(Clazz clazz) {
320: String n = System.getProperty("line.separator");
321: StringBuffer result = new StringBuffer();
322:
323: result.append(n);
324: result.append("/* Header for class " + clazz.getName() + " */"
325: + n);
326: result.append(n);
327:
328: String headerMacro = "_"
329: + Mangler.mangleMacro(clazz.getName()).toUpperCase()
330: + "_H";
331: result.append("#ifndef " + headerMacro + n);
332: result.append("#define " + headerMacro + n);
333: result.append(n);
334:
335: result.append("#ifdef __cplusplus" + n);
336: result.append("extern \"C\" {" + n);
337: result.append("#endif" + n);
338: result.append(n);
339:
340: result.append(clazz);
341: result.append(n);
342:
343: result.append("#ifdef __cplusplus" + n);
344: result.append("}" + n);
345: result.append("#endif" + n);
346: result.append(n);
347:
348: result.append("#endif /* " + headerMacro + " */" + n);
349: result.append(n);
350:
351: return result.toString();
352: }
353: }
|