View Javadoc

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