001: // Copyright (c) 2005 Per M.A. Bothner
002: // This is free software; for terms and warranty disclaimer see COPYING.
003:
004: package gnu.kawa.util;
005:
006: import java.io.*;
007: import java.util.*;
008:
009: /** Help class to pre-process Java source. */
010:
011: public class PreProcess {
012: // JAVA5:
013: // Hashtable<String,Boolean> keywords = new Hashtable<String,Boolean>();
014: Hashtable keywords = new Hashtable();
015:
016: String filename;
017: int lineno;
018:
019: static final String JAVA4_FEATURES = "+JAVA2 +use:java.util.IdentityHashMap +use:java.lang.CharSequence +use:java.lang.Throwable.getCause +use:java.net.URI +use:java.util.regex +SAX2 +use:java.nio";
020: static final String NO_JAVA4_FEATURES = "-use:java.util.IdentityHashMap -use:java.lang.CharSequence -use:java.lang.Throwable.getCause -use:java.net.URI -use:java.util.regex -use:org.w3c.dom.Node -JAXP-1.3 -JAVA5 -JAVA6 -JAXP-QName -use:java.text.Normalizer -SAX2 -use:java.nio";
021: static final String JAVA5_FEATURES = "+JAVA5 " + JAVA4_FEATURES
022: + " +use:org.w3c.dom.Node +JAXP-1.3 -JAXP-QName";
023:
024: static String[] version_features = {
025: "java1",
026: "-JAVA2 " + NO_JAVA4_FEATURES,
027: "java2",
028: "+JAVA2 " + NO_JAVA4_FEATURES,
029: // We don't use Node for java4 because there are inconsistencies between
030: // the version of DOM used in Java4 and the one in Java 5 (and GCJ).
031: "java4",
032: "-JAVA5 "
033: + JAVA4_FEATURES
034: + " -use:org.w3c.dom.Node -JAXP-1.3 -JAXP-QName -JAVA6 -use:java.text.Normalizer",
035: "java4x",
036: "-JAVA5 "
037: + JAVA4_FEATURES
038: + " +use:org.w3c.dom.Node +JAXP-1.3 -JAXP-QName -JAVA6 -use:java.text.Normalizer",
039: "java5",
040: JAVA5_FEATURES + " -JAVA6 -use:java.text.Normalizer",
041: "java6",
042: JAVA5_FEATURES + " +JAVA6 +use:java.text.Normalizer", };
043:
044: void error(String msg) {
045: System.err.println(filename + ':' + lineno + ": " + msg);
046: System.exit(-1);
047: }
048:
049: public void filter(String filename) throws Throwable {
050: this .filename = filename;
051: boolean changed = false;
052: BufferedInputStream in = new BufferedInputStream(
053: new FileInputStream(filename));
054:
055: byte[] buf = new byte[2000];
056: int len = 0;
057: ;
058: int lineStart = 0;
059: int dataStart = -1;
060: int cmdLine = 0;
061: lineno = 1;
062: // If non-negative, comment out at this indentation.
063: int commentAt = -1;
064: int curIndent = 0;
065: int nesting = 0;
066: // If currently skipping, the nesting level of the controlling
067: // conditional. Otherwise, 0.
068: int skipNesting = 0;
069: String cmd = null;
070: int changedLine = 0; // -1: comment added or moved; +1 comment removed
071: for (;;) {
072: int c = in.read();
073: if (c < 0)
074: break;
075: if (len + 10 >= buf.length) // Allow a little extra for look-ahead.
076: {
077: byte[] nbuf = new byte[2 * len];
078: System.arraycopy(buf, 0, nbuf, 0, len);
079: buf = nbuf;
080: }
081: if (c == '\n' && len > 0 && buf[len - 1] == '\r') {
082: buf[len++] = (byte) c;
083: continue;
084: }
085: if (commentAt >= 0
086: && dataStart < 0
087: && changedLine <= 0
088: && c != '\r'
089: && c != '\n'
090: && (commentAt == curIndent || (c != ' ' && c != '\t'))) {
091: boolean doComment;
092: if (c == '/') {
093: // This is a little tricky. We want to comment out regular
094: // comments, because they might continue over multiple lines,
095: // or because they might be documentation comments (which
096: // we want to comment out so javadoc doesn't see them).
097: // However, we don't want to comment out directives.
098: in.mark(100);
099: int d = in.read();
100: if (d == '/')
101: doComment = false;
102: else if (d == '*') {
103: do {
104: d = in.read();
105: } while (d == ' ' || d == '\t');
106: doComment = d != '#';
107: } else
108: doComment = true;
109: in.reset();
110: } else
111: doComment = true;
112: if (doComment) {
113: buf[len++] = '/';
114: buf[len++] = '/';
115: buf[len++] = ' ';
116: changedLine = 1;
117: changed = true;
118: }
119: }
120: if (c != ' ' && c != '\t' && dataStart < 0) {
121: // First non-space character.
122: dataStart = len;
123: if (nesting > 0 && commentAt != curIndent && c == '/') {
124: c = in.read();
125: if (c < 0)
126: break;
127: else if (c != '/')
128: buf[len++] = '/';
129: else {
130: c = in.read();
131: if (c < 0)
132: break;
133: changedLine = -1;
134: changed = true;
135: if (c == ' ') {
136: c = in.read();
137: if (c == ' ' || c == '\t')
138: dataStart = -1;
139: }
140: }
141: }
142: }
143: buf[len] = (byte) c;
144: len++;
145: if (c == '\r' || c == '\n') {
146: int firstNonSpace = -1;
147: int lastNonSpace = 0;
148: for (int i = lineStart; i < len - 1; i++) {
149: if (buf[i] != ' ' && buf[i] != '\t') {
150: lastNonSpace = i;
151: if (firstNonSpace < 0)
152: firstNonSpace = i;
153: }
154: }
155: if (lastNonSpace - firstNonSpace >= 4
156: && buf[firstNonSpace] == '/'
157: && buf[firstNonSpace + 1] == '*'
158: && buf[lastNonSpace - 1] == '*'
159: && buf[lastNonSpace] == '/') {
160: firstNonSpace += 2;
161: while (firstNonSpace < lastNonSpace
162: && buf[firstNonSpace] == ' ')
163: firstNonSpace++;
164: lastNonSpace -= 2;
165: while (lastNonSpace > firstNonSpace
166: && buf[lastNonSpace] == ' ')
167: lastNonSpace--;
168: if (buf[firstNonSpace] == '#') {
169: String cmnt = new String(buf, firstNonSpace,
170: lastNonSpace - firstNonSpace + 1,
171: "ISO-8859-1");
172: int sp = cmnt.indexOf(' ');
173: String rest;
174: Object binding;
175: cmdLine = lineno;
176: if (sp > 0) {
177: cmd = cmnt.substring(0, sp);
178: rest = cmnt.substring(sp).trim();
179: binding = keywords.get(rest);
180: } else {
181: cmd = cmnt;
182: rest = "";
183: binding = null;
184: }
185: if ("#ifdef".equals(cmd)
186: || "#ifndef".equals(cmd)) {
187: if (binding == null) {
188: System.err
189: .println(filename
190: + ":"
191: + lineno
192: + ": warning - undefined keyword: "
193: + rest);
194: binding = Boolean.FALSE;
195: }
196: nesting++;
197: if (skipNesting > 0)
198: commentAt = curIndent;
199: else if ((cmd.charAt(3) == 'n') != (binding == Boolean.FALSE)) {
200: commentAt = curIndent;
201: skipNesting = nesting;
202: }
203: } else if ("#else".equals(cmd)) {
204: if (nesting == 0)
205: error("unexpected " + cmd);
206: else if (nesting == skipNesting) {
207: commentAt = -1;
208: skipNesting = 0;
209: } else {
210: commentAt = curIndent;
211: if (skipNesting == 0)
212: skipNesting = nesting;
213: }
214: } else if ("#endif".equals(cmd)) {
215: if (nesting == 0)
216: error("unexpected " + cmd);
217: if (nesting == skipNesting) {
218: skipNesting = 0;
219: commentAt = -1;
220: } else if (skipNesting > 0)
221: commentAt = curIndent;
222: nesting--;
223: } else
224: error("unknown command: " + cmnt);
225: }
226: }
227: lineStart = len;
228: dataStart = -1;
229: curIndent = 0;
230: lineno++;
231: changedLine = 0;
232: } else if (dataStart < 0)
233: curIndent = c == '\t' ? (curIndent + 8) & ~7
234: : curIndent + 1;
235: }
236: if (nesting != 0) {
237: lineno = cmdLine;
238: error("unterminated " + cmd);
239: }
240: if (changed) {
241: FileOutputStream out = new FileOutputStream(filename);
242: out.write(buf, 0, len);
243: out.close();
244: System.err.println("Pre-processed " + filename);
245: }
246: }
247:
248: void handleArg(String arg) {
249: if (arg.charAt(0) == '%') {
250: arg = arg.substring(1);
251: for (int i = 0;; i += 2) {
252: if (i >= version_features.length) {
253: System.err.println("Unknown version: " + arg);
254: System.exit(-1);
255: }
256: if (arg.equals(version_features[i])) {
257: String features = version_features[i + 1];
258: System.err.println("(variant " + arg + " maps to: "
259: + features + ")");
260: StringTokenizer tokenizer = new StringTokenizer(
261: features);
262: while (tokenizer.hasMoreTokens())
263: handleArg(tokenizer.nextToken());
264: break;
265: }
266: }
267: } else if (arg.charAt(0) == '+')
268: keywords.put(arg.substring(1), Boolean.TRUE);
269: else if (arg.charAt(0) == '-') {
270: int eq = arg.indexOf('=');
271: if (eq > 1) {
272: String keyword = arg.substring(arg.charAt(1) == '-' ? 2
273: : 1, eq);
274: String value = arg.substring(eq + 1);
275: Boolean b = Boolean.FALSE;
276: if (value.equalsIgnoreCase("true"))
277: b = Boolean.TRUE;
278: else if (!value.equalsIgnoreCase("false")) {
279: System.err.println("invalid value " + value
280: + " for " + keyword);
281: System.exit(-1);
282: }
283: keywords.put(keyword, b);
284: } else
285: keywords.put(arg.substring(1), Boolean.FALSE);
286: } else {
287: try {
288: filter(arg);
289: } catch (Throwable ex) {
290: System.err.println("caught " + ex);
291: ex.printStackTrace();
292: System.exit(-1);
293: }
294: }
295: }
296:
297: public static void main(String[] args) {
298: PreProcess pp = new PreProcess();
299:
300: pp.keywords.put("true", Boolean.TRUE);
301: pp.keywords.put("false", Boolean.FALSE);
302:
303: for (int i = 0; i < args.length; i++)
304: pp.handleArg(args[i]);
305: }
306: }
|