org.mozilla.javascript.tools.idswitch.Main Maven / Gradle / Ivy
The newest version!
/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript.tools.idswitch;
import java.io.*;
import java.util.*;
import java.text.SimpleDateFormat;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.tools.ToolErrorReporter;
public class Main {
private static final String SWITCH_TAG_STR = "string_id_map";
private static final String GENERATED_TAG_STR = "generated";
private static final String STRING_TAG_STR = "string";
private static final int
NORMAL_LINE = 0,
SWITCH_TAG = 1,
GENERATED_TAG = 2,
STRING_TAG = 3;
private final List all_pairs = new ArrayList();
private ToolErrorReporter R;
private CodePrinter P;
private FileBody body;
private String source_file;
private int tag_definition_end;
private int tag_value_start;
private int tag_value_end;
private static boolean is_value_type(int id) {
if (id == STRING_TAG) { return true; }
return false;
}
private static String tag_name(int id) {
switch (id) {
case SWITCH_TAG: return SWITCH_TAG_STR;
case -SWITCH_TAG: return "/" + SWITCH_TAG_STR;
case GENERATED_TAG: return GENERATED_TAG_STR;
case -GENERATED_TAG: return "/" + GENERATED_TAG_STR;
}
return "";
}
void process_file(String file_path) throws IOException {
source_file = file_path;
body = new FileBody();
InputStream is;
if (file_path.equals("-")) {
is = System.in;
}
else {
is = new FileInputStream(file_path);
}
try {
Reader r = new InputStreamReader(is, "ASCII");
body.readData(r);
}
finally { is.close(); }
process_file();
if (body.wasModified()) {
OutputStream os;
if (file_path.equals("-")) {
os = System.out;
}
else {
os = new FileOutputStream(file_path);
}
try {
Writer w = new OutputStreamWriter(os);
body.writeData(w);
w.flush();
}
finally { os.close(); }
}
}
private void process_file() {
int cur_state = 0;
char[] buffer = body.getBuffer();
int generated_begin = -1, generated_end = -1;
int time_stamp_begin = -1, time_stamp_end = -1;
body.startLineLoop();
while (body.nextLine()) {
int begin = body.getLineBegin();
int end = body.getLineEnd();
int tag_id = extract_line_tag_id(buffer, begin, end);
boolean bad_tag = false;
switch (cur_state) {
case NORMAL_LINE:
if (tag_id == SWITCH_TAG) {
cur_state = SWITCH_TAG;
all_pairs.clear();
generated_begin = -1;
}
else if (tag_id == -SWITCH_TAG) {
bad_tag = true;
}
break;
case SWITCH_TAG:
if (tag_id == 0) {
look_for_id_definitions(buffer, begin, end, false);
}
else if (tag_id == STRING_TAG) {
look_for_id_definitions(buffer, begin, end, true);
}
else if (tag_id == GENERATED_TAG) {
if (generated_begin >= 0) { bad_tag = true; }
else {
cur_state = GENERATED_TAG;
time_stamp_begin = tag_definition_end;
time_stamp_end = end;
}
}
else if (tag_id == -SWITCH_TAG) {
cur_state = 0;
if (generated_begin >= 0 && !all_pairs.isEmpty()) {
generate_java_code();
String code = P.toString();
boolean different = body.setReplacement
(generated_begin, generated_end, code);
if (different) {
String stamp = get_time_stamp();
body.setReplacement
(time_stamp_begin, time_stamp_end, stamp);
}
}
break;
}
else {
bad_tag = true;
}
break;
case GENERATED_TAG:
if (tag_id == 0) {
if (generated_begin < 0) { generated_begin = begin; }
}
else if (tag_id == -GENERATED_TAG) {
if (generated_begin < 0) { generated_begin = begin; }
cur_state = SWITCH_TAG;
generated_end = begin;
}
else {
bad_tag = true;
}
break;
}
if (bad_tag) {
String text = ToolErrorReporter.getMessage(
"msg.idswitch.bad_tag_order", tag_name(tag_id));
throw R.runtimeError
(text, source_file, body.getLineNumber(), null, 0);
}
}
if (cur_state != 0) {
String text = ToolErrorReporter.getMessage(
"msg.idswitch.file_end_in_switch", tag_name(cur_state));
throw R.runtimeError
(text, source_file, body.getLineNumber(), null, 0);
}
}
private String get_time_stamp() {
SimpleDateFormat f = new SimpleDateFormat
(" 'Last update:' yyyy-MM-dd HH:mm:ss z");
return f.format(new Date());
}
private void generate_java_code() {
P.clear();
IdValuePair[] pairs = new IdValuePair[all_pairs.size()];
all_pairs.toArray(pairs);
SwitchGenerator g = new SwitchGenerator();
g.char_tail_test_threshold = 2;
g.setReporter(R);
g.setCodePrinter(P);
g.generateSwitch(pairs, "0");
}
private int extract_line_tag_id(char[] array, int cursor, int end) {
int id = 0;
cursor = skip_white_space(array, cursor, end);
int after_leading_white_space = cursor;
cursor = look_for_slash_slash(array, cursor, end);
if (cursor != end) {
boolean at_line_start = (after_leading_white_space + 2 == cursor);
cursor = skip_white_space(array, cursor, end);
if (cursor != end && array[cursor] == '#') {
++cursor;
boolean end_tag = false;
if (cursor != end && array[cursor] == '/') {
++cursor; end_tag = true;
}
int tag_start = cursor;
for (; cursor != end; ++cursor) {
int c = array[cursor];
if (c == '#' || c == '=' ||is_white_space(c)) { break; }
}
if (cursor != end) {
int tag_end = cursor;
cursor = skip_white_space(array, cursor, end);
if (cursor != end) {
int c = array[cursor];
if (c == '=' || c == '#') {
id = get_tag_id
(array, tag_start, tag_end, at_line_start);
if (id != 0) {
String bad = null;
if (c == '#') {
if (end_tag) {
id = -id;
if (is_value_type(id)) {
bad = "msg.idswitch.no_end_usage";
}
}
tag_definition_end = cursor + 1;
}
else {
if (end_tag) {
bad = "msg.idswitch.no_end_with_value";
}
else if (!is_value_type(id)) {
bad = "msg.idswitch.no_value_allowed";
}
id = extract_tag_value
(array, cursor + 1, end, id);
}
if (bad != null) {
String s = ToolErrorReporter.getMessage(
bad, tag_name(id));
throw R.runtimeError
(s, source_file, body.getLineNumber(),
null, 0);
}
}
}
}
}
}
}
return id;
}
// Return position after first of // or end if not found
private int look_for_slash_slash(char[] array, int cursor, int end) {
while (cursor + 2 <= end) {
int c = array[cursor++];
if (c == '/') {
c = array[cursor++];
if (c == '/') {
return cursor;
}
}
}
return end;
}
private int extract_tag_value(char[] array, int cursor, int end, int id) {
// cursor points after #[^#=]+=
// ALERT: implement support for quoted strings
boolean found = false;
cursor = skip_white_space(array, cursor, end);
if (cursor != end) {
int value_start = cursor;
int value_end = cursor;
while (cursor != end) {
int c = array[cursor];
if (is_white_space(c)) {
int after_space = skip_white_space(array, cursor + 1, end);
if (after_space != end && array[after_space] == '#') {
value_end = cursor;
cursor = after_space;
break;
}
cursor = after_space + 1;
}
else if (c == '#') {
value_end = cursor;
break;
}
else {
++cursor;
}
}
if (cursor != end) {
// array[cursor] is '#' here
found = true;
tag_value_start = value_start;
tag_value_end = value_end;
tag_definition_end = cursor + 1;
}
}
return (found) ? id : 0;
}
private int get_tag_id
(char[] array, int begin, int end, boolean at_line_start)
{
if (at_line_start) {
if (equals(SWITCH_TAG_STR, array, begin, end)) {
return SWITCH_TAG;
}
if (equals(GENERATED_TAG_STR, array, begin, end)) {
return GENERATED_TAG;
}
}
if (equals(STRING_TAG_STR, array, begin, end)) {
return STRING_TAG;
}
return 0;
}
private void look_for_id_definitions
(char[] array, int begin, int end, boolean use_tag_value_as_string)
{
// Look for the pattern
// '^[ \t]+Id_([a-zA-Z0-9_]+)[ \t]*=.*$'
// where \1 gives field or method name
int cursor = begin;
// Skip tab and spaces at the beginning
cursor = skip_white_space(array, cursor, end);
int id_start = cursor;
int name_start = skip_matched_prefix("Id_", array, cursor, end);
if (name_start >= 0) {
// Found Id_ prefix
cursor = name_start;
cursor = skip_name_char(array, cursor, end);
int name_end = cursor;
if (name_start != name_end) {
cursor = skip_white_space(array, cursor, end);
if (cursor != end) {
if (array[cursor] == '=') {
int id_end = name_end;
if (use_tag_value_as_string) {
name_start = tag_value_start;
name_end = tag_value_end;
}
// Got the match
add_id(array, id_start, id_end, name_start, name_end);
}
}
}
}
}
private void add_id
(char[] array, int id_start, int id_end, int name_start, int name_end)
{
String name = new String(array, name_start, name_end - name_start);
String value = new String(array, id_start, id_end - id_start);
IdValuePair pair = new IdValuePair(name, value);
pair.setLineNumber(body.getLineNumber());
all_pairs.add(pair);
}
private static boolean is_white_space(int c) {
return c == ' ' || c == '\t';
}
private static int skip_white_space(char[] array, int begin, int end) {
int cursor = begin;
for (; cursor != end; ++cursor) {
int c = array[cursor];
if (!is_white_space(c)) { break; }
}
return cursor;
}
private static int skip_matched_prefix
(String prefix, char[] array, int begin, int end)
{
int cursor = -1;
int prefix_length = prefix.length();
if (prefix_length <= end - begin) {
cursor = begin;
for (int i = 0; i != prefix_length; ++i, ++cursor) {
if (prefix.charAt(i) != array[cursor]) {
cursor = -1; break;
}
}
}
return cursor;
}
private static boolean equals(String str, char[] array, int begin, int end)
{
if (str.length() == end - begin) {
for (int i = begin, j = 0; i != end; ++i, ++j) {
if (array[i] != str.charAt(j)) { return false; }
}
return true;
}
return false;
}
private static int skip_name_char(char[] array, int begin, int end) {
int cursor = begin;
for (; cursor != end; ++cursor) {
int c = array[cursor];
if (!('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z')) {
if (!('0' <= c && c <= '9')) {
if (c != '_') {
break;
}
}
}
}
return cursor;
}
public static void main(String[] args) {
Main self = new Main();
int status = self.exec(args);
System.exit(status);
}
private int exec(String[] args) {
R = new ToolErrorReporter(true, System.err);
int arg_count = process_options(args);
if (arg_count == 0) {
option_error(ToolErrorReporter.getMessage(
"msg.idswitch.no_file_argument"));
return -1;
}
if (arg_count > 1) {
option_error(ToolErrorReporter.getMessage(
"msg.idswitch.too_many_arguments"));
return -1;
}
P = new CodePrinter();
P.setIndentStep(4);
P.setIndentTabSize(0);
try {
process_file(args[0]);
}
catch (IOException ex) {
print_error(ToolErrorReporter.getMessage(
"msg.idswitch.io_error", ex.toString()));
return -1;
}
catch (EvaluatorException ex) {
return -1;
}
return 0;
}
private int process_options(String[] args) {
int status = 1;
boolean show_usage = false;
boolean show_version = false;
int N = args.length;
L: for (int i = 0; i != N; ++i) {
String arg = args[i];
int arg_length = arg.length();
if (arg_length >= 2) {
if (arg.charAt(0) == '-') {
if (arg.charAt(1) == '-') {
if (arg_length == 2) {
args[i] = null; break;
}
if (arg.equals("--help")) {
show_usage = true;
}
else if (arg.equals("--version")) {
show_version = true;
}
else {
option_error(ToolErrorReporter.getMessage(
"msg.idswitch.bad_option", arg));
status = -1; break L;
}
}
else {
for (int j = 1; j != arg_length; ++j) {
char c = arg.charAt(j);
switch (c) {
case 'h': show_usage = true; break;
default:
option_error(
ToolErrorReporter.getMessage(
"msg.idswitch.bad_option_char",
String.valueOf(c)));
status = -1;
break L;
}
}
}
args[i] = null;
}
}
}
if (status == 1) {
if (show_usage) { show_usage(); status = 0; }
if (show_version) { show_version(); status = 0; }
}
if (status != 1) { System.exit(status); }
return remove_nulls(args);
}
private void show_usage() {
System.out.println(
ToolErrorReporter.getMessage("msg.idswitch.usage"));
System.out.println();
}
private void show_version() {
System.out.println(
ToolErrorReporter.getMessage("msg.idswitch.version"));
}
private void option_error(String str) {
print_error(
ToolErrorReporter.getMessage("msg.idswitch.bad_invocation", str));
}
private void print_error(String text) {
System.err.println(text);
}
private int remove_nulls(String[] array) {
int N = array.length;
int cursor = 0;
for (; cursor != N; ++cursor) {
if (array[cursor] == null) { break; }
}
int destination = cursor;
if (cursor != N) {
++cursor;
for (; cursor != N; ++cursor) {
String elem = array[cursor];
if (elem != null) {
array[destination] = elem; ++destination;
}
}
}
return destination;
}
}