mirror of
https://github.com/collinsmith/riiablo.git
synced 2025-07-04 15:27:30 +07:00
Moved com.riiablo.excel2 to com.riiablo.excel
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@ -12,33 +12,39 @@ import com.riiablo.logger.Logger;
|
||||
public class BinGenerator {
|
||||
private static final Logger log = LogManager.getLogger(BinGenerator.class);
|
||||
|
||||
String sourcePackage = "com.riiablo.excel2.txt";
|
||||
String sourcePackage = "com.riiablo.excel.txt";
|
||||
String excelPath = "DATA\\GLOBAL\\EXCEL2".toLowerCase(); // string will be defined elsewhere in caps
|
||||
FileHandle binDir;
|
||||
|
||||
public void generateBins() {
|
||||
log.info("Generating bins for {}...", sourcePackage);
|
||||
|
||||
BinGenerator configure(FileHandle binDir) {
|
||||
this.binDir = binDir;
|
||||
return this;
|
||||
}
|
||||
|
||||
public <E extends Excel.Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
void generateBin(T excel) {
|
||||
final Class<? extends Excel> excelClass = excel.excelClass();
|
||||
log.trace("excel: {}", excelClass.getCanonicalName());
|
||||
public void generateBins() {
|
||||
log.info("Generating bins for {}...", sourcePackage);
|
||||
|
||||
FileHandle excelDir = binDir.child(excelPath);
|
||||
log.trace("excelDir: {}", excelDir);
|
||||
excelDir.mkdirs();
|
||||
|
||||
generateBin(excelDir, null);
|
||||
}
|
||||
|
||||
public <E extends Excel.Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
void generateBin(FileHandle excelDir, T excel) {
|
||||
final Class<? extends Excel> excelClass = excel.excelClass();
|
||||
log.trace("excel: {}", excelClass.getCanonicalName());
|
||||
|
||||
FileHandle binFile = excelDir.child(excelClass.getSimpleName() + "." + "bin");
|
||||
log.trace("binFile: {}", binFile);
|
||||
|
||||
ByteOutput out = ByteOutput.wrap(Unpooled.buffer());
|
||||
S serializer = excel.newSerializer();
|
||||
for (E entry : excel) {
|
||||
serializer.writeBin(entry, out);
|
||||
}
|
||||
// S serializer = excel.newSerializer();
|
||||
// for (E entry : excel) {
|
||||
// serializer.writeBin(entry, out);
|
||||
// }
|
||||
|
||||
log.trace("\n{}", ByteBufUtil.prettyHexDump(out.buffer()));
|
||||
log.trace("dump of {}:\n{}", binFile, ByteBufUtil.prettyHexDump(out.buffer()));
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
public class ColumnFormat extends RuntimeException {
|
||||
final String columnText;
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
@ -1,80 +1,100 @@
|
||||
package com.riiablo.excel;
|
||||
|
||||
import android.support.annotation.CallSuper;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.IntMap;
|
||||
import com.badlogic.gdx.utils.ObjectIntMap;
|
||||
import com.badlogic.gdx.utils.ObjectMap;
|
||||
|
||||
import com.riiablo.io.ByteInput;
|
||||
import com.riiablo.logger.LogManager;
|
||||
import com.riiablo.logger.Logger;
|
||||
import com.riiablo.logger.MDC;
|
||||
import com.riiablo.util.ClassUtils;
|
||||
|
||||
public abstract class Excel<T extends Excel.Entry, U extends Serializer<T>> implements Iterable<T> {
|
||||
/**
|
||||
* Root class of an excel table.
|
||||
*/
|
||||
public abstract class Excel<
|
||||
E extends Excel.Entry,
|
||||
S extends Serializer<E>
|
||||
>
|
||||
implements Iterable<E>
|
||||
{
|
||||
private static final Logger log = LogManager.getLogger(Excel.class);
|
||||
|
||||
private static final boolean DEBUG = true;
|
||||
private static final boolean DEBUG_COLS = DEBUG && !true;
|
||||
private static final boolean DEBUG_COL_IDS = DEBUG && !true;
|
||||
private static final boolean DEBUG_IGNORED = DEBUG && true;
|
||||
private static final boolean DEBUG_ENTRIES = DEBUG && true;
|
||||
private static final boolean DEBUG_INDEXES = DEBUG && true;
|
||||
private static final boolean DEBUG_KEY = DEBUG && true;
|
||||
private static final boolean DEBUG_BIN = DEBUG && true;
|
||||
private static final boolean DEBUG_TIME = DEBUG && true;
|
||||
|
||||
/** Forces excels to either have a {@link PrimaryKey} set or be {@link Indexed} */
|
||||
private static final boolean FORCE_PRIMARY_KEY = !true;
|
||||
private static final boolean FORCE_TXT = !true;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Index {}
|
||||
private static final ObjectIntMap EMPTY_OBJECT_INT_MAP = new ObjectIntMap();
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Bin {}
|
||||
@SuppressWarnings("unchecked") // doesn't store anything
|
||||
static <T> ObjectIntMap<T> emptyMap() {
|
||||
return (ObjectIntMap<T>) EMPTY_OBJECT_INT_MAP;
|
||||
}
|
||||
|
||||
public static class Entry {
|
||||
/**
|
||||
* Root class of an excel entry.
|
||||
*/
|
||||
public static abstract class Entry {
|
||||
/**
|
||||
* Tags a specified field as a column within the excel table.
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Column {
|
||||
/** Used to index format */
|
||||
int startIndex() default 0;
|
||||
/** Used to index format */
|
||||
int endIndex() default 0;
|
||||
/** Format of column name, if not set, then field name is used */
|
||||
String format() default "";
|
||||
/** Used to index format not non-numerical indexes */
|
||||
String values()[] default {};
|
||||
/** Sets index of column of value (used for cases like weapons.txt where one column has no name) */
|
||||
int columnIndex() default -1;
|
||||
/** Whether or not to read/write value in bin codec */
|
||||
boolean bin() default true;
|
||||
/**
|
||||
* Start index of {@link #format()} (inclusive)
|
||||
*/
|
||||
int startIndex() default 0;
|
||||
|
||||
/**
|
||||
* End index of {@link #format()} (exclusive)
|
||||
*/
|
||||
int endIndex() default 0;
|
||||
|
||||
/**
|
||||
* String format of column name, {@code ""} to use field name
|
||||
* <p>
|
||||
* <p>Examples:
|
||||
* <ul>
|
||||
* <li>{@code "class"}
|
||||
* <li>{@code "Transform Color"}
|
||||
* <li>{@code "Level%s"}
|
||||
* <li>{@code "Skill %d"}
|
||||
*/
|
||||
String format() default "";
|
||||
|
||||
/**
|
||||
* Index values of format in the case of non-numerical indexes.
|
||||
* <p>
|
||||
* <p>Examples:
|
||||
* <ul>
|
||||
* <li>{@code {"", "(N)", "(H)"}}
|
||||
* <li>{@code {"r", "g", "b"}}
|
||||
* <li>{@code {"Min", "Max", "MagicMin", "MagicMax", "MagicLvl"}}
|
||||
*/
|
||||
String[] values() default {};
|
||||
|
||||
/**
|
||||
* Manually sets the column index. This property overrides all other
|
||||
* properties.
|
||||
*/
|
||||
int columnIndex() default -1;
|
||||
}
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Key {}
|
||||
}
|
||||
|
||||
private static final ObjectIntMap EMPTY_MAP = new ObjectIntMap();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> ObjectIntMap<T> emptyMap() {
|
||||
return (ObjectIntMap<T>) EMPTY_MAP;
|
||||
}
|
||||
|
||||
public static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
@ -84,83 +104,69 @@ public abstract class Excel<T extends Excel.Entry, U extends Serializer<T>> impl
|
||||
|
||||
public static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
T load(T excel, FileHandle txt, FileHandle bin) throws IOException {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
final FileHandle handle;
|
||||
final boolean loadBin;
|
||||
if (!FORCE_TXT && bin != null && bin.exists()) {
|
||||
handle = bin;
|
||||
loadBin = true;
|
||||
} else {
|
||||
handle = txt;
|
||||
loadBin = false;
|
||||
}
|
||||
|
||||
log.debug("Parsing {}", handle);
|
||||
try {
|
||||
MDC.put("excelClass", excel.getClass().getCanonicalName());
|
||||
try {
|
||||
MDC.put("entryClass", excel.getEntryClass().getCanonicalName());
|
||||
if (loadBin) {
|
||||
loadBin(excel, bin);
|
||||
} else {
|
||||
loadTxt(excel, txt);
|
||||
}
|
||||
} finally {
|
||||
MDC.remove("entryClass");
|
||||
}
|
||||
} finally {
|
||||
MDC.remove("excelClass");
|
||||
}
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
if (DEBUG_TIME) log.debug("{} parsed in {} ms", handle, end - start);
|
||||
return excel;
|
||||
throw null;
|
||||
}
|
||||
|
||||
private static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
T loadTxt(T excel, FileHandle handle) throws IOException {
|
||||
TxtParser parser = null;
|
||||
InputStream in = handle.read();
|
||||
try {
|
||||
parser = TxtParser.parse(handle);
|
||||
MDC.put("excel", handle.path());
|
||||
TxtParser parser = TxtParser.parse(in);
|
||||
return loadTxt(excel, parser);
|
||||
} catch (IllegalAccessException t) {
|
||||
log.error("Unable to load txt {}: {}",
|
||||
} catch (Throwable t) {
|
||||
log.fatal("Unable to load {} as {}: {}",
|
||||
handle,
|
||||
excel.getClass().getCanonicalName(),
|
||||
ExceptionUtils.getRootCauseMessage(t),
|
||||
t);
|
||||
return ExceptionUtils.wrapAndThrow(t);
|
||||
return ExceptionUtils.rethrow(t);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(parser);
|
||||
MDC.remove("excel");
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
private static <E extends Entry, T extends Excel<E, ?>>
|
||||
T loadTxt(T excel, TxtParser parser) throws IOException, IllegalAccessException {
|
||||
final Class<E> entryClass = excel.getEntryClass();
|
||||
final boolean index = ClassUtils.hasAnnotation(entryClass, Index.class);
|
||||
static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
T loadTxt(T excel, TxtParser parser)
|
||||
throws IOException, ParseException, IllegalAccessException
|
||||
{
|
||||
final Class<E> entryClass = excel.entryClass();
|
||||
final boolean indexed = ClassUtils.hasAnnotation(entryClass, Indexed.class);
|
||||
|
||||
final String[] TMP = new String[1];
|
||||
Field primaryKey = null, firstKey = null;
|
||||
ObjectMap<Field, int[]> columns = new ObjectMap<>();
|
||||
String[] TMP = new String[1];
|
||||
Array<Triple<Field, int[], String[]>> columns = new Array<>(true, parser.numColumns(), Triple.class);
|
||||
for (Field field : entryClass.getFields()) {
|
||||
Entry.Column column = field.getAnnotation(Entry.Column.class);
|
||||
if (column == null) continue; // TODO: DEBUG MESSAGE WARNING
|
||||
if (column == null) {
|
||||
log.warn("{} is not tagged with {}", field, Entry.Column.class.getCanonicalName());
|
||||
continue;
|
||||
}
|
||||
|
||||
Entry.Key key = field.getAnnotation(Entry.Key.class);
|
||||
PrimaryKey key = field.getAnnotation(PrimaryKey.class);
|
||||
if (key != null) {
|
||||
if (index) {
|
||||
log.error("primary key set in class annotated with {}", Excel.Index.class);
|
||||
if (!ArrayUtils.contains(PrimaryKey.SUPPORTED_TYPES, field.getType())) {
|
||||
throw new ParseException(field, "%s must be one of %s",
|
||||
field, Arrays.toString(PrimaryKey.SUPPORTED_TYPES));
|
||||
}
|
||||
|
||||
if (indexed) {
|
||||
// Indexed excels have their primary key assigned automatically based on row index
|
||||
log.warn("{} has {} set to the primary key, but class is tagged with {}",
|
||||
entryClass, field, Indexed.class.getCanonicalName());
|
||||
} else if (primaryKey != null) {
|
||||
// Allow declared field tagged as a primary key to override inherited ones
|
||||
boolean primaryDeclared = ClassUtils.isDeclaredField(entryClass, primaryKey);
|
||||
boolean fieldDeclared = ClassUtils.isDeclaredField(entryClass, field);
|
||||
if (primaryDeclared != fieldDeclared) {
|
||||
if (fieldDeclared) {
|
||||
if (DEBUG_KEY) log.debug("primary key {} -> {}", primaryKey.getName(), field.getName());
|
||||
log.debug("primary key {} changed to {}", primaryKey, field);
|
||||
primaryKey = field;
|
||||
}
|
||||
} else {
|
||||
log.error("more than one primary key for {}: {} and {}", entryClass, primaryKey.getName(), field.getName());
|
||||
log.warn("multiple primary keys set within {}: {} and {}",
|
||||
entryClass, primaryKey.getName(), field.getName());
|
||||
}
|
||||
} else {
|
||||
primaryKey = field;
|
||||
@ -168,255 +174,466 @@ public abstract class Excel<T extends Excel.Entry, U extends Serializer<T>> impl
|
||||
}
|
||||
|
||||
if (firstKey == null) firstKey = field;
|
||||
|
||||
final String format = column.format();
|
||||
final String values[] = column.values();
|
||||
final int startIndex = column.startIndex();
|
||||
final int endIndex = column.endIndex();
|
||||
final int columnIndex = column.columnIndex();
|
||||
if (columnIndex >= 0) {
|
||||
columns.put(field, new int[] { columnIndex });
|
||||
} else if (format.isEmpty()) {
|
||||
final String fieldName = field.getName();
|
||||
if (values.length > 0) {
|
||||
String[] columnNames = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
String name = values[i];
|
||||
if (DEBUG_COLS) log.trace(name);
|
||||
columnNames[i] = name;
|
||||
}
|
||||
|
||||
columns.put(field, parser.getColumnId(columnNames));
|
||||
} else if (startIndex == 0 && endIndex == 0) {
|
||||
if (DEBUG_COLS) log.trace(fieldName);
|
||||
TMP[0] = fieldName;
|
||||
columns.put(field, parser.getColumnId(TMP));
|
||||
} else {
|
||||
String[] columnNames = new String[endIndex - startIndex];
|
||||
for (int i = startIndex, j = 0; i < endIndex; i++, j++) {
|
||||
String name = fieldName + i;
|
||||
if (DEBUG_COLS) log.trace(name);
|
||||
columnNames[j] = name;
|
||||
}
|
||||
|
||||
columns.put(field, parser.getColumnId(columnNames));
|
||||
}
|
||||
} else {
|
||||
if (startIndex == 0 && endIndex == 0) {
|
||||
TMP[0] = format;
|
||||
columns.put(field, parser.getColumnId(TMP));
|
||||
} else {
|
||||
String[] columnNames = new String[endIndex - startIndex];
|
||||
if (values.length == 0) {
|
||||
for (int i = startIndex, j = 0; i < endIndex; i++, j++) {
|
||||
String name = String.format(format, i);
|
||||
if (DEBUG_COLS) log.trace(name);
|
||||
columnNames[j] = name;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
String name = String.format(format, values[i]);
|
||||
if (DEBUG_COLS) log.trace(name);
|
||||
columnNames[i] = name;
|
||||
}
|
||||
}
|
||||
|
||||
columns.put(field, parser.getColumnId(columnNames));
|
||||
}
|
||||
}
|
||||
populateColumnIndexes(columns, parser, column, field, TMP);
|
||||
}
|
||||
|
||||
if (primaryKey == null && !index) {
|
||||
if (primaryKey == null && !indexed) {
|
||||
if (FORCE_PRIMARY_KEY) {
|
||||
throw new IllegalStateException(entryClass + " does not have a " + Entry.Key.class + " set!");
|
||||
throw new ParseException(entryClass, "%s does not have a %s set!",
|
||||
entryClass, PrimaryKey.class.getCanonicalName());
|
||||
} else {
|
||||
log.error("{} does not have a {} set! Using {}", entryClass, Entry.Key.class, firstKey.getName());
|
||||
log.warn("{} does not have a {} set! Defaulting to first key: {}",
|
||||
entryClass, PrimaryKey.class.getCanonicalName(), firstKey);
|
||||
primaryKey = firstKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG_COL_IDS) {
|
||||
for (ObjectMap.Entry<Field, int[]> entry : columns.entries()) {
|
||||
log.debug("{}: {}", entry.key.getName(), entry.value);
|
||||
// Locate the column index of the primary key
|
||||
// TODO: this operation can be cleaned up, but this is only an identity test
|
||||
int[] primaryKeyColumnIds = null;
|
||||
final Triple<Field, int[], String[]>[] columnTriples = columns.items;
|
||||
for (int i = 0, s = columnTriples.length; i < s; i++) {
|
||||
if (columnTriples[i].getLeft() == primaryKey) {
|
||||
primaryKeyColumnIds = columnTriples[i].getMiddle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final int primaryKeyCol = index ? -1 : columns.get(primaryKey)[0];
|
||||
final Class primaryKeyType = index ? null : primaryKey.getType();
|
||||
for (int i = excel.offset(); parser.nextLine() != null; i++) {
|
||||
E entry = excel.newEntry();
|
||||
String name = index ? null : parser.getString(primaryKeyCol);
|
||||
try {
|
||||
if (DEBUG_ENTRIES) MDC.put("index", i);
|
||||
try {
|
||||
if (DEBUG_ENTRIES) MDC.put("primaryKey", name);
|
||||
for (ObjectMap.Entry<Field, int[]> row : columns.entries()) {
|
||||
Field field = row.key;
|
||||
int[] columnIds = row.value;
|
||||
Class type = field.getType();
|
||||
assert type.isArray() || columnIds.length == 1 : "field should only correspond to 1 column: " + field.getName() + ", " + columnIds.length + " columns (is it supposed to be an array?)";
|
||||
if (type == String.class) {
|
||||
String value = parser.getString(columnIds[0]);
|
||||
field.set(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else if (type == String[].class) {
|
||||
String[] value = parser.getString(columnIds);
|
||||
field.set(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else if (type == byte.class) {
|
||||
byte value = parser.getByte(columnIds[0]);
|
||||
field.setByte(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else if (type == byte[].class) {
|
||||
byte[] value = parser.getByte(columnIds);
|
||||
field.set(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else if (type == short.class) {
|
||||
short value = parser.getShort(columnIds[0]);
|
||||
field.setShort(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else if (type == short[].class) {
|
||||
short[] value = parser.getShort(columnIds);
|
||||
field.set(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else if (type == int.class) {
|
||||
int value = parser.getInt(columnIds[0]);
|
||||
field.setInt(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else if (type == int[].class) {
|
||||
int[] value = parser.getInt(columnIds);
|
||||
field.set(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else if (type == long.class) {
|
||||
long value = parser.getLong(columnIds[0]);
|
||||
field.setLong(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else if (type == long[].class) {
|
||||
long[] value = parser.getLong(columnIds);
|
||||
field.set(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else if (type == boolean.class) {
|
||||
boolean value = parser.getBoolean(columnIds[0]);
|
||||
field.setBoolean(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else if (type == boolean[].class) {
|
||||
boolean[] value = parser.getBoolean(columnIds);
|
||||
field.set(entry, value);
|
||||
if (DEBUG_ENTRIES) log.trace("{}={}", field.getName(), value);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("No support for " + type + " fields");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (DEBUG_ENTRIES) MDC.remove("primaryKey");
|
||||
int nonzeroIndex = -1;
|
||||
if (!indexed) {
|
||||
for (int i = 0, s = primaryKeyColumnIds.length; i < s; i++) {
|
||||
if (primaryKeyColumnIds[i] >= 0) {
|
||||
nonzeroIndex = i;
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
if (DEBUG_ENTRIES) MDC.remove("index");
|
||||
}
|
||||
|
||||
putIndex(primaryKey, primaryKeyType, i++, index, excel, entry);
|
||||
if (nonzeroIndex == -1) {
|
||||
throw new ParseException(primaryKey,
|
||||
"primary key %s does not have any columns associated with it",
|
||||
primaryKey);
|
||||
}
|
||||
}
|
||||
|
||||
final int primaryKeyColumnId = indexed ? -1 : primaryKeyColumnIds[nonzeroIndex];
|
||||
final Class primaryKeyType = indexed ? null : primaryKey.getType();
|
||||
for (int i = excel.offset(); parser.cacheLine() != -1; i++) {
|
||||
E entry = excel.newEntry();
|
||||
String name = indexed ? null : parser.parseString(primaryKeyColumnId, "");
|
||||
try {
|
||||
MDC.put("entry", indexed || StringUtils.isBlank(name) ? "" + i : name);
|
||||
parseColumns(excel, entry, name, columns, parser);
|
||||
} finally {
|
||||
MDC.remove("entry");
|
||||
}
|
||||
putIndex(primaryKey, primaryKeyType, i++, indexed, excel, entry);
|
||||
}
|
||||
|
||||
return excel;
|
||||
}
|
||||
|
||||
private static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
T loadBin(T excel, FileHandle handle) {
|
||||
final Class<E> entryClass = excel.getEntryClass();
|
||||
final boolean index = ClassUtils.hasAnnotation(entryClass, Index.class);
|
||||
final S serializer = excel.newSerializer();
|
||||
static void
|
||||
catchParseException(
|
||||
Throwable t,
|
||||
Field field,
|
||||
Class type,
|
||||
String key,
|
||||
String columnName,
|
||||
CharSequence token
|
||||
) {
|
||||
ParseException parseException = new ParseException(t, field,
|
||||
"error parsing field %s row: '%s' column: '%s': '%s' as %s",
|
||||
field, key, columnName, token.toString(),
|
||||
type.isArray() ? type.getComponentType().getCanonicalName() : type.getCanonicalName());
|
||||
log.warn(parseException.getMessage(), parseException);
|
||||
}
|
||||
|
||||
try {
|
||||
Field primaryKey = ClassUtils.findField(entryClass, Entry.Key.class);
|
||||
if (primaryKey == null && !index) {
|
||||
if (FORCE_PRIMARY_KEY) {
|
||||
throw new IllegalStateException(entryClass + " does not have a " + Entry.Key.class + " set!");
|
||||
static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
void parseColumns(
|
||||
T excel,
|
||||
E entry,
|
||||
String key,
|
||||
Array<Triple<Field, int[], String[]>> columns,
|
||||
TxtParser parser
|
||||
)
|
||||
throws IllegalAccessException, ParseException
|
||||
{
|
||||
for (Triple<Field, int[], String[]> column : columns) {
|
||||
final Field field = column.getLeft();
|
||||
final int[] columnIds = column.getMiddle();
|
||||
final int numColumns = columnIds.length;
|
||||
final String[] columnNames = column.getRight();
|
||||
final Class type = field.getType();
|
||||
try {
|
||||
if (type == String.class) {
|
||||
try {
|
||||
field.set(entry, parser.parseString(columnIds[0], ""));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == String[].class) {
|
||||
final String[] value = new String[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseString(columnIds[i], "");
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == byte.class) {
|
||||
try {
|
||||
field.setByte(entry, parser.parseByte(columnIds[0], (byte) 0));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == byte[].class) {
|
||||
final byte[] value = new byte[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseByte(columnIds[i], (byte) 0);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == short.class) {
|
||||
try {
|
||||
field.setShort(entry, parser.parseShort(columnIds[0], (short) 0));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == short[].class) {
|
||||
final short[] value = new short[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseShort(columnIds[i], (short) 0);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == int.class) {
|
||||
try {
|
||||
field.setInt(entry, parser.parseInt(columnIds[0], 0));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == int[].class) {
|
||||
final int[] value = new int[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseInt(columnIds[i], 0);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == long.class) {
|
||||
try {
|
||||
field.setLong(entry, parser.parseLong(columnIds[0], 0L));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == long[].class) {
|
||||
final long[] value = new long[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseLong(columnIds[i], 0L);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == boolean.class) {
|
||||
try {
|
||||
field.setBoolean(entry, parser.parseBoolean(columnIds[0], false));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == boolean[].class) {
|
||||
final boolean[] value = new boolean[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseBoolean(columnIds[i], false);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == float.class) {
|
||||
try {
|
||||
field.setFloat(entry, parser.parseFloat(columnIds[0], 0f));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == float[].class) {
|
||||
final float[] value = new float[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseFloat(columnIds[i], 0f);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == double.class) {
|
||||
try {
|
||||
field.setDouble(entry, parser.parseDouble(columnIds[0], 0d));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == double[].class) {
|
||||
final double[] value = new double[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseDouble(columnIds[i], 0d);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else {
|
||||
throw new ParseException(field, "Cannot parse fields of type %s",
|
||||
org.apache.commons.lang3.ClassUtils.getCanonicalName(type));
|
||||
}
|
||||
} catch (ColumnFormat t) {
|
||||
ParseException parseException = new ParseException(field,
|
||||
"error parsing field %s row: '%s' column: '%s': '%s' as %s",
|
||||
field, key, columnNames[t.columnIndex()], t.columnText(),
|
||||
type.isArray() ? type.getComponentType().getCanonicalName() : type.getCanonicalName());
|
||||
parseException.initCause(t);
|
||||
throw parseException;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified field using it's column definition annotation to
|
||||
* generate a list of column names and indexes associated with them. These
|
||||
* indexes are then stored as a mapping from field to associated column
|
||||
* indexes which can be used to retrieve data from the backing excel.
|
||||
*/
|
||||
static void populateColumnIndexes(
|
||||
final Array<Triple<Field, int[], String[]>> columns,
|
||||
final TxtParser parser,
|
||||
final Entry.Column column,
|
||||
final Field field,
|
||||
final String[] TMP
|
||||
) throws ParseException {
|
||||
final String format = column.format();
|
||||
final String[] values = column.values();
|
||||
final int startIndex = column.startIndex();
|
||||
final int endIndex = column.endIndex();
|
||||
final int columnIndex = column.columnIndex();
|
||||
if (columnIndex >= 0) {
|
||||
final int[] columnIndexes = new int[] { columnIndex };
|
||||
final String[] columnNames = new String[] { null };
|
||||
columns.add(Triple.of(field, columnIndexes, columnNames));
|
||||
log.trace("pushing column <{}>->{}", field, columnIndexes);
|
||||
} else if (format.isEmpty()) {
|
||||
final String fieldName = field.getName();
|
||||
if (values.length > 0) {
|
||||
// values[] used as literal column names
|
||||
checkArrayColumns(field, values.length);
|
||||
String[] columnNames = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
columnNames[i] = values[i];
|
||||
}
|
||||
|
||||
putColumns(columns, parser, field, columnNames);
|
||||
} else if (startIndex == 0 && endIndex == 0) {
|
||||
// field name used as literal column name
|
||||
TMP[0] = fieldName;
|
||||
putColumns(columns, parser, field, TMP);
|
||||
} else {
|
||||
// field name + indexes used as column names
|
||||
checkArrayColumns(field, endIndex - startIndex);
|
||||
String[] columnNames = new String[endIndex - startIndex];
|
||||
for (int i = startIndex, j = 0; i < endIndex; i++, j++) {
|
||||
columnNames[j] = fieldName + i;
|
||||
}
|
||||
|
||||
putColumns(columns, parser, field, columnNames);
|
||||
}
|
||||
} else {
|
||||
if (startIndex == 0 && endIndex == 0) {
|
||||
// format used as literal column name
|
||||
TMP[0] = format;
|
||||
putColumns(columns, parser, field, TMP);
|
||||
} else {
|
||||
checkArrayColumns(field, endIndex - startIndex);
|
||||
String[] columnNames = new String[endIndex - startIndex];
|
||||
if (values.length == 0) {
|
||||
// format used in conjunction with indexes as column names
|
||||
// format must contain %d within it, replaced with indexes
|
||||
for (int i = startIndex, j = 0; i < endIndex; i++, j++) {
|
||||
columnNames[j] = String.format(format, i);
|
||||
}
|
||||
} else {
|
||||
Field firstKey = entryClass.getFields()[0];
|
||||
log.error("{} does not have a {} set! Using {}", entryClass, Entry.Key.class, firstKey.getName());
|
||||
primaryKey = firstKey;
|
||||
// format used in conjunction with values as column names
|
||||
// format must contain as many values as indexes
|
||||
for (int i = 0, s = values.length; i < s; i++) {
|
||||
columnNames[i] = String.format(format, values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
putColumns(columns, parser, field, columnNames);
|
||||
}
|
||||
}
|
||||
|
||||
if (log.debugEnabled()) {
|
||||
StringBuilder builder = new StringBuilder(256);
|
||||
builder.append('{');
|
||||
for (Triple<Field, int[], String[]> pair : columns) {
|
||||
builder
|
||||
.append(pair.getLeft().getName())
|
||||
.append('=')
|
||||
.append(Arrays.toString(pair.getMiddle()))
|
||||
.append(", ");
|
||||
}
|
||||
if (columns.size > 0) builder.setLength(builder.length() - 2);
|
||||
builder.append('}');
|
||||
log.debug("columns: {}", builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
static void checkArrayColumns(Field field, int length) throws ParseException {
|
||||
if (!field.getType().isArray() && length > 1) {
|
||||
throw new ParseException(field, ""
|
||||
+ "field %s corresponds to multiple columns. "
|
||||
+ "is it supposed to be an array type?", field);
|
||||
}
|
||||
}
|
||||
|
||||
static int putColumns(
|
||||
Array<Triple<Field, int[], String[]>> columns,
|
||||
TxtParser parser,
|
||||
Field field,
|
||||
String[] columnNames
|
||||
) {
|
||||
final int index = columns.size;
|
||||
final int[] columnIndexes = parser.columnId(columnNames);
|
||||
columns.add(Triple.of(field, columnIndexes, columnNames));
|
||||
log.trace("pushing columns {}->{}", columnNames, columnIndexes);
|
||||
if (log.warnEnabled()) {
|
||||
for (int i = 0, s = columnIndexes.length; i < s; i++) {
|
||||
if (columnIndexes[i] == -1) {
|
||||
log.warn("Unable to parse column named '{}'", columnNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Class primaryKeyType = index ? null : primaryKey.getType();
|
||||
|
||||
ByteInput in = ByteInput.wrap(handle.readBytes());
|
||||
int size = in.readSafe32u();
|
||||
log.debug("reading {} entries...", size);
|
||||
for (int i = 0, j = excel.offset(); i < size; i++, j++) {
|
||||
E entry = excel.newEntry();
|
||||
serializer.readBin(entry, in);
|
||||
putIndex(primaryKey, primaryKeyType, j, index, excel, entry);
|
||||
}
|
||||
|
||||
return excel;
|
||||
} catch (Throwable t) {
|
||||
throw new GdxRuntimeException("Couldn't load excel: " + handle, t);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private static <E extends Entry, T extends Excel<E, ?>>
|
||||
void putIndex(Field primaryKey, Class primaryKeyType, int i, boolean indexed, T excel, E entry) throws IllegalAccessException {
|
||||
static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
T loadBin(T excel, FileHandle handle) {
|
||||
throw null;
|
||||
}
|
||||
|
||||
static <E extends Entry, T extends Excel<E, ?>>
|
||||
void putIndex(
|
||||
Field primaryKey,
|
||||
Class primaryKeyType,
|
||||
int i,
|
||||
boolean indexed,
|
||||
T excel,
|
||||
E entry
|
||||
) throws IllegalAccessException {
|
||||
if (indexed) {
|
||||
excel.put(i, entry);
|
||||
excel._put(i, entry);
|
||||
} else if (primaryKeyType == int.class) {
|
||||
int id = primaryKey.getInt(entry);
|
||||
excel.put(id, entry);
|
||||
excel._put(id, entry);
|
||||
} else if (primaryKeyType == String.class) {
|
||||
String id = (String) primaryKey.get(entry);
|
||||
excel.put(i, entry);
|
||||
excel._put(i, entry);
|
||||
|
||||
if (excel.STRING_TO_ID == EMPTY_MAP) excel.STRING_TO_ID = new ObjectIntMap<>();
|
||||
if (!excel.STRING_TO_ID.containsKey(id)) excel.STRING_TO_ID.put(id, i);
|
||||
if (excel.stringToIndex == EMPTY_OBJECT_INT_MAP) excel.stringToIndex = new ObjectIntMap<>();
|
||||
if (!excel.stringToIndex.containsKey(id)) excel.stringToIndex.put(id, i);
|
||||
}
|
||||
}
|
||||
|
||||
protected ObjectIntMap<String> STRING_TO_ID = emptyMap();
|
||||
protected IntMap<T> entries = new IntMap<>();
|
||||
protected Class<T> entryClass;
|
||||
protected final Class<E> entryClass;
|
||||
protected ObjectIntMap<String> stringToIndex;
|
||||
protected IntMap<E> entries;
|
||||
protected Array<Entry> ordered;
|
||||
|
||||
protected Excel(Class<T> entryClass) {
|
||||
protected Excel(Class<E> entryClass) {
|
||||
this(entryClass, 53);
|
||||
}
|
||||
|
||||
protected Excel(Class<E> entryClass, int initialCapacity) {
|
||||
this(entryClass, initialCapacity, 0.8f);
|
||||
}
|
||||
|
||||
protected Excel(Class<E> entryClass, int initialCapacity, float loadFactor) {
|
||||
this.entryClass = entryClass;
|
||||
this.stringToIndex = emptyMap();
|
||||
this.entries = new IntMap<>(initialCapacity, loadFactor);
|
||||
this.ordered = new Array<>(true, (int) (initialCapacity * loadFactor), Entry.class);
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected void put(int id, T value) {
|
||||
public Class<? extends Excel> excelClass() {
|
||||
return getClass();
|
||||
}
|
||||
|
||||
public Class<E> entryClass() {
|
||||
return entryClass;
|
||||
}
|
||||
|
||||
final void _put(int id, E value) {
|
||||
entries.put(id, value);
|
||||
put(id, value);
|
||||
}
|
||||
|
||||
protected void put(int id, E value) {}
|
||||
|
||||
protected int offset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected void init() {}
|
||||
|
||||
public T get(String id) {
|
||||
public E get(String id) {
|
||||
return get(index(id));
|
||||
}
|
||||
|
||||
public T get(int id) {
|
||||
T value = entries.get(id);
|
||||
if (DEBUG_INDEXES) log.trace("{} = {}", id, value);
|
||||
return value;
|
||||
public E get(int id) {
|
||||
return entries.get(id);
|
||||
}
|
||||
|
||||
public int index(String id) {
|
||||
return STRING_TO_ID.get(id, -1);
|
||||
return stringToIndex.get(id, -1);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return entries.size;
|
||||
}
|
||||
|
||||
public Class<T> getEntryClass() {
|
||||
return entryClass;
|
||||
}
|
||||
public abstract E newEntry();
|
||||
|
||||
public abstract T newEntry();
|
||||
|
||||
public abstract U newSerializer();
|
||||
public abstract S newSerializer();
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
public Iterator<E> iterator() {
|
||||
return entries.values().iterator();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
@ -1,11 +1,11 @@
|
||||
package com.riiablo.excel;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.riiablo.io.ByteInput;
|
||||
import com.riiablo.io.ByteOutput;
|
||||
|
||||
public interface Serializer<T extends Excel.Entry> {
|
||||
void readBin(T entry, ByteInput in) throws IOException;
|
||||
void writeBin(T entry, ByteOutput out) throws IOException;
|
||||
void readBin(T entry, ByteInput in);
|
||||
void writeBin(T entry, ByteOutput out);
|
||||
boolean equals(T e1, T e2);
|
||||
void logErrors(T e1, T e2);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import com.squareup.javapoet.JavaFile;
|
||||
import java.io.File;
|
||||
@ -13,8 +13,8 @@ import com.riiablo.util.ClassUtils;
|
||||
public class SerializerGenerator {
|
||||
private static final Logger log = LogManager.getLogger(SerializerGenerator.class);
|
||||
|
||||
String sourcePackage = "com.riiablo.excel2.txt";
|
||||
String serializerPackage = "com.riiablo.excel2.serializer";
|
||||
String sourcePackage = "com.riiablo.excel.txt";
|
||||
String serializerPackage = "com.riiablo.excel.serializer";
|
||||
|
||||
FileHandle sourceDir;
|
||||
FileHandle serializerDir;
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import com.squareup.javapoet.AnnotationSpec;
|
||||
import com.squareup.javapoet.ClassName;
|
||||
@ -22,7 +22,7 @@ import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
import com.riiablo.excel2.Excel.Entry.Column;
|
||||
import com.riiablo.excel.Excel.Entry.Column;
|
||||
import com.riiablo.io.ByteInput;
|
||||
import com.riiablo.io.ByteOutput;
|
||||
import com.riiablo.logger.LogManager;
|
||||
@ -43,7 +43,7 @@ public class SerializerSourceGenerator {
|
||||
ClassName entryName;
|
||||
|
||||
public SerializerSourceGenerator() {
|
||||
this("com.riiablo.excel2.txt", "com.riiablo.excel2.serializer");
|
||||
this("com.riiablo.excel.txt", "com.riiablo.excel.serializer");
|
||||
}
|
||||
|
||||
static final class ColumnInfo {
|
@ -1,182 +1,278 @@
|
||||
package com.riiablo.excel;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.ByteArray;
|
||||
import com.badlogic.gdx.utils.IntArray;
|
||||
import com.badlogic.gdx.utils.ObjectIntMap;
|
||||
|
||||
import com.riiablo.logger.LogManager;
|
||||
import com.riiablo.logger.Logger;
|
||||
|
||||
public class TxtParser implements Closeable {
|
||||
public class TxtParser {
|
||||
private static final Logger log = LogManager.getLogger(TxtParser.class);
|
||||
|
||||
private static final boolean DEBUG = true;
|
||||
private static final boolean DEBUG_ROWS = DEBUG && true;
|
||||
private static final int HT = '\t';
|
||||
private static final int CR = '\r';
|
||||
private static final int LF = '\n';
|
||||
|
||||
private static final boolean FORCE_BOOL = true; // logs error if boolean is not 0 or 1
|
||||
private static final boolean FORCE_COLS = true; // ignores row if token count != columns count
|
||||
private static final byte[] TO_UPPER;
|
||||
static {
|
||||
TO_UPPER = new byte[1 << Byte.SIZE];
|
||||
for (int i = 0; i < TO_UPPER.length; i++) {
|
||||
TO_UPPER[i] = (byte) i;
|
||||
}
|
||||
|
||||
private static final String EXPANSION = "Expansion";
|
||||
|
||||
public static TxtParser parse(FileHandle handle) throws IOException {
|
||||
return parse(handle.read());
|
||||
for (int i = 'a'; i <= 'z'; i++) {
|
||||
TO_UPPER[i] &= ~0x20;
|
||||
}
|
||||
}
|
||||
|
||||
private static final AsciiString EXPANSION = AsciiString.cached("EXPANSION");
|
||||
|
||||
public static TxtParser parse(InputStream in) throws IOException {
|
||||
BufferedReader reader = null;
|
||||
reader = IOUtils.buffer(new InputStreamReader(in, StandardCharsets.US_ASCII));
|
||||
return new TxtParser(reader);
|
||||
return parse(in, 8192);
|
||||
}
|
||||
|
||||
final BufferedReader reader;
|
||||
public static TxtParser parse(InputStream in, int bufferSize) throws IOException {
|
||||
return new TxtParser(in, bufferSize);
|
||||
}
|
||||
|
||||
final int numColumns;
|
||||
final Array<String> columnNames;
|
||||
final ObjectIntMap<String> columnIds;
|
||||
final String columnNames[];
|
||||
|
||||
BufferedInputStream in;
|
||||
AsciiString line;
|
||||
int index;
|
||||
final ByteArray cache;
|
||||
final IntArray tokenOffsets;
|
||||
int[] tokenOffsetsCache;
|
||||
int numTokens;
|
||||
|
||||
String line;
|
||||
String tokens[];
|
||||
|
||||
private TxtParser(BufferedReader reader) throws IOException {
|
||||
this.reader = reader;
|
||||
line = reader.readLine();
|
||||
columnNames = StringUtils.splitPreserveAllTokens(line, '\t');
|
||||
log.debug("columnNames: {}", (Object) columnNames);
|
||||
TxtParser(InputStream in, int bufferSize) throws IOException {
|
||||
this.in = IOUtils.buffer(in, bufferSize);
|
||||
|
||||
cache = new ByteArray(512);
|
||||
columnNames = new Array<>();
|
||||
columnIds = new ObjectIntMap<>();
|
||||
for (int i = 0; i < columnNames.length; i++) {
|
||||
String key = columnNames[i].toLowerCase();
|
||||
if (!columnIds.containsKey(key)) columnIds.put(key, i);
|
||||
numColumns = parseColumnNames();
|
||||
|
||||
log.info("numColumns: {}", numColumns);
|
||||
log.debug("columnNames: {}", columnNames);
|
||||
log.trace("columnIds: {}", columnIds);
|
||||
|
||||
tokenOffsets = new IntArray();
|
||||
}
|
||||
|
||||
private static String toString(ByteArray array) {
|
||||
if (array.size == 0) return "";
|
||||
String stringValue = new String(array.items, 0, array.size, CharsetUtil.US_ASCII);
|
||||
array.clear();
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
private int parseColumnNames() throws IOException {
|
||||
for (int i; (i = in.read()) != -1;) {
|
||||
switch (i) {
|
||||
case HT:
|
||||
putColumnName(toString(cache));
|
||||
break;
|
||||
case CR:
|
||||
in.skip(1);
|
||||
case LF:
|
||||
putColumnName(toString(cache));
|
||||
return columnNames.size;
|
||||
default:
|
||||
cache.add(TO_UPPER[i]);
|
||||
}
|
||||
}
|
||||
log.debug("columnIds: {}", columnIds);
|
||||
|
||||
throw new IOException("Unexpected end of file while parsing column names");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
reader.close();
|
||||
private void putColumnName(String columnName) {
|
||||
if (!columnIds.containsKey(columnName)) {
|
||||
columnIds.put(columnName, columnNames.size);
|
||||
}
|
||||
|
||||
columnNames.add(columnName);
|
||||
}
|
||||
|
||||
public String[] getColumnNames() {
|
||||
public int cacheLine() throws IOException {
|
||||
cache.clear();
|
||||
tokenOffsets.clear();
|
||||
tokenOffsets.add(0);
|
||||
index++;
|
||||
lineBuilder:
|
||||
for (;;) {
|
||||
final int i = in.read();
|
||||
switch (i) {
|
||||
case -1:
|
||||
return -1;
|
||||
case HT:
|
||||
tokenOffsets.add(cache.size);
|
||||
break;
|
||||
case CR:
|
||||
in.skip(1);
|
||||
case LF:
|
||||
tokenOffsets.add(cache.size);
|
||||
break lineBuilder;
|
||||
default:
|
||||
cache.add((byte) i);
|
||||
}
|
||||
}
|
||||
|
||||
numTokens = tokenOffsets.size - 1;
|
||||
tokenOffsetsCache = tokenOffsets.items;
|
||||
line = new AsciiString(cache.items, 0, cache.size, false);
|
||||
log.debug("line: {}", line);
|
||||
if (line.contentEqualsIgnoreCase(EXPANSION)) {
|
||||
log.trace("skipping row {}: {} is an ignored symbol", index, EXPANSION);
|
||||
return cacheLine();
|
||||
}
|
||||
|
||||
if (numTokens != numColumns) {
|
||||
log.warn("skipping row {}: contains {} tokens, expected {}; tokens: {}",
|
||||
index, numTokens, numColumns, tokens());
|
||||
return cacheLine();
|
||||
}
|
||||
|
||||
if (log.traceEnabled()) {
|
||||
final int[] tokenOffsets = this.tokenOffsets.items;
|
||||
for (int i = 1, j = tokenOffsets[i - 1], s = this.tokenOffsets.size; i < s; i++) {
|
||||
final int tokenOffset = tokenOffsets[i];
|
||||
log.trace("{}={}", columnName(i - 1), line.subSequence(j, tokenOffset, false));
|
||||
j = tokenOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return tokenOffsets.size - 1;
|
||||
}
|
||||
|
||||
public int numColumns() {
|
||||
return numColumns;
|
||||
}
|
||||
|
||||
public String[] columnNames() {
|
||||
final String[] columnNames = new String[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) columnNames[i] = columnName(i);
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
public int getNumColumns() {
|
||||
return columnNames.length;
|
||||
public String columnName(int i) {
|
||||
return columnNames.get(i);
|
||||
}
|
||||
|
||||
public String getColumnName(int i) {
|
||||
return columnNames[i];
|
||||
public String rowName() {
|
||||
return parseString(0, "");
|
||||
}
|
||||
|
||||
public String[] getTokens() {
|
||||
return tokens;
|
||||
public int columnId(String columnName) {
|
||||
return columnIds.get(columnName.toUpperCase(), -1);
|
||||
}
|
||||
|
||||
public int getNumTokens() {
|
||||
return tokens.length;
|
||||
}
|
||||
|
||||
public int getColumnId(String s) {
|
||||
return columnIds.get(s.toLowerCase(), -1);
|
||||
}
|
||||
|
||||
public int[] getColumnId(String[] s) {
|
||||
int[] columnIds = new int[s.length];
|
||||
for (int i = 0; i < s.length; i++) columnIds[i] = getColumnId(s[i]);
|
||||
public int[] columnId(String[] columnNames) {
|
||||
final int numColumns = columnNames.length;
|
||||
final int[] columnIds = new int[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) columnIds[i] = columnId(columnNames[i]);
|
||||
return columnIds;
|
||||
}
|
||||
|
||||
public String nextLine() throws IOException {
|
||||
index++;
|
||||
line = reader.readLine();
|
||||
if (line == null) {
|
||||
return null;
|
||||
} else if (line.equalsIgnoreCase(EXPANSION)) {
|
||||
return nextLine();
|
||||
public int numTokens() {
|
||||
return numTokens;
|
||||
}
|
||||
|
||||
public AsciiString[] tokens() {
|
||||
final int numTokens = numTokens();
|
||||
final AsciiString[] tokens = new AsciiString[numTokens];
|
||||
for (int i = 0; i < numTokens; i++) tokens[i] = token(i);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
public AsciiString token(int i) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
return line.subSequence(tokenOffsets[i], tokenOffsets[i + 1]);
|
||||
}
|
||||
|
||||
public byte parseByte(int i, byte defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
final int intValue = line.parseInt(startOffset, endOffset);
|
||||
final byte result = (byte) intValue;
|
||||
if (result != intValue) {
|
||||
throw new NumberFormatException(line.subSequence(startOffset, endOffset, false).toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public short parseShort(int i, short defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.parseShort(startOffset, endOffset);
|
||||
}
|
||||
|
||||
public int parseInt(int i, int defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.parseInt(startOffset, endOffset);
|
||||
}
|
||||
|
||||
public long parseLong(int i, long defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.parseLong(startOffset, endOffset);
|
||||
}
|
||||
|
||||
public boolean parseBoolean(int i, boolean defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
final int intValue = line.parseInt(startOffset, endOffset);
|
||||
if ((intValue & 1) != intValue) {
|
||||
log.warn("boolean exceeds boolean radix at {}:{} (\"{}\", \"{}\"): {}",
|
||||
index, i, rowName(), columnName(i), intValue);
|
||||
}
|
||||
|
||||
tokens = StringUtils.splitPreserveAllTokens(line, '\t');
|
||||
if (log.traceEnabled()) log.trace("{}: {}", (index - 1), Arrays.toString(tokens));
|
||||
if (FORCE_COLS && tokens.length != columnNames.length) {
|
||||
log.warn("skipping row {}: contains {} tokens, expected {}; tokens: {}",
|
||||
index, tokens.length, columnNames.length, Arrays.toString(tokens));
|
||||
return nextLine();
|
||||
}
|
||||
|
||||
return line;
|
||||
return intValue != 0;
|
||||
}
|
||||
|
||||
public String getString(int i) {
|
||||
return tokens[i];
|
||||
public float parseFloat(int i, float defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.parseFloat(startOffset, endOffset);
|
||||
}
|
||||
|
||||
public byte getByte(int i) {
|
||||
return NumberUtils.toByte(tokens[i]);
|
||||
public double parseDouble(int i, double defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.parseDouble(startOffset, endOffset);
|
||||
}
|
||||
|
||||
public short getShort(int i) {
|
||||
return NumberUtils.toShort(tokens[i]);
|
||||
}
|
||||
|
||||
public int getInt(int i) {
|
||||
return NumberUtils.toInt(tokens[i]);
|
||||
}
|
||||
|
||||
public long getLong(int i) {
|
||||
return NumberUtils.toLong(tokens[i]);
|
||||
}
|
||||
|
||||
public boolean getBoolean(int i) {
|
||||
int value = getInt(i);
|
||||
if (FORCE_BOOL && (value & 1) != value) {
|
||||
log.warn("boolean value != 0 or 1 at {}:{} (\"{}\", \"{}\"): {}",
|
||||
index, i, getString(0), getColumnName(i), value);
|
||||
}
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
public String[] getString(int[] cols) {
|
||||
String[] data = new String[cols.length];
|
||||
for (int i = 0; i < cols.length; i++) data[i] = getString(cols[i]);
|
||||
return data;
|
||||
}
|
||||
|
||||
public byte[] getByte(int[] cols) {
|
||||
byte[] data = new byte[cols.length];
|
||||
for (int i = 0; i < cols.length; i++) data[i] = getByte(cols[i]);
|
||||
return data;
|
||||
}
|
||||
|
||||
public short[] getShort(int[] cols) {
|
||||
short[] data = new short[cols.length];
|
||||
for (int i = 0; i < cols.length; i++) data[i] = getShort(cols[i]);
|
||||
return data;
|
||||
}
|
||||
|
||||
public int[] getInt(int[] cols) {
|
||||
int[] data = new int[cols.length];
|
||||
for (int i = 0; i < cols.length; i++) data[i] = getInt(cols[i]);
|
||||
return data;
|
||||
}
|
||||
|
||||
public long[] getLong(int[] cols) {
|
||||
long[] data = new long[cols.length];
|
||||
for (int i = 0; i < cols.length; i++) data[i] = getLong(cols[i]);
|
||||
return data;
|
||||
}
|
||||
|
||||
public boolean[] getBoolean(int[] cols) {
|
||||
boolean[] data = new boolean[cols.length];
|
||||
for (int i = 0; i < cols.length; i++) data[i] = getBoolean(cols[i]);
|
||||
return data;
|
||||
public String parseString(int i, String defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.toString(tokenOffsets[i], tokenOffsets[i + 1]);
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
package com.riiablo.excel.gen;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
|
||||
import com.riiablo.logger.LogManager;
|
||||
import com.riiablo.logger.Logger;
|
||||
|
||||
public class BinGenerator {
|
||||
private static final Logger log = LogManager.getLogger(BinGenerator.class);
|
||||
|
||||
public String serializerPackage;
|
||||
public FileHandle serializerDir;
|
||||
|
||||
public String excelPath = "DATA\\GLOBAL\\EXCEL";
|
||||
|
||||
public FileHandle assetsDir;
|
||||
|
||||
public void generateBins() {
|
||||
log.info("Generating bins for {}...", serializerPackage);
|
||||
|
||||
}
|
||||
}
|
@ -1,308 +0,0 @@
|
||||
package com.riiablo.excel.gen;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
|
||||
import com.riiablo.excel.Excel;
|
||||
import com.riiablo.excel.Excel.Entry;
|
||||
import com.riiablo.io.ByteInput;
|
||||
import com.riiablo.io.ByteOutput;
|
||||
import com.riiablo.logger.LogManager;
|
||||
import com.riiablo.logger.Logger;
|
||||
import com.riiablo.util.ClassUtils;
|
||||
|
||||
public class SerializerGenerator {
|
||||
private static final Logger log = LogManager.getLogger(SerializerGenerator.class);
|
||||
|
||||
public String txtPackage;
|
||||
public String binPackage;
|
||||
|
||||
public FileHandle txtDir;
|
||||
public FileHandle binDir;
|
||||
|
||||
public int maxStringLen;
|
||||
|
||||
public String getSerializerPart() {
|
||||
return "Bin";
|
||||
}
|
||||
|
||||
public String formatSerializerName(String name) {
|
||||
return StringUtils.appendIfMissingIgnoreCase(name, getSerializerPart());
|
||||
}
|
||||
|
||||
private static Class findDeclaredClass(Class c, Class impl) throws ClassNotFoundException {
|
||||
final Class[] classes = c.getDeclaredClasses();
|
||||
for (Class declaredClass : classes) {
|
||||
if (impl.isAssignableFrom(declaredClass)) {
|
||||
return declaredClass;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ClassNotFoundException(c + " does not implement " + impl);
|
||||
}
|
||||
|
||||
public void generateSerializers() {
|
||||
log.info("Generating serializers for {}...", txtDir);
|
||||
FileHandle[] txtFiles = txtDir.list("java");
|
||||
for (FileHandle txtFile : txtFiles) {
|
||||
try {
|
||||
generateSerializer(txtFile);
|
||||
} catch (Throwable t) {
|
||||
log.error("Error generating serializer for {}", txtFile, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void generateSerializer(FileHandle txtFile)
|
||||
throws ClassNotFoundException {
|
||||
String txtName = txtFile.nameWithoutExtension();
|
||||
log.trace("txtName: {}", txtName);
|
||||
Class txtClass = Class.forName(txtPackage + "." + txtName);
|
||||
|
||||
// Prevent serializing literal Excel.class and non-subclasses of Excel.class
|
||||
if (txtClass == Excel.class || !Excel.class.isAssignableFrom(txtClass)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String binName = formatSerializerName(txtName);
|
||||
log.trace("binName: {}", binName);
|
||||
FileHandle binFile = binDir.child(binName + "." + "java");
|
||||
log.info("{}->{}", txtFile, binFile);
|
||||
|
||||
// Find impls of Entry.class within txtClass
|
||||
Class entryClass;
|
||||
Class[] entryClasses = ClassUtils.findDeclaredClasses(txtClass, Entry.class);
|
||||
switch (entryClasses.length) {
|
||||
case 0:
|
||||
log.error("{} does not contain an implementation of {}", txtClass, Entry.class);
|
||||
return;
|
||||
case 1:
|
||||
entryClass = entryClasses[0];
|
||||
log.trace("entryClass: {}", entryClass.getCanonicalName());
|
||||
break;
|
||||
default:
|
||||
log.error("{} contains ambiguous implementations of {}: {}",
|
||||
txtClass, Entry.class, Arrays.toString(entryClasses));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entryClass.getSimpleName().equals(Entry.class.getSimpleName())) {
|
||||
log.warn("entry class {} not named {}",
|
||||
entryClass.getCanonicalName(),
|
||||
txtClass.getCanonicalName() + "$" + Entry.class.getSimpleName());
|
||||
// return; // Allow it for now
|
||||
}
|
||||
|
||||
Context context = new Context();
|
||||
context.txtClass = txtClass;
|
||||
context.entryClass = entryClass;
|
||||
context.binName = binName;
|
||||
context.binPackage = binPackage;
|
||||
|
||||
PrintStream out = System.out;
|
||||
try {
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||
out = new PrintStream(outStream, false, "UTF-8");
|
||||
writeSerializer(out, context);
|
||||
|
||||
byte[] bytes = IOUtils.toByteArray(outStream.toInputStream());
|
||||
binFile.writeBytes(bytes, false);
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.rethrow(t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static class Context {
|
||||
public Class txtClass;
|
||||
public Class entryClass;
|
||||
public String binName;
|
||||
public String binPackage;
|
||||
public int indent = 2;
|
||||
public int currentIndent = 0;
|
||||
String indentSpaces = "";
|
||||
|
||||
public String push() {
|
||||
return indentSpaces = StringUtils.repeat(' ', currentIndent += indent);
|
||||
}
|
||||
|
||||
public String pop() {
|
||||
assert currentIndent >= 0 : "currentIndent(" + currentIndent + ") < " + 0;
|
||||
return indentSpaces = StringUtils.repeat(' ', currentIndent -= indent);
|
||||
}
|
||||
|
||||
public String peek() {
|
||||
return indentSpaces;
|
||||
}
|
||||
}
|
||||
|
||||
void writeSerializer(PrintStream out, Context context) {
|
||||
out.println("package " + context.binPackage + ";");
|
||||
out.println();
|
||||
out.println("import " + StringUtils.class.getCanonicalName() + ";");
|
||||
out.println();
|
||||
out.println("public class " + context.binName + " {");
|
||||
context.push();
|
||||
|
||||
print_readBin(out, context);
|
||||
out.println();
|
||||
print_writeBin(out, context);
|
||||
out.println();
|
||||
print_equals(out, context);
|
||||
out.println();
|
||||
print_validate(out, context);
|
||||
|
||||
context.pop();
|
||||
out.println("}");
|
||||
out.println();
|
||||
}
|
||||
|
||||
void print_readBin(PrintStream out, Context context) {
|
||||
out.print(context.peek());
|
||||
out.printf("public static void readBin(%s entry, %s in) throws java.io.IOException {%n",
|
||||
context.entryClass.getCanonicalName(), ByteInput.class.getName());
|
||||
String indent = context.push();
|
||||
|
||||
for (Field field : context.entryClass.getFields()) {
|
||||
log.trace("Creating readBin for {}", field);
|
||||
// if (field.getAnnotation(Excel.Entry.Key.class) != null) continue;
|
||||
Excel.Entry.Column column = field.getAnnotation(Excel.Entry.Column.class);
|
||||
if (!column.bin()) continue;
|
||||
Class type = field.getType();
|
||||
if (type.isArray()) {
|
||||
type = type.getComponentType();
|
||||
out.print(indent);
|
||||
out.printf("entry.%s = new %s[%d];%n", field.getName(), type.getSimpleName(), column.endIndex() - column.startIndex());
|
||||
out.print(indent);
|
||||
if (type == String.class) {
|
||||
out.printf("for (int x = %d; x < %d; x++) entry.%s[x] = in.read%s(%d, %b);%n", 0, column.endIndex() - column.startIndex(), field.getName(), getMethod(field), maxStringLen, true);
|
||||
} else {
|
||||
out.printf("for (int x = %d; x < %d; x++) entry.%s[x] = in.read%s();%n", 0, column.endIndex() - column.startIndex(), field.getName(), getMethod(field));
|
||||
}
|
||||
} else {
|
||||
out.print(indent);
|
||||
if (type == String.class) {
|
||||
out.printf("entry.%s = in.read%s(%d, %b);%n", field.getName(), getMethod(field), maxStringLen, true);
|
||||
} else {
|
||||
out.printf("entry.%s = in.read%s();%n", field.getName(), getMethod(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.print(context.pop());
|
||||
out.println("}");
|
||||
}
|
||||
|
||||
void print_writeBin(PrintStream out, Context context) {
|
||||
out.print(context.peek());
|
||||
out.printf("public static void writeBin(%s entry, %s out) throws java.io.IOException {%n",
|
||||
context.entryClass.getCanonicalName(), ByteOutput.class.getName());
|
||||
String indent = context.push();
|
||||
|
||||
for (Field field : context.entryClass.getFields()) {
|
||||
Excel.Entry.Column column = field.getAnnotation(Excel.Entry.Column.class);
|
||||
if (!column.bin()) continue;
|
||||
Class type = field.getType();
|
||||
out.print(indent);
|
||||
if (type.isArray()) {
|
||||
type = type.getComponentType();
|
||||
if (type == String.class) {
|
||||
out.printf("for (%s x : entry.%s) out.write%s(StringUtils.defaultString(x));%n", type.getSimpleName(), field.getName(), getMethod(field));
|
||||
} else {
|
||||
out.printf("for (%s x : entry.%s) out.write%s(x);%n", type.getSimpleName(), field.getName(), getMethod(field));
|
||||
}
|
||||
} else {
|
||||
if (type == String.class) {
|
||||
out.printf("out.write%s(StringUtils.defaultString(entry.%s));%n", getMethod(field), field.getName());
|
||||
} else {
|
||||
out.printf("out.write%s(entry.%s);%n", getMethod(field), field.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.print(context.pop());
|
||||
out.println("}");
|
||||
}
|
||||
|
||||
void print_equals(PrintStream out, Context context) {
|
||||
out.print(context.peek());
|
||||
out.printf("public static boolean equals(%s e1, %s e2) {%n",
|
||||
context.entryClass.getCanonicalName(), context.entryClass.getCanonicalName());
|
||||
String indent = context.push();
|
||||
|
||||
for (Field field : context.entryClass.getFields()) {
|
||||
Excel.Entry.Column column = field.getAnnotation(Excel.Entry.Column.class);
|
||||
if (!column.bin()) continue;
|
||||
Class type = field.getType();
|
||||
out.print(indent);
|
||||
if (type.isArray()) {
|
||||
out.printf("if (!java.util.Arrays.equals(e1.%s, e2.%s)) return false;%n", field.getName(), field.getName());
|
||||
} else if (type.isPrimitive()) {
|
||||
out.printf("if (e1.%s != e2.%s) return false;%n", field.getName(), field.getName());
|
||||
} else {
|
||||
out.printf("if (!java.util.Objects.equals(e1.%s, e2.%s)) return false;%n", field.getName(), field.getName());
|
||||
}
|
||||
}
|
||||
|
||||
out.print(indent);
|
||||
out.println("return true;");
|
||||
|
||||
out.print(context.pop());
|
||||
out.println("}");
|
||||
}
|
||||
|
||||
void print_validate(PrintStream out, Context context) {
|
||||
out.print(context.peek());
|
||||
out.printf("public static void validate(%s log, %s e1, %s e2) {%n",
|
||||
Logger.class.getCanonicalName(), context.entryClass.getCanonicalName(), context.entryClass.getCanonicalName());
|
||||
String indent = context.push();
|
||||
|
||||
for (Field field : context.entryClass.getFields()) {
|
||||
Excel.Entry.Column column = field.getAnnotation(Excel.Entry.Column.class);
|
||||
if (!column.bin()) continue;
|
||||
Class type = field.getType();
|
||||
out.print(indent);
|
||||
if (type.isArray()) {
|
||||
out.printf("if (!java.util.Arrays.equals(e1.%s, e2.%s)) log.warn(\"%s does not match: e1=\" + e1.%s + \" e2=\" + e2.%s);%n", field.getName(), field.getName(), field.getName(), field.getName(), field.getName());
|
||||
} else if (type.isPrimitive()) {
|
||||
out.printf("if (e1.%s != e2.%s) log.warn(\"%s does not match: e1=\" + e1.%s + \" e2=\" + e2.%s);%n", field.getName(), field.getName(), field.getName(), field.getName(), field.getName());
|
||||
} else {
|
||||
out.printf("if (!java.util.Objects.equals(e1.%s, e2.%s)) log.warn(\"%s does not match: e1=\" + e1.%s + \" e2=\" + e2.%s);%n", field.getName(), field.getName(), field.getName(), field.getName(), field.getName());
|
||||
}
|
||||
}
|
||||
|
||||
out.print(context.pop());
|
||||
out.println("}");
|
||||
}
|
||||
|
||||
private static String getMethod(Field field) {
|
||||
Class type = field.getType();
|
||||
if (type.isArray()) {
|
||||
type = type.getComponentType();
|
||||
}
|
||||
|
||||
if (type == String.class) {
|
||||
return "String";
|
||||
} else if (type == byte.class) {
|
||||
return "8";
|
||||
} else if (type == short.class) {
|
||||
return "16";
|
||||
} else if (type == int.class) {
|
||||
return "32";
|
||||
} else if (type == long.class) {
|
||||
return "64";
|
||||
} else if (type == boolean.class) {
|
||||
return "Boolean";
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"No support for " + type.getCanonicalName() + " fields");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,18 @@
|
||||
package com.riiablo.excel.txt;
|
||||
|
||||
import com.riiablo.excel.Entry;
|
||||
import com.riiablo.excel.Excel;
|
||||
import com.riiablo.excel.PrimaryKey;
|
||||
import com.riiablo.excel.SerializedWith;
|
||||
import com.riiablo.excel.serializer.MonStatsSerializer;
|
||||
import com.riiablo.io.ByteInput;
|
||||
import com.riiablo.io.ByteOutput;
|
||||
|
||||
public class MonStats extends Excel<MonStats.Entry, MonStats.Serializer> {
|
||||
@Entry(MonStats.Entry.class)
|
||||
@SerializedWith(MonStatsSerializer.class)
|
||||
public class MonStats extends Excel<MonStats.Entry, MonStatsSerializer> {
|
||||
public MonStats() {
|
||||
super(Entry.class);
|
||||
super(Entry.class, 1543); // 736 entries
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -15,8 +21,8 @@ public class MonStats extends Excel<MonStats.Entry, MonStats.Serializer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializer newSerializer() {
|
||||
return new Serializer();
|
||||
public MonStatsSerializer newSerializer() {
|
||||
return new MonStatsSerializer();
|
||||
}
|
||||
|
||||
public static class Entry extends Excel.Entry {
|
||||
@ -25,7 +31,7 @@ public class MonStats extends Excel<MonStats.Entry, MonStats.Serializer> {
|
||||
return NameStr;
|
||||
}
|
||||
|
||||
@Key
|
||||
@PrimaryKey
|
||||
@Column public String Id;
|
||||
@Column public int hcIdx;
|
||||
@Column public String BaseId;
|
||||
@ -236,5 +242,7 @@ public class MonStats extends Excel<MonStats.Entry, MonStats.Serializer> {
|
||||
public static class Serializer implements com.riiablo.excel.Serializer<Entry> {
|
||||
@Override public void readBin(Entry entry, ByteInput in) {}
|
||||
@Override public void writeBin(Entry entry, ByteOutput out) {}
|
||||
@Override public boolean equals(Entry e1, Entry e2) { throw new UnsupportedOperationException(); }
|
||||
@Override public void logErrors(Entry e1, Entry e2) {}
|
||||
}
|
||||
}
|
||||
|
@ -1,639 +0,0 @@
|
||||
package com.riiablo.excel2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.IntMap;
|
||||
import com.badlogic.gdx.utils.ObjectIntMap;
|
||||
|
||||
import com.riiablo.logger.LogManager;
|
||||
import com.riiablo.logger.Logger;
|
||||
import com.riiablo.logger.MDC;
|
||||
import com.riiablo.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Root class of an excel table.
|
||||
*/
|
||||
public abstract class Excel<
|
||||
E extends Excel.Entry,
|
||||
S extends Serializer<E>
|
||||
>
|
||||
implements Iterable<E>
|
||||
{
|
||||
private static final Logger log = LogManager.getLogger(Excel.class);
|
||||
|
||||
/** Forces excels to either have a {@link PrimaryKey} set or be {@link Indexed} */
|
||||
private static final boolean FORCE_PRIMARY_KEY = !true;
|
||||
|
||||
private static final ObjectIntMap EMPTY_OBJECT_INT_MAP = new ObjectIntMap();
|
||||
|
||||
@SuppressWarnings("unchecked") // doesn't store anything
|
||||
static <T> ObjectIntMap<T> emptyMap() {
|
||||
return (ObjectIntMap<T>) EMPTY_OBJECT_INT_MAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Root class of an excel entry.
|
||||
*/
|
||||
public static abstract class Entry {
|
||||
/**
|
||||
* Tags a specified field as a column within the excel table.
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Column {
|
||||
/**
|
||||
* Start index of {@link #format()} (inclusive)
|
||||
*/
|
||||
int startIndex() default 0;
|
||||
|
||||
/**
|
||||
* End index of {@link #format()} (exclusive)
|
||||
*/
|
||||
int endIndex() default 0;
|
||||
|
||||
/**
|
||||
* String format of column name, {@code ""} to use field name
|
||||
* <p>
|
||||
* <p>Examples:
|
||||
* <ul>
|
||||
* <li>{@code "class"}
|
||||
* <li>{@code "Transform Color"}
|
||||
* <li>{@code "Level%s"}
|
||||
* <li>{@code "Skill %d"}
|
||||
*/
|
||||
String format() default "";
|
||||
|
||||
/**
|
||||
* Index values of format in the case of non-numerical indexes.
|
||||
* <p>
|
||||
* <p>Examples:
|
||||
* <ul>
|
||||
* <li>{@code {"", "(N)", "(H)"}}
|
||||
* <li>{@code {"r", "g", "b"}}
|
||||
* <li>{@code {"Min", "Max", "MagicMin", "MagicMax", "MagicLvl"}}
|
||||
*/
|
||||
String[] values() default {};
|
||||
|
||||
/**
|
||||
* Manually sets the column index. This property overrides all other
|
||||
* properties.
|
||||
*/
|
||||
int columnIndex() default -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
T load(T excel, FileHandle txt) throws IOException {
|
||||
return load(excel, txt, null);
|
||||
}
|
||||
|
||||
public static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
T load(T excel, FileHandle txt, FileHandle bin) throws IOException {
|
||||
throw null;
|
||||
}
|
||||
|
||||
static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
T loadTxt(T excel, FileHandle handle) throws IOException {
|
||||
InputStream in = handle.read();
|
||||
try {
|
||||
MDC.put("excel", handle.path());
|
||||
TxtParser parser = TxtParser.parse(in);
|
||||
return loadTxt(excel, parser);
|
||||
} catch (Throwable t) {
|
||||
log.fatal("Unable to load {} as {}: {}",
|
||||
handle,
|
||||
excel.getClass().getCanonicalName(),
|
||||
ExceptionUtils.getRootCauseMessage(t),
|
||||
t);
|
||||
return ExceptionUtils.rethrow(t);
|
||||
} finally {
|
||||
MDC.remove("excel");
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
T loadTxt(T excel, TxtParser parser)
|
||||
throws IOException, ParseException, IllegalAccessException
|
||||
{
|
||||
final Class<E> entryClass = excel.entryClass();
|
||||
final boolean indexed = ClassUtils.hasAnnotation(entryClass, Indexed.class);
|
||||
|
||||
final String[] TMP = new String[1];
|
||||
Field primaryKey = null, firstKey = null;
|
||||
Array<Triple<Field, int[], String[]>> columns = new Array<>(true, parser.numColumns(), Triple.class);
|
||||
for (Field field : entryClass.getFields()) {
|
||||
Entry.Column column = field.getAnnotation(Entry.Column.class);
|
||||
if (column == null) {
|
||||
log.warn("{} is not tagged with {}", field, Entry.Column.class.getCanonicalName());
|
||||
continue;
|
||||
}
|
||||
|
||||
PrimaryKey key = field.getAnnotation(PrimaryKey.class);
|
||||
if (key != null) {
|
||||
if (!ArrayUtils.contains(PrimaryKey.SUPPORTED_TYPES, field.getType())) {
|
||||
throw new ParseException(field, "%s must be one of %s",
|
||||
field, Arrays.toString(PrimaryKey.SUPPORTED_TYPES));
|
||||
}
|
||||
|
||||
if (indexed) {
|
||||
// Indexed excels have their primary key assigned automatically based on row index
|
||||
log.warn("{} has {} set to the primary key, but class is tagged with {}",
|
||||
entryClass, field, Indexed.class.getCanonicalName());
|
||||
} else if (primaryKey != null) {
|
||||
// Allow declared field tagged as a primary key to override inherited ones
|
||||
boolean primaryDeclared = ClassUtils.isDeclaredField(entryClass, primaryKey);
|
||||
boolean fieldDeclared = ClassUtils.isDeclaredField(entryClass, field);
|
||||
if (primaryDeclared != fieldDeclared) {
|
||||
if (fieldDeclared) {
|
||||
log.debug("primary key {} changed to {}", primaryKey, field);
|
||||
primaryKey = field;
|
||||
}
|
||||
} else {
|
||||
log.warn("multiple primary keys set within {}: {} and {}",
|
||||
entryClass, primaryKey.getName(), field.getName());
|
||||
}
|
||||
} else {
|
||||
primaryKey = field;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstKey == null) firstKey = field;
|
||||
populateColumnIndexes(columns, parser, column, field, TMP);
|
||||
}
|
||||
|
||||
if (primaryKey == null && !indexed) {
|
||||
if (FORCE_PRIMARY_KEY) {
|
||||
throw new ParseException(entryClass, "%s does not have a %s set!",
|
||||
entryClass, PrimaryKey.class.getCanonicalName());
|
||||
} else {
|
||||
log.warn("{} does not have a {} set! Defaulting to first key: {}",
|
||||
entryClass, PrimaryKey.class.getCanonicalName(), firstKey);
|
||||
primaryKey = firstKey;
|
||||
}
|
||||
}
|
||||
|
||||
// Locate the column index of the primary key
|
||||
// TODO: this operation can be cleaned up, but this is only an identity test
|
||||
int[] primaryKeyColumnIds = null;
|
||||
final Triple<Field, int[], String[]>[] columnTriples = columns.items;
|
||||
for (int i = 0, s = columnTriples.length; i < s; i++) {
|
||||
if (columnTriples[i].getLeft() == primaryKey) {
|
||||
primaryKeyColumnIds = columnTriples[i].getMiddle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int nonzeroIndex = -1;
|
||||
if (!indexed) {
|
||||
for (int i = 0, s = primaryKeyColumnIds.length; i < s; i++) {
|
||||
if (primaryKeyColumnIds[i] >= 0) {
|
||||
nonzeroIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nonzeroIndex == -1) {
|
||||
throw new ParseException(primaryKey,
|
||||
"primary key %s does not have any columns associated with it",
|
||||
primaryKey);
|
||||
}
|
||||
}
|
||||
|
||||
final int primaryKeyColumnId = indexed ? -1 : primaryKeyColumnIds[nonzeroIndex];
|
||||
final Class primaryKeyType = indexed ? null : primaryKey.getType();
|
||||
for (int i = excel.offset(); parser.cacheLine() != -1; i++) {
|
||||
E entry = excel.newEntry();
|
||||
String name = indexed ? null : parser.parseString(primaryKeyColumnId, "");
|
||||
try {
|
||||
MDC.put("entry", indexed || StringUtils.isBlank(name) ? "" + i : name);
|
||||
parseColumns(excel, entry, name, columns, parser);
|
||||
} finally {
|
||||
MDC.remove("entry");
|
||||
}
|
||||
putIndex(primaryKey, primaryKeyType, i++, indexed, excel, entry);
|
||||
}
|
||||
|
||||
return excel;
|
||||
}
|
||||
|
||||
static void
|
||||
catchParseException(
|
||||
Throwable t,
|
||||
Field field,
|
||||
Class type,
|
||||
String key,
|
||||
String columnName,
|
||||
CharSequence token
|
||||
) {
|
||||
ParseException parseException = new ParseException(t, field,
|
||||
"error parsing field %s row: '%s' column: '%s': '%s' as %s",
|
||||
field, key, columnName, token.toString(),
|
||||
type.isArray() ? type.getComponentType().getCanonicalName() : type.getCanonicalName());
|
||||
log.warn(parseException.getMessage(), parseException);
|
||||
}
|
||||
|
||||
static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
void parseColumns(
|
||||
T excel,
|
||||
E entry,
|
||||
String key,
|
||||
Array<Triple<Field, int[], String[]>> columns,
|
||||
TxtParser parser
|
||||
)
|
||||
throws IllegalAccessException, ParseException
|
||||
{
|
||||
for (Triple<Field, int[], String[]> column : columns) {
|
||||
final Field field = column.getLeft();
|
||||
final int[] columnIds = column.getMiddle();
|
||||
final int numColumns = columnIds.length;
|
||||
final String[] columnNames = column.getRight();
|
||||
final Class type = field.getType();
|
||||
try {
|
||||
if (type == String.class) {
|
||||
try {
|
||||
field.set(entry, parser.parseString(columnIds[0], ""));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == String[].class) {
|
||||
final String[] value = new String[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseString(columnIds[i], "");
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == byte.class) {
|
||||
try {
|
||||
field.setByte(entry, parser.parseByte(columnIds[0], (byte) 0));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == byte[].class) {
|
||||
final byte[] value = new byte[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseByte(columnIds[i], (byte) 0);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == short.class) {
|
||||
try {
|
||||
field.setShort(entry, parser.parseShort(columnIds[0], (short) 0));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == short[].class) {
|
||||
final short[] value = new short[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseShort(columnIds[i], (short) 0);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == int.class) {
|
||||
try {
|
||||
field.setInt(entry, parser.parseInt(columnIds[0], 0));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == int[].class) {
|
||||
final int[] value = new int[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseInt(columnIds[i], 0);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == long.class) {
|
||||
try {
|
||||
field.setLong(entry, parser.parseLong(columnIds[0], 0L));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == long[].class) {
|
||||
final long[] value = new long[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseLong(columnIds[i], 0L);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == boolean.class) {
|
||||
try {
|
||||
field.setBoolean(entry, parser.parseBoolean(columnIds[0], false));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == boolean[].class) {
|
||||
final boolean[] value = new boolean[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseBoolean(columnIds[i], false);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == float.class) {
|
||||
try {
|
||||
field.setFloat(entry, parser.parseFloat(columnIds[0], 0f));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == float[].class) {
|
||||
final float[] value = new float[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseFloat(columnIds[i], 0f);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else if (type == double.class) {
|
||||
try {
|
||||
field.setDouble(entry, parser.parseDouble(columnIds[0], 0d));
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[0], parser.token(columnIds[0]));
|
||||
}
|
||||
} else if (type == double[].class) {
|
||||
final double[] value = new double[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
try {
|
||||
value[i] = parser.parseDouble(columnIds[i], 0d);
|
||||
} catch (Throwable t) {
|
||||
catchParseException(t, field, type, key, columnNames[i], parser.token(columnIds[i]));
|
||||
}
|
||||
}
|
||||
field.set(entry, value);
|
||||
}
|
||||
|
||||
else {
|
||||
throw new ParseException(field, "Cannot parse fields of type %s",
|
||||
org.apache.commons.lang3.ClassUtils.getCanonicalName(type));
|
||||
}
|
||||
} catch (ColumnFormat t) {
|
||||
ParseException parseException = new ParseException(field,
|
||||
"error parsing field %s row: '%s' column: '%s': '%s' as %s",
|
||||
field, key, columnNames[t.columnIndex()], t.columnText(),
|
||||
type.isArray() ? type.getComponentType().getCanonicalName() : type.getCanonicalName());
|
||||
parseException.initCause(t);
|
||||
throw parseException;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified field using it's column definition annotation to
|
||||
* generate a list of column names and indexes associated with them. These
|
||||
* indexes are then stored as a mapping from field to associated column
|
||||
* indexes which can be used to retrieve data from the backing excel.
|
||||
*/
|
||||
static void populateColumnIndexes(
|
||||
final Array<Triple<Field, int[], String[]>> columns,
|
||||
final TxtParser parser,
|
||||
final Entry.Column column,
|
||||
final Field field,
|
||||
final String[] TMP
|
||||
) throws ParseException {
|
||||
final String format = column.format();
|
||||
final String[] values = column.values();
|
||||
final int startIndex = column.startIndex();
|
||||
final int endIndex = column.endIndex();
|
||||
final int columnIndex = column.columnIndex();
|
||||
if (columnIndex >= 0) {
|
||||
final int[] columnIndexes = new int[] { columnIndex };
|
||||
final String[] columnNames = new String[] { null };
|
||||
columns.add(Triple.of(field, columnIndexes, columnNames));
|
||||
log.trace("pushing column <{}>->{}", field, columnIndexes);
|
||||
} else if (format.isEmpty()) {
|
||||
final String fieldName = field.getName();
|
||||
if (values.length > 0) {
|
||||
// values[] used as literal column names
|
||||
checkArrayColumns(field, values.length);
|
||||
String[] columnNames = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
columnNames[i] = values[i];
|
||||
}
|
||||
|
||||
putColumns(columns, parser, field, columnNames);
|
||||
} else if (startIndex == 0 && endIndex == 0) {
|
||||
// field name used as literal column name
|
||||
TMP[0] = fieldName;
|
||||
putColumns(columns, parser, field, TMP);
|
||||
} else {
|
||||
// field name + indexes used as column names
|
||||
checkArrayColumns(field, endIndex - startIndex);
|
||||
String[] columnNames = new String[endIndex - startIndex];
|
||||
for (int i = startIndex, j = 0; i < endIndex; i++, j++) {
|
||||
columnNames[j] = fieldName + i;
|
||||
}
|
||||
|
||||
putColumns(columns, parser, field, columnNames);
|
||||
}
|
||||
} else {
|
||||
if (startIndex == 0 && endIndex == 0) {
|
||||
// format used as literal column name
|
||||
TMP[0] = format;
|
||||
putColumns(columns, parser, field, TMP);
|
||||
} else {
|
||||
checkArrayColumns(field, endIndex - startIndex);
|
||||
String[] columnNames = new String[endIndex - startIndex];
|
||||
if (values.length == 0) {
|
||||
// format used in conjunction with indexes as column names
|
||||
// format must contain %d within it, replaced with indexes
|
||||
for (int i = startIndex, j = 0; i < endIndex; i++, j++) {
|
||||
columnNames[j] = String.format(format, i);
|
||||
}
|
||||
} else {
|
||||
// format used in conjunction with values as column names
|
||||
// format must contain as many values as indexes
|
||||
for (int i = 0, s = values.length; i < s; i++) {
|
||||
columnNames[i] = String.format(format, values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
putColumns(columns, parser, field, columnNames);
|
||||
}
|
||||
}
|
||||
|
||||
if (log.debugEnabled()) {
|
||||
StringBuilder builder = new StringBuilder(256);
|
||||
builder.append('{');
|
||||
for (Triple<Field, int[], String[]> pair : columns) {
|
||||
builder
|
||||
.append(pair.getLeft().getName())
|
||||
.append('=')
|
||||
.append(Arrays.toString(pair.getMiddle()))
|
||||
.append(", ");
|
||||
}
|
||||
if (columns.size > 0) builder.setLength(builder.length() - 2);
|
||||
builder.append('}');
|
||||
log.debug("columns: {}", builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
static void checkArrayColumns(Field field, int length) throws ParseException {
|
||||
if (!field.getType().isArray() && length > 1) {
|
||||
throw new ParseException(field, ""
|
||||
+ "field %s corresponds to multiple columns. "
|
||||
+ "is it supposed to be an array type?", field);
|
||||
}
|
||||
}
|
||||
|
||||
static int putColumns(
|
||||
Array<Triple<Field, int[], String[]>> columns,
|
||||
TxtParser parser,
|
||||
Field field,
|
||||
String[] columnNames
|
||||
) {
|
||||
final int index = columns.size;
|
||||
final int[] columnIndexes = parser.columnId(columnNames);
|
||||
columns.add(Triple.of(field, columnIndexes, columnNames));
|
||||
log.trace("pushing columns {}->{}", columnNames, columnIndexes);
|
||||
if (log.warnEnabled()) {
|
||||
for (int i = 0, s = columnIndexes.length; i < s; i++) {
|
||||
if (columnIndexes[i] == -1) {
|
||||
log.warn("Unable to parse column named '{}'", columnNames[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static <E extends Entry, S extends Serializer<E>, T extends Excel<E, S>>
|
||||
T loadBin(T excel, FileHandle handle) {
|
||||
throw null;
|
||||
}
|
||||
|
||||
static <E extends Entry, T extends Excel<E, ?>>
|
||||
void putIndex(
|
||||
Field primaryKey,
|
||||
Class primaryKeyType,
|
||||
int i,
|
||||
boolean indexed,
|
||||
T excel,
|
||||
E entry
|
||||
) throws IllegalAccessException {
|
||||
if (indexed) {
|
||||
excel._put(i, entry);
|
||||
} else if (primaryKeyType == int.class) {
|
||||
int id = primaryKey.getInt(entry);
|
||||
excel._put(id, entry);
|
||||
} else if (primaryKeyType == String.class) {
|
||||
String id = (String) primaryKey.get(entry);
|
||||
excel._put(i, entry);
|
||||
|
||||
if (excel.stringToIndex == EMPTY_OBJECT_INT_MAP) excel.stringToIndex = new ObjectIntMap<>();
|
||||
if (!excel.stringToIndex.containsKey(id)) excel.stringToIndex.put(id, i);
|
||||
}
|
||||
}
|
||||
|
||||
protected final Class<E> entryClass;
|
||||
protected ObjectIntMap<String> stringToIndex;
|
||||
protected IntMap<E> entries;
|
||||
protected Array<Entry> ordered;
|
||||
|
||||
protected Excel(Class<E> entryClass) {
|
||||
this(entryClass, 53);
|
||||
}
|
||||
|
||||
protected Excel(Class<E> entryClass, int initialCapacity) {
|
||||
this(entryClass, initialCapacity, 0.8f);
|
||||
}
|
||||
|
||||
protected Excel(Class<E> entryClass, int initialCapacity, float loadFactor) {
|
||||
this.entryClass = entryClass;
|
||||
this.stringToIndex = emptyMap();
|
||||
this.entries = new IntMap<>(initialCapacity, loadFactor);
|
||||
this.ordered = new Array<>(true, (int) (initialCapacity * loadFactor), Entry.class);
|
||||
}
|
||||
|
||||
public Class<? extends Excel> excelClass() {
|
||||
return getClass();
|
||||
}
|
||||
|
||||
public Class<E> entryClass() {
|
||||
return entryClass;
|
||||
}
|
||||
|
||||
final void _put(int id, E value) {
|
||||
entries.put(id, value);
|
||||
put(id, value);
|
||||
}
|
||||
|
||||
protected void put(int id, E value) {}
|
||||
|
||||
protected int offset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected void init() {}
|
||||
|
||||
public E get(String id) {
|
||||
return get(index(id));
|
||||
}
|
||||
|
||||
public E get(int id) {
|
||||
return entries.get(id);
|
||||
}
|
||||
|
||||
public int index(String id) {
|
||||
return stringToIndex.get(id, -1);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return entries.size;
|
||||
}
|
||||
|
||||
public abstract E newEntry();
|
||||
|
||||
public abstract S newSerializer();
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return entries.values().iterator();
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package com.riiablo.excel2;
|
||||
|
||||
import com.riiablo.io.ByteInput;
|
||||
import com.riiablo.io.ByteOutput;
|
||||
|
||||
public interface Serializer<T extends Excel.Entry> {
|
||||
void readBin(T entry, ByteInput in);
|
||||
void writeBin(T entry, ByteOutput out);
|
||||
boolean equals(T e1, T e2);
|
||||
void logErrors(T e1, T e2);
|
||||
}
|
@ -1,278 +0,0 @@
|
||||
package com.riiablo.excel2;
|
||||
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.ByteArray;
|
||||
import com.badlogic.gdx.utils.IntArray;
|
||||
import com.badlogic.gdx.utils.ObjectIntMap;
|
||||
|
||||
import com.riiablo.logger.LogManager;
|
||||
import com.riiablo.logger.Logger;
|
||||
|
||||
public class TxtParser {
|
||||
private static final Logger log = LogManager.getLogger(TxtParser.class);
|
||||
|
||||
private static final int HT = '\t';
|
||||
private static final int CR = '\r';
|
||||
private static final int LF = '\n';
|
||||
|
||||
private static final byte[] TO_UPPER;
|
||||
static {
|
||||
TO_UPPER = new byte[1 << Byte.SIZE];
|
||||
for (int i = 0; i < TO_UPPER.length; i++) {
|
||||
TO_UPPER[i] = (byte) i;
|
||||
}
|
||||
|
||||
for (int i = 'a'; i <= 'z'; i++) {
|
||||
TO_UPPER[i] &= ~0x20;
|
||||
}
|
||||
}
|
||||
|
||||
private static final AsciiString EXPANSION = AsciiString.cached("EXPANSION");
|
||||
|
||||
public static TxtParser parse(InputStream in) throws IOException {
|
||||
return parse(in, 8192);
|
||||
}
|
||||
|
||||
public static TxtParser parse(InputStream in, int bufferSize) throws IOException {
|
||||
return new TxtParser(in, bufferSize);
|
||||
}
|
||||
|
||||
final int numColumns;
|
||||
final Array<String> columnNames;
|
||||
final ObjectIntMap<String> columnIds;
|
||||
|
||||
BufferedInputStream in;
|
||||
AsciiString line;
|
||||
int index;
|
||||
final ByteArray cache;
|
||||
final IntArray tokenOffsets;
|
||||
int[] tokenOffsetsCache;
|
||||
int numTokens;
|
||||
|
||||
TxtParser(InputStream in, int bufferSize) throws IOException {
|
||||
this.in = IOUtils.buffer(in, bufferSize);
|
||||
|
||||
cache = new ByteArray(512);
|
||||
columnNames = new Array<>();
|
||||
columnIds = new ObjectIntMap<>();
|
||||
numColumns = parseColumnNames();
|
||||
|
||||
log.info("numColumns: {}", numColumns);
|
||||
log.debug("columnNames: {}", columnNames);
|
||||
log.trace("columnIds: {}", columnIds);
|
||||
|
||||
tokenOffsets = new IntArray();
|
||||
}
|
||||
|
||||
private static String toString(ByteArray array) {
|
||||
if (array.size == 0) return "";
|
||||
String stringValue = new String(array.items, 0, array.size, CharsetUtil.US_ASCII);
|
||||
array.clear();
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
private int parseColumnNames() throws IOException {
|
||||
for (int i; (i = in.read()) != -1;) {
|
||||
switch (i) {
|
||||
case HT:
|
||||
putColumnName(toString(cache));
|
||||
break;
|
||||
case CR:
|
||||
in.skip(1);
|
||||
case LF:
|
||||
putColumnName(toString(cache));
|
||||
return columnNames.size;
|
||||
default:
|
||||
cache.add(TO_UPPER[i]);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IOException("Unexpected end of file while parsing column names");
|
||||
}
|
||||
|
||||
private void putColumnName(String columnName) {
|
||||
if (!columnIds.containsKey(columnName)) {
|
||||
columnIds.put(columnName, columnNames.size);
|
||||
}
|
||||
|
||||
columnNames.add(columnName);
|
||||
}
|
||||
|
||||
public int cacheLine() throws IOException {
|
||||
cache.clear();
|
||||
tokenOffsets.clear();
|
||||
tokenOffsets.add(0);
|
||||
index++;
|
||||
lineBuilder:
|
||||
for (;;) {
|
||||
final int i = in.read();
|
||||
switch (i) {
|
||||
case -1:
|
||||
return -1;
|
||||
case HT:
|
||||
tokenOffsets.add(cache.size);
|
||||
break;
|
||||
case CR:
|
||||
in.skip(1);
|
||||
case LF:
|
||||
tokenOffsets.add(cache.size);
|
||||
break lineBuilder;
|
||||
default:
|
||||
cache.add((byte) i);
|
||||
}
|
||||
}
|
||||
|
||||
numTokens = tokenOffsets.size - 1;
|
||||
tokenOffsetsCache = tokenOffsets.items;
|
||||
line = new AsciiString(cache.items, 0, cache.size, false);
|
||||
log.debug("line: {}", line);
|
||||
if (line.contentEqualsIgnoreCase(EXPANSION)) {
|
||||
log.trace("skipping row {}: {} is an ignored symbol", index, EXPANSION);
|
||||
return cacheLine();
|
||||
}
|
||||
|
||||
if (numTokens != numColumns) {
|
||||
log.warn("skipping row {}: contains {} tokens, expected {}; tokens: {}",
|
||||
index, numTokens, numColumns, tokens());
|
||||
return cacheLine();
|
||||
}
|
||||
|
||||
if (log.traceEnabled()) {
|
||||
final int[] tokenOffsets = this.tokenOffsets.items;
|
||||
for (int i = 1, j = tokenOffsets[i - 1], s = this.tokenOffsets.size; i < s; i++) {
|
||||
final int tokenOffset = tokenOffsets[i];
|
||||
log.trace("{}={}", columnName(i - 1), line.subSequence(j, tokenOffset, false));
|
||||
j = tokenOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return tokenOffsets.size - 1;
|
||||
}
|
||||
|
||||
public int numColumns() {
|
||||
return numColumns;
|
||||
}
|
||||
|
||||
public String[] columnNames() {
|
||||
final String[] columnNames = new String[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) columnNames[i] = columnName(i);
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
public String columnName(int i) {
|
||||
return columnNames.get(i);
|
||||
}
|
||||
|
||||
public String rowName() {
|
||||
return parseString(0, "");
|
||||
}
|
||||
|
||||
public int columnId(String columnName) {
|
||||
return columnIds.get(columnName.toUpperCase(), -1);
|
||||
}
|
||||
|
||||
public int[] columnId(String[] columnNames) {
|
||||
final int numColumns = columnNames.length;
|
||||
final int[] columnIds = new int[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) columnIds[i] = columnId(columnNames[i]);
|
||||
return columnIds;
|
||||
}
|
||||
|
||||
public int numTokens() {
|
||||
return numTokens;
|
||||
}
|
||||
|
||||
public AsciiString[] tokens() {
|
||||
final int numTokens = numTokens();
|
||||
final AsciiString[] tokens = new AsciiString[numTokens];
|
||||
for (int i = 0; i < numTokens; i++) tokens[i] = token(i);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
public AsciiString token(int i) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
return line.subSequence(tokenOffsets[i], tokenOffsets[i + 1]);
|
||||
}
|
||||
|
||||
public byte parseByte(int i, byte defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
final int intValue = line.parseInt(startOffset, endOffset);
|
||||
final byte result = (byte) intValue;
|
||||
if (result != intValue) {
|
||||
throw new NumberFormatException(line.subSequence(startOffset, endOffset, false).toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public short parseShort(int i, short defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.parseShort(startOffset, endOffset);
|
||||
}
|
||||
|
||||
public int parseInt(int i, int defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.parseInt(startOffset, endOffset);
|
||||
}
|
||||
|
||||
public long parseLong(int i, long defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.parseLong(startOffset, endOffset);
|
||||
}
|
||||
|
||||
public boolean parseBoolean(int i, boolean defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
final int intValue = line.parseInt(startOffset, endOffset);
|
||||
if ((intValue & 1) != intValue) {
|
||||
log.warn("boolean exceeds boolean radix at {}:{} (\"{}\", \"{}\"): {}",
|
||||
index, i, rowName(), columnName(i), intValue);
|
||||
}
|
||||
|
||||
return intValue != 0;
|
||||
}
|
||||
|
||||
public float parseFloat(int i, float defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.parseFloat(startOffset, endOffset);
|
||||
}
|
||||
|
||||
public double parseDouble(int i, double defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.parseDouble(startOffset, endOffset);
|
||||
}
|
||||
|
||||
public String parseString(int i, String defaultValue) {
|
||||
final int[] tokenOffsets = tokenOffsetsCache;
|
||||
final int startOffset = tokenOffsets[i];
|
||||
final int endOffset = tokenOffsets[i + 1];
|
||||
if (startOffset >= endOffset) return defaultValue;
|
||||
return line.toString(tokenOffsets[i], tokenOffsets[i + 1]);
|
||||
}
|
||||
}
|
@ -1,248 +0,0 @@
|
||||
package com.riiablo.excel2.txt;
|
||||
|
||||
import com.riiablo.excel2.Entry;
|
||||
import com.riiablo.excel2.Excel;
|
||||
import com.riiablo.excel2.PrimaryKey;
|
||||
import com.riiablo.excel2.SerializedWith;
|
||||
import com.riiablo.excel2.serializer.MonStatsSerializer;
|
||||
import com.riiablo.io.ByteInput;
|
||||
import com.riiablo.io.ByteOutput;
|
||||
|
||||
@Entry(MonStats.Entry.class)
|
||||
@SerializedWith(MonStatsSerializer.class)
|
||||
public class MonStats extends Excel<MonStats.Entry, MonStatsSerializer> {
|
||||
public MonStats() {
|
||||
super(Entry.class, 1543); // 736 entries
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry newEntry() {
|
||||
return new Entry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonStatsSerializer newSerializer() {
|
||||
return new MonStatsSerializer();
|
||||
}
|
||||
|
||||
public static class Entry extends Excel.Entry {
|
||||
@Override
|
||||
public String toString() {
|
||||
return NameStr;
|
||||
}
|
||||
|
||||
@PrimaryKey
|
||||
@Column public String Id;
|
||||
@Column public int hcIdx;
|
||||
@Column public String BaseId;
|
||||
@Column public String NextInClass;
|
||||
@Column public int TransLvl;
|
||||
@Column public String NameStr;
|
||||
@Column public String MonStatsEx;
|
||||
@Column public String MonProp;
|
||||
@Column public String MonType;
|
||||
@Column public String AI;
|
||||
@Column public String DescStr;
|
||||
@Column public String Code;
|
||||
@Column public boolean enabled;
|
||||
@Column public boolean rangedtype;
|
||||
@Column public boolean placespawn;
|
||||
@Column public String spawn;
|
||||
@Column public int spawnx;
|
||||
@Column public int spawny;
|
||||
@Column public String spawnmode;
|
||||
@Column public String minion1;
|
||||
@Column public String minion2;
|
||||
@Column public boolean SetBoss;
|
||||
@Column public boolean BossXfer;
|
||||
@Column public int PartyMin;
|
||||
@Column public int PartyMax;
|
||||
@Column public int MinGrp;
|
||||
@Column public int MaxGrp;
|
||||
@Column public int sparsePopulate;
|
||||
@Column public int Velocity;
|
||||
@Column public int Run;
|
||||
@Column public int Rarity;
|
||||
@Column(format = "Level%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int Level[];
|
||||
@Column public String MonSound;
|
||||
@Column public String UMonSound;
|
||||
@Column public int threat;
|
||||
@Column(format = "aidel%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int aidel[];
|
||||
@Column(format = "aidist%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int aidist[];
|
||||
@Column(format = "aip1%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int aip1[];
|
||||
@Column(format = "aip2%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int aip2[];
|
||||
@Column(format = "aip3%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int aip3[];
|
||||
@Column(format = "aip4%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int aip4[];
|
||||
@Column(format = "aip5%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int aip5[];
|
||||
@Column(format = "aip6%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int aip6[];
|
||||
@Column(format = "aip7%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int aip7[];
|
||||
@Column(format = "aip8%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int aip8[];
|
||||
@Column public String MissA1;
|
||||
@Column public String MissA2;
|
||||
@Column public String MissS1;
|
||||
@Column public String MissS2;
|
||||
@Column public String MissS3;
|
||||
@Column public String MissS4;
|
||||
@Column public String MissC;
|
||||
@Column public String MissSQ;
|
||||
@Column public int Align;
|
||||
@Column public boolean isSpawn;
|
||||
@Column public boolean isMelee;
|
||||
@Column public boolean npc;
|
||||
@Column public boolean interact;
|
||||
@Column public boolean inventory;
|
||||
@Column public boolean inTown;
|
||||
@Column public boolean lUndead;
|
||||
@Column public boolean hUndead;
|
||||
@Column public boolean demon;
|
||||
@Column public boolean flying;
|
||||
@Column public boolean opendoors;
|
||||
@Column public boolean boss;
|
||||
@Column public boolean primeevil;
|
||||
@Column public boolean killable;
|
||||
@Column public boolean switchai;
|
||||
@Column public boolean noAura;
|
||||
@Column public boolean nomultishot;
|
||||
@Column public boolean neverCount;
|
||||
@Column public boolean petIgnore;
|
||||
@Column public boolean deathDmg;
|
||||
@Column public boolean genericSpawn;
|
||||
@Column public boolean zoo;
|
||||
@Column public int SendSkills;
|
||||
@Column public String Skill1;
|
||||
@Column public String Sk1mode;
|
||||
@Column public int Sk1lvl;
|
||||
@Column public String Skill2;
|
||||
@Column public String Sk2mode;
|
||||
@Column public int Sk2lvl;
|
||||
@Column public String Skill3;
|
||||
@Column public String Sk3mode;
|
||||
@Column public int Sk3lvl;
|
||||
@Column public String Skill4;
|
||||
@Column public String Sk4mode;
|
||||
@Column public int Sk4lvl;
|
||||
@Column public String Skill5;
|
||||
@Column public String Sk5mode;
|
||||
@Column public int Sk5lvl;
|
||||
@Column public String Skill6;
|
||||
@Column public String Sk6mode;
|
||||
@Column public int Sk6lvl;
|
||||
@Column public String Skill7;
|
||||
@Column public String Sk7mode;
|
||||
@Column public int Sk7lvl;
|
||||
@Column public String Skill8;
|
||||
@Column public String Sk8mode;
|
||||
@Column public int Sk8lvl;
|
||||
@Column(format = "Drain%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int Drain[];
|
||||
@Column(format = "coldeffect%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int coldeffect[];
|
||||
@Column(format = "ResDm%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int ResDm[];
|
||||
@Column(format = "ResMa%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int ResMa[];
|
||||
@Column(format = "ResFi%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int ResFi[];
|
||||
@Column(format = "ResLi%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int ResLi[];
|
||||
@Column(format = "ResCo%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int ResCo[];
|
||||
@Column(format = "ResPo%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int ResPo[];
|
||||
@Column public int DamageRegen;
|
||||
@Column public String SkillDamage;
|
||||
@Column public boolean noRatio;
|
||||
@Column public boolean NoShldBlock;
|
||||
@Column(format = "ToBlock%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int ToBlock[];
|
||||
@Column public int Crit;
|
||||
@Column(format = "minHP%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int minHP[];
|
||||
@Column(format = "maxHP%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int maxHP[];
|
||||
@Column(format = "AC%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int AC[];
|
||||
@Column(format = "Exp%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int Exp[];
|
||||
@Column(format = "A1MinD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int A1MinD[];
|
||||
@Column(format = "A1MaxD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int A1MaxD[];
|
||||
@Column(format = "A1TH%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int A1TH[];
|
||||
@Column(format = "A2MinD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int A2MinD[];
|
||||
@Column(format = "A2MaxD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int A2MaxD[];
|
||||
@Column(format = "A2TH%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int A2TH[];
|
||||
@Column(format = "S1MinD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int S1MinD[];
|
||||
@Column(format = "S1MaxD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int S1MaxD[];
|
||||
@Column(format = "S1TH%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int S1TH[];
|
||||
@Column public String El1Mode;
|
||||
@Column public String El1Type;
|
||||
@Column(format = "El1Pct%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El1Pct[];
|
||||
@Column(format = "El1MinD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El1MinD[];
|
||||
@Column(format = "El1MaxD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El1MaxD[];
|
||||
@Column(format = "El1Dur%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El1Dur[];
|
||||
@Column public String El2Mode;
|
||||
@Column public String El2Type;
|
||||
@Column(format = "El2Pct%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El2Pct[];
|
||||
@Column(format = "El2MinD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El2MinD[];
|
||||
@Column(format = "El2MaxD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El2MaxD[];
|
||||
@Column(format = "El2Dur%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El2Dur[];
|
||||
@Column public String El3Mode;
|
||||
@Column public String El3Type;
|
||||
@Column(format = "El3Pct%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El3Pct[];
|
||||
@Column(format = "El3MinD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El3MinD[];
|
||||
@Column(format = "El3MaxD%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El3MaxD[];
|
||||
@Column(format = "El3Dur%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public int El3Dur[];
|
||||
@Column(format = "TreasureClass1%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public String TreasureClass1[];
|
||||
@Column(format = "TreasureClass2%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public String TreasureClass2[];
|
||||
@Column(format = "TreasureClass3%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public String TreasureClass3[];
|
||||
@Column(format = "TreasureClass4%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
public String TreasureClass4[];
|
||||
@Column public int TCQuestId;
|
||||
@Column public int TCQuestCP;
|
||||
@Column public int SplEndDeath;
|
||||
@Column public boolean SplGetModeChart;
|
||||
@Column public boolean SplEndGeneric;
|
||||
@Column public boolean SplClientEnd;
|
||||
}
|
||||
|
||||
public static class Serializer implements com.riiablo.excel2.Serializer<Entry> {
|
||||
@Override public void readBin(Entry entry, ByteInput in) {}
|
||||
@Override public void writeBin(Entry entry, ByteOutput out) {}
|
||||
@Override public boolean equals(Entry e1, Entry e2) { throw new UnsupportedOperationException(); }
|
||||
@Override public void logErrors(Entry e1, Entry e2) {}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.junit.BeforeClass;
|
||||
@ -8,14 +8,14 @@ import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
|
||||
import com.riiablo.RiiabloTest;
|
||||
import com.riiablo.excel2.txt.MonStats;
|
||||
import com.riiablo.excel.txt.MonStats;
|
||||
import com.riiablo.logger.Level;
|
||||
import com.riiablo.logger.LogManager;
|
||||
|
||||
public class BinGeneratorTest extends RiiabloTest {
|
||||
@BeforeClass
|
||||
public static void before() {
|
||||
LogManager.setLevel("com.riiablo.excel2.BinGenerator", Level.TRACE);
|
||||
LogManager.setLevel("com.riiablo.excel.BinGenerator", Level.TRACE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -26,6 +26,10 @@ public class BinGeneratorTest extends RiiabloTest {
|
||||
BinGenerator generator = new BinGenerator();
|
||||
generator.binDir = Gdx.files.absolute(
|
||||
"C:\\Users\\csmith\\projects\\libgdx\\riiablo\\assets");
|
||||
generator.generateBin(monstats);
|
||||
|
||||
FileHandle excelDir = generator.binDir.child("data/global/excel2");
|
||||
excelDir.mkdirs();
|
||||
|
||||
generator.generateBin(excelDir, monstats);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.junit.Test;
|
||||
@ -7,14 +7,14 @@ import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
|
||||
import com.riiablo.RiiabloTest;
|
||||
import com.riiablo.excel2.txt.MonStats;
|
||||
import com.riiablo.excel.txt.MonStats;
|
||||
import com.riiablo.logger.Level;
|
||||
import com.riiablo.logger.LogManager;
|
||||
|
||||
public class ExcelTest extends RiiabloTest {
|
||||
@org.junit.BeforeClass
|
||||
public static void before() {
|
||||
LogManager.setLevel("com.riiablo.excel2.Excel", Level.TRACE);
|
||||
LogManager.setLevel("com.riiablo.excel.Excel", Level.TRACE);
|
||||
}
|
||||
|
||||
@Test
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
@ -12,7 +12,7 @@ import com.riiablo.logger.LogManager;
|
||||
public class SerializerGeneratorTest extends RiiabloTest {
|
||||
@BeforeClass
|
||||
public static void before() {
|
||||
LogManager.setLevel("com.riiablo.excel2.SerializerGenerator", Level.TRACE);
|
||||
LogManager.setLevel("com.riiablo.excel.SerializerGenerator", Level.TRACE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -21,12 +21,12 @@ public class SerializerGeneratorTest extends RiiabloTest {
|
||||
generator.init();
|
||||
generator.sourceDir = Gdx.files.absolute(
|
||||
"C:\\Users\\csmith\\projects\\libgdx\\riiablo"
|
||||
+ "\\core\\src\\main\\java\\com\\riiablo\\excel2\\txt");
|
||||
+ "\\core\\src\\main\\java\\com\\riiablo\\excel\\txt");
|
||||
generator.serializerDir = Gdx.files.absolute(
|
||||
"C:\\Users\\csmith\\projects\\libgdx\\riiablo"
|
||||
+ "\\core\\src\\main\\java");
|
||||
generator.sourcePackage = "com.riiablo.excel2.txt";
|
||||
generator.serializerPackage = "com.riiablo.excel2.serializer";
|
||||
generator.sourcePackage = "com.riiablo.excel.txt";
|
||||
generator.serializerPackage = "com.riiablo.excel.serializer";
|
||||
generator.generateSerializers();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.excel2;
|
||||
package com.riiablo.excel;
|
||||
|
||||
import com.squareup.javapoet.JavaFile;
|
||||
import java.io.IOException;
|
||||
@ -6,7 +6,7 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
import com.riiablo.excel2.txt.MonStats;
|
||||
import com.riiablo.excel.txt.MonStats;
|
||||
|
||||
public class SerializerSourceGeneratorTest {
|
||||
@Rule
|
@ -1,7 +1,7 @@
|
||||
package com.riiablo.excel;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
@ -12,15 +12,41 @@ import com.riiablo.logger.Level;
|
||||
import com.riiablo.logger.LogManager;
|
||||
|
||||
public class TxtParserTest extends RiiabloTest {
|
||||
@BeforeClass
|
||||
@org.junit.BeforeClass
|
||||
public static void before() {
|
||||
LogManager.setLevel("com.riiablo.excel.TxtParser", Level.TRACE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_monstats_header() throws IOException {
|
||||
FileHandle monstats = Gdx.files.internal("test/monstats.txt");
|
||||
TxtParser parser = TxtParser.parse(monstats);
|
||||
String[] columnNames = parser.getColumnNames();
|
||||
public void parse_monstats_columns() throws IOException {
|
||||
FileHandle handle = Gdx.files.internal("test/monstats.txt");
|
||||
TxtParser.parse(handle.read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_monstats_first() throws IOException {
|
||||
FileHandle handle = Gdx.files.internal("test/monstats.txt");
|
||||
TxtParser parser = TxtParser.parse(handle.read());
|
||||
parser.cacheLine();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_monstats_first_2() throws IOException {
|
||||
LogManager.setLevel("com.riiablo.excel", Level.DEBUG);
|
||||
FileHandle handle = Gdx.files.internal("test/monstats.txt");
|
||||
TxtParser parser = TxtParser.parse(handle.read());
|
||||
parser.cacheLine();
|
||||
parser.cacheLine();
|
||||
LogManager.setLevel("com.riiablo.excel", Level.TRACE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_monstats_parseInt() throws IOException {
|
||||
LogManager.setLevel("com.riiablo.excel", Level.DEBUG);
|
||||
FileHandle handle = Gdx.files.internal("test/monstats.txt");
|
||||
TxtParser parser = TxtParser.parse(handle.read());
|
||||
parser.cacheLine();
|
||||
Assert.assertEquals(0, parser.parseInt(1, -1));
|
||||
LogManager.setLevel("com.riiablo.excel", Level.TRACE);
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +0,0 @@
|
||||
package com.riiablo.excel2;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
|
||||
import com.riiablo.RiiabloTest;
|
||||
import com.riiablo.logger.Level;
|
||||
import com.riiablo.logger.LogManager;
|
||||
|
||||
public class TxtParserTest extends RiiabloTest {
|
||||
@org.junit.BeforeClass
|
||||
public static void before() {
|
||||
LogManager.setLevel("com.riiablo.excel2.TxtParser", Level.TRACE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_monstats_columns() throws IOException {
|
||||
FileHandle handle = Gdx.files.internal("test/monstats.txt");
|
||||
TxtParser.parse(handle.read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_monstats_first() throws IOException {
|
||||
FileHandle handle = Gdx.files.internal("test/monstats.txt");
|
||||
TxtParser parser = TxtParser.parse(handle.read());
|
||||
parser.cacheLine();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_monstats_first_2() throws IOException {
|
||||
LogManager.setLevel("com.riiablo.excel2", Level.DEBUG);
|
||||
FileHandle handle = Gdx.files.internal("test/monstats.txt");
|
||||
TxtParser parser = TxtParser.parse(handle.read());
|
||||
parser.cacheLine();
|
||||
parser.cacheLine();
|
||||
LogManager.setLevel("com.riiablo.excel2", Level.TRACE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_monstats_parseInt() throws IOException {
|
||||
LogManager.setLevel("com.riiablo.excel2", Level.DEBUG);
|
||||
FileHandle handle = Gdx.files.internal("test/monstats.txt");
|
||||
TxtParser parser = TxtParser.parse(handle.read());
|
||||
parser.cacheLine();
|
||||
Assert.assertEquals(0, parser.parseInt(1, -1));
|
||||
LogManager.setLevel("com.riiablo.excel2", Level.TRACE);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user