/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.jini.config;
import java.io.IOException;
import java.io.Reader;
/**
* A Reader that converts Unicode escape sequences, throwing IOException for
* malformed escapes.
*
* @author Sun Microsystems, Inc.
* @since 2.0
*/
final class UnicodeEscapesDecodingReader extends Reader {
/** Marker for when peekc is empty. */
private static final int NO_CHAR = Integer.MIN_VALUE;
/** The source of characters to translate. */
private Reader reader;
/**
* A peeked character -- the character after a backslash if it wasn't 'u'
* -- or NO_CHAR if no peek was done.
*/
private int peekc = NO_CHAR;
/** Buffer to hold the Unicode value being parsed. */
private final char[] code = new char[4];
/** Creates an instance of this class. */
UnicodeEscapesDecodingReader(Reader reader) {
if (reader == null) {
throw new NullPointerException("reader is null");
}
this.reader = reader;
}
public synchronized int read() throws IOException {
if (reader == null) {
throw new IOException("stream is closed");
}
return readInternal();
}
public synchronized int read(char[] cbuf, int off, int len)
throws IOException
{
if (reader == null) {
throw new IOException("stream is closed");
} else if (off < 0 || off > cbuf.length ||
len < 0 || off + len > cbuf.length ||
off + len < 0)
{
throw new IndexOutOfBoundsException();
}
for (int nchars = 0; nchars < len; nchars++) {
int c = readInternal();
if (c < 0) {
return (nchars == 0) ? -1 : nchars;
}
cbuf[off + nchars] = (char) c;
}
return len;
}
/** Implements read() without checking if this reader is closed. */
private int readInternal() throws IOException {
int c;
if (peekc == NO_CHAR) {
c = reader.read();
} else {
c = peekc;
peekc = NO_CHAR;
if (c == '\\') { /* an escaped slash */
return c;
}
}
if (c != '\\') { /* not a Unicode escape */
return c;
}
c = reader.read();
if (c != 'u') { /* not a Unicode escape */
peekc = c;
return '\\';
}
do {
c = reader.read();
} while (c == 'u');
int nchars = 0;
if (c >= 0) {
code[nchars++] = (char) c;
while (nchars < 4) {
int n = reader.read(code, nchars, 4 - nchars);
if (n < 0) {
break;
}
nchars += n;
}
}
String s = new String(code, 0, nchars);
if (nchars == 4) {
try {
int i = Integer.parseInt(s, 16);
if (i >= 0) {
return (char) i;
}
} catch (NumberFormatException e) {
}
}
throw new IOException("illegal Unicode escape: \\u" + s);
}
public synchronized void close() throws IOException {
if (reader != null) {
reader.close();
reader = null;
}
}
}