001: /**
002: *******************************************************************************
003: * Copyright (C) 2004-2006, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */package com.ibm.icu.dev.tool.docs;
007:
008: import java.io.BufferedReader;
009: import java.io.File;
010: import java.io.FileInputStream;
011: import java.io.FilenameFilter;
012: import java.io.FileOutputStream;
013: import java.io.InputStream;
014: import java.io.InputStreamReader;
015: import java.io.IOException;
016: import java.io.PrintStream;
017: import java.util.ArrayList;
018: import java.util.Iterator;
019: import java.util.Map;
020: import java.util.HashMap;
021: import java.util.TreeMap;
022:
023: // import java.util.regex.*;
024:
025: /**
026: * A simple facility for adding C-like preprocessing to .java files.
027: * This only understands a subset of the C preprocessing syntax.
028: * Its used to manage files that with only small differences can be
029: * compiled for different JVMs. This changes files in place,
030: * commenting out lines based on the current flag settings.
031: */
032: public class CodeMangler {
033: private File indir; // root of input
034: private File outdir; // root of output
035: private String suffix; // suffix to process, default '.jpp'
036: private boolean recurse; // true if recurse on directories
037: private boolean force; // true if force reprocess of files
038: private boolean clean; // true if output is to be cleaned
039: private boolean timestamp; // true if we read/write timestamp
040: private HashMap map; // defines
041: private ArrayList names; // files/directories to process
042: private String header; // sorted list of defines passed in
043:
044: private boolean verbose; // true if we emit debug output
045:
046: private static final String IGNORE_PREFIX = "//##";
047: private static final String HEADER_PREFIX = "//##header";
048:
049: // static final Pattern pat = Pattern.compile(
050: // "(?i)^(\\s*(?://+)??\\s*)#(ifdef\\s|ifndef\\s|else|endif|undef\\s|define\\s|if\\s|elif\\s)\\s*(.*)$");
051: // // static final Pattern pat2 = Pattern.compile("([^=!]+)\\s*([!=]?=)??\\s*(\\w+)");
052: // static final Pattern pat2 = Pattern.compile("\\s*(\\w+)\\s*([!=]?=)??\\s*([^\\s]?.*$)");
053: // static final Pattern pat3 = Pattern.compile("^(\\s*//##).*");
054:
055: public static void main(String[] args) {
056: // test();
057: new CodeMangler(args).run();
058: }
059:
060: // private static final void test() {
061: // testPat();
062: // testPat2();
063: // testPat3();
064: // }
065: // private static final void testPat() {
066: // System.out.println("test pat");
067: // String[] tests = {
068: // "",
069: // " ",
070: // "#endif",
071: // "# endif",
072: // "#ENDIF",
073: // "#eNdIf",
074: // "//#endif",
075: // "// #endif",
076: // "// # endif",
077: // " // #ifdef foo",
078: // " // #ifndef foo",
079: // " // #else",
080: // " // #endif",
081: // " // #undef foo",
082: // " // #define foo bar",
083: // " // #if foo == bar",
084: // " // #elif bar != baz",
085: // };
086: // for (int i = 0; i < tests.length; ++i) {
087: // System.out.print("pat '" + tests[i] + "' --> ");
088: // Matcher m = pat.matcher(tests[i]);
089: // if (m.find()) {
090: // System.out.println("'" + m.group(1) + "' '" + m.group(2) + "' '" + m.group(3) + "'");
091: // } else {
092: // System.out.println("didn't match");
093: // }
094: // System.out.print("dug '" + tests[i] + "' --> ");
095: // String[] res = new String[3];
096: // if (patMatch(tests[i], res)) {
097: // System.out.println("'" + res[0] + "' '" + res[1] + "' '" + res[2] + "'");
098: // } else {
099: // System.out.println("didn't match");
100: // }
101: // }
102: // }
103:
104: // private static final void testPat2() {
105: // System.out.println("test pat2");
106: // String[] tests = {
107: // "",
108: // " ",
109: // "test",
110: // " test",
111: // "test ",
112: // " test ",
113: // " test ==",
114: // " !=",
115: // " !=foo",
116: // "foo==bar",
117: // "foo ==bar",
118: // "foo== bar",
119: // "foo == bar",
120: // "foo bar baz, wompf",
121: // "foo=bar=baz, wompf a loo",
122: // };
123: // for (int i = 0; i < tests.length; ++i) {
124: // System.out.print("pat '" + tests[i] + "' --> ");
125: // Matcher m2 = pat2.matcher(tests[i]);
126: // if (m2.find()) {
127: // System.out.println("'" + m2.group(1) + "' '" + m2.group(2) + "' '" + m2.group(3) + "'");
128: // } else {
129: // System.out.println("didn't match");
130: // }
131: // System.out.print("dug '" + tests[i] + "' --> ");
132: // String[] res = new String[3];
133: // if (pat2Match(tests[i], res)) {
134: // System.out.println("'" + res[0] + "' '" + res[1] + "' '" + res[2] + "'");
135: // } else {
136: // System.out.println("didn't match");
137: // }
138: // }
139: // }
140: // private static final void testPat3() {
141: // System.out.println("test pat3");
142: // String[] tests = {
143: // "",
144: // " ",
145: // " //#",
146: // " /##",
147: // "//##",
148: // " //##",
149: // " //##//",
150: // " /////##",
151: // };
152: // for (int i = 0; i < tests.length; ++i) {
153: // System.out.print("pat '" + tests[i] + "' --> ");
154: // Matcher m = pat3.matcher(tests[i]);
155: // if (m.find()) {
156: // System.out.println("'" + m.group(1) + "'");
157: // } else {
158: // System.out.println("didn't match");
159: // }
160: // System.out.print("dug '" + tests[i] + "' --> ");
161: // String match = pat3Match(tests[i]);
162: // if (match != null) {
163: // System.out.println("'" + match + "'");
164: // } else {
165: // System.out.println("didn't match");
166: // }
167: // }
168: // }
169:
170: private static final String usage = "Usage:\n"
171: + " CodeMangler [flags] file... dir... @argfile... \n"
172: + "-in[dir] path - root directory of input files, otherwise use current directory\n"
173: + "-out[dir] path - root directory of output files, otherwise use input directory\n"
174: + "-s[uffix] string - suffix of inputfiles to process, otherwise use '.java' (directories only)\n"
175: + "-c[lean] - remove all control flags from code on output (does not proceed if overwriting)\n"
176: + "-r[ecurse] - if present, recursively process subdirectories\n"
177: + "-f[orce] - force reprocessing of files even if timestamp and headers match\n"
178: + "-t[imestamp] - expect/write timestamp in header\n"
179: + "-dNAME[=VALUE] - define NAME with optional value VALUE\n"
180: + " (or -d NAME[=VALUE])\n"
181: + "-help - print this usage message and exit.\n"
182: + "\n"
183: + "For file arguments, output '.java' files using the same path/name under the output directory.\n"
184: + "For directory arguments, process all files with the defined suffix in the directory.\n"
185: + " (if recursing, do the same for all files recursively under each directory)\n"
186: + "For @argfile arguments, read the specified text file (strip the '@'), and process each line of that file as \n"
187: + "an argument.\n"
188: + "\n"
189: + "Directives are one of the following:\n"
190: + " #ifdef, #ifndef, #else, #endif, #if, #elif, #define, #undef\n"
191: + "These may optionally be preceeded by whitespace or //.\n"
192: + "#if, #elif args are of the form 'key == value' or 'key != value'.\n"
193: + "Only exact character match key with value is performed.\n"
194: + "#define args are 'key [==] value', the '==' is optional.\n";
195:
196: CodeMangler(String[] args) {
197: map = new HashMap();
198: names = new ArrayList();
199: suffix = ".java";
200: clean = false;
201: timestamp = false;
202:
203: String inname = null;
204: String outname = null;
205: boolean processArgs = true;
206: String arg = null;
207: try {
208: for (int i = 0; i < args.length; ++i) {
209: arg = args[i];
210: if ("--".equals(arg)) {
211: processArgs = false;
212: } else if (processArgs && arg.charAt(0) == '-') {
213: if (arg.startsWith("-in")) {
214: inname = args[++i];
215: } else if (arg.startsWith("-out")) {
216: outname = args[++i];
217: } else if (arg.startsWith("-d")) {
218: String id = arg.substring(2);
219: if (id.length() == 0) {
220: id = args[++i];
221: }
222: String val = "";
223: int ix = id.indexOf('=');
224: if (ix >= 0) {
225: val = id.substring(ix + 1);
226: id = id.substring(0, ix);
227: }
228: map.put(id, val);
229: } else if (arg.startsWith("-s")) {
230: suffix = args[++i];
231: } else if (arg.startsWith("-r")) {
232: recurse = true;
233: } else if (arg.startsWith("-f")) {
234: force = true;
235: } else if (arg.startsWith("-c")) {
236: clean = true;
237: } else if (arg.startsWith("-t")) {
238: timestamp = true;
239: } else if (arg.startsWith("-h")) {
240: System.out.print(usage);
241: break; // stop before processing arguments, so we will do nothing
242: } else if (arg.startsWith("-v")) {
243: verbose = true;
244: } else {
245: System.err
246: .println("Error: unrecognized argument '"
247: + arg + "'");
248: System.err.println(usage);
249: throw new IllegalArgumentException(arg);
250: }
251: } else {
252: if (arg.charAt(0) == '@') {
253: File argfile = new File(arg.substring(1));
254: if (argfile.exists() && !argfile.isDirectory()) {
255: try {
256: BufferedReader br = new BufferedReader(
257: new InputStreamReader(
258: new FileInputStream(
259: argfile)));
260: ArrayList list = new ArrayList();
261: for (int x = 0; x < args.length; ++x) {
262: list.add(args[x]);
263: }
264: String line;
265: while (null != (line = br.readLine())) {
266: line = line.trim();
267: if (line.length() > 0
268: && line.charAt(0) != '#') {
269: if (verbose)
270: System.out
271: .println("adding argument: "
272: + line);
273: list.add(line);
274: }
275: }
276: args = (String[]) list
277: .toArray(new String[list.size()]);
278: } catch (IOException e) {
279: System.err
280: .println("error reading arg file: "
281: + e);
282: }
283: }
284: } else {
285: names.add(arg);
286: }
287: }
288: }
289: } catch (IndexOutOfBoundsException e) {
290: String msg = "Error: argument '" + arg + "' missing value";
291: System.err.println(msg);
292: System.err.println(usage);
293: throw new IllegalArgumentException(msg);
294: }
295:
296: String username = System.getProperty("user.dir");
297: if (inname == null) {
298: inname = username;
299: } else if (!(inname.startsWith("\\") || inname.startsWith("/"))) {
300: inname = username + File.separator + inname;
301: }
302: indir = new File(inname);
303: try {
304: indir = indir.getCanonicalFile();
305: } catch (IOException e) {
306: // continue, but most likely we'll fail later
307: }
308: if (!indir.exists()) {
309: throw new IllegalArgumentException("Input directory '"
310: + indir.getAbsolutePath() + "' does not exist.");
311: } else if (!indir.isDirectory()) {
312: throw new IllegalArgumentException("Input path '"
313: + indir.getAbsolutePath() + "' is not a directory.");
314: }
315: if (verbose)
316: System.out.println("indir: " + indir.getAbsolutePath());
317:
318: if (outname == null) {
319: outname = inname;
320: } else if (!(outname.startsWith("\\") || outname
321: .startsWith("/"))) {
322: outname = username + File.separator + outname;
323: }
324: outdir = new File(outname);
325: try {
326: outdir = outdir.getCanonicalFile();
327: } catch (IOException e) {
328: // continue, but most likely we'll fail later
329: }
330: if (!outdir.exists()) {
331: throw new IllegalArgumentException("Output directory '"
332: + outdir.getAbsolutePath() + "' does not exist.");
333: } else if (!outdir.isDirectory()) {
334: throw new IllegalArgumentException("Output path '"
335: + outdir.getAbsolutePath()
336: + "' is not a directory.");
337: }
338: if (verbose)
339: System.out.println("outdir: " + outdir.getAbsolutePath());
340:
341: if (clean && suffix.equals(".java")) {
342: try {
343: if (outdir.getCanonicalPath().equals(
344: indir.getCanonicalPath())) {
345: throw new IllegalArgumentException(
346: "Cannot use 'clean' to overwrite .java files in same directory tree");
347: }
348: } catch (IOException e) {
349: System.err.println("possible overwrite, error: "
350: + e.getMessage());
351: throw new IllegalArgumentException(
352: "Cannot use 'clean' to overrwrite .java files");
353: }
354: }
355:
356: if (names.isEmpty()) {
357: names.add(".");
358: }
359:
360: TreeMap sort = new TreeMap(String.CASE_INSENSITIVE_ORDER);
361: sort.putAll(map);
362: Iterator iter = sort.entrySet().iterator();
363: StringBuffer buf = new StringBuffer();
364: while (iter.hasNext()) {
365: Map.Entry e = (Map.Entry) iter.next();
366: if (buf.length() > 0) {
367: buf.append(", ");
368: }
369: buf.append(e.getKey());
370: String v = (String) e.getValue();
371: if (v != null && v.length() > 0) {
372: buf.append('=');
373: buf.append(v);
374: }
375: }
376: header = buf.toString();
377: }
378:
379: public int run() {
380: return process("", (String[]) names.toArray(new String[names
381: .size()]));
382: }
383:
384: public int process(String path, String[] filenames) {
385: if (verbose)
386: System.out.println("path: '" + path + "'");
387: int count = 0;
388: for (int i = 0; i < filenames.length; ++i) {
389: if (verbose)
390: System.out
391: .println("name " + i + " of "
392: + filenames.length + ": '"
393: + filenames[i] + "'");
394: String name = path + filenames[i];
395: File fin = new File(indir, name);
396: try {
397: fin = fin.getCanonicalFile();
398: } catch (IOException e) {
399: }
400: if (!fin.exists()) {
401: System.err.println("File " + fin.getAbsolutePath()
402: + " does not exist.");
403: continue;
404: }
405: if (fin.isFile()) {
406: if (verbose)
407: System.out.println("processing file: '"
408: + fin.getAbsolutePath() + "'");
409: String oname;
410: int ix = name.lastIndexOf(".");
411: if (ix != -1) {
412: oname = name.substring(0, ix);
413: } else {
414: oname = name;
415: }
416: oname += ".java";
417: File fout = new File(outdir, oname);
418: if (processFile(fin, fout)) {
419: ++count;
420: }
421: } else if (fin.isDirectory()) {
422: if (verbose)
423: System.out.println("recursing on directory '"
424: + fin.getAbsolutePath() + "'");
425: String npath = ".".equals(name) ? path : path
426: + fin.getName() + File.separator;
427: count += process(npath, fin.list(filter)); // recursive call
428: }
429: }
430: return count;
431: }
432:
433: private final FilenameFilter filter = new FilenameFilter() {
434: public boolean accept(File dir, String name) {
435: File f = new File(dir, name);
436: return (f.isFile() && name.endsWith(suffix))
437: || (f.isDirectory() && recurse);
438: }
439: };
440:
441: public boolean processFile(File infile, File outfile) {
442: File backup = null;
443:
444: class State {
445: int lc;
446: String line;
447: boolean emit = true;
448: boolean tripped;
449: private State next;
450:
451: public String toString() {
452: return "line " + lc + ": '" + line + "' (emit: " + emit
453: + " tripped: " + tripped + ")";
454: }
455:
456: void trip(boolean trip) {
457: if (!tripped & trip) {
458: tripped = true;
459: emit = next != null ? next.emit : true;
460: } else {
461: emit = false;
462: }
463: }
464:
465: State push(int lc, String line, boolean trip) {
466: this .lc = lc;
467: this .line = line;
468: State ret = new State();
469: ret.next = this ;
470: ret.emit = this .emit & trip;
471: ret.tripped = trip;
472: return ret;
473: }
474:
475: State pop() {
476: return next;
477: }
478: }
479:
480: HashMap oldMap = null;
481:
482: long outModTime = 0;
483:
484: try {
485: PrintStream outstream = null;
486: InputStream instream = new FileInputStream(infile);
487:
488: BufferedReader reader = new BufferedReader(
489: new InputStreamReader(instream));
490: int lc = 0;
491: State state = new State();
492: String line;
493: while ((line = reader.readLine()) != null) {
494: if (lc == 0) { // check and write header for output file if needed
495: boolean hasHeader = line.startsWith(HEADER_PREFIX);
496: if (hasHeader && !force) {
497: long expectLastModified = ((infile
498: .lastModified() + 999) / 1000) * 1000;
499: String headerline = HEADER_PREFIX
500: + ' '
501: + (timestamp ? String
502: .valueOf(expectLastModified)
503: : "") + ' ' + header;
504: headerline = headerline.trim();
505: if (line.equals(headerline)) {
506: if (verbose)
507: System.out
508: .println("no changes necessary to "
509: + infile
510: .getCanonicalPath());
511: instream.close();
512: return false; // nothing to do
513: }
514: if (verbose) {
515: System.out
516: .println(" old header: " + line);
517: System.out.println(" != expected: "
518: + headerline);
519: }
520: }
521:
522: // create output file directory structure
523: String outpname = outfile.getParent();
524: if (outpname != null) {
525: File outp = new File(outpname);
526: if (!(outp.exists() || outp.mkdirs())) {
527: System.err
528: .println("could not create directory: '"
529: + outpname + "'");
530: return false;
531: }
532: }
533:
534: // if we're overwriting, use a temporary file
535: if (suffix.equals(".java")) {
536: backup = outfile;
537: try {
538: outfile = File.createTempFile(outfile
539: .getName(), null, outfile
540: .getParentFile());
541: } catch (IOException ex) {
542: System.err.println(ex.getMessage());
543: return false;
544: }
545: }
546:
547: outModTime = ((outfile.lastModified() + 999) / 1000) * 1000; // round up
548: outstream = new PrintStream(new FileOutputStream(
549: outfile));
550: String headerline = HEADER_PREFIX
551: + ' '
552: + (timestamp ? String.valueOf(outModTime)
553: : "") + ' ' + header;
554: headerline = headerline.trim();
555: outstream.println(headerline);
556: if (verbose)
557: System.out.println("header: " + headerline);
558:
559: // discard the old header if we had one, otherwise match this line like any other
560: if (hasHeader) {
561: ++lc; // mark as having read a line so we never reexecute this block
562: continue;
563: }
564: }
565:
566: String[] res = new String[3];
567: if (patMatch(line, res)) {
568: String lead = res[0];
569: String key = res[1];
570: String val = res[2];
571:
572: // Matcher m = pat.matcher(line);
573: // if (m.find()) {
574: // String lead = m.group(1);
575: // String key = m.group(2).toLowerCase().trim();
576: // String val = m.group(3).trim();
577:
578: if (verbose)
579: System.out.println("directive: " + line
580: + " key: '" + key + "' val: '" + val
581: + "' " + state);
582: if (key.equals("ifdef")) {
583: state = state.push(lc, line,
584: map.get(val) != null);
585: } else if (key.equals("ifndef")) {
586: state = state.push(lc, line,
587: map.get(val) == null);
588: } else if (key.equals("else")) {
589: state.trip(true);
590: } else if (key.equals("endif")) {
591: state = state.pop();
592: } else if (key.equals("undef")) {
593: if (state.emit) {
594: if (oldMap == null) {
595: oldMap = (HashMap) map.clone();
596: }
597: map.remove(val);
598: }
599: } else { // #define, #if, #elif
600: if (pat2Match(val, res)) {
601: String key2 = res[0];
602: boolean neq = "!=".equals(res[1]); // optional
603: String val2 = res[2];
604:
605: // Matcher m2 = pat2.matcher(val);
606: // if (m2.find()) {
607: // String key2 = m2.group(1).trim();
608: // boolean neq = "!=".equals(m2.group(2)); // optional
609: // String val2 = m2.group(3).trim();
610: if (verbose)
611: System.out.println("val2: '" + val2
612: + "' neq: '" + neq
613: + "' key2: '" + key2 + "'");
614: if (key.equals("if")) {
615: state = state.push(lc, line, val2
616: .equals(map.get(key2)) != neq);
617: } else if (key.equals("elif")) {
618: state
619: .trip(val2
620: .equals(map.get(key2)) != neq);
621: } else if (key.equals("define")) {
622: if (state.emit) {
623: if (oldMap == null) {
624: oldMap = (HashMap) map.clone();
625: }
626: map.put(key2, val2);
627: }
628: }
629: }
630: }
631: if (!clean) {
632: lc++;
633: if (!lead.equals("//")) {
634: outstream.print("//");
635: line = line.substring(lead.length());
636: }
637: outstream.println(line);
638: }
639: continue;
640: }
641:
642: lc++;
643: String found = pat3Match(line);
644: boolean hasIgnore = found != null;
645: if (state.emit == hasIgnore) {
646: if (state.emit) {
647: line = line.substring(found.length());
648: } else {
649: line = IGNORE_PREFIX + line;
650: }
651: } else if (hasIgnore && !found.equals(IGNORE_PREFIX)) {
652: line = IGNORE_PREFIX
653: + line.substring(found.length());
654: }
655: // m = pat3.matcher(line);
656: // boolean hasIgnore = m.find();
657: // if (state.emit == hasIgnore) {
658: // if (state.emit) {
659: // line = line.substring(m.group(1).length());
660: // } else {
661: // line = IGNORE_PREFIX + line;
662: // }
663: // } else if (hasIgnore && !m.group(1).equals(IGNORE_PREFIX)) {
664: // line = IGNORE_PREFIX + line.substring(m.group(1).length());
665: // }
666: if (!clean || state.emit) {
667: outstream.println(line);
668: }
669: }
670:
671: state = state.pop();
672: if (state != null) {
673: System.err.println("Error: unclosed directive(s):");
674: do {
675: System.err.println(state);
676: } while ((state = state.pop()) != null);
677: System.err.println(" in file: "
678: + outfile.getCanonicalPath());
679: if (oldMap != null) {
680: map = oldMap;
681: }
682: outstream.close();
683: return false;
684: }
685:
686: outstream.close();
687: instream.close();
688:
689: if (backup != null) {
690: if (backup.exists()) {
691: backup.delete();
692: }
693: outfile.renameTo(backup);
694: }
695:
696: if (timestamp) {
697: outfile.setLastModified(outModTime); // synch with timestamp
698: }
699:
700: if (oldMap != null) {
701: map = oldMap;
702: }
703: } catch (IOException e) {
704: System.err.println(e);
705: return false;
706: }
707: return true;
708: }
709:
710: /**
711: * Perform same operation as matching on pat. on exit
712: * leadKeyValue contains the three strings lead, key, and value.
713: * 'lead' is the portion before the #ifdef directive. 'key' is
714: * the directive. 'value' is the portion after the directive. if
715: * there is a match, return true, else return false.
716: */
717: static boolean patMatch(String line, String[] leadKeyValue) {
718: // final Pattern pat = Pattern.compile(
719: // "(?i)^(\\s*(?://+)??\\s*)#(ifdef\\s|ifndef\\s|else|endif|undef\\s|define\\s|if\\s|elif\\s)\\s*(.*)$");
720:
721: if (line.length() == 0) {
722: return false;
723: }
724: if (!line.endsWith("\n")) {
725: line = line + '\n';
726: }
727: int mark = 0;
728: int state = 0;
729: loop: for (int i = 0; i < line.length(); ++i) {
730: char c = line.charAt(i);
731: switch (state) {
732: case 0: // at start of line, haven't seen anything but whitespace yet
733: if (c == ' ' || c == '\t' || c == '\r')
734: continue;
735: if (c == '/') {
736: state = 1;
737: continue;
738: }
739: if (c == '#') {
740: state = 4;
741: continue;
742: }
743: return false;
744: case 1: // have seen a single slash after start of line
745: if (c == '/') {
746: state = 2;
747: continue;
748: }
749: return false;
750: case 2: // have seen two or more slashes
751: if (c == '/')
752: continue;
753: if (c == ' ' || c == '\t' || c == '\r') {
754: state = 3;
755: continue;
756: }
757: if (c == '#') {
758: state = 4;
759: continue;
760: }
761: return false;
762: case 3: // have seen a space after two or more slashes
763: if (c == ' ' || c == '\t' || c == '\r')
764: continue;
765: if (c == '#') {
766: state = 4;
767: continue;
768: }
769: return false;
770: case 4: // have seen a '#'
771: leadKeyValue[0] = line.substring(mark, i - 1);
772: if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
773: mark = i;
774: state = 5;
775: continue;
776: }
777: return false;
778: case 5: // an ascii char followed the '#'
779: if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
780: continue;
781: if (c == ' ' || c == '\t' || c == '\n') {
782: String key = line.substring(mark, i).toLowerCase();
783: if (key.equals("ifdef") || key.equals("ifndef")
784: || key.equals("else")
785: || key.equals("endif")
786: || key.equals("undef")
787: || key.equals("define") || key.equals("if")
788: || key.equals("elif")) {
789: leadKeyValue[1] = key;
790: mark = i;
791: state = 6;
792: break loop;
793: }
794: }
795: return false;
796: default:
797: throw new IllegalStateException();
798: }
799: }
800: if (state == 6) {
801: leadKeyValue[2] = line.substring(mark, line.length())
802: .trim();
803: return true;
804: }
805: return false; // never reached, does the compiler know this?
806: }
807:
808: /**
809: * Perform same operation as matching on pat2. on exit
810: * keyRelValue contains the three strings key, rel, and value.
811: * 'key' is the portion before the relation (or final word). 'rel' is
812: * the relation, if present, either == or !=. 'value' is the final
813: * word. if there is a match, return true, else return false.
814: */
815: static boolean pat2Match(String line, String[] keyRelVal) {
816: // final Pattern pat2 = Pattern.compile("([^=!]+)\\s*([!=]?=)??\\s*(\\w+)");
817: // hmmm, this pattern doesn't look right. a pattern consisting of 'abcd' should
818: // return {"abcd", "", ""} but it looks like it returns {"", "", "abcd"}.
819:
820: if (line.length() == 0) {
821: return false;
822: }
823: keyRelVal[0] = keyRelVal[1] = keyRelVal[2] = "";
824: int mark = 0;
825: int state = 0;
826: loop: for (int i = 0; i < line.length(); ++i) {
827: char c = line.charAt(i);
828: switch (state) {
829: case 0: // saw beginning or space, no rel yet
830: if (c == ' ' || c == '\t' || c == '\n') {
831: continue;
832: }
833: if ((c == '!' || c == '=')) {
834: return false;
835: }
836: state = 1;
837: continue;
838: case 1: // saw start of a word
839: if (c == ' ' || c == '\t') {
840: state = 2;
841: } else if (c == '!' || c == '=') {
842: state = 3;
843: }
844: continue;
845: case 2: // saw end of word, and space
846: if (c == ' ' || c == '\t') {
847: continue;
848: } else if (c == '!' || c == '=') {
849: state = 3;
850: continue;
851: }
852: keyRelVal[0] = line.substring(0, i - 1).trim();
853: mark = i;
854: state = 4;
855: break loop;
856: case 3: // saw end of word, and '!' or '='
857: if (c == '=') {
858: keyRelVal[0] = line.substring(0, i - 1).trim();
859: keyRelVal[1] = line.substring(i - 1, i + 1);
860: mark = i + 1;
861: state = 4;
862: break loop;
863: }
864: return false;
865: default:
866: break;
867: }
868: }
869: switch (state) {
870: case 0:
871: return false; // found nothing
872: case 1:
873: case 2:
874: keyRelVal[0] = line.trim();
875: break; // found only a word
876: case 3:
877: return false; // found a word and '!' or '=" then end of line, incomplete
878: case 4:
879: keyRelVal[2] = line.substring(mark).trim();
880: break; // found a word, possible rel, and who knows what
881: default:
882: throw new IllegalStateException();
883: }
884: return true;
885: }
886:
887: static String pat3Match(String line) {
888: int state = 0;
889: loop: for (int i = 0; i < line.length(); ++i) {
890: char c = line.charAt(i);
891: switch (state) {
892: case 0:
893: if (c == ' ' || c == '\t')
894: continue;
895: if (c == '/') {
896: state = 1;
897: continue;
898: }
899: break loop;
900: case 1:
901: if (c == '/') {
902: state = 2;
903: continue;
904: }
905: break loop;
906: case 2:
907: if (c == '#') {
908: state = 3;
909: continue;
910: }
911: break loop;
912: case 3:
913: if (c == '#')
914: return line.substring(0, i + 1);
915: break loop;
916: default:
917: break loop;
918: }
919: }
920: return null;
921: }
922:
923: // final Pattern pat3 = Pattern.compile("^(\\s*//##).*");
924:
925: }
|