package TaiGameCore;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import TaiGameCore.TaiDAWG.WordByRef;
/**
* Game databases are basically maps of data with string keys.
*
* This class was written on Saturday, December 12, 2009 7:36 PM
*
* and it shouldn't be edited.
*/
public abstract class GameDataBase {
public static interface StringBase<Assignment> {
public static interface Validator{
public void validate(String fieldname, String data) throws ValidationException;
}
public static class ValidationException extends Exception{
public ValidationException(String message, String fieldname, Throwable source){
super(message,source);
this.fieldname = fieldname;
}
public ValidationException(String message, String fieldname){
this(message,fieldname,null);
}
public String fieldname;
};
public ArrayList<Exception> parseFromStrings(TaiDAWG<Assignment> data, Validator ... valid) throws FieldRequiredException, ValidationException;
/**
* Describes a field that we want to be script editable
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FromScript {
}
/**
* Causes a template being read to autogenerate the getter methods for its fields.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MakeGetters {
}
/**
* When the autowriter is run on a class with this annotation, the corresponding template classes
* are loaded and invoked in writing the class.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ExtendsData {
public Class[] parents();
}
/**
* Describes a field that must be provided
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CriticalScriptField {
}
/**
* Describes a field that must be validated, by the nth validator
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface HasValidator {
public int num();
}
/**
* Describes fields that are processed before being validated, depending on
* their lexical scope.
*
* The method called to do the processing must reside in the same class as the field declarer.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ScopePreValidate {
public String method();
}
/**
* Describes a field that bypasses the ScopePreValidate step.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface IsUnscoped {
}
public static class FieldRequiredException extends Exception{
public FieldRequiredException(String msg){
super(msg);
}
}
}
/**
* Describes the DefaultValue that accompanies a given field in a database.
* If a given field cannot be given a default value, also add the
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DefaultValue {
String value();
}
public static void hashToClipboard(GameDataBase gdb){
//Put it onto the clipboard.
TaiScriptEditor tse = new TaiScriptEditor();
String got = gdb.hashToString();
if(got==null){
return;
}
tse.insertText(got);
tse.selectLine(0);
tse.copy();
}
public abstract void autoWrittenDeSerializeCode();
public abstract void autoWrittenSerializeCode();
private DataBaseDawg currentData;
/*
* New database: (or a subclass):
GameDataBase gdb = new GameDataBase("");
gdb.writeField("Name", new StringEntry("Garglesmash attaack"));
String val = gdb.hashToString();
Reading a database:
GameDataBase readBack = new GameDataBase(val);
String got = ((StringEntry)readBack.readField("Name", new StringEntry("Unknown Pokemon"))).getString();
System.out.println(got);
*/
public GameDataBase(String hash) {
initFromHash(hash);
}
/**
* Modifiers
*/
public void writeField(String key, DataEntry val) {
currentData.insert(key, val);
}
public DataEntry readField(String name, DataEntry defaultValue) {
DataEntry toRet = defaultValue;
WordByRef<DataEntry> wordByRef = currentData.get(name);
if (wordByRef!=null){
DataEntry got = wordByRef.getContentData();
if (got != null) {
toRet = got;
}
}
return toRet;
}
/**
* Kinds of data entries
*/
public static abstract class DataEntry {
public abstract void readExternal(ObjectInput in) throws IOException;
public abstract void writeExternal(ObjectOutput out) throws IOException;
}
public static class IntEntry extends DataEntry {
public static final int BYTE_TYPE = 0;
public IntEntry() {
// Deserialization.
}
public IntEntry(int value) {
val = value;
}
private int val;
public int getInt() {
return val;
}
public void readExternal(ObjectInput in) throws IOException {
val = in.readInt();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(val);
}
}
public static class StringEntry extends DataEntry {
public static final int BYTE_TYPE = 1;
public StringEntry() {
// Deserialization.
}
public StringEntry(String value) {
val = value;
}
private String val;
public String getString() {
return val;
}
public void readExternal(ObjectInput in) throws IOException {
int length = in.readInt();
char[] len = new char[length];
for(int k = 0; k < length; k++){
len[k] = in.readChar();
}
val = new String(len);
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(val.length());
for(char k : val.toCharArray()){
out.writeChar(k);
}
}
}
public static class IntArrayEntry extends DataEntry {
public static final int BYTE_TYPE = 2;
public IntArrayEntry() {
// Deserialization.
}
public IntArrayEntry(int[] value) {
val = value;
}
private int[] val;
public int[] getIntArray() {
return val;
}
public void readExternal(ObjectInput in) throws IOException {
int length = in.readInt();
int[] len = new int[length];
for(int k = 0; k < length; k++){
len[k] = in.readInt();
}
val = len;
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(val.length);
for(int k : val){
out.writeInt(k);
}
}
}
public static class ByteArrayEntry extends DataEntry {
public static final int BYTE_TYPE = 3;
public ByteArrayEntry() {
// Deserialization.
}
public ByteArrayEntry(byte[] value) {
val = value;
}
private byte[] val;
public byte[] getByteArray() {
return val;
}
public void readExternal(ObjectInput in) throws IOException {
int length = in.readInt();
byte[] len = new byte[length];
for(int k = 0; k < length; k++){
len[k] = in.readByte();
}
val = len;
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(val.length);
for(int k : val){
out.writeByte(k);
}
}
}
public static class DoubleArrayEntry extends DataEntry {
public static final int BYTE_TYPE = 4;
public DoubleArrayEntry() {
// Deserialization.
}
public DoubleArrayEntry(double[] value) {
val = value;
}
private double[] val;
public double[] getDoubleArray() {
return val;
}
public void readExternal(ObjectInput in) throws IOException {
int length = in.readInt();
double[] len = new double[length];
for(int k = 0; k < length; k++){
len[k] = in.readDouble();
}
val = len;
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(val.length);
for(double k : val){
out.writeDouble(k);
}
}
}
public static class DoubleEntry extends DataEntry {
public static final int BYTE_TYPE = 5;
public DoubleEntry() {
// Deserialization.
}
public DoubleEntry(double value) {
val = value;
}
private double val;
public double getDouble() {
return val;
}
public void readExternal(ObjectInput in) throws IOException {
val = in.readDouble();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeDouble(val);
}
}
public static class FloatArrayEntry extends DataEntry {
public static final int BYTE_TYPE = 6;
public FloatArrayEntry() {
// Deserialization.
}
public FloatArrayEntry(float[] value) {
val = value;
}
private float[] val;
public float[] getFloatArray() {
return val;
}
public void readExternal(ObjectInput in) throws IOException {
int length = in.readInt();
float[] len = new float[length];
for(int k = 0; k < length; k++){
len[k] = in.readFloat();
}
val = len;
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(val.length);
for(float k : val){
out.writeFloat(k);
}
}
}
private static class DataBaseDawg extends TaiDAWG<DataEntry> {
public void writeValue(ObjectOutput oos, DataEntry value)
throws IOException {
if (value instanceof IntEntry) {
oos.writeByte(IntEntry.BYTE_TYPE);
} else if (value instanceof StringEntry){
oos.writeByte(StringEntry.BYTE_TYPE);
} else if (value instanceof IntArrayEntry){
oos.writeByte(IntArrayEntry.BYTE_TYPE);
} else if (value instanceof ByteArrayEntry){
oos.writeByte(ByteArrayEntry.BYTE_TYPE);
} else if (value instanceof DoubleArrayEntry){
oos.writeByte(DoubleArrayEntry.BYTE_TYPE);
} else if (value instanceof DoubleEntry){
oos.writeByte(DoubleEntry.BYTE_TYPE);
} else if (value instanceof FloatArrayEntry){
oos.writeByte(FloatArrayEntry.BYTE_TYPE);
} else {
throw new RuntimeException("Unknwon DataEntryType: "
+ value.getClass());
}
value.writeExternal(oos);
}
public DataEntry readValue(ObjectInput ois) throws IOException {
DataEntry toRet = null;
byte type = ois.readByte();
switch (type) {
case IntEntry.BYTE_TYPE:
toRet = new IntEntry();
break;
case StringEntry.BYTE_TYPE:
toRet = new StringEntry();
break;
case IntArrayEntry.BYTE_TYPE:
toRet = new IntArrayEntry();
break;
case ByteArrayEntry.BYTE_TYPE:
toRet = new ByteArrayEntry();
break;
case DoubleArrayEntry.BYTE_TYPE:
toRet = new DoubleArrayEntry();
break;
case DoubleEntry.BYTE_TYPE:
toRet = new DoubleEntry();
break;
case FloatArrayEntry.BYTE_TYPE:
toRet = new FloatArrayEntry();
break;
}
if (toRet==null){
throw new RuntimeException("Unknown DatEntryType: " + type);
}
toRet.readExternal(ois);
return toRet;
}
}
private final void initFromHash(String hash) {
//New tree!
currentData = new DataBaseDawg();
//Read the tree
if (hash.length() > 0) {
try {
byte[] input = decodeString(hash);
ByteArrayInputStream bais = new ByteArrayInputStream(input);
ZipInputStream zis = new ZipInputStream(bais);
zis.getNextEntry();
ObjectInputStream ois = new ObjectInputStream(zis);
currentData.readInTree(ois,null);
} catch (Throwable e){
e.printStackTrace();
throw new RuntimeException("Invalid hash "+hash);
}
} else {
// Base case: This case is used only by the utility that creates new
// databases.
//
// Ok.
}
//Read it in!
autoWrittenDeSerializeCode();
//Clear it up!
currentData = null;
}
public final String hashAllToString(GameDataBase[] stuffs){
StringBuffer sb = new StringBuffer();
for(int k = 0; k < stuffs.length; k++){
if (stuffs[k]!=null){
sb.append(stuffs[k].hashToString());
}
if (k+1<stuffs.length){
sb.append(",");
}
}
return sb.toString();
}
public final String hashToString() {
//New tree!
currentData = new DataBaseDawg();
//Write to the tree..
autoWrittenSerializeCode();
//Encode the tree...
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
zos.putNextEntry(new ZipEntry("A"));
ObjectOutputStream oos = new ObjectOutputStream(zos);
currentData.writeOutTree(oos);
oos.flush();
zos.closeEntry();
zos.finish();
zos.flush();
baos.flush();
byte[] array = baos.toByteArray();
return encodeString(array);
} catch (Throwable e) {
System.err.println("This code should always be safe!!!!!");
e.printStackTrace();
throw new RuntimeException(e);
} finally {
//Clear the tree.
currentData = null;
}
}
/**
* HASHING TRANSFORMATION:
*/
private static final char[] charMap = ("ABCDEFGH" + "qrstuvwx" + "IJKLMNOP"
+ "01234567").toCharArray();
private static final int[] invCharMap = new int[255];
static {
for (int k = 0; k < charMap.length; k++) {
invCharMap[(int) charMap[k]] = k;
}
}
public static byte[] decodeString(String hash) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(
hash.length() * 2);
for (int l = 0; l < hash.length(); l += 2) {
int lowByte = invCharMap[hash.charAt(l)];
lowByte = (lowByte / 2);
int hiByte = invCharMap[hash.charAt(l + 1)];
hiByte = (hiByte - 1) / 2;
int together = hiByte * 16 + lowByte;
baos.write((byte) (together - 128));
}
return baos.toByteArray();
}
public static String encodeString(byte[] toEncode) {
StringBuffer sb = new StringBuffer();
for (byte k : toEncode) {
int val = k + 128;
sb.append(charMap[(val % 16) * 2]);
sb.append(charMap[(val / 16) * 2 + 1]);
}
return sb.toString();
}
}