This commit is contained in:
nboyd%atg.com 2001-05-09 17:12:07 +00:00
Родитель 5c6de514ee
Коммит e479370659
5 изменённых файлов: 1441 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,209 @@
/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
package org.mozilla.javascript.tools.idswitch;
class CodePrinter {
// length of u-type escape like \u12AB
private static final int LITERAL_CHAR_MAX_SIZE = 6;
private String lineTerminator = "\n";
private int indentStep = 4;
private int indentTabSize = 8;
private char[] buffer = new char[1 << 12]; // 4K
private int offset;
public String getLineTerminator() { return lineTerminator; }
public void setLineTerminator(String value) { lineTerminator = value; }
public int getIndentStep() { return indentStep; }
public void setIndentStep(int char_count) { indentStep = char_count; }
public int getIndentTabSize() { return indentTabSize; }
public void setIndentTabSize(int tab_size) { indentTabSize = tab_size; }
public void clear() {
offset = 0;
}
private int ensure_area(int area_size) {
int begin = offset;
int end = begin + area_size;
if (end > buffer.length) {
int new_capacity = buffer.length * 2;
if (end > new_capacity) { new_capacity = end; }
char[] tmp = new char[new_capacity];
System.arraycopy(buffer, 0, tmp, 0, begin);
buffer = tmp;
}
return begin;
}
private int add_area(int area_size) {
int pos = ensure_area(area_size);
offset = pos + area_size;
return pos;
}
public int getOffset() {
return offset;
}
public int getLastChar() {
return offset == 0 ? -1 : buffer[offset - 1];
}
public void p(char c) {
int pos = add_area(1);
buffer[pos] = c;
}
public void p(String s) {
int l = s.length();
int pos = add_area(l);
s.getChars(0, l, buffer, pos);
}
public final void p(char[] array) {
p(array, 0, array.length);
}
public void p(char[] array, int begin, int end) {
int l = end - begin;
int pos = add_area(l);
System.arraycopy(array, begin, buffer, pos, l);
}
public void p(int i) {
p(Integer.toString(i));
}
public void qchar(int c) {
int pos = ensure_area(2 + LITERAL_CHAR_MAX_SIZE);
buffer[pos] = '\'';
pos = put_string_literal_char(pos + 1, c, false);
buffer[pos] = '\'';
offset = pos + 1;
}
public void qstring(String s) {
int l = s.length();
int pos = ensure_area(2 + LITERAL_CHAR_MAX_SIZE * l);
buffer[pos] = '"';
++pos;
for (int i = 0; i != l; ++i) {
pos = put_string_literal_char(pos, s.charAt(i), true);
}
buffer[pos] = '"';
offset = pos + 1;
}
private int put_string_literal_char(int pos, int c, boolean in_string) {
boolean backslash_symbol = true;
switch (c) {
case '\b': c = 'b'; break;
case '\t': c = 't'; break;
case '\n': c = 'n'; break;
case '\f': c = 'f'; break;
case '\r': c = 'r'; break;
case '\'': backslash_symbol = !in_string; break;
case '"': backslash_symbol = in_string; break;
default: backslash_symbol = false;
}
if (backslash_symbol) {
buffer[pos] = '\\';
buffer[pos + 1] = (char)c;
pos += 2;
}
else if (' ' <= c && c <= 126) {
buffer[pos] = (char)c;
++pos;
}
else {
buffer[pos] = '\\';
buffer[pos + 1] = 'u';
buffer[pos + 2] = digit_to_hex_letter(0xF & (c >> 12));
buffer[pos + 3] = digit_to_hex_letter(0xF & (c >> 8));
buffer[pos + 4] = digit_to_hex_letter(0xF & (c >> 4));
buffer[pos + 5] = digit_to_hex_letter(0xF & c);
pos += 6;
}
return pos;
}
private static char digit_to_hex_letter(int d) {
return (char)((d < 10) ? '0' + d : 'A' - 10 + d);
}
public void indent(int level) {
int visible_size = indentStep * level;
int indent_size, tab_count;
if (indentTabSize <= 0) {
tab_count = 0; indent_size = visible_size;
}
else {
tab_count = visible_size / indentTabSize;
indent_size = tab_count + visible_size % indentTabSize;
}
int pos = add_area(indent_size);
int tab_end = pos + tab_count;
int indent_end = pos + indent_size;
for (; pos != tab_end; ++pos) { buffer[pos] = '\t'; }
for (; pos != indent_end; ++pos) { buffer[pos] = ' '; }
}
public void nl() {
p('\n');
}
public void line(int indent_level, String s) {
indent(indent_level); p(s); nl();
}
public void erase(int begin, int end) {
System.arraycopy(buffer, end, buffer, begin, offset - end);
offset -= end - begin;
}
public String toString() {
return new String(buffer, 0, offset);
}
}

Просмотреть файл

@ -0,0 +1,188 @@
/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
package org.mozilla.javascript.tools.idswitch;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
public class FileBody {
private static class ReplaceItem {
ReplaceItem next;
int begin;
int end;
String replacement;
ReplaceItem(int begin, int end, String text) {
this.begin = begin;
this.end = end;
this.replacement = text;
}
}
private char[] buffer = new char[1 << 14]; // 16K
private int bufferEnd;
private int lineBegin;
private int lineEnd;
private int nextLineStart;
private int lineNumber;
ReplaceItem firstReplace;
ReplaceItem lastReplace;
public char[] getBuffer() { return buffer; }
public void readData(Reader r) throws IOException {
int capacity = buffer.length;
int offset = 0;
for (;;) {
int n_read = r.read(buffer, offset, capacity - offset);
if (n_read < 0) { break; }
offset += n_read;
if (capacity == offset) {
capacity *= 2;
char[] tmp = new char[capacity];
System.arraycopy(buffer, 0, tmp, 0, offset);
buffer = tmp;
}
}
bufferEnd = offset;
}
public void writeInitialData(Writer w) throws IOException {
w.write(buffer, 0, bufferEnd);
}
public void writeData(Writer w) throws IOException {
int offset = 0;
for (ReplaceItem x = firstReplace; x != null; x = x.next) {
int before_replace = x.begin - offset;
if (before_replace > 0) {
w.write(buffer, offset, before_replace);
}
w.write(x.replacement);
offset = x.end;
}
int tail = bufferEnd - offset;
if (tail != 0) {
w.write(buffer, offset, tail);
}
}
public boolean wasModified() { return firstReplace != null; }
public boolean setReplacement(int begin, int end, String text) {
if (equals(text, buffer, begin, end)) { return false; }
ReplaceItem item = new ReplaceItem(begin, end, text);
if (firstReplace == null) {
firstReplace = lastReplace = item;
}
else if (begin < firstReplace.begin) {
item.next = firstReplace;
firstReplace = item;
}
else {
ReplaceItem cursor = firstReplace;
ReplaceItem next = cursor.next;
while (next != null) {
if (begin < next.begin) {
item.next = next;
cursor.next = item;
break;
}
cursor = next;
next = next.next;
}
if (next == null) {
lastReplace.next = item;
}
}
return true;
}
public int getLineNumber() { return lineNumber; }
public int getLineBegin() { return lineBegin; }
public int getLineEnd() { return lineEnd; }
public void startLineLoop() {
lineNumber = 0;
lineBegin = lineEnd = nextLineStart = 0;
}
public boolean nextLine() {
if (nextLineStart == bufferEnd) {
lineNumber = 0; return false;
}
int i; int c = 0;
for (i = nextLineStart; i != bufferEnd; ++i) {
c = buffer[i];
if (c == '\n' || c == '\r') { break; }
}
lineBegin = nextLineStart;
lineEnd = i;
if (i == bufferEnd) {
nextLineStart = i;
}
else if (c == '\r' && i + 1 != bufferEnd && buffer[i + 1] == '\n') {
nextLineStart = i + 2;
}
else {
nextLineStart = i + 1;
}
++lineNumber;
return true;
}
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;
}
}

Просмотреть файл

@ -0,0 +1,55 @@
/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
package org.mozilla.javascript.tools.idswitch;
public class IdValuePair
{
public final int idLength;
public final String id;
public final String value;
private int lineNumber;
public IdValuePair(String id, String value) {
this.idLength = id.length();
this.id = id;
this.value = value;
}
public int getLineNumber() { return lineNumber; }
public void setLineNumber(int value) { lineNumber = value; }
}

Просмотреть файл

@ -0,0 +1,504 @@
/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
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 StringIdMap {
private static final String PROGRAM_NAME = "StringIdMap";
private static final String SWITCH_TAG_STR = "string_id_map";
private static final String GENERATED_TAG_STR = "generated";
private static final int
NORMAL_LINE = 0,
SWITCH_TAG = 1,
GENERATED_TAG = 2;
private final Vector all_pairs = new Vector();
private ToolErrorReporter R;
private CodePrinter P;
private FileBody body;
private String source_file;
private int tag_definition_end;
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() throws IOException {
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();
L: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 0:
if (tag_id == SWITCH_TAG) {
cur_state = SWITCH_TAG;
all_pairs.removeAllElements();
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);
}
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 = R.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 = R.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");
String dateString = f.format(new Date());
return f.format(new Date());
}
private void generate_java_code() {
P.clear();
IdValuePair[] pairs = new IdValuePair[all_pairs.size()];
all_pairs.copyInto(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);
if (cursor + 2 <= end) {
if (array[cursor] == '/' || array[cursor + 1] == '/') {
cursor += 2;
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 == '#' || is_white_space(c)) { break; }
}
if (cursor != end) {
int tag_end = cursor;
cursor = skip_white_space(array, cursor, end);
if (cursor != end && array[cursor] == '#') {
id = get_tag_id(array, tag_start, tag_end);
if (id != 0) {
if (end_tag) { id = -id; }
tag_definition_end = cursor + 1;
}
}
}
}
}
}
return id;
}
private int get_tag_id(char[] array, int begin, int end) {
if (equals(SWITCH_TAG_STR, array, begin, end)) {
return SWITCH_TAG;
}
if (equals(GENERATED_TAG_STR, array, begin, end)) {
return GENERATED_TAG;
}
return 0;
}
private void look_for_id_definitions(char[] array, int begin, int end)
{
// 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 begining
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] == '=') {
// Got the match
add_id(array, id_start, name_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.addElement(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) {
StringIdMap self = new StringIdMap();
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(R.getMessage
("msg.idswitch.no_file_argument"));
return -1;
}
if (arg_count > 1) {
option_error(R.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(R.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(R.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(R.getMessage
("msg.idswitch.bad_option_char",
"" + 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(R.getMessage("msg.idswitch.usage"));
System.out.println();
}
private void show_version() {
System.out.println(R.getMessage("msg.idswitch.version"));
}
private void option_error(String str) {
print_error(R.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;
}
}

Просмотреть файл

@ -0,0 +1,485 @@
/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
package org.mozilla.javascript.tools.idswitch;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.tools.ToolErrorReporter;
public class SwitchGenerator {
String v_switch_label = "L0";
String v_label = "L";
String v_s = "s";
String v_c = "c";
String v_guess = "X";
String v_id = "id";
String v_length_suffix = "_length";
int use_if_threshold = 3;
int char_tail_test_threshold = 2;
private IdValuePair[] pairs;
private String default_value;
private int[] columns;
private boolean c_was_defined;
private CodePrinter P;
private ToolErrorReporter R;
private String source_file;
public CodePrinter getCodePrinter() { return P; }
public void setCodePrinter(CodePrinter value) { P = value; }
public ToolErrorReporter getReporter() { return R; }
public void setReporter(ToolErrorReporter value) { R = value; }
public String getSourceFileName() { return source_file; }
public void setSourceFileName(String value) { source_file = value; }
public void generateSwitch(String[] pairs, String default_value) {
int N = pairs.length / 2;
IdValuePair[] id_pairs = new IdValuePair[N];
for (int i = 0; i != N; ++i) {
id_pairs[i] = new IdValuePair(pairs[2 * i], pairs[2 * i + 1]);
}
generateSwitch(id_pairs, default_value);
}
public void generateSwitch(IdValuePair[] pairs, String default_value) {
int begin = 0;
int end = pairs.length;
if (begin == end) { return; }
this.pairs = pairs;
this.default_value = default_value;
generate_body(begin, end, 2);
}
private void generate_body(int begin, int end, int indent_level) {
P.indent(indent_level);
P.p(v_switch_label); P.p(": { ");
P.p(v_id); P.p(" = "); P.p(default_value);
P.p("; String "); P.p(v_guess); P.p(" = null;");
c_was_defined = false;
int c_def_begin = P.getOffset();
P.p(" int "); P.p(v_c); P.p(';');
int c_def_end = P.getOffset();
P.nl();
generate_length_switch(begin, end, indent_level + 1);
if (!c_was_defined) {
P.erase(c_def_begin, c_def_end);
}
P.indent(indent_level + 1);
P.p("if ("); P.p(v_guess); P.p("!=null && ");
P.p(v_guess); P.p("!="); P.p(v_s);
P.p(" && !"); P.p(v_guess); P.p(".equals("); P.p(v_s); P.p(")) ");
P.p(v_id); P.p(" = "); P.p(default_value); P.p(";"); P.nl();
P.line(indent_level, "}");
}
private void generate_length_switch(int begin, int end, int indent_level) {
sort_pairs(begin, end, -1);
check_all_is_different(begin, end);
int lengths_count = count_different_lengths(begin, end);
columns = new int[pairs[end - 1].idLength];
boolean use_if;
if (lengths_count <= use_if_threshold) {
use_if = true;
if (lengths_count != 1) {
P.indent(indent_level);
P.p("int "); P.p(v_s); P.p(v_length_suffix);
P.p(" = "); P.p(v_s); P.p(".length();");
P.nl();
}
}
else {
use_if = false;
P.indent(indent_level);
P.p(v_label); P.p(": switch (");
P.p(v_s); P.p(".length()) {");
P.nl();
}
int same_length_begin = begin;
int cur_l = pairs[begin].idLength, l = 0;
for (int i = begin;;) {
++i;
if (i == end || (l = pairs[i].idLength) != cur_l) {
int next_indent;
if (use_if) {
P.indent(indent_level);
if (same_length_begin != begin) { P.p("else "); }
P.p("if (");
if (lengths_count == 1) {
P.p(v_s); P.p(".length()==");
}
else {
P.p(v_s); P.p(v_length_suffix); P.p("==");
}
P.p(cur_l);
P.p(") {");
next_indent = indent_level + 1;
}
else {
P.indent(indent_level);
P.p("case "); P.p(cur_l); P.p(":");
next_indent = indent_level + 1;
}
generate_letter_switch
(same_length_begin, i, next_indent, !use_if, use_if);
if (use_if) {
P.p("}"); P.nl();
}
else {
P.p("break "); P.p(v_label); P.p(";"); P.nl();
}
if (i == end) { break; }
same_length_begin = i;
cur_l = l;
}
}
if (!use_if) {
P.indent(indent_level); P.p("}"); P.nl();
}
}
private void generate_letter_switch
(int begin, int end,
int indent_level, boolean label_was_defined, boolean inside_if)
{
int L = pairs[begin].idLength;
for (int i = 0; i != L; ++i) {
columns[i] = i;
}
generate_letter_switch_r
(begin, end, L, indent_level, label_was_defined, inside_if);
}
private boolean generate_letter_switch_r
(int begin, int end, int L,
int indent_level, boolean label_was_defined, boolean inside_if)
{
boolean next_is_unreachable = false;
if (begin + 1 == end) {
P.p(' ');
IdValuePair pair = pairs[begin];
if (L > char_tail_test_threshold) {
P.p(v_guess); P.p("="); P.qstring(pair.id); P.p(";");
P.p(v_id); P.p("="); P.p(pair.value); P.p(";");
}
else {
if (L == 0) {
next_is_unreachable = true;
P.p(v_id); P.p("="); P.p(pair.value);
P.p("; break "); P.p(v_switch_label); P.p(";");
}
else {
P.p("if (");
int column = columns[0];
P.p(v_s); P.p(".charAt("); P.p(column); P.p(")==");
P.qchar(pair.id.charAt(column));
for (int i = 1; i != L; ++i) {
P.p(" && ");
column = columns[i];
P.p(v_s); P.p(".charAt("); P.p(column); P.p(")==");
P.qchar(pair.id.charAt(column));
}
P.p(") {");
P.p(v_id); P.p("="); P.p(pair.value);
P.p("; break "); P.p(v_switch_label); P.p(";}");
}
}
P.p(' ');
return next_is_unreachable;
}
int max_column_index = find_max_different_column(begin, end, L);
int max_column = columns[max_column_index];
int count = count_different_chars(begin, end, max_column);
columns[max_column_index] = columns[L - 1];
if (inside_if) { P.nl(); P.indent(indent_level); }
else { P.p(' '); }
boolean use_if;
if (count <= use_if_threshold) {
use_if = true;
c_was_defined = true;
P.p(v_c); P.p("="); P.p(v_s);
P.p(".charAt("); P.p(max_column); P.p(");");
}
else {
use_if = false;
if (!label_was_defined) {
label_was_defined = true;
P.p(v_label); P.p(": ");
}
P.p("switch ("); P.p(v_s);
P.p(".charAt("); P.p(max_column); P.p(")) {");
}
int same_char_begin = begin;
int cur_ch = pairs[begin].id.charAt(max_column), ch = 0;
for (int i = begin;;) {
++i;
if (i == end || (ch = pairs[i].id.charAt(max_column)) != cur_ch) {
int next_indent;
if (use_if) {
P.nl(); P.indent(indent_level);
if (same_char_begin != begin) { P.p("else "); }
P.p("if ("); P.p(v_c); P.p("==");
P.qchar(cur_ch); P.p(") {");
next_indent = indent_level + 1;
}
else {
P.nl(); P.indent(indent_level);
P.p("case "); P.qchar(cur_ch); P.p(":");
next_indent = indent_level + 1;
}
boolean after_unreachable = generate_letter_switch_r
(same_char_begin, i, L - 1,
next_indent, label_was_defined, use_if);
if (use_if) {
P.p("}");
}
else {
if (!after_unreachable) {
P.p("break "); P.p(v_label); P.p(";");
}
}
if (i == end) { break; }
same_char_begin = i;
cur_ch = ch;
}
}
if (use_if) {
P.nl();
if (inside_if) { P.indent(indent_level - 1); }
else { P.indent(indent_level); }
}
else {
P.nl(); P.indent(indent_level); P.p("}");
if (inside_if) { P.nl(); P.indent(indent_level - 1);}
else { P.p(' '); }
}
columns[max_column_index] = max_column;
return next_is_unreachable;
}
private int count_different_lengths(int begin, int end) {
int lengths_count = 0;
int cur_l = -1;
for (; begin != end; ++begin) {
int l = pairs[begin].idLength;
if (cur_l != l) {
++lengths_count; cur_l = l;
}
}
return lengths_count;
}
private int find_max_different_column(int begin, int end, int L) {
int max_count = 0;
int max_index = 0;
for (int i = 0; i != L; ++i) {
int column = columns[i];
sort_pairs(begin, end, column);
int count = count_different_chars(begin, end, column);
if (count == end - begin) { return i; }
if (max_count < count) {
max_count = count;
max_index = i;
}
}
if (max_index != L - 1) {
sort_pairs(begin, end, columns[max_index]);
}
return max_index;
}
private int count_different_chars(int begin, int end, int column) {
int chars_count = 0;
int cur_ch = -1;
for (; begin != end; ++begin) {
int ch = pairs[begin].id.charAt(column);
if (ch != cur_ch) {
++chars_count; cur_ch = ch;
}
}
return chars_count;
}
private void check_all_is_different(int begin, int end) {
if (begin != end) {
IdValuePair prev = pairs[begin];
while (++begin != end) {
IdValuePair current = pairs[begin];
if (prev.id.equals(current.id)) {
throw on_same_pair_fail(prev, current);
}
prev = current;
}
}
}
private EvaluatorException on_same_pair_fail(IdValuePair a, IdValuePair b) {
StringBuffer sb = new StringBuffer();
int line1 = a.getLineNumber(), line2 = b.getLineNumber();
if (line2 > line1) { int tmp = line1; line1 = line2; line2 = tmp; }
String error_text = R.getMessage("msg.idswitch.same_string",
a.id, new Integer(line2));
return R.runtimeError(error_text, source_file, line1, null, 0);
}
private void sort_pairs(int begin, int end, int comparator) {
heap4Sort(pairs, begin, end - begin, comparator);
}
private static boolean bigger
(IdValuePair a, IdValuePair b, int comparator)
{
if (comparator < 0) {
// For length selection switch it is enough to compare just length,
// but to detect same strings full comparison is essential
//return a.idLength > b.idLength;
int diff = a.idLength - b.idLength;
if (diff != 0) { return diff > 0; }
return a.id.compareTo(b.id) > 0;
}
else {
return a.id.charAt(comparator) > b.id.charAt(comparator);
}
}
private static void heap4Sort
(IdValuePair[] array, int offset, int size, int comparator)
{
if (size <= 1) { return; }
makeHeap4(array, offset, size, comparator);
while (size > 1) {
--size;
IdValuePair v1 = array[offset + size];
IdValuePair v2 = array[offset + 0];
array[offset + size] = v2;
array[offset + 0] = v1;
heapify4(array, offset, size, 0, comparator);
}
}
private static void makeHeap4
(IdValuePair[] array, int offset, int size, int comparator)
{
for (int i = ((size + 2) >> 2); i != 0;) {
--i;
heapify4(array, offset, size, i, comparator);
}
}
private static void heapify4
(IdValuePair[] array, int offset, int size, int i, int comparator)
{
int new_i1, new_i2, new_i3;
IdValuePair i_val = array[offset + i];
for (;;) {
int base = (i << 2);
new_i1 = base | 1;
new_i2 = base | 2;
new_i3 = base | 3;
int new_i4 = base + 4;
if (new_i4 >= size) { break; }
IdValuePair val1 = array[offset + new_i1];
IdValuePair val2 = array[offset + new_i2];
IdValuePair val3 = array[offset + new_i3];
IdValuePair val4 = array[offset + new_i4];
if (bigger(val2, val1, comparator)) {
val1 = val2; new_i1 = new_i2;
}
if (bigger(val4, val3, comparator)) {
val3 = val4; new_i3 = new_i4;
}
if (bigger(val3, val1, comparator)) {
val1 = val3; new_i1 = new_i3;
}
if (bigger(i_val, val1, comparator)) { return; }
array[offset + i] = val1;
array[offset + new_i1] = i_val;
i = new_i1;
}
if (new_i1 < size) {
IdValuePair val1 = array[offset + new_i1];
if (new_i2 != size) {
IdValuePair val2 = array[offset + new_i2];
if (bigger(val2, val1, comparator)) {
val1 = val2; new_i1 = new_i2;
}
if (new_i3 != size) {
IdValuePair val3 = array[offset + new_i3];
if (bigger(val3, val1, comparator)) {
val1 = val3; new_i1 = new_i3;
}
}
}
if (bigger(val1, i_val, comparator)) {
array[offset + i] = val1;
array[offset + new_i1] = i_val;
}
}
}
}