001: /*
002: *******************************************************************************
003: * Copyright (C) 2002-2004, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */
007: package com.ibm.icu.dev.tool.localeconverter;
008:
009: import java.io.*;
010:
011: /**
012: * An escape transition parses a POSIX escape sequence. An escape
013: * sequence can be a hex, octal, or decimal constant, or an escaped
014: * character. The resultant value is ALWAYS on byte long. Longer
015: * escaped values (ie.\xFF63) overflow and are truncated. An escape
016: * character followed by an EOL sequence is silently eaten.
017: */
018: public class EscapeTransition extends ComplexTransition {
019: public static final EscapeTransition GLOBAL = new EscapeTransition(
020: SUCCESS);
021:
022: public static final char DEFAULT_ESCAPE_CHAR = '\\';
023: public static char ESCAPE_CHAR = DEFAULT_ESCAPE_CHAR;
024:
025: private static final int DECIMAL = 1; //decimal escape sequence was parsed
026: private static final int OCTAL = 2; //octal escape sequence was parsed
027: private static final int HEX = 3; //hex escape sequence was parsed
028: private static final int ESCAPE = 4; //character escape sequence was parsed
029: private static final int EOL = 5; //an escape character followed by EOF eaten
030:
031: private static final String OCTAL_CHARS = "01234567";
032: private static final String DECIMAL_CHARS = "0123456789";
033: private static final String HEX_CHARS = "0123456789abcdefABCDEF";
034:
035: /** Set the escape character to the default */
036: public static synchronized char setDefaultEscapeChar() {
037: return setEscapeChar(DEFAULT_ESCAPE_CHAR);
038: }
039:
040: /** Set the escape character */
041: public static synchronized char setEscapeChar(char c) {
042: char result = ESCAPE_CHAR;
043: ESCAPE_CHAR = c;
044: theStates = null;
045: return result;
046: }
047:
048: public EscapeTransition(int success) {
049: super (success);
050: //{{INIT_CONTROLS
051: //}}
052: }
053:
054: public boolean accepts(int c) {
055: return ESCAPE_CHAR == (char) c;
056: }
057:
058: /** Convert the accepted text into the appropriate unicode character */
059: protected void handleSuccess(Lex parser, StringBuffer output)
060: throws IOException {
061: switch (parser.getState()) {
062: case DECIMAL:
063: output.append((char) parser.dataAsNumber(10));
064: break;
065: case OCTAL:
066: output.append((char) parser.dataAsNumber(8));
067: break;
068: case HEX:
069: output.append((char) parser.dataAsNumber(16));
070: break;
071: case ESCAPE:
072: parser.appendDataTo(output);
073: break;
074: case EOL:
075: //silently eat the EOL characters
076: break;
077: default:
078: //should never get here
079: throw new Lex.ParseException(
080: "Internal error parsing escape sequence");
081: // parser.appendDataTo(output);
082: }
083: }
084:
085: /** return the states for this transaction */
086: protected Lex.Transition[][] getStates() {
087: synchronized (getClass()) {
088: if (theStates == null) {
089: //cache the states so they can be shared. They must
090: //be rebuilt when the escape character is changed.
091: theStates = new Lex.Transition[][] {
092: { //state 0:
093: new Lex.CharTransition(ESCAPE_CHAR,
094: Lex.IGNORE_CONSUME, -1),
095: new Lex.ParseExceptionTransition(
096: "illegal escape character") },
097: { //state 1:
098: new Lex.EOFTransition(OCTAL),
099: new Lex.CharTransition('d',
100: Lex.IGNORE_CONSUME, -3),
101: new Lex.CharTransition('x',
102: Lex.IGNORE_CONSUME, -2),
103: new Lex.StringTransition(OCTAL_CHARS,
104: Lex.ACCUMULATE_CONSUME, -4),
105: new Lex.CharTransition(ESCAPE_CHAR,
106: Lex.ACCUMULATE_CONSUME, ESCAPE),
107: new EOLTransition(EOL),
108: new Lex.DefaultTransition(
109: Lex.ACCUMULATE_CONSUME, ESCAPE) },
110: { //state 2: hex
111: new Lex.EOFTransition(HEX),
112: new Lex.StringTransition(HEX_CHARS,
113: Lex.ACCUMULATE_CONSUME, -2),
114: new Lex.DefaultTransition(
115: Lex.IGNORE_PUTBACK, HEX) },
116: { //state 3: decimal
117: new Lex.EOFTransition(DECIMAL),
118: new Lex.StringTransition(DECIMAL_CHARS,
119: Lex.ACCUMULATE_CONSUME, -3),
120: new Lex.DefaultTransition(
121: Lex.IGNORE_PUTBACK, DECIMAL) },
122: { //state 4: octal
123: new Lex.EOFTransition(OCTAL),
124: new Lex.StringTransition(OCTAL_CHARS,
125: Lex.ACCUMULATE_CONSUME, -4),
126: new Lex.DefaultTransition(
127: Lex.IGNORE_PUTBACK, OCTAL) }, };
128: }
129: }
130: return theStates;
131: }
132:
133: private static Lex.Transition[][] theStates = null;
134:
135: public static void main(String[] args) {
136: try {
137: Lex.Transition[][] states = { {
138: new EscapeTransition(SUCCESS),
139: new Lex.EOFTransition(),
140: new Lex.ParseExceptionTransition("bad test input") } };
141: String text = "\\d100\\xAf\\\\\\777\\\n\\123\\x2028\\x2029";
142: StringReader sr = new StringReader(text);
143: PushbackReader pr = new PushbackReader(sr);
144: Lex parser = new Lex(states, pr);
145: //parser.debug(true);
146: int s = parser.nextToken();
147: while (s == SUCCESS) {
148: System.out.println(parser.getState());
149: s = parser.nextToken();
150: }
151: } catch (Exception e) {
152: System.out.println(e);
153: }
154: }
155: //{{DECLARE_CONTROLS
156: //}}
157: }
|