001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (license2)
004: * Initial Developer: H2 Group
005: */
006: package org.h2.tools.code;
007:
008: import java.io.BufferedInputStream;
009: import java.io.BufferedOutputStream;
010: import java.io.File;
011: import java.io.FileInputStream;
012: import java.io.FileOutputStream;
013: import java.io.FileWriter;
014: import java.io.IOException;
015: import java.io.InputStream;
016: import java.io.OutputStream;
017: import java.io.RandomAccessFile;
018: import java.util.ArrayList;
019: import java.util.Collections;
020: import java.util.Enumeration;
021: import java.util.Properties;
022: import java.util.Vector;
023:
024: /**
025: * This application allows to switch source code to different 'modes', so that
026: * it can be compiled for different JDKs.
027: */
028: public class CodeSwitch {
029: // TODO codeswitch: replace with ant 'Replace' task is possible
030: private boolean recurse;
031: private ArrayList list = new ArrayList();
032: private ArrayList switchOn = new ArrayList();
033: private ArrayList switchOff = new ArrayList();
034: private ArrayList switches = new ArrayList();
035: private byte[] file;
036: private String endOfLine;
037: private ArrayList lines;
038: private boolean changed;
039:
040: public static void main(String[] argv) throws Exception {
041: (new CodeSwitch()).run(argv);
042: }
043:
044: private void run(String[] a) throws Exception {
045: if (a.length == 0) {
046: showUsage();
047: return;
048: }
049: String propertiesFile = null, property = null, value = null;
050: boolean path = false;
051: recurse = true;
052: for (int i = 0; i < a.length; i++) {
053: String p = a[i];
054: if (p.startsWith("+")) {
055: switchOn.add(p.substring(1));
056: } else if (p.startsWith("-r+")) {
057: // (default)
058: recurse = true;
059: } else if (p.startsWith("-r-")) {
060: recurse = false;
061: } else if (p.startsWith("-set")) {
062: propertiesFile = a[++i];
063: property = a[++i];
064: value = a[++i];
065: } else if (p.startsWith("-")) {
066: switchOff.add(p.substring(1));
067: } else {
068: addDir(p, true);
069: path = true;
070: }
071: }
072: if (!path) {
073: printError("no path specified");
074: showUsage();
075: }
076: process();
077: if (switchOff.size() == 0 && switchOn.size() == 0) {
078: printSwitches();
079: }
080: if (propertiesFile != null) {
081: setProperty(propertiesFile, property, value);
082: }
083: }
084:
085: private static class SortedProperties extends Properties {
086:
087: private static final long serialVersionUID = 3926204645298674434L;
088:
089: public synchronized Enumeration keys() {
090: Vector v = new Vector(keySet());
091: Collections.sort(v);
092: return v.elements();
093: }
094:
095: }
096:
097: private void setProperty(String propertiesFile, String property,
098: String value) throws IOException {
099: SortedProperties prop = new SortedProperties();
100: InputStream in = new BufferedInputStream(new FileInputStream(
101: propertiesFile));
102: prop.load(in);
103: in.close();
104: prop.setProperty(property, value);
105: OutputStream out = new BufferedOutputStream(
106: new FileOutputStream(propertiesFile));
107: prop.store(out, null);
108: out.close();
109: }
110:
111: private void showUsage() {
112: String className = getClass().getName();
113: System.out
114: .println("Usage: java "
115: + className
116: + " [-r+] [-r-] paths [+|-][labels] [-set file property value]");
117: System.out.println("If no labels are specified then all used");
118: System.out.println("labels in the source code are shown.");
119: System.out.println("-r+ recurse subdirectories (default)");
120: System.out.println("-r- do not recurse subdirectories");
121: System.out
122: .println("-set will update a value in a properties file after switching");
123: System.out.println("Use +MODE to switch on code labeled MODE");
124: System.out.println("Use -MODE to switch off code labeled MODE");
125: System.out
126: .println("Path: Any number of path or files may be specified.");
127: System.out
128: .println(" Use . for the current directory (including sub-directories).");
129: System.out.println("Example: java " + className + " +JAVA2 .");
130: System.out
131: .println("This example switches on code labeled JAVA2 in all *.java files");
132: System.out
133: .println("in the current directory and all subdirectories.");
134: }
135:
136: private void process() {
137: int len = list.size();
138: for (int i = 0; i < len; i++) {
139: String fileName = (String) list.get(i);
140: if (!processFile(fileName)) {
141: System.out.println("in file " + fileName
142: + " - this file is skipped");
143: }
144: }
145: }
146:
147: private void printSwitches() {
148: System.out.println("Used labels:");
149: for (int i = 0; i < switches.size(); i++) {
150: System.out.println((String) (switches.get(i)));
151: }
152: }
153:
154: private void addDir(String path, boolean recurseMore) {
155: File f = new File(path);
156: if (f.isFile() && path.endsWith(".java")) {
157: list.add(path);
158: } else if (f.isDirectory()) {
159: if (recurse || recurseMore) {
160: // one recursion at least
161: String[] files = f.list();
162: for (int i = 0; i < files.length; i++) {
163: addDir(path + File.separatorChar + files[i], false);
164: }
165: }
166: }
167: }
168:
169: // lines are terminated with \r, \n or \r\n
170: private void breakIntoLines() {
171: lines = new ArrayList();
172: int len = file.length;
173: int last = 0;
174: int cr = 0, lf = 0, crlf = 0;
175: for (int i = 0; i < len; i++) {
176: byte c = file[i];
177: if (c == '\r' || c == '\n') {
178: if (c == '\r') {
179: if (i < len - 1 && file[i + 1] == '\n') {
180: i++;
181: crlf++;
182: } else {
183: cr++;
184: }
185: } else {
186: lf++;
187: }
188: if (i < len) {
189: lines.add(new String(file, last, i - last + 1));
190: last = i + 1;
191: }
192: }
193: }
194: if (cr > lf && cr > crlf) {
195: endOfLine = "\r";
196: } else if (lf > crlf) {
197: endOfLine = "\n";
198: } else {
199: endOfLine = "\r\n";
200: }
201: lines.add(new String(file, last, len - last));
202: }
203:
204: private String getLine(int line) {
205: return (String) lines.get(line);
206: }
207:
208: private void insertLine(int line, String s) {
209: lines.add(line, s);
210: changed = true;
211: }
212:
213: private void removeLine(int line) {
214: lines.remove(line);
215: changed = true;
216: }
217:
218: private boolean processFile(String name) {
219: File f = new File(name);
220: boolean off = false;
221: boolean working = false;
222: int state = 0;
223: try {
224: long rawLen = f.length();
225: if (rawLen > Integer.MAX_VALUE) {
226: printError("Files bigger than Integer.MAX_VALUE are not supported");
227: return false;
228: }
229: int len = (int) rawLen;
230: file = new byte[len];
231: RandomAccessFile read = new RandomAccessFile(f, "r");
232: read.readFully(file);
233: read.close();
234: breakIntoLines();
235: changed = false;
236:
237: for (int i = 0; i < lines.size(); i++) {
238: String line = getLine(i);
239: String lineTrim = line.trim();
240: if (working) {
241: if (lineTrim.startsWith("/*")
242: || lineTrim.startsWith("*/")) {
243: removeLine(i);
244: i--;
245: continue;
246: }
247: }
248: if (lineTrim.startsWith("//#")) {
249: if (lineTrim.startsWith("//#ifdef ")) {
250: if (state != 0) {
251: printError("//#ifdef not allowed inside "
252: + "//#ifdef");
253: return false;
254: }
255: state = 1;
256: String s = lineTrim.substring(9);
257: boolean switchedOn = false;
258: boolean switchedOff = false;
259: if (switchOn.indexOf(s) != -1) {
260: switchedOn = true;
261: }
262: if (switchOff.indexOf(s) != -1) {
263: switchedOff = true;
264: }
265: if (s.indexOf("&&") != -1) {
266: switchedOn = true;
267: s += "&&";
268: while (s.length() > 0) {
269: int id = s.indexOf("&&");
270: if (id == -1) {
271: break;
272: }
273: String s1 = s.substring(0, id).trim();
274: s = s.substring(id + 2).trim();
275: if (switches.indexOf(s1) == -1) {
276: switches.add(s1);
277: switchedOn = false;
278: }
279: if (switchOn.indexOf(s1) == -1) {
280: switchedOff = true;
281: switchedOn = false;
282: }
283: if (switchOff.indexOf(s1) != -1) {
284: switchedOff = true;
285: switchedOn = false;
286: }
287: }
288: }
289: if (switchedOn) {
290: working = true;
291: off = false;
292: } else if (switchedOff) {
293: working = true;
294: insertLine(++i, "/*" + endOfLine);
295: off = true;
296: }
297: if (switches.indexOf(s) == -1) {
298: switches.add(s);
299: }
300: } else if (lineTrim.startsWith("//#else")) {
301: if (state != 1) {
302: printError("//#else without " + "//#ifdef");
303: return false;
304: }
305: state = 2;
306: if (working) {
307: if (off) {
308: insertLine(++i, "*/" + endOfLine);
309: off = false;
310: } else {
311: insertLine(++i, "/*" + endOfLine);
312: off = true;
313: }
314: }
315: } else if (lineTrim.startsWith("//#endif")) {
316: if (state == 0) {
317: printError("//#endif without " + "//#ifdef");
318: return false;
319: }
320: state = 0;
321: if (working && off) {
322: insertLine(i++, "*/" + endOfLine);
323: }
324: working = false;
325: }
326: }
327: }
328: if (state != 0) {
329: printError("//#endif missing");
330: return false;
331: }
332: if (changed) {
333: File fileNew = new File(name + ".new");
334: FileWriter write = new FileWriter(fileNew);
335: for (int i = 0; i < lines.size(); i++) {
336: write.write(getLine(i));
337: }
338: write.close();
339: File fileBack = new File(name + ".bak");
340: fileBack.delete();
341: f.renameTo(fileBack);
342: File fileCopy = new File(name);
343: fileNew.renameTo(fileCopy);
344: fileBack.delete();
345: System.out.println(name);
346: }
347: return true;
348: } catch (Exception e) {
349: printError(e);
350: return false;
351: }
352: }
353:
354: private static void printError(Exception e) {
355: e.printStackTrace();
356: }
357:
358: private static void printError(String s) {
359: System.out.println("ERROR: " + s);
360: }
361: }
|