001: /* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
002: *
003: * ***** BEGIN LICENSE BLOCK *****
004: * Version: MPL 1.1/GPL 2.0
005: *
006: * The contents of this file are subject to the Mozilla Public License Version
007: * 1.1 (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: * http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the
014: * License.
015: *
016: * The Original Code is Rhino code, released
017: * May 6, 1999.
018: *
019: * The Initial Developer of the Original Code is
020: * Netscape Communications Corporation.
021: * Portions created by the Initial Developer are Copyright (C) 1997-1999
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Igor Bukanov
026: *
027: * Alternatively, the contents of this file may be used under the terms of
028: * the GNU General Public License Version 2 or later (the "GPL"), in which
029: * case the provisions of the GPL are applicable instead of those above. If
030: * you wish to allow use of your version of this file only under the terms of
031: * the GPL and not to allow others to use your version of this file under the
032: * MPL, indicate your decision by deleting the provisions above and replacing
033: * them with the notice and other provisions required by the GPL. If you do
034: * not delete the provisions above, a recipient may use your version of this
035: * file under either the MPL or the GPL.
036: *
037: * ***** END LICENSE BLOCK ***** */
038: package org.mozilla.javascript.tools.idswitch;
039:
040: import java.io.*;
041: import java.util.*;
042: import java.text.SimpleDateFormat;
043:
044: import org.mozilla.javascript.EvaluatorException;
045: import org.mozilla.javascript.tools.ToolErrorReporter;
046:
047: public class Main {
048:
049: private static final String SWITCH_TAG_STR = "string_id_map";
050: private static final String GENERATED_TAG_STR = "generated";
051: private static final String STRING_TAG_STR = "string";
052:
053: private static final int NORMAL_LINE = 0, SWITCH_TAG = 1,
054: GENERATED_TAG = 2, STRING_TAG = 3;
055:
056: private final Vector all_pairs = new Vector();
057:
058: private ToolErrorReporter R;
059: private CodePrinter P;
060: private FileBody body;
061: private String source_file;
062:
063: private int tag_definition_end;
064:
065: private int tag_value_start;
066: private int tag_value_end;
067:
068: private static boolean is_value_type(int id) {
069: if (id == STRING_TAG) {
070: return true;
071: }
072: return false;
073: }
074:
075: private static String tag_name(int id) {
076: switch (id) {
077: case SWITCH_TAG:
078: return SWITCH_TAG_STR;
079: case -SWITCH_TAG:
080: return "/" + SWITCH_TAG_STR;
081: case GENERATED_TAG:
082: return GENERATED_TAG_STR;
083: case -GENERATED_TAG:
084: return "/" + GENERATED_TAG_STR;
085: }
086: return "";
087: }
088:
089: void process_file(String file_path) throws IOException {
090: source_file = file_path;
091:
092: body = new FileBody();
093:
094: InputStream is;
095: if (file_path.equals("-")) {
096: is = System.in;
097: } else {
098: is = new FileInputStream(file_path);
099: }
100: try {
101: Reader r = new InputStreamReader(is, "ASCII");
102: body.readData(r);
103: } finally {
104: is.close();
105: }
106:
107: process_file();
108:
109: if (body.wasModified()) {
110: OutputStream os;
111: if (file_path.equals("-")) {
112: os = System.out;
113: } else {
114: os = new FileOutputStream(file_path);
115: }
116:
117: try {
118: Writer w = new OutputStreamWriter(os);
119: body.writeData(w);
120: w.flush();
121: } finally {
122: os.close();
123: }
124: }
125: }
126:
127: private void process_file() {
128: int cur_state = 0;
129: char[] buffer = body.getBuffer();
130:
131: int generated_begin = -1, generated_end = -1;
132: int time_stamp_begin = -1, time_stamp_end = -1;
133:
134: body.startLineLoop();
135: while (body.nextLine()) {
136: int begin = body.getLineBegin();
137: int end = body.getLineEnd();
138:
139: int tag_id = extract_line_tag_id(buffer, begin, end);
140: boolean bad_tag = false;
141: switch (cur_state) {
142: case NORMAL_LINE:
143: if (tag_id == SWITCH_TAG) {
144: cur_state = SWITCH_TAG;
145: all_pairs.removeAllElements();
146: generated_begin = -1;
147: } else if (tag_id == -SWITCH_TAG) {
148: bad_tag = true;
149: }
150: break;
151: case SWITCH_TAG:
152: if (tag_id == 0) {
153: look_for_id_definitions(buffer, begin, end, false);
154: } else if (tag_id == STRING_TAG) {
155: look_for_id_definitions(buffer, begin, end, true);
156: } else if (tag_id == GENERATED_TAG) {
157: if (generated_begin >= 0) {
158: bad_tag = true;
159: } else {
160: cur_state = GENERATED_TAG;
161: time_stamp_begin = tag_definition_end;
162: time_stamp_end = end;
163: }
164: } else if (tag_id == -SWITCH_TAG) {
165: cur_state = 0;
166: if (generated_begin >= 0 && !all_pairs.isEmpty()) {
167: generate_java_code();
168: String code = P.toString();
169: boolean different = body.setReplacement(
170: generated_begin, generated_end, code);
171: if (different) {
172: String stamp = get_time_stamp();
173: body.setReplacement(time_stamp_begin,
174: time_stamp_end, stamp);
175: }
176: }
177:
178: break;
179: } else {
180: bad_tag = true;
181: }
182: break;
183: case GENERATED_TAG:
184: if (tag_id == 0) {
185: if (generated_begin < 0) {
186: generated_begin = begin;
187: }
188: } else if (tag_id == -GENERATED_TAG) {
189: if (generated_begin < 0) {
190: generated_begin = begin;
191: }
192: cur_state = SWITCH_TAG;
193: generated_end = begin;
194: } else {
195: bad_tag = true;
196: }
197: break;
198: }
199: if (bad_tag) {
200: String text = ToolErrorReporter.getMessage(
201: "msg.idswitch.bad_tag_order", tag_name(tag_id));
202: throw R.runtimeError(text, source_file, body
203: .getLineNumber(), null, 0);
204: }
205: }
206:
207: if (cur_state != 0) {
208: String text = ToolErrorReporter.getMessage(
209: "msg.idswitch.file_end_in_switch",
210: tag_name(cur_state));
211: throw R.runtimeError(text, source_file, body
212: .getLineNumber(), null, 0);
213: }
214: }
215:
216: private String get_time_stamp() {
217: SimpleDateFormat f = new SimpleDateFormat(
218: " 'Last update:' yyyy-MM-dd HH:mm:ss z");
219: return f.format(new Date());
220: }
221:
222: private void generate_java_code() {
223:
224: P.clear();
225:
226: IdValuePair[] pairs = new IdValuePair[all_pairs.size()];
227: all_pairs.copyInto(pairs);
228:
229: SwitchGenerator g = new SwitchGenerator();
230: g.char_tail_test_threshold = 2;
231: g.setReporter(R);
232: g.setCodePrinter(P);
233:
234: g.generateSwitch(pairs, "0");
235: }
236:
237: private int extract_line_tag_id(char[] array, int cursor, int end) {
238: int id = 0;
239: cursor = skip_white_space(array, cursor, end);
240: int after_leading_white_space = cursor;
241: cursor = look_for_slash_slash(array, cursor, end);
242: if (cursor != end) {
243: boolean at_line_start = (after_leading_white_space + 2 == cursor);
244: cursor = skip_white_space(array, cursor, end);
245: if (cursor != end && array[cursor] == '#') {
246: ++cursor;
247:
248: boolean end_tag = false;
249: if (cursor != end && array[cursor] == '/') {
250: ++cursor;
251: end_tag = true;
252: }
253:
254: int tag_start = cursor;
255:
256: for (; cursor != end; ++cursor) {
257: int c = array[cursor];
258: if (c == '#' || c == '=' || is_white_space(c)) {
259: break;
260: }
261: }
262:
263: if (cursor != end) {
264: int tag_end = cursor;
265: cursor = skip_white_space(array, cursor, end);
266: if (cursor != end) {
267: int c = array[cursor];
268: if (c == '=' || c == '#') {
269: id = get_tag_id(array, tag_start, tag_end,
270: at_line_start);
271: if (id != 0) {
272: String bad = null;
273: if (c == '#') {
274: if (end_tag) {
275: id = -id;
276: if (is_value_type(id)) {
277: bad = "msg.idswitch.no_end_usage";
278: }
279: }
280: tag_definition_end = cursor + 1;
281: } else {
282: if (end_tag) {
283: bad = "msg.idswitch.no_end_with_value";
284: } else if (!is_value_type(id)) {
285: bad = "msg.idswitch.no_value_allowed";
286: }
287: id = extract_tag_value(array,
288: cursor + 1, end, id);
289: }
290: if (bad != null) {
291: String s = ToolErrorReporter
292: .getMessage(bad,
293: tag_name(id));
294: throw R.runtimeError(s,
295: source_file, body
296: .getLineNumber(),
297: null, 0);
298: }
299: }
300: }
301: }
302: }
303: }
304: }
305: return id;
306: }
307:
308: // Return position after first of // or end if not found
309: private int look_for_slash_slash(char[] array, int cursor, int end) {
310: while (cursor + 2 <= end) {
311: int c = array[cursor++];
312: if (c == '/') {
313: c = array[cursor++];
314: if (c == '/') {
315: return cursor;
316: }
317: }
318: }
319: return end;
320: }
321:
322: private int extract_tag_value(char[] array, int cursor, int end,
323: int id) {
324: // cursor points after #[^#=]+=
325: // ALERT: implement support for quoted strings
326: boolean found = false;
327: cursor = skip_white_space(array, cursor, end);
328: if (cursor != end) {
329: int value_start = cursor;
330: int value_end = cursor;
331: while (cursor != end) {
332: int c = array[cursor];
333: if (is_white_space(c)) {
334: int after_space = skip_white_space(array,
335: cursor + 1, end);
336: if (after_space != end && array[after_space] == '#') {
337: value_end = cursor;
338: cursor = after_space;
339: break;
340: }
341: cursor = after_space + 1;
342: } else if (c == '#') {
343: value_end = cursor;
344: break;
345: } else {
346: ++cursor;
347: }
348: }
349: if (cursor != end) {
350: // array[cursor] is '#' here
351: found = true;
352: tag_value_start = value_start;
353: tag_value_end = value_end;
354: tag_definition_end = cursor + 1;
355: }
356: }
357: return (found) ? id : 0;
358: }
359:
360: private int get_tag_id(char[] array, int begin, int end,
361: boolean at_line_start) {
362: if (at_line_start) {
363: if (equals(SWITCH_TAG_STR, array, begin, end)) {
364: return SWITCH_TAG;
365: }
366: if (equals(GENERATED_TAG_STR, array, begin, end)) {
367: return GENERATED_TAG;
368: }
369: }
370: if (equals(STRING_TAG_STR, array, begin, end)) {
371: return STRING_TAG;
372: }
373: return 0;
374: }
375:
376: private void look_for_id_definitions(char[] array, int begin,
377: int end, boolean use_tag_value_as_string) {
378: // Look for the pattern
379: // '^[ \t]+Id_([a-zA-Z0-9_]+)[ \t]*=.*$'
380: // where \1 gives field or method name
381: int cursor = begin;
382: // Skip tab and spaces at the beginning
383: cursor = skip_white_space(array, cursor, end);
384: int id_start = cursor;
385: int name_start = skip_matched_prefix("Id_", array, cursor, end);
386: if (name_start >= 0) {
387: // Found Id_ prefix
388: cursor = name_start;
389: cursor = skip_name_char(array, cursor, end);
390: int name_end = cursor;
391: if (name_start != name_end) {
392: cursor = skip_white_space(array, cursor, end);
393: if (cursor != end) {
394: if (array[cursor] == '=') {
395: int id_end = name_end;
396: if (use_tag_value_as_string) {
397: name_start = tag_value_start;
398: name_end = tag_value_end;
399: }
400: // Got the match
401: add_id(array, id_start, id_end, name_start,
402: name_end);
403: }
404: }
405: }
406: }
407: }
408:
409: private void add_id(char[] array, int id_start, int id_end,
410: int name_start, int name_end) {
411: String name = new String(array, name_start, name_end
412: - name_start);
413: String value = new String(array, id_start, id_end - id_start);
414:
415: IdValuePair pair = new IdValuePair(name, value);
416:
417: pair.setLineNumber(body.getLineNumber());
418:
419: all_pairs.addElement(pair);
420: }
421:
422: private static boolean is_white_space(int c) {
423: return c == ' ' || c == '\t';
424: }
425:
426: private static int skip_white_space(char[] array, int begin, int end) {
427: int cursor = begin;
428: for (; cursor != end; ++cursor) {
429: int c = array[cursor];
430: if (!is_white_space(c)) {
431: break;
432: }
433: }
434: return cursor;
435: }
436:
437: private static int skip_matched_prefix(String prefix, char[] array,
438: int begin, int end) {
439: int cursor = -1;
440: int prefix_length = prefix.length();
441: if (prefix_length <= end - begin) {
442: cursor = begin;
443: for (int i = 0; i != prefix_length; ++i, ++cursor) {
444: if (prefix.charAt(i) != array[cursor]) {
445: cursor = -1;
446: break;
447: }
448: }
449: }
450: return cursor;
451: }
452:
453: private static boolean equals(String str, char[] array, int begin,
454: int end) {
455: if (str.length() == end - begin) {
456: for (int i = begin, j = 0; i != end; ++i, ++j) {
457: if (array[i] != str.charAt(j)) {
458: return false;
459: }
460: }
461: return true;
462: }
463: return false;
464: }
465:
466: private static int skip_name_char(char[] array, int begin, int end) {
467: int cursor = begin;
468: for (; cursor != end; ++cursor) {
469: int c = array[cursor];
470: if (!('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z')) {
471: if (!('0' <= c && c <= '9')) {
472: if (c != '_') {
473: break;
474: }
475: }
476: }
477: }
478: return cursor;
479: }
480:
481: public static void main(String[] args) {
482: Main self = new Main();
483: int status = self.exec(args);
484: System.exit(status);
485: }
486:
487: private int exec(String[] args) {
488: R = new ToolErrorReporter(true, System.err);
489:
490: int arg_count = process_options(args);
491:
492: if (arg_count == 0) {
493: option_error(ToolErrorReporter
494: .getMessage("msg.idswitch.no_file_argument"));
495: return -1;
496: }
497: if (arg_count > 1) {
498: option_error(ToolErrorReporter
499: .getMessage("msg.idswitch.too_many_arguments"));
500: return -1;
501: }
502:
503: P = new CodePrinter();
504: P.setIndentStep(4);
505: P.setIndentTabSize(0);
506:
507: try {
508: process_file(args[0]);
509: } catch (IOException ex) {
510: print_error(ToolErrorReporter.getMessage(
511: "msg.idswitch.io_error", ex.toString()));
512: return -1;
513: } catch (EvaluatorException ex) {
514: return -1;
515: }
516: return 0;
517: }
518:
519: private int process_options(String[] args) {
520:
521: int status = 1;
522:
523: boolean show_usage = false;
524: boolean show_version = false;
525:
526: int N = args.length;
527: L: for (int i = 0; i != N; ++i) {
528: String arg = args[i];
529: int arg_length = arg.length();
530: if (arg_length >= 2) {
531: if (arg.charAt(0) == '-') {
532: if (arg.charAt(1) == '-') {
533: if (arg_length == 2) {
534: args[i] = null;
535: break;
536: }
537: if (arg.equals("--help")) {
538: show_usage = true;
539: } else if (arg.equals("--version")) {
540: show_version = true;
541: } else {
542: option_error(ToolErrorReporter.getMessage(
543: "msg.idswitch.bad_option", arg));
544: status = -1;
545: break L;
546: }
547: } else {
548: for (int j = 1; j != arg_length; ++j) {
549: char c = arg.charAt(j);
550: switch (c) {
551: case 'h':
552: show_usage = true;
553: break;
554: default:
555: option_error(ToolErrorReporter
556: .getMessage(
557: "msg.idswitch.bad_option_char",
558: String.valueOf(c)));
559: status = -1;
560: break L;
561: }
562:
563: }
564: }
565: args[i] = null;
566: }
567: }
568: }
569:
570: if (status == 1) {
571: if (show_usage) {
572: show_usage();
573: status = 0;
574: }
575: if (show_version) {
576: show_version();
577: status = 0;
578: }
579: }
580:
581: if (status != 1) {
582: System.exit(status);
583: }
584:
585: return remove_nulls(args);
586: }
587:
588: private void show_usage() {
589: System.out.println(ToolErrorReporter
590: .getMessage("msg.idswitch.usage"));
591: System.out.println();
592: }
593:
594: private void show_version() {
595: System.out.println(ToolErrorReporter
596: .getMessage("msg.idswitch.version"));
597: }
598:
599: private void option_error(String str) {
600: print_error(ToolErrorReporter.getMessage(
601: "msg.idswitch.bad_invocation", str));
602: }
603:
604: private void print_error(String text) {
605: System.err.println(text);
606: }
607:
608: private int remove_nulls(String[] array) {
609: int N = array.length;
610: int cursor = 0;
611: for (; cursor != N; ++cursor) {
612: if (array[cursor] == null) {
613: break;
614: }
615: }
616: int destination = cursor;
617: if (cursor != N) {
618: ++cursor;
619: for (; cursor != N; ++cursor) {
620: String elem = array[cursor];
621: if (elem != null) {
622: array[destination] = elem;
623: ++destination;
624: }
625: }
626: }
627: return destination;
628: }
629: }
|