mirror of
http://git.eaglercraft.rip/eaglercraft/eaglercraft-1.8.git
synced 2025-04-30 02:01:59 -05:00
327 lines
8.0 KiB
Java
327 lines
8.0 KiB
Java
/*
|
|
* Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*/
|
|
|
|
package net.lax1dude.eaglercraft.v1_8;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.Reader;
|
|
import java.util.Properties;
|
|
|
|
public class EaglerProperties extends Properties {
|
|
|
|
public void load(Reader reader) throws IOException {
|
|
load0(new LineReader(reader));
|
|
}
|
|
|
|
public void load(InputStream inStream) throws IOException {
|
|
load0(new LineReader(inStream));
|
|
}
|
|
|
|
private void load0(LineReader lr) throws IOException {
|
|
StringBuilder outBuffer = new StringBuilder();
|
|
int limit;
|
|
int keyLen;
|
|
int valueStart;
|
|
boolean hasSep;
|
|
boolean precedingBackslash;
|
|
boolean first = true;
|
|
|
|
while ((limit = lr.readLine()) >= 0) {
|
|
keyLen = 0;
|
|
valueStart = limit;
|
|
hasSep = false;
|
|
if(first && limit > 0) {
|
|
if(lr.lineBuf[0] == 65279) {
|
|
keyLen = 1;
|
|
}
|
|
}
|
|
first = false;
|
|
|
|
// System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
|
|
precedingBackslash = false;
|
|
while (keyLen < limit) {
|
|
char c = lr.lineBuf[keyLen];
|
|
// need check if escaped.
|
|
if ((c == '=' || c == ':') && !precedingBackslash) {
|
|
valueStart = keyLen + 1;
|
|
hasSep = true;
|
|
break;
|
|
} else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
|
|
valueStart = keyLen + 1;
|
|
break;
|
|
}
|
|
if (c == '\\') {
|
|
precedingBackslash = !precedingBackslash;
|
|
} else {
|
|
precedingBackslash = false;
|
|
}
|
|
keyLen++;
|
|
}
|
|
while (valueStart < limit) {
|
|
char c = lr.lineBuf[valueStart];
|
|
if (c != ' ' && c != '\t' && c != '\f') {
|
|
if (!hasSep && (c == '=' || c == ':')) {
|
|
hasSep = true;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
valueStart++;
|
|
}
|
|
String key = loadConvert(lr.lineBuf, 0, keyLen, outBuffer);
|
|
String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, outBuffer);
|
|
put(key, value);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read in a "logical line" from an InputStream/Reader, skip all comment and
|
|
* blank lines and filter out those leading whitespace characters (\u0020,
|
|
* \u0009 and \u000c) from the beginning of a "natural line". Method returns the
|
|
* char length of the "logical line" and stores the line in "lineBuf".
|
|
*/
|
|
private static class LineReader {
|
|
LineReader(InputStream inStream) {
|
|
this.inStream = inStream;
|
|
inByteBuf = new byte[8192];
|
|
}
|
|
|
|
LineReader(Reader reader) {
|
|
this.reader = reader;
|
|
inCharBuf = new char[8192];
|
|
}
|
|
|
|
char[] lineBuf = new char[1024];
|
|
private byte[] inByteBuf;
|
|
private char[] inCharBuf;
|
|
private int inLimit = 0;
|
|
private int inOff = 0;
|
|
private InputStream inStream;
|
|
private Reader reader;
|
|
|
|
int readLine() throws IOException {
|
|
// use locals to optimize for interpreted performance
|
|
int len = 0;
|
|
int off = inOff;
|
|
int limit = inLimit;
|
|
|
|
boolean skipWhiteSpace = true;
|
|
boolean appendedLineBegin = false;
|
|
boolean precedingBackslash = false;
|
|
boolean fromStream = inStream != null;
|
|
byte[] byteBuf = inByteBuf;
|
|
char[] charBuf = inCharBuf;
|
|
char[] lineBuf = this.lineBuf;
|
|
char c;
|
|
|
|
while (true) {
|
|
if (off >= limit) {
|
|
inLimit = limit = fromStream ? inStream.read(byteBuf) : reader.read(charBuf);
|
|
if (limit <= 0) {
|
|
if (len == 0) {
|
|
return -1;
|
|
}
|
|
return precedingBackslash ? len - 1 : len;
|
|
}
|
|
off = 0;
|
|
}
|
|
|
|
// (char)(byte & 0xFF) is equivalent to calling a ISO8859-1 decoder.
|
|
c = (fromStream) ? (char) (byteBuf[off++] & 0xFF) : charBuf[off++];
|
|
|
|
if (skipWhiteSpace) {
|
|
if (c == ' ' || c == '\t' || c == '\f') {
|
|
continue;
|
|
}
|
|
if (!appendedLineBegin && (c == '\r' || c == '\n')) {
|
|
continue;
|
|
}
|
|
skipWhiteSpace = false;
|
|
appendedLineBegin = false;
|
|
|
|
}
|
|
if (len == 0) { // Still on a new logical line
|
|
if (c == '#' || c == '!') {
|
|
// Comment, quickly consume the rest of the line
|
|
|
|
// When checking for new line characters a range check,
|
|
// starting with the higher bound ('\r') means one less
|
|
// branch in the common case.
|
|
commentLoop: while (true) {
|
|
if (fromStream) {
|
|
byte b;
|
|
while (off < limit) {
|
|
b = byteBuf[off++];
|
|
if (b <= '\r' && (b == '\r' || b == '\n'))
|
|
break commentLoop;
|
|
}
|
|
if (off == limit) {
|
|
inLimit = limit = inStream.read(byteBuf);
|
|
if (limit <= 0) { // EOF
|
|
return -1;
|
|
}
|
|
off = 0;
|
|
}
|
|
} else {
|
|
while (off < limit) {
|
|
c = charBuf[off++];
|
|
if (c <= '\r' && (c == '\r' || c == '\n'))
|
|
break commentLoop;
|
|
}
|
|
if (off == limit) {
|
|
inLimit = limit = reader.read(charBuf);
|
|
if (limit <= 0) { // EOF
|
|
return -1;
|
|
}
|
|
off = 0;
|
|
}
|
|
}
|
|
}
|
|
skipWhiteSpace = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (c != '\n' && c != '\r') {
|
|
lineBuf[len++] = c;
|
|
if (len == lineBuf.length) {
|
|
lineBuf = new char[len + (len << 1)];
|
|
System.arraycopy(this.lineBuf, 0, lineBuf, 0, len);
|
|
this.lineBuf = lineBuf;
|
|
}
|
|
// flip the preceding backslash flag
|
|
precedingBackslash = (c == '\\') ? !precedingBackslash : false;
|
|
} else {
|
|
// reached EOL
|
|
if (len == 0) {
|
|
skipWhiteSpace = true;
|
|
continue;
|
|
}
|
|
if (off >= limit) {
|
|
inLimit = limit = fromStream ? inStream.read(byteBuf) : reader.read(charBuf);
|
|
off = 0;
|
|
if (limit <= 0) { // EOF
|
|
return precedingBackslash ? len - 1 : len;
|
|
}
|
|
}
|
|
if (precedingBackslash) {
|
|
// backslash at EOL is not part of the line
|
|
len -= 1;
|
|
// skip leading whitespace characters in the following line
|
|
skipWhiteSpace = true;
|
|
appendedLineBegin = true;
|
|
precedingBackslash = false;
|
|
// take care not to include any subsequent \n
|
|
if (c == '\r') {
|
|
if (fromStream) {
|
|
if (byteBuf[off] == '\n') {
|
|
off++;
|
|
}
|
|
} else {
|
|
if (charBuf[off] == '\n') {
|
|
off++;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
inOff = off;
|
|
return len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Converts encoded \uxxxx to unicode chars and changes special saved chars
|
|
* to their original forms
|
|
*/
|
|
private String loadConvert(char[] in, int off, int len, StringBuilder out) {
|
|
char aChar;
|
|
int end = off + len;
|
|
int start = off;
|
|
while (off < end) {
|
|
aChar = in[off++];
|
|
if (aChar == '\\') {
|
|
break;
|
|
}
|
|
}
|
|
if (off == end) { // No backslash
|
|
return new String(in, start, len);
|
|
}
|
|
|
|
// backslash found at off - 1, reset the shared buffer, rewind offset
|
|
out.setLength(0);
|
|
off--;
|
|
out.append(in, start, off - start);
|
|
|
|
while (off < end) {
|
|
aChar = in[off++];
|
|
if (aChar == '\\') {
|
|
// No need to bounds check since LineReader::readLine excludes
|
|
// unescaped \s at the end of the line
|
|
aChar = in[off++];
|
|
if (aChar == 'u') {
|
|
// Read the xxxx
|
|
if (off > end - 4)
|
|
throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
|
|
int value = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
aChar = in[off++];
|
|
switch (aChar) {
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
value = (value << 4) + aChar - '0';
|
|
break;
|
|
case 'a':
|
|
case 'b':
|
|
case 'c':
|
|
case 'd':
|
|
case 'e':
|
|
case 'f':
|
|
value = (value << 4) + 10 + aChar - 'a';
|
|
break;
|
|
case 'A':
|
|
case 'B':
|
|
case 'C':
|
|
case 'D':
|
|
case 'E':
|
|
case 'F':
|
|
value = (value << 4) + 10 + aChar - 'A';
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
|
|
};
|
|
}
|
|
out.append((char) value);
|
|
} else {
|
|
if (aChar == 't')
|
|
aChar = '\t';
|
|
else if (aChar == 'r')
|
|
aChar = '\r';
|
|
else if (aChar == 'n')
|
|
aChar = '\n';
|
|
else if (aChar == 'f')
|
|
aChar = '\f';
|
|
out.append(aChar);
|
|
}
|
|
} else {
|
|
out.append(aChar);
|
|
}
|
|
}
|
|
return out.toString();
|
|
}
|
|
|
|
}
|