/*
* Copyright © 2010-2011 Rebecca G. Bettencourt / Kreative Software
* <p>
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a>
* <p>
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
* <p>
* Alternatively, the contents of this file may be used under the terms
* of the GNU Lesser General Public License (the "LGPL License"), in which
* case the provisions of LGPL License are applicable instead of those
* above. If you wish to allow use of your version of this file only
* under the terms of the LGPL License and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the LGPL License. If you do not delete
* the provisions above, a recipient may use your version of this file
* under either the MPL or the LGPL License.
* @since KSFL 1.2
* @author Rebecca G. Bettencourt, Kreative Software
*/
package com.kreative.binpack;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;
public class DataFormatParser {
// Change this number to adjust how long tokens can be.
private static final int LOOKAHEAD_LIMIT = 65536;
private static final List<DataField> POINT = new Vector<DataField>();
private static final List<DataField> RECT = new Vector<DataField>();
private static final List<DataField> COLOR = new Vector<DataField>();
static {
POINT.add(new DataField(DataType.SINT, 16, false, null, null, "y", "Y coordinate"));
POINT.add(new DataField(DataType.SINT, 16, false, null, null, "x", "X coordinate"));
RECT.add(new DataField(DataType.SINT, 16, false, null, null, "top", "top Y coordinate"));
RECT.add(new DataField(DataType.SINT, 16, false, null, null, "left", "left X coordinate"));
RECT.add(new DataField(DataType.SINT, 16, false, null, null, "bottom", "bottom Y coordinate"));
RECT.add(new DataField(DataType.SINT, 16, false, null, null, "right", "right X coordinate"));
COLOR.add(new DataField(DataType.UINT, 16, false, null, null, "red", "red channel intensity"));
COLOR.add(new DataField(DataType.UINT, 16, false, null, null, "green", "green channel intensity"));
COLOR.add(new DataField(DataType.UINT, 16, false, null, null, "blue", "blue channel intensity"));
}
private static final Object[][] TYPES = {
{ "character", DataType.CHAR },
{ "rectangle", DataType.STRUCT, 0, false, RECT },
{ "rgbcolour", DataType.STRUCT, 0, false, COLOR },
{ "bitfield", DataType.BITFIELD },
{ "datetime", DataType.DATE },
{ "rgbcolor", DataType.STRUCT, 0, false, COLOR },
{ "boolean", DataType.BOOLEAN },
{ "complex", DataType.COMPLEX },
{ "pstring", DataType.PSTRING },
{ "cstring", DataType.CSTRING },
{ "eightcc", DataType.CHAR, 64 },
{ "wstring", DataType.PSTRING, 16 },
{ "lstring", DataType.PSTRING, 32 },
{ "wcharbe", DataType.CHAR, 16, false, "UTF-16BE" },
{ "wcharle", DataType.CHAR, 16, true, "UTF-16BE" },
{ "ufixed", DataType.UFIXED },
{ "sfixed", DataType.SFIXED },
{ "filler", DataType.FILLER },
{ "binary", DataType.BINARY },
{ "struct", DataType.STRUCT },
{ "offset", DataType.OFFSET },
{ "string", DataType.CSTRING },
{ "colour", DataType.COLOR },
{ "bshort", DataType.BINT, 16 },
{ "oshort", DataType.OINT, 16 },
{ "hshort", DataType.HINT, 16 },
{ "ushort", DataType.UINT, 16 },
{ "sshort", DataType.SINT, 16 },
{ "single", DataType.FLOAT, 32 },
{ "double", DataType.FLOAT, 64 },
{ "fourcc", DataType.CHAR, 32 },
{ "ostype", DataType.CHAR, 32, false, "MACROMAN" },
{ "symbol", DataType.CHAR, 64, false, "ISO-8859-1" },
{ "float", DataType.FLOAT },
{ "color", DataType.COLOR },
{ "magic", DataType.MAGIC },
{ "align", DataType.ALIGN },
{ "fixed", DataType.SFIXED },
{ "bbyte", DataType.BINT, 8 },
{ "obyte", DataType.OINT, 8 },
{ "hbyte", DataType.HINT, 8 },
{ "ubyte", DataType.UINT, 8 },
{ "sbyte", DataType.SINT, 8 },
{ "short", DataType.SINT, 16 },
{ "blong", DataType.BINT, 64 },
{ "olong", DataType.OINT, 64 },
{ "hlong", DataType.HINT, 64 },
{ "ulong", DataType.UINT, 64 },
{ "slong", DataType.SINT, 64 },
{ "onecc", DataType.CHAR, 8 },
{ "twocc", DataType.CHAR, 16 },
{ "wchar", DataType.CHAR, 16, false, "UTF-16BE" },
{ "point", DataType.STRUCT, 0, false, POINT },
{ "enum", DataType.ENUM },
{ "bint", DataType.BINT },
{ "oint", DataType.OINT },
{ "hint", DataType.HINT },
{ "uint", DataType.UINT },
{ "sint", DataType.SINT },
{ "char", DataType.CHAR },
{ "date", DataType.DATE },
{ "bool", DataType.BOOLEAN },
{ "time", DataType.DATE },
{ "blob", DataType.BINARY },
{ "byte", DataType.SINT, 8 },
{ "long", DataType.SINT, 64 },
{ "half", DataType.FLOAT, 16 },
{ "real", DataType.FLOAT, 32 },
{ "quad", DataType.FLOAT, 128 },
{ "rect", DataType.STRUCT, 0, false, RECT },
{ "int", DataType.SINT },
{ "occ", DataType.CHAR, 8 },
{ "tcc", DataType.CHAR, 16 },
{ "fcc", DataType.CHAR, 32 },
{ "ecc", DataType.CHAR, 64 },
};
private Reader reader;
public DataFormatParser(Reader r) {
this.reader = r;
}
public boolean isShortForm() throws IOException {
parseWhitespace();
reader.mark(LOOKAHEAD_LIMIT);
int numchars = 0;
while (Character.isLetter(reader.read())) {
numchars++;
}
reader.reset();
return (numchars < 2);
}
public List<DataField> parseAuto() throws IOException {
if (isShortForm()) {
return parseShortForm();
} else {
return parseLongForm();
}
}
public List<DataField> parseLongForm() throws IOException {
List<DataField> format = new Vector<DataField>();
while (!atEnd()) {
DataType datatype;
int size;
boolean littleEndian;
Object elaboration;
DFExpression count;
String name;
String description;
Object[] dt = parseDataType();
datatype = (DataType)dt[1];
if (dt.length > 2) {
size = ((Number)dt[2]).intValue();
} else if (datatype.usesSize()) {
parseWhitespace();
size = parseFieldSize(datatype.defaultSize());
} else {
size = 0;
}
if (dt.length > 3) {
littleEndian = ((Boolean)dt[3]).booleanValue();
} else if (datatype.usesEndianness()) {
parseWhitespace();
littleEndian = parseEndianness();
} else {
littleEndian = false;
}
if (dt.length > 4) {
elaboration = dt[4];
} else if (datatype.usesElaboration()) {
parseWhitespace();
elaboration = parseElaboration(size, datatype.elaborationType());
} else {
elaboration = null;
}
parseWhitespace();
count = parseItemCount();
parseWhitespace();
name = parseFieldName();
parseWhitespace();
description = parseFieldDescription();
parseWhitespace();
parseTerminator();
format.add(new DataField(datatype, size, littleEndian, elaboration, count, name, description));
}
return format;
}
public List<DataField> parseShortForm() throws IOException {
List<DataField> format = new Vector<DataField>();
while (!atEnd()) {
DataType datatype;
int size;
boolean littleEndian;
Object elaboration;
DFExpression count;
String name;
String description;
Object[] dt = parseShortDataType();
datatype = (DataType)dt[0];
if (datatype.usesSize()) {
parseWhitespace();
size = parseFieldSize(datatype.defaultSize());
} else {
size = 0;
}
if (datatype.usesEndianness()) {
littleEndian = ((Boolean)dt[1]).booleanValue();
} else {
littleEndian = false;
}
if (datatype.usesElaboration()) {
parseWhitespace();
elaboration = parseElaboration(size, datatype.elaborationType());
} else {
elaboration = null;
}
parseWhitespace();
count = parseItemCount();
parseWhitespace();
name = parseShortFieldName();
parseWhitespace();
description = parseShortFieldDescription();
format.add(new DataField(datatype, size, littleEndian, elaboration, count, name, description));
}
return format;
}
public static String toLongString(List<DataField> format) {
StringBuffer s = new StringBuffer();
for (DataField df : format) {
DataType dt = df.type();
s.append(dt.toString());
if (dt.usesSize()) s.append(df.size());
if (dt.usesEndianness()) s.append(endiannessToString(df.littleEndian()));
if (dt.usesElaboration() && df.elaboration() != null) {
s.append(" ");
s.append(elaborationToString(dt.elaborationType(), df.elaboration()));
}
if (df.count() != null) {
s.append(" ");
s.append(itemCountToString(df.count()));
}
if (df.name() != null) {
s.append(" ");
s.append(fieldNameToString(df.name()));
}
if (df.description() != null) {
s.append(" ");
s.append(fieldDescriptionToString(df.description()));
}
s.append(";\n");
}
if (s.length() > 0 && s.charAt(s.length()-1) == '\n') {
s.deleteCharAt(s.length()-1);
}
return s.toString();
}
public static String toShortString(List<DataField> format) {
StringBuffer s = new StringBuffer();
for (DataField df : format) {
DataType dt = df.type();
if (dt.usesEndianness()) {
if (df.littleEndian()) {
s.append(dt.toShortString().toLowerCase());
} else {
s.append(dt.toShortString().toUpperCase());
}
} else {
s.append(dt.toShortString());
}
if (dt.usesSize()) s.append(df.size());
if (dt.usesElaboration() && df.elaboration() != null) s.append(elaborationToShortString(dt.elaborationType(), df.elaboration()));
if (df.count() != null) s.append(itemCountToString(df.count()));
if (df.name() != null) s.append(fieldNameToShortString(df.name()));
if (df.description() != null) s.append(fieldDescriptionToShortString(df.description()));
}
return s.toString();
}
private boolean atEnd() throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
while (true) {
int ch = reader.read();
if (ch < 0) {
return true;
} else if (Character.isWhitespace(ch)) {
reader.mark(LOOKAHEAD_LIMIT);
} else {
reader.reset();
return false;
}
}
}
private void parseWhitespace() throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
while (Character.isWhitespace(reader.read())) {
reader.mark(LOOKAHEAD_LIMIT);
}
reader.reset();
}
private Object[] parseDataType() throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
for (Object[] dt : TYPES) {
char[] nc = new char[dt[0].toString().length()];
reader.read(nc);
if (new String(nc).equalsIgnoreCase(dt[0].toString())) {
return dt;
} else {
reader.reset();
}
}
throw new RuntimeException("Parse error: expected data type");
}
private Object[] parseShortDataType() throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
int ch = reader.read();
DataType dt = DataType.fromChar((char)ch);
if (dt == null) {
reader.reset();
throw new RuntimeException("Parse error: expected data type");
} else {
return new Object[]{ dt, Character.isLowerCase(ch) };
}
}
private int parseFieldSize(int def) throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
int numchars = 0;
while (Character.isDigit(reader.read())) {
numchars++;
}
reader.reset();
if (numchars == 0) {
return def;
}
int size = 0;
while (numchars-->0) {
size *= 10;
size += Character.digit(reader.read(), 10);
}
return size;
}
private boolean parseEndianness() throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
int char1 = reader.read();
int char2 = reader.read();
if ((char1 == 'b' || char1 == 'B') && (char2 == 'e' || char2 == 'E')) return false;
if ((char1 == 'l' || char1 == 'L') && (char2 == 'e' || char2 == 'E')) return true;
reader.reset();
return false;
}
private static String endiannessToString(boolean littleEndian) {
return littleEndian ? "le" : "be";
}
private Object parseElaboration(int size, ElaborationType elabType) throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
if (reader.read() == '{') {
int level = 0;
StringBuffer e = new StringBuffer();
while (true) {
int ch = reader.read();
if (ch < 0) {
break;
} else if (ch == '{') {
e.append(Character.toChars(ch));
level++;
} else if (ch == '}') {
if (level == 0) break;
e.append(Character.toChars(ch));
level--;
} else {
e.append(Character.toChars(ch));
}
}
String es = e.toString();
switch (elabType) {
case TEXT_ENCODING: return es.trim().toUpperCase();
case DATE_FORMAT: return parseDateFormat(es);
case COLOR_FORMAT: return new ColorFormat(size, es);
case FP_FORMAT: return parseFPFormat(es);
case VALUE: return parseInt(es);
case KV_SIGNED: return parseIntStringMap(-1, es);
case KV_UNSIGNED: return parseIntStringMap(size, es);
case STRUCT: return new DataFormatParser(new StringReader(es)).parseAuto();
default: return es;
}
} else {
reader.reset();
switch (elabType) {
case TEXT_ENCODING: return "ISO-8859-1";
case DATE_FORMAT: return new DateFormat(DateFormat.UNIX);
case COLOR_FORMAT: return new ColorFormat(size, "ARGB");
case FP_FORMAT:
return new int[]{
FPUtilities.optimalSignWidth(size),
FPUtilities.optimalExponentWidth(size),
FPUtilities.optimalMantissaWidth(size),
FPUtilities.optimalBias(FPUtilities.optimalExponentWidth(size))
};
case VALUE: return BigInteger.ZERO;
case KV_SIGNED: return new HashMap<BigInteger,String>();
case KV_UNSIGNED: return new HashMap<BigInteger,String>();
case STRUCT: return new Vector<DataType>();
default: return "";
}
}
}
private static String elaborationToString(ElaborationType elabType, Object elab) {
StringBuffer s = new StringBuffer();
s.append("{");
switch (elabType) {
case TEXT_ENCODING: s.append(elab.toString().toUpperCase()); break;
case DATE_FORMAT: s.append(dateFormatToString((DateFormat)elab)); break;
case COLOR_FORMAT: s.append(((ColorFormat)elab).toString()); break;
case FP_FORMAT: s.append(fpFormatToString((int[])elab)); break;
case VALUE: s.append(elab.toString()); break;
case KV_SIGNED: case KV_UNSIGNED:
Map<?,?> m = (Map<?,?>)elab;
s.append("\n\t");
s.append(mapToString(m).trim().replace("\n", "\n\t"));
s.append("\n");
break;
case STRUCT:
@SuppressWarnings("unchecked")
List<DataField> format = (List<DataField>)elab;
s.append("\n\t");
s.append(toLongString(format).trim().replace("\n", "\n\t"));
s.append("\n");
break;
default: s.append(elab.toString()); break;
}
s.append("}");
return s.toString();
}
private static String elaborationToShortString(ElaborationType elabType, Object elab) {
StringBuffer s = new StringBuffer();
s.append("{");
switch (elabType) {
case TEXT_ENCODING: s.append(elab.toString().toUpperCase()); break;
case DATE_FORMAT: s.append(dateFormatToString((DateFormat)elab)); break;
case COLOR_FORMAT: s.append(((ColorFormat)elab).toString()); break;
case FP_FORMAT: s.append(fpFormatToString((int[])elab)); break;
case VALUE: s.append(elab.toString()); break;
case KV_SIGNED: case KV_UNSIGNED:
Map<?,?> m = (Map<?,?>)elab;
s.append(mapToShortString(m));
break;
case STRUCT:
@SuppressWarnings("unchecked")
List<DataField> format = (List<DataField>)elab;
s.append(toShortString(format));
break;
default: s.append(elab.toString()); break;
}
s.append("}");
return s.toString();
}
private DFExpression parseItemCount() throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
if (reader.read() == '[') {
int level = 0;
StringBuffer c = new StringBuffer();
while (true) {
int ch = reader.read();
if (ch < 0) {
break;
} else if (ch == '[') {
c.append(Character.toChars(ch));
level++;
} else if (ch == ']') {
if (level == 0) break;
c.append(Character.toChars(ch));
level--;
} else {
c.append(Character.toChars(ch));
}
}
String cs = c.toString().trim();
if (cs.length() == 0) return null;
else return new DFExpressionParser(new DFExpressionLexer(new StringReader(cs))).parse();
} else {
reader.reset();
return null;
}
}
private static String itemCountToString(DFExpression expr) {
return "[" + expr.toString() + "]";
}
private String parseFieldName() throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
int firstChar = reader.read();
if (firstChar == '`') {
StringBuffer s = new StringBuffer();
while (true) {
int nextChar = reader.read();
if (nextChar < 0 || nextChar == '`') break;
else s.append(Character.toChars(nextChar));
}
return s.toString();
} else if (firstChar == '_' || Character.isLetter(firstChar)) {
reader.mark(LOOKAHEAD_LIMIT);
int numChars = 0;
while (true) {
int nextChar = reader.read();
if (nextChar < 0 || !(
Character.isDigit(nextChar)
|| Character.isLetter(nextChar)
|| (nextChar == '_')
)) break;
numChars++;
}
reader.reset();
StringBuffer s = new StringBuffer();
s.append(Character.toChars(firstChar));
while (numChars-->0) {
s.append(Character.toChars(reader.read()));
}
return s.toString();
} else {
reader.reset();
return null;
}
}
private String parseShortFieldName() throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
int firstChar = reader.read();
if (firstChar == '`') {
StringBuffer s = new StringBuffer();
while (true) {
int nextChar = reader.read();
if (nextChar < 0 || nextChar == '`') break;
else s.append(Character.toChars(nextChar));
}
return s.toString();
} else {
reader.reset();
return null;
}
}
private static String fieldNameToString(String s) {
if (s.matches("^[A-Za-z_][A-Za-z0-9_]*$")) return s;
else return "`" + s.replace("`", "") + "`";
}
private static String fieldNameToShortString(String s) {
return "`" + s.replace("`", "") + "`";
}
private String parseFieldDescription() throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
int firstChar = reader.read();
if (firstChar == '"' || firstChar == '\'') {
StringBuffer s = new StringBuffer();
while (true) {
int nextChar = reader.read();
if (nextChar < 0 || nextChar == firstChar) break;
else s.append(Character.toChars(nextChar));
}
return s.toString();
} else if (firstChar < 0 || firstChar == ';') {
reader.reset();
return null;
} else {
reader.mark(LOOKAHEAD_LIMIT);
int numChars = 0;
while (true) {
int nextChar = reader.read();
if (nextChar < 0 || nextChar == ';') break;
numChars++;
}
reader.reset();
StringBuffer s = new StringBuffer();
s.append(Character.toChars(firstChar));
while (numChars-->0) {
s.append(Character.toChars(reader.read()));
}
String ds = s.toString().trim();
if (ds.length() == 0) return null;
else return ds;
}
}
private String parseShortFieldDescription() throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
int firstChar = reader.read();
if (firstChar == '"' || firstChar == '\'') {
StringBuffer s = new StringBuffer();
while (true) {
int nextChar = reader.read();
if (nextChar < 0 || nextChar == firstChar) break;
else s.append(Character.toChars(nextChar));
}
return s.toString();
} else {
reader.reset();
return null;
}
}
private static String fieldDescriptionToString(String s) {
if (!s.contains("\"")) {
return "\"" + s + "\"";
} else if (!s.contains("'")) {
return "'" + s + "'";
} else if (!s.contains(";")) {
return s;
} else {
return "\"" + s.replace("\"", "") + "\"";
}
}
private static String fieldDescriptionToShortString(String s) {
if (!s.contains("\"")) {
return "\"" + s + "\"";
} else if (!s.contains("'")) {
return "'" + s + "'";
} else {
return "\"" + s.replace("\"", "") + "\"";
}
}
private void parseTerminator() throws IOException {
reader.mark(LOOKAHEAD_LIMIT);
int ch = reader.read();
if (!(ch < 0 || ch == ';')) {
reader.reset();
throw new RuntimeException("Parse error: expected terminator");
}
}
private DateFormat parseDateFormat(String s) {
String[] ss = s.trim().split("\\s*[.,:;]\\s*");
switch (ss.length) {
case 7:
return new DateFormat(
Integer.parseInt(ss[0]), Calendar.JANUARY+Integer.parseInt(ss[1])-1, Integer.parseInt(ss[2]), // Y M D
Integer.parseInt(ss[3]), Integer.parseInt(ss[4]), Integer.parseInt(ss[5]), // H M S
Integer.parseInt(ss[6]) // scale
);
case 6:
return new DateFormat(
Integer.parseInt(ss[0]), Calendar.JANUARY+Integer.parseInt(ss[1])-1, Integer.parseInt(ss[2]), // Y M D
Integer.parseInt(ss[3]), Integer.parseInt(ss[4]), Integer.parseInt(ss[5]) // H M S
);
case 4:
return new DateFormat(
Integer.parseInt(ss[0]), Calendar.JANUARY+Integer.parseInt(ss[1])-1, Integer.parseInt(ss[2]), // Y M D
Integer.parseInt(ss[3]) // scale
);
case 3:
return new DateFormat(
Integer.parseInt(ss[0]), Calendar.JANUARY+Integer.parseInt(ss[1])-1, Integer.parseInt(ss[2]) // Y M D
);
case 2: return new DateFormat(Integer.parseInt(ss[0]), Integer.parseInt(ss[1])); // Y scale
case 1: return new DateFormat(Integer.parseInt(ss[0])); // Y
default: throw new RuntimeException("Invalid date format");
}
}
private static String dateFormatToString(DateFormat df) {
Calendar epoch = df.getEpoch();
int scale = df.getScale();
StringBuffer s = new StringBuffer();
s.append(epoch.get(Calendar.YEAR));
s.append(",");
s.append(epoch.get(Calendar.MONTH) - Calendar.JANUARY + 1);
s.append(",");
s.append(epoch.get(Calendar.DAY_OF_MONTH));
s.append(",");
s.append(epoch.get(Calendar.HOUR_OF_DAY));
s.append(",");
s.append(epoch.get(Calendar.MINUTE));
s.append(",");
s.append(epoch.get(Calendar.SECOND));
s.append(",");
s.append(scale);
return s.toString();
}
private int[] parseFPFormat(String s) {
String[] ss = s.trim().split("\\s*[.,:;]\\s*");
switch (ss.length) {
case 4:
return new int[] {
Integer.parseInt(ss[0]), Integer.parseInt(ss[1]), Integer.parseInt(ss[2]), // s e m
Integer.parseInt(ss[3]) // bias
};
case 3:
return new int[] {
Integer.parseInt(ss[0]), Integer.parseInt(ss[1]), Integer.parseInt(ss[2]), // s e m
FPUtilities.optimalBias(Integer.parseInt(ss[1])) // bias
};
default:
throw new RuntimeException("Invalid FP format");
}
}
private static String fpFormatToString(int[] i) {
StringBuffer s = new StringBuffer();
s.append(i[0]);
s.append(".");
s.append(i[1]);
s.append(".");
s.append(i[2]);
s.append(".");
s.append(i[3]);
return s.toString();
}
private BigInteger parseInt(String s) {
s = s.trim();
if ((s.startsWith("\"") && s.endsWith("\"")) || (s.startsWith("'") && s.endsWith("'"))) {
BigInteger r = BigInteger.ZERO;
for (char ch : s.substring(1, s.length()-1).toCharArray()) {
r = r.shiftLeft(8);
r = r.or(BigInteger.valueOf((long)ch & 0xFFL));
}
return r;
}
if (s.startsWith("0x") || s.startsWith("0X") || s.startsWith("U+") || s.startsWith("U-") || s.startsWith("u+") || s.startsWith("u-")) {
return new BigInteger(s.substring(2).trim(), 16);
}
if (s.startsWith("0o") || s.startsWith("0O")) {
return new BigInteger(s.substring(2).trim(), 8);
}
if (s.startsWith("0b") || s.startsWith("0B")) {
return new BigInteger(s.substring(2).trim(), 2);
}
if (s.startsWith("$")) {
return new BigInteger(s.substring(1).trim(), 16);
}
if (s.startsWith("0") && s.length() > 1) {
return new BigInteger(s.substring(1).trim(), 8);
}
return new BigInteger(s);
}
private Map<BigInteger, String> parseIntStringMap(int width, String s) {
BigInteger km = (width > 0) ? BigInteger.ONE.shiftLeft(width).subtract(BigInteger.ONE) : null;
Map<BigInteger, String> kvm = new HashMap<BigInteger, String>();
String[] kvs = s.trim().split("\\s*[,;]\\s*");
BigInteger klast = BigInteger.ONE.negate();
for (String kvl : kvs) {
String[] kvf = kvl.split("\\s*[.:=]\\s*", 2);
if (kvf.length > 1) {
klast = parseInt(kvf[0]);
if (km != null) klast = klast.and(km);
kvm.put(klast, kvf[1]);
} else {
klast = klast.add(BigInteger.ONE);
if (km != null) klast = klast.and(km);
kvm.put(klast, kvf[0]);
}
}
return kvm;
}
private static String mapToString(Map<?,?> m) {
TreeMap<Comparable<?>, Object> tm = new TreeMap<Comparable<?>, Object>();
for (Map.Entry<?,?> e : m.entrySet()) {
if (e.getKey() instanceof Comparable) {
tm.put((Comparable<?>)e.getKey(), e.getValue());
} else {
tm = null;
break;
}
}
if (tm != null) m = tm;
StringBuffer s = new StringBuffer();
for (Map.Entry<?,?> e : m.entrySet()) {
s.append(e.getKey().toString());
s.append(" = ");
s.append(e.getValue().toString());
s.append(";\n");
}
if (s.length() > 0 && s.charAt(s.length()-1) == '\n') {
s.deleteCharAt(s.length()-1);
}
return s.toString();
}
private static String mapToShortString(Map<?,?> m) {
TreeMap<Comparable<?>, Object> tm = new TreeMap<Comparable<?>, Object>();
for (Map.Entry<?,?> e : m.entrySet()) {
if (e.getKey() instanceof Comparable) {
tm.put((Comparable<?>)e.getKey(), e.getValue());
} else {
tm = null;
break;
}
}
if (tm != null) m = tm;
StringBuffer s = new StringBuffer();
for (Map.Entry<?,?> e : m.entrySet()) {
s.append(e.getKey().toString());
s.append("=");
s.append(e.getValue().toString());
s.append(";");
}
if (s.length() > 0 && s.charAt(s.length()-1) == ';') {
s.deleteCharAt(s.length()-1);
}
return s.toString();
}
}