diff --git a/core/src/main/java/com/riiablo/excel2/BinGenerator.java b/core/src/main/java/com/riiablo/excel/BinGenerator.java similarity index 69% rename from core/src/main/java/com/riiablo/excel2/BinGenerator.java rename to core/src/main/java/com/riiablo/excel/BinGenerator.java index b3c9d56a..7fcb5c3a 100644 --- a/core/src/main/java/com/riiablo/excel2/BinGenerator.java +++ b/core/src/main/java/com/riiablo/excel/BinGenerator.java @@ -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 , T extends Excel> - void generateBin(T excel) { - final Class 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 , T extends Excel> + void generateBin(FileHandle excelDir, T excel) { + final Class 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())); } } diff --git a/core/src/main/java/com/riiablo/excel2/ColumnFormat.java b/core/src/main/java/com/riiablo/excel/ColumnFormat.java similarity index 95% rename from core/src/main/java/com/riiablo/excel2/ColumnFormat.java rename to core/src/main/java/com/riiablo/excel/ColumnFormat.java index d2486969..53b3f062 100644 --- a/core/src/main/java/com/riiablo/excel2/ColumnFormat.java +++ b/core/src/main/java/com/riiablo/excel/ColumnFormat.java @@ -1,4 +1,4 @@ -package com.riiablo.excel2; +package com.riiablo.excel; public class ColumnFormat extends RuntimeException { final String columnText; diff --git a/core/src/main/java/com/riiablo/excel2/Entry.java b/core/src/main/java/com/riiablo/excel/Entry.java similarity index 93% rename from core/src/main/java/com/riiablo/excel2/Entry.java rename to core/src/main/java/com/riiablo/excel/Entry.java index c4b4bd06..da205b2c 100644 --- a/core/src/main/java/com/riiablo/excel2/Entry.java +++ b/core/src/main/java/com/riiablo/excel/Entry.java @@ -1,4 +1,4 @@ -package com.riiablo.excel2; +package com.riiablo.excel; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/core/src/main/java/com/riiablo/excel/Excel.java b/core/src/main/java/com/riiablo/excel/Excel.java index 2e1b5c10..3e1e30e8 100644 --- a/core/src/main/java/com/riiablo/excel/Excel.java +++ b/core/src/main/java/com/riiablo/excel/Excel.java @@ -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> implements Iterable { +/** + * Root class of an excel table. + */ +public abstract class Excel< + E extends Excel.Entry, + S extends Serializer +> + implements Iterable +{ 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 ObjectIntMap emptyMap() { + return (ObjectIntMap) 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 + *

+ *

Examples: + *

    + *
  • {@code "class"} + *
  • {@code "Transform Color"} + *
  • {@code "Level%s"} + *
  • {@code "Skill %d"} + */ + String format() default ""; + + /** + * Index values of format in the case of non-numerical indexes. + *

    + *

    Examples: + *

      + *
    • {@code {"", "(N)", "(H)"}} + *
    • {@code {"r", "g", "b"}} + *
    • {@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 ObjectIntMap emptyMap() { - return (ObjectIntMap) EMPTY_MAP; } public static , T extends Excel> @@ -84,83 +104,69 @@ public abstract class Excel> impl public static , T extends Excel> 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 , T extends Excel> + static , T extends Excel> 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 > - T loadTxt(T excel, TxtParser parser) throws IOException, IllegalAccessException { - final Class entryClass = excel.getEntryClass(); - final boolean index = ClassUtils.hasAnnotation(entryClass, Index.class); + static , T extends Excel> + T loadTxt(T excel, TxtParser parser) + throws IOException, ParseException, IllegalAccessException + { + final Class entryClass = excel.entryClass(); + final boolean indexed = ClassUtils.hasAnnotation(entryClass, Indexed.class); + final String[] TMP = new String[1]; Field primaryKey = null, firstKey = null; - ObjectMap columns = new ObjectMap<>(); - String[] TMP = new String[1]; + Array> 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> 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 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[] 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 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 , T extends Excel> - T loadBin(T excel, FileHandle handle) { - final Class 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 , T extends Excel> + void parseColumns( + T excel, + E entry, + String key, + Array> columns, + TxtParser parser + ) + throws IllegalAccessException, ParseException + { + for (Triple 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> 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 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> 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 > - void putIndex(Field primaryKey, Class primaryKeyType, int i, boolean indexed, T excel, E entry) throws IllegalAccessException { + static , T extends Excel> + T loadBin(T excel, FileHandle handle) { + throw null; + } + + static > + 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_TO_ID = emptyMap(); - protected IntMap entries = new IntMap<>(); - protected Class entryClass; + protected final Class entryClass; + protected ObjectIntMap stringToIndex; + protected IntMap entries; + protected Array ordered; - protected Excel(Class entryClass) { + protected Excel(Class entryClass) { + this(entryClass, 53); + } + + protected Excel(Class entryClass, int initialCapacity) { + this(entryClass, initialCapacity, 0.8f); + } + + protected Excel(Class 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 excelClass() { + return getClass(); } + public Class 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 getEntryClass() { - return entryClass; - } + public abstract E newEntry(); - public abstract T newEntry(); - - public abstract U newSerializer(); + public abstract S newSerializer(); @Override - public Iterator iterator() { + public Iterator iterator() { return entries.values().iterator(); } } diff --git a/core/src/main/java/com/riiablo/excel2/ForeignKey.java b/core/src/main/java/com/riiablo/excel/ForeignKey.java similarity index 93% rename from core/src/main/java/com/riiablo/excel2/ForeignKey.java rename to core/src/main/java/com/riiablo/excel/ForeignKey.java index d2082e7d..856e7d97 100644 --- a/core/src/main/java/com/riiablo/excel2/ForeignKey.java +++ b/core/src/main/java/com/riiablo/excel/ForeignKey.java @@ -1,4 +1,4 @@ -package com.riiablo.excel2; +package com.riiablo.excel; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/core/src/main/java/com/riiablo/excel2/Indexed.java b/core/src/main/java/com/riiablo/excel/Indexed.java similarity index 93% rename from core/src/main/java/com/riiablo/excel2/Indexed.java rename to core/src/main/java/com/riiablo/excel/Indexed.java index 89c232a4..3c8d32d0 100644 --- a/core/src/main/java/com/riiablo/excel2/Indexed.java +++ b/core/src/main/java/com/riiablo/excel/Indexed.java @@ -1,4 +1,4 @@ -package com.riiablo.excel2; +package com.riiablo.excel; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/core/src/main/java/com/riiablo/excel2/ParseException.java b/core/src/main/java/com/riiablo/excel/ParseException.java similarity index 99% rename from core/src/main/java/com/riiablo/excel2/ParseException.java rename to core/src/main/java/com/riiablo/excel/ParseException.java index b706e956..9b2945d1 100644 --- a/core/src/main/java/com/riiablo/excel2/ParseException.java +++ b/core/src/main/java/com/riiablo/excel/ParseException.java @@ -1,4 +1,4 @@ -package com.riiablo.excel2; +package com.riiablo.excel; import java.lang.reflect.Field; diff --git a/core/src/main/java/com/riiablo/excel2/PrimaryKey.java b/core/src/main/java/com/riiablo/excel/PrimaryKey.java similarity index 94% rename from core/src/main/java/com/riiablo/excel2/PrimaryKey.java rename to core/src/main/java/com/riiablo/excel/PrimaryKey.java index 059f07cc..ad16f470 100644 --- a/core/src/main/java/com/riiablo/excel2/PrimaryKey.java +++ b/core/src/main/java/com/riiablo/excel/PrimaryKey.java @@ -1,4 +1,4 @@ -package com.riiablo.excel2; +package com.riiablo.excel; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/core/src/main/java/com/riiablo/excel2/SerializedWith.java b/core/src/main/java/com/riiablo/excel/SerializedWith.java similarity index 91% rename from core/src/main/java/com/riiablo/excel2/SerializedWith.java rename to core/src/main/java/com/riiablo/excel/SerializedWith.java index 41791440..8bff8a73 100644 --- a/core/src/main/java/com/riiablo/excel2/SerializedWith.java +++ b/core/src/main/java/com/riiablo/excel/SerializedWith.java @@ -1,4 +1,4 @@ -package com.riiablo.excel2; +package com.riiablo.excel; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/core/src/main/java/com/riiablo/excel/Serializer.java b/core/src/main/java/com/riiablo/excel/Serializer.java index 532d855a..451896d9 100644 --- a/core/src/main/java/com/riiablo/excel/Serializer.java +++ b/core/src/main/java/com/riiablo/excel/Serializer.java @@ -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 { - 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); } diff --git a/core/src/main/java/com/riiablo/excel2/SerializerGenerator.java b/core/src/main/java/com/riiablo/excel/SerializerGenerator.java similarity index 95% rename from core/src/main/java/com/riiablo/excel2/SerializerGenerator.java rename to core/src/main/java/com/riiablo/excel/SerializerGenerator.java index a87a3495..756bf4e4 100644 --- a/core/src/main/java/com/riiablo/excel2/SerializerGenerator.java +++ b/core/src/main/java/com/riiablo/excel/SerializerGenerator.java @@ -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; diff --git a/core/src/main/java/com/riiablo/excel2/SerializerSourceGenerator.java b/core/src/main/java/com/riiablo/excel/SerializerSourceGenerator.java similarity index 98% rename from core/src/main/java/com/riiablo/excel2/SerializerSourceGenerator.java rename to core/src/main/java/com/riiablo/excel/SerializerSourceGenerator.java index addc8388..99fe05b7 100644 --- a/core/src/main/java/com/riiablo/excel2/SerializerSourceGenerator.java +++ b/core/src/main/java/com/riiablo/excel/SerializerSourceGenerator.java @@ -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 { diff --git a/core/src/main/java/com/riiablo/excel/TxtParser.java b/core/src/main/java/com/riiablo/excel/TxtParser.java index 4d54b0da..de3a3cc3 100644 --- a/core/src/main/java/com/riiablo/excel/TxtParser.java +++ b/core/src/main/java/com/riiablo/excel/TxtParser.java @@ -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 columnNames; final ObjectIntMap 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]); } } diff --git a/core/src/main/java/com/riiablo/excel/gen/BinGenerator.java b/core/src/main/java/com/riiablo/excel/gen/BinGenerator.java deleted file mode 100644 index 7d3b2ce8..00000000 --- a/core/src/main/java/com/riiablo/excel/gen/BinGenerator.java +++ /dev/null @@ -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); - - } -} diff --git a/core/src/main/java/com/riiablo/excel/gen/SerializerGenerator.java b/core/src/main/java/com/riiablo/excel/gen/SerializerGenerator.java deleted file mode 100644 index e798a2ca..00000000 --- a/core/src/main/java/com/riiablo/excel/gen/SerializerGenerator.java +++ /dev/null @@ -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"); - } - } -} diff --git a/core/src/main/java/com/riiablo/excel/txt/MonStats.java b/core/src/main/java/com/riiablo/excel/txt/MonStats.java index b63148a1..1031d7f4 100644 --- a/core/src/main/java/com/riiablo/excel/txt/MonStats.java +++ b/core/src/main/java/com/riiablo/excel/txt/MonStats.java @@ -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 { +@Entry(MonStats.Entry.class) +@SerializedWith(MonStatsSerializer.class) +public class MonStats extends Excel { public MonStats() { - super(Entry.class); + super(Entry.class, 1543); // 736 entries } @Override @@ -15,8 +21,8 @@ public class MonStats extends Excel { } @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 { 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 { public static class Serializer implements com.riiablo.excel.Serializer { @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) {} } } diff --git a/core/src/main/java/com/riiablo/excel2/Excel.java b/core/src/main/java/com/riiablo/excel2/Excel.java deleted file mode 100644 index 0eaa0040..00000000 --- a/core/src/main/java/com/riiablo/excel2/Excel.java +++ /dev/null @@ -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 -> - implements Iterable -{ - 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 ObjectIntMap emptyMap() { - return (ObjectIntMap) 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 - *

      - *

      Examples: - *

        - *
      • {@code "class"} - *
      • {@code "Transform Color"} - *
      • {@code "Level%s"} - *
      • {@code "Skill %d"} - */ - String format() default ""; - - /** - * Index values of format in the case of non-numerical indexes. - *

        - *

        Examples: - *

          - *
        • {@code {"", "(N)", "(H)"}} - *
        • {@code {"r", "g", "b"}} - *
        • {@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 , T extends Excel> - T load(T excel, FileHandle txt) throws IOException { - return load(excel, txt, null); - } - - public static , T extends Excel> - T load(T excel, FileHandle txt, FileHandle bin) throws IOException { - throw null; - } - - static , T extends Excel> - 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 , T extends Excel> - T loadTxt(T excel, TxtParser parser) - throws IOException, ParseException, IllegalAccessException - { - final Class entryClass = excel.entryClass(); - final boolean indexed = ClassUtils.hasAnnotation(entryClass, Indexed.class); - - final String[] TMP = new String[1]; - Field primaryKey = null, firstKey = null; - Array> 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[] 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 , T extends Excel> - void parseColumns( - T excel, - E entry, - String key, - Array> columns, - TxtParser parser - ) - throws IllegalAccessException, ParseException - { - for (Triple 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> 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 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> 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 , T extends Excel> - T loadBin(T excel, FileHandle handle) { - throw null; - } - - static > - 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 entryClass; - protected ObjectIntMap stringToIndex; - protected IntMap entries; - protected Array ordered; - - protected Excel(Class entryClass) { - this(entryClass, 53); - } - - protected Excel(Class entryClass, int initialCapacity) { - this(entryClass, initialCapacity, 0.8f); - } - - protected Excel(Class 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 excelClass() { - return getClass(); - } - - public Class 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 iterator() { - return entries.values().iterator(); - } -} diff --git a/core/src/main/java/com/riiablo/excel2/Serializer.java b/core/src/main/java/com/riiablo/excel2/Serializer.java deleted file mode 100644 index 82e7f1ea..00000000 --- a/core/src/main/java/com/riiablo/excel2/Serializer.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.riiablo.excel2; - -import com.riiablo.io.ByteInput; -import com.riiablo.io.ByteOutput; - -public interface Serializer { - void readBin(T entry, ByteInput in); - void writeBin(T entry, ByteOutput out); - boolean equals(T e1, T e2); - void logErrors(T e1, T e2); -} diff --git a/core/src/main/java/com/riiablo/excel2/TxtParser.java b/core/src/main/java/com/riiablo/excel2/TxtParser.java deleted file mode 100644 index 6603f67e..00000000 --- a/core/src/main/java/com/riiablo/excel2/TxtParser.java +++ /dev/null @@ -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 columnNames; - final ObjectIntMap 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]); - } -} diff --git a/core/src/main/java/com/riiablo/excel2/txt/MonStats.java b/core/src/main/java/com/riiablo/excel2/txt/MonStats.java deleted file mode 100644 index 83428952..00000000 --- a/core/src/main/java/com/riiablo/excel2/txt/MonStats.java +++ /dev/null @@ -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 { - 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 { - @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) {} - } -} diff --git a/core/src/test/java/com/riiablo/excel2/BinGeneratorTest.java b/core/src/test/java/com/riiablo/excel/BinGeneratorTest.java similarity index 71% rename from core/src/test/java/com/riiablo/excel2/BinGeneratorTest.java rename to core/src/test/java/com/riiablo/excel/BinGeneratorTest.java index e47bba45..d044f4e9 100644 --- a/core/src/test/java/com/riiablo/excel2/BinGeneratorTest.java +++ b/core/src/test/java/com/riiablo/excel/BinGeneratorTest.java @@ -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); } } diff --git a/core/src/test/java/com/riiablo/excel2/ExcelTest.java b/core/src/test/java/com/riiablo/excel/ExcelTest.java similarity index 79% rename from core/src/test/java/com/riiablo/excel2/ExcelTest.java rename to core/src/test/java/com/riiablo/excel/ExcelTest.java index 37e1ecd3..d9c2c176 100644 --- a/core/src/test/java/com/riiablo/excel2/ExcelTest.java +++ b/core/src/test/java/com/riiablo/excel/ExcelTest.java @@ -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 diff --git a/core/src/test/java/com/riiablo/excel2/SerializerGeneratorTest.java b/core/src/test/java/com/riiablo/excel/SerializerGeneratorTest.java similarity index 70% rename from core/src/test/java/com/riiablo/excel2/SerializerGeneratorTest.java rename to core/src/test/java/com/riiablo/excel/SerializerGeneratorTest.java index 9e15f1dd..3868b161 100644 --- a/core/src/test/java/com/riiablo/excel2/SerializerGeneratorTest.java +++ b/core/src/test/java/com/riiablo/excel/SerializerGeneratorTest.java @@ -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(); } } diff --git a/core/src/test/java/com/riiablo/excel2/SerializerSourceGeneratorTest.java b/core/src/test/java/com/riiablo/excel/SerializerSourceGeneratorTest.java similarity index 91% rename from core/src/test/java/com/riiablo/excel2/SerializerSourceGeneratorTest.java rename to core/src/test/java/com/riiablo/excel/SerializerSourceGeneratorTest.java index 610f08a0..6fc18b49 100644 --- a/core/src/test/java/com/riiablo/excel2/SerializerSourceGeneratorTest.java +++ b/core/src/test/java/com/riiablo/excel/SerializerSourceGeneratorTest.java @@ -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 diff --git a/core/src/test/java/com/riiablo/excel/TxtParserTest.java b/core/src/test/java/com/riiablo/excel/TxtParserTest.java index 23d64dda..13154921 100644 --- a/core/src/test/java/com/riiablo/excel/TxtParserTest.java +++ b/core/src/test/java/com/riiablo/excel/TxtParserTest.java @@ -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); } } diff --git a/core/src/test/java/com/riiablo/excel2/TxtParserTest.java b/core/src/test/java/com/riiablo/excel2/TxtParserTest.java deleted file mode 100644 index 5eda159a..00000000 --- a/core/src/test/java/com/riiablo/excel2/TxtParserTest.java +++ /dev/null @@ -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); - } -}