1 /***
2 *
3 * Copyright 2005 Alan Green
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 **/
18 package org.codehaus.groovy.antlr;
19
20 import org.codehaus.groovy.antlr.parser.GroovyLexer;
21 import java.io.IOException;
22 import java.io.Reader;
23
24 /***
25 * Translates GLS-defined unicode escapes into characters. Throws an exception
26 * in the event of an invalid unicode escape being detected.
27 *
28 * <p>No attempt has been made to optimise this class for speed or
29 * space.</p>
30 *
31 * @version $Revision: 1.1 $
32 */
33 public class UnicodeEscapingReader extends Reader {
34
35 private Reader reader;
36 private GroovyLexer lexer;
37 private boolean hasNextChar = false;
38 private int nextChar;
39
40 /***
41 * Constructor.
42 * @param reader The reader that this reader will filter over.
43 */
44 public UnicodeEscapingReader(Reader reader) {
45 this.reader = reader;
46 }
47
48 /***
49 * Sets the lexer that is using this reader. Must be called before the
50 * lexer is used.
51 */
52 public void setLexer(GroovyLexer lexer) {
53 this.lexer = lexer;
54 }
55
56 /***
57 * Reads characters from the underlying reader.
58 * @see java.io.Reader#read(char[],int,int)
59 */
60 public int read(char cbuf[], int off, int len) throws IOException {
61
62 int c = 0;
63 int count = 0;
64 while (count < len && (c = read())!= -1) {
65 cbuf[off + count] = (char) c;
66 count++;
67 }
68 return (count == 0 && c == -1) ? -1 : count;
69 }
70
71 /***
72 * Gets the next character from the underlying reader,
73 * translating escapes as required.
74 * @see java.io.Reader#close()
75 */
76 public int read() throws IOException {
77
78 if (hasNextChar) {
79 hasNextChar = false;
80 return nextChar;
81 }
82
83 int c = reader.read();
84 if (c != '//') {
85 return c;
86 }
87
88
89 c = reader.read();
90 if (c != 'u') {
91 hasNextChar = true;
92 nextChar = c;
93 return '//';
94 }
95
96
97 do {
98 c = reader.read();
99 } while (c == 'u');
100
101
102 checkHexDigit(c);
103 StringBuffer charNum = new StringBuffer();
104 charNum.append((char) c);
105
106
107 for (int i = 0; i < 3; i++) {
108 c = reader.read();
109 checkHexDigit(c);
110 charNum.append((char) c);
111 }
112 return Integer.parseInt(charNum.toString(), 16);
113 }
114
115 /***
116 * Checks that the given character is indeed a hex digit.
117 */
118 private void checkHexDigit(int c) throws IOException {
119 if (c >= '0' && c <= '9') {
120 return;
121 }
122 if (c >= 'a' && c <= 'f') {
123 return;
124 }
125 if (c >= 'A' && c <= 'F') {
126 return;
127 }
128
129 hasNextChar = true;
130 nextChar = c;
131 throw new IOException("Did not find four digit hex character code."
132 + " line: " + lexer.getLine() + " col:" + lexer.getColumn());
133 }
134
135
136 /***
137 * Closes this reader by calling close on the underlying reader.
138 * @see java.io.Reader#close()
139 */
140 public void close() throws IOException {
141 reader.close();
142 }
143
144 }