001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package sample.preproc;
017:
018: import java.io.IOException;
019: import java.io.BufferedReader;
020: import java.io.FileReader;
021: import java.io.BufferedWriter;
022: import java.io.FileWriter;
023: import java.util.Vector;
024: import javassist.CannotCompileException;
025: import javassist.CtClass;
026: import javassist.ClassPool;
027:
028: /**
029: * This is a preprocessor for Java source programs using annotated
030: * import declarations.
031: *
032: * <ul><pre>
033: * import <i>class-name</i> by <i>assistant-name</i> [(<i>arg1, arg2, ...</i>)]
034: * </pre></ul>
035: *
036: * <p>To process this annotation, run this class as follows:
037: *
038: * <ul><pre>
039: * java sample.preproc.Compiler sample.j
040: * </pre></ul>
041: *
042: * <p>This command produces <code>sample.java</code>, which only includes
043: * regular import declarations. Also, the Javassist program
044: * specified by <i>assistant-name</i> is executed so that it produces
045: * class files under the <code>./tmpjvst</code> directory. The class
046: * specified by <i>assistant-name</i> must implement
047: * <code>sample.preproc.Assistant</code>.
048: *
049: * @see sample.preproc.Assistant
050: */
051:
052: public class Compiler {
053: protected BufferedReader input;
054: protected BufferedWriter output;
055: protected ClassPool classPool;
056:
057: /**
058: * Constructs a <code>Compiler</code> with a source file.
059: *
060: * @param inputname the name of the source file.
061: */
062: public Compiler(String inputname) throws CannotCompileException {
063: try {
064: input = new BufferedReader(new FileReader(inputname));
065: } catch (IOException e) {
066: throw new CannotCompileException("cannot open: "
067: + inputname);
068: }
069:
070: String outputname = getOutputFilename(inputname);
071: if (outputname.equals(inputname))
072: throw new CannotCompileException("invalid source name: "
073: + inputname);
074:
075: try {
076: output = new BufferedWriter(new FileWriter(outputname));
077: } catch (IOException e) {
078: throw new CannotCompileException("cannot open: "
079: + outputname);
080: }
081:
082: classPool = ClassPool.getDefault();
083: }
084:
085: /**
086: * Starts preprocessing.
087: */
088: public void process() throws IOException, CannotCompileException {
089: int c;
090: CommentSkipper reader = new CommentSkipper(input, output);
091: while ((c = reader.read()) != -1) {
092: output.write(c);
093: if (c == 'p') {
094: if (skipPackage(reader))
095: break;
096: } else if (c == 'i')
097: readImport(reader);
098: else if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
099: break;
100: }
101:
102: while ((c = input.read()) != -1)
103: output.write(c);
104:
105: input.close();
106: output.close();
107: }
108:
109: private boolean skipPackage(CommentSkipper reader)
110: throws IOException {
111: int c;
112: c = reader.read();
113: output.write(c);
114: if (c != 'a')
115: return true;
116:
117: while ((c = reader.read()) != -1) {
118: output.write(c);
119: if (c == ';')
120: break;
121: }
122:
123: return false;
124: }
125:
126: private void readImport(CommentSkipper reader) throws IOException,
127: CannotCompileException {
128: int word[] = new int[5];
129: int c;
130: for (int i = 0; i < 5; ++i) {
131: word[i] = reader.read();
132: output.write(word[i]);
133: }
134:
135: if (word[0] != 'm' || word[1] != 'p' || word[2] != 'o'
136: || word[3] != 'r' || word[4] != 't')
137: return; // syntax error?
138:
139: c = skipSpaces(reader, ' ');
140: StringBuffer classbuf = new StringBuffer();
141: while (c != ' ' && c != '\t' && c != '\n' && c != '\r'
142: && c != ';' && c != -1) {
143: classbuf.append((char) c);
144: c = reader.read();
145: }
146:
147: String importclass = classbuf.toString();
148: c = skipSpaces(reader, c);
149: if (c == ';') {
150: output.write(importclass);
151: output.write(';');
152: return;
153: }
154: if (c != 'b')
155: syntaxError(importclass);
156:
157: reader.read(); // skip 'y'
158:
159: StringBuffer assistant = new StringBuffer();
160: Vector args = new Vector();
161: c = readAssistant(reader, importclass, assistant, args);
162: c = skipSpaces(reader, c);
163: if (c != ';')
164: syntaxError(importclass);
165:
166: runAssistant(importclass, assistant.toString(), args);
167: }
168:
169: void syntaxError(String importclass) throws CannotCompileException {
170: throw new CannotCompileException(
171: "Syntax error. Cannot import " + importclass);
172: }
173:
174: int readAssistant(CommentSkipper reader, String importclass,
175: StringBuffer assistant, Vector args) throws IOException,
176: CannotCompileException {
177: int c = readArgument(reader, assistant);
178: c = skipSpaces(reader, c);
179: if (c == '(') {
180: do {
181: StringBuffer arg = new StringBuffer();
182: c = readArgument(reader, arg);
183: args.addElement(arg.toString());
184: c = skipSpaces(reader, c);
185: } while (c == ',');
186:
187: if (c != ')')
188: syntaxError(importclass);
189:
190: return reader.read();
191: }
192:
193: return c;
194: }
195:
196: int readArgument(CommentSkipper reader, StringBuffer buf)
197: throws IOException {
198: int c = skipSpaces(reader, ' ');
199: while ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c
200: && c <= '9' || c == '.' || c == '_') {
201: buf.append((char) c);
202: c = reader.read();
203: }
204:
205: return c;
206: }
207:
208: int skipSpaces(CommentSkipper reader, int c) throws IOException {
209: while (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
210: if (c == '\n' || c == '\r')
211: output.write(c);
212:
213: c = reader.read();
214: }
215:
216: return c;
217: }
218:
219: /**
220: * Is invoked if this compiler encoutenrs:
221: *
222: * <ul><pre>
223: * import <i>class name</i> by <i>assistant</i> (<i>args1</i>, <i>args2</i>, ...);
224: * </pre></ul>
225: *
226: * @param classname class name
227: * @param assistantname assistant
228: * @param argv args1, args2, ...
229: */
230: private void runAssistant(String importname, String assistantname,
231: Vector argv) throws IOException, CannotCompileException {
232: Class assistant;
233: Assistant a;
234: int s = argv.size();
235: String[] args = new String[s];
236: for (int i = 0; i < s; ++i)
237: args[i] = (String) argv.elementAt(i);
238:
239: try {
240: assistant = Class.forName(assistantname);
241: } catch (ClassNotFoundException e) {
242: throw new CannotCompileException("Cannot find "
243: + assistantname);
244: }
245:
246: try {
247: a = (Assistant) assistant.newInstance();
248: } catch (Exception e) {
249: throw new CannotCompileException(e);
250: }
251:
252: CtClass[] imports = a.assist(classPool, importname, args);
253: s = imports.length;
254: if (s < 1)
255: output.write(" java.lang.Object;");
256: else {
257: output.write(' ');
258: output.write(imports[0].getName());
259: output.write(';');
260: for (int i = 1; i < s; ++i) {
261: output.write(" import ");
262: output.write(imports[1].getName());
263: output.write(';');
264: }
265: }
266: }
267:
268: private String getOutputFilename(String input) {
269: int i = input.lastIndexOf('.');
270: if (i < 0)
271: i = input.length();
272:
273: return input.substring(0, i) + ".java";
274: }
275:
276: public static void main(String[] args) {
277: if (args.length > 0)
278: try {
279: Compiler c = new Compiler(args[0]);
280: c.process();
281: } catch (IOException e) {
282: System.err.println(e);
283: } catch (CannotCompileException e) {
284: System.err.println(e);
285: }
286: else {
287: System.err.println("Javassist version " + CtClass.version);
288: System.err.println("No source file is specified.");
289: }
290: }
291: }
292:
293: class CommentSkipper {
294: private BufferedReader input;
295: private BufferedWriter output;
296:
297: public CommentSkipper(BufferedReader reader, BufferedWriter writer) {
298: input = reader;
299: output = writer;
300: }
301:
302: public int read() throws IOException {
303: int c;
304: while ((c = input.read()) != -1)
305: if (c != '/')
306: return c;
307: else {
308: c = input.read();
309: if (c == '/')
310: skipCxxComments();
311: else if (c == '*')
312: skipCComments();
313: else
314: output.write('/');
315: }
316:
317: return c;
318: }
319:
320: private void skipCxxComments() throws IOException {
321: int c;
322: output.write("//");
323: while ((c = input.read()) != -1) {
324: output.write(c);
325: if (c == '\n' || c == '\r')
326: break;
327: }
328: }
329:
330: private void skipCComments() throws IOException {
331: int c;
332: boolean star = false;
333: output.write("/*");
334: while ((c = input.read()) != -1) {
335: output.write(c);
336: if (c == '*')
337: star = true;
338: else if (star && c == '/')
339: break;
340: else
341: star = false;
342: }
343: }
344: }
|