mirror of
https://github.com/collinsmith/riiablo.git
synced 2025-02-02 04:13:50 +07:00
Added support for foreign keys within schemas
This commit is contained in:
parent
4e9f356b12
commit
e0b4904efe
@ -23,6 +23,20 @@ abstract class AnnotationElement<A extends Annotation> {
|
||||
return context.elementUtils.getElementValuesWithDefaults(mirror);
|
||||
}
|
||||
|
||||
AnnotationValue value(String key) {
|
||||
for (
|
||||
Map.Entry<
|
||||
? extends ExecutableElement,
|
||||
? extends AnnotationValue
|
||||
> entry : defaults().entrySet()) {
|
||||
if (entry.getKey().getSimpleName().contentEquals(key)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
|
@ -4,8 +4,17 @@ import com.squareup.javapoet.ClassName;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.Name;
|
||||
import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import static com.squareup.javapoet.TypeName.BOOLEAN;
|
||||
import static com.squareup.javapoet.TypeName.BYTE;
|
||||
import static com.squareup.javapoet.TypeName.INT;
|
||||
import static com.squareup.javapoet.TypeName.LONG;
|
||||
import static com.squareup.javapoet.TypeName.SHORT;
|
||||
|
||||
final class Constants {
|
||||
private Constants() {}
|
||||
|
||||
@ -17,11 +26,24 @@ final class Constants {
|
||||
|
||||
static final ClassName STRING = ClassName.get(String.class);
|
||||
static final ClassName PRIMARY_KEY = ClassName.get(PrimaryKey.class);
|
||||
static final ClassName FOREIGN_KEY = ClassName.get(ForeignKey.class);
|
||||
static final ClassName FORMAT = ClassName.get(Format.class);
|
||||
|
||||
static final TypeName[] PRIMARY_KEY_TYPES = { TypeName.INT, STRING };
|
||||
static final TypeName[] PRIMARY_KEY_TYPES = { INT, STRING };
|
||||
|
||||
static boolean isPrimaryKey(Element element) {
|
||||
static boolean isPrimaryKeyType(Element element) {
|
||||
return ArrayUtils.contains(PRIMARY_KEY_TYPES, TypeName.get(element.asType()));
|
||||
}
|
||||
|
||||
static final TypeName[] RECORD_FIELD_TYPES = {
|
||||
BYTE, SHORT, INT, LONG, BOOLEAN, STRING
|
||||
};
|
||||
|
||||
static boolean isRecordFieldType(Element element) {
|
||||
TypeMirror mirror = element.asType();
|
||||
return ArrayUtils.contains(RECORD_FIELD_TYPES,
|
||||
TypeName.get(mirror.getKind() == TypeKind.ARRAY
|
||||
? ((ArrayType) mirror).getComponentType()
|
||||
: mirror));
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ final class FieldElement {
|
||||
static FieldElement get(Context context, VariableElement element) {
|
||||
FormatElement formatElement = FormatElement.get(context, element);
|
||||
PrimaryKeyElement primaryKeyElement = PrimaryKeyElement.get(context, element);
|
||||
ForeignKeyElement foreignKeyElement = ForeignKeyElement.get(context, element);
|
||||
Set<Modifier> modifiers = element.getModifiers();
|
||||
if (!modifiers.contains(Modifier.PUBLIC)) {
|
||||
context.warn(element, "record fields should be declared {}", Modifier.PUBLIC);
|
||||
@ -27,12 +28,15 @@ final class FieldElement {
|
||||
context.error(element, "'{}' is an illegal record field name", Constants.RESERVED_NAME);
|
||||
return null;
|
||||
}
|
||||
return new FieldElement(element, formatElement, primaryKeyElement);
|
||||
if (foreignKeyElement == null && !Constants.isRecordFieldType(element)) {
|
||||
context.error(element, "{element} is not a supported record field type");
|
||||
}
|
||||
return new FieldElement(element, formatElement, primaryKeyElement, foreignKeyElement);
|
||||
}
|
||||
|
||||
static FieldElement firstPrimaryKey(Collection<FieldElement> fields) {
|
||||
for (FieldElement field : fields) {
|
||||
if (field.primaryKeyElement != null || Constants.isPrimaryKey(field.element)) {
|
||||
if (field.primaryKeyElement != null || Constants.isPrimaryKeyType(field.element)) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
@ -44,13 +48,19 @@ final class FieldElement {
|
||||
final TypeMirror mirror;
|
||||
final FormatElement formatElement;
|
||||
final PrimaryKeyElement primaryKeyElement;
|
||||
final ForeignKeyElement foreignKeyElement;
|
||||
final String[] fieldNames;
|
||||
|
||||
FieldElement(VariableElement element, FormatElement formatElement, PrimaryKeyElement primaryKeyElement) {
|
||||
FieldElement(
|
||||
VariableElement element,
|
||||
FormatElement formatElement,
|
||||
PrimaryKeyElement primaryKeyElement,
|
||||
ForeignKeyElement foreignKeyElement) {
|
||||
this.element = element;
|
||||
this.mirror = element.asType();
|
||||
this.formatElement = formatElement;
|
||||
this.primaryKeyElement = primaryKeyElement;
|
||||
this.foreignKeyElement = foreignKeyElement;
|
||||
fieldNames = formatElement != null
|
||||
? formatElement.fieldNames
|
||||
: ArrayUtils.toArray(element.getSimpleName().toString());
|
||||
@ -60,6 +70,14 @@ final class FieldElement {
|
||||
return element.getSimpleName();
|
||||
}
|
||||
|
||||
boolean isPrimaryKey() {
|
||||
return primaryKeyElement != null;
|
||||
}
|
||||
|
||||
boolean isForeignKey() {
|
||||
return foreignKeyElement != null;
|
||||
}
|
||||
|
||||
boolean isTransient() {
|
||||
return element.getModifiers().contains(Modifier.TRANSIENT);
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.riiablo.table.annotation;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
|
||||
final class ForeignKeyElement extends AnnotationElement<ForeignKey> {
|
||||
static ForeignKeyElement get(Context context, VariableElement element) {
|
||||
ForeignKey annotation = element.getAnnotation(ForeignKey.class);
|
||||
if (annotation == null) return null;
|
||||
if (!element.getModifiers().contains(Modifier.PUBLIC)) {
|
||||
context.warn(element, "{} fields must be declared {}", ForeignKey.class, Modifier.PUBLIC);
|
||||
return null;
|
||||
}
|
||||
|
||||
AnnotationMirror mirror = context.getAnnotationMirror(element, Constants.FOREIGN_KEY);
|
||||
return new ForeignKeyElement(context, annotation, mirror);
|
||||
}
|
||||
|
||||
ForeignKeyElement(Context context, ForeignKey annotation, AnnotationMirror mirror) {
|
||||
super(context, annotation, mirror);
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package com.riiablo.table.annotation;
|
||||
|
||||
import com.squareup.javapoet.ClassName;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import java.util.Map;
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
final class InjectorCodeGenerator extends CodeGenerator {
|
||||
final ClassName tableManifest;
|
||||
final Map<ClassName, FieldSpec> tables;
|
||||
|
||||
InjectorCodeGenerator(
|
||||
Context context,
|
||||
String injectorPackage,
|
||||
ClassName tableManifest,
|
||||
Map<ClassName, FieldSpec> tables) {
|
||||
super(context, injectorPackage);
|
||||
this.tableManifest = tableManifest;
|
||||
this.tables = tables;
|
||||
}
|
||||
|
||||
@Override
|
||||
ClassName formatName(String packageName, SchemaElement schemaElement) {
|
||||
return schemaElement.parserClassName = ClassName.get(
|
||||
packageName,
|
||||
schemaElement.element.getSimpleName() + Injector.class.getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
TypeSpec.Builder newTypeSpec(SchemaElement schemaElement) {
|
||||
return super.newTypeSpec(schemaElement)
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
|
||||
.addSuperinterface(ParameterizedTypeName.get(
|
||||
ClassName.get(com.riiablo.table.Injector.class),
|
||||
ClassName.get(schemaElement.element),
|
||||
tableManifest))
|
||||
.addMethod(inject(schemaElement))
|
||||
;
|
||||
}
|
||||
|
||||
// R inject(Object manifest, R record);
|
||||
MethodSpec inject(SchemaElement schemaElement) {
|
||||
ClassName schemaName = ClassName.get(schemaElement.element);
|
||||
final ParameterSpec manifest = ParameterSpec.builder(tableManifest, "arg0").build();
|
||||
final ParameterSpec record = ParameterSpec.builder(schemaName, "arg1").build();
|
||||
MethodSpec.Builder method = MethodSpec
|
||||
.methodBuilder("inject")
|
||||
.addAnnotation(Override.class)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.returns(schemaName)
|
||||
.addParameter(manifest)
|
||||
.addParameter(record)
|
||||
;
|
||||
|
||||
for (FieldElement field : schemaElement.foreignKeys) {
|
||||
FieldSpec fieldSpec = tables.get(ClassName.get(field.element()));
|
||||
if (fieldSpec == null) continue;
|
||||
method.addStatement("$N.$N = $N.$N.get($N.$N)",
|
||||
record,
|
||||
field.name(),
|
||||
manifest,
|
||||
fieldSpec,
|
||||
record,
|
||||
field.foreignKeyElement.annotation.value());
|
||||
}
|
||||
|
||||
method.addStatement("return $N", record);
|
||||
return method.build();
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.riiablo.table.annotation;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.MirroredTypeException;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
final class InjectorElement {
|
||||
static InjectorElement get(Context context, Element element) {
|
||||
Injector annotation = element.getAnnotation(Injector.class);
|
||||
final TypeElement injectorElement, injectorImplElement;
|
||||
if (annotation == null) {
|
||||
// Only need injectorElement if generating Injector impl
|
||||
injectorElement = context.elementUtils.getTypeElement(com.riiablo.table.Injector.class.getCanonicalName());
|
||||
injectorImplElement = null;
|
||||
} else {
|
||||
// Only need injectorImplElement if @Injector present
|
||||
injectorImplElement = getInjectorImpl(context, annotation);
|
||||
injectorElement = null;
|
||||
}
|
||||
return new InjectorElement(annotation, injectorElement, injectorImplElement);
|
||||
}
|
||||
|
||||
static TypeElement getInjectorImpl(Context context, Injector annotation) {
|
||||
if (annotation == null) return null;
|
||||
try {
|
||||
Class<?> injectorImpl = annotation.value();
|
||||
return context.elementUtils.getTypeElement(injectorImpl.getCanonicalName());
|
||||
} catch (MirroredTypeException t) {
|
||||
DeclaredType injectorImplMirror = (DeclaredType) t.getTypeMirror();
|
||||
return (TypeElement) injectorImplMirror.asElement();
|
||||
}
|
||||
}
|
||||
|
||||
final Injector annotation;
|
||||
final TypeElement injectorElement;
|
||||
final TypeElement injectorImplElement;
|
||||
|
||||
InjectorElement(
|
||||
Injector annotation,
|
||||
TypeElement injectorElement,
|
||||
TypeElement injectorImplElement) {
|
||||
this.annotation = annotation;
|
||||
this.injectorElement = injectorElement;
|
||||
this.injectorImplElement = injectorImplElement;
|
||||
}
|
||||
|
||||
ExecutableElement getMethod(CharSequence methodName) {
|
||||
for (Element e : injectorElement.getEnclosedElements()) {
|
||||
if (e.getKind() == ElementKind.METHOD) {
|
||||
ExecutableElement methodElement = (ExecutableElement) e;
|
||||
if (methodElement.getSimpleName().contentEquals(methodName)) {
|
||||
return methodElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new AssertionError(injectorElement + " does not contain " + methodName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("annotation", annotation)
|
||||
.append("injectorElement", injectorElement)
|
||||
.append("injectorImplElement", injectorImplElement)
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -15,15 +15,14 @@ import com.riiablo.table.ParserInput;
|
||||
|
||||
import static com.riiablo.table.annotation.Constants.STRING;
|
||||
|
||||
class ParserCodeGenerator extends CodeGenerator {
|
||||
final class ParserCodeGenerator extends CodeGenerator {
|
||||
ParserCodeGenerator(Context context, String parserPackage) {
|
||||
super(context, parserPackage);
|
||||
}
|
||||
|
||||
@Override
|
||||
ClassName formatName(String packageName, SchemaElement schemaElement) {
|
||||
return schemaElement.parserClassName
|
||||
= ClassName.get(
|
||||
return schemaElement.parserClassName = ClassName.get(
|
||||
packageName,
|
||||
schemaElement.element.getSimpleName() + Parser.class.getSimpleName());
|
||||
}
|
||||
@ -90,6 +89,7 @@ class ParserCodeGenerator extends CodeGenerator {
|
||||
final ParameterSpec recordId = method.parameters.get(1);
|
||||
final ParameterSpec record = method.parameters.get(2);
|
||||
for (FieldElement field : schemaElement.fields) {
|
||||
if (field.isForeignKey()) continue;
|
||||
final TypeName fieldTypeName = TypeName.get(field.element());
|
||||
final CodeBlock fqFieldName = qualify(record, field.name());
|
||||
if (field.isArray()) {
|
||||
|
@ -29,7 +29,7 @@ final class SchemaElement {
|
||||
return null;
|
||||
}
|
||||
|
||||
ExecutableElement defaultConstructor = defaultConstructor(context, typeElement);
|
||||
ExecutableElement defaultConstructor = defaultConstructor(context, typeElement);
|
||||
if (defaultConstructor == null) {
|
||||
context.error(typeElement, "{element} must contain a default constructor");
|
||||
return null;
|
||||
@ -68,6 +68,7 @@ final class SchemaElement {
|
||||
}
|
||||
|
||||
TableElement tableElement = TableElement.get(context, typeElement);
|
||||
InjectorElement injectorElement = InjectorElement.get(context, typeElement);
|
||||
SerializerElement serializerElement = SerializerElement.get(context, typeElement);
|
||||
ParserElement parserElement = ParserElement.get(context, typeElement);
|
||||
|
||||
@ -75,6 +76,7 @@ final class SchemaElement {
|
||||
annotation,
|
||||
typeElement,
|
||||
tableElement,
|
||||
injectorElement,
|
||||
serializerElement,
|
||||
parserElement,
|
||||
primaryKeyFieldElement,
|
||||
@ -122,15 +124,28 @@ final class SchemaElement {
|
||||
return fields;
|
||||
}
|
||||
|
||||
static Collection<FieldElement> collectForeignKeys(Collection<FieldElement> fields) {
|
||||
return CollectionUtils.select(fields, new Predicate<FieldElement>() {
|
||||
@Override
|
||||
public boolean evaluate(FieldElement field) {
|
||||
return field.isForeignKey();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final Schema annotation;
|
||||
final TypeElement element;
|
||||
final TableElement tableElement;
|
||||
final InjectorElement injectorElement;
|
||||
final SerializerElement serializerElement;
|
||||
final ParserElement parserElement;
|
||||
final FieldElement primaryKeyFieldElement;
|
||||
final Collection<FieldElement> fields;
|
||||
final int numFields;
|
||||
final Collection<FieldElement> foreignKeys;
|
||||
|
||||
ClassName tableClassName;
|
||||
ClassName injectorClassName;
|
||||
ClassName serializerClassName;
|
||||
ClassName parserClassName;
|
||||
|
||||
@ -138,6 +153,7 @@ final class SchemaElement {
|
||||
Schema annotation,
|
||||
TypeElement element,
|
||||
TableElement tableElement,
|
||||
InjectorElement injectorElement,
|
||||
SerializerElement serializerElement,
|
||||
ParserElement parserElement,
|
||||
FieldElement primaryKeyFieldElement,
|
||||
@ -145,11 +161,19 @@ final class SchemaElement {
|
||||
this.annotation = annotation;
|
||||
this.element = element;
|
||||
this.tableElement = tableElement;
|
||||
this.injectorElement = injectorElement;
|
||||
this.serializerElement = serializerElement;
|
||||
this.parserElement = parserElement;
|
||||
this.primaryKeyFieldElement = primaryKeyFieldElement;
|
||||
this.fields = fields;
|
||||
this.numFields = countNumFields(fields);
|
||||
this.foreignKeys = collectForeignKeys(fields);
|
||||
if (tableElement.tableImplElement != null) {
|
||||
tableClassName = ClassName.get(tableElement.tableImplElement);
|
||||
}
|
||||
if (injectorElement.injectorImplElement != null) {
|
||||
injectorClassName = ClassName.get(injectorElement.injectorImplElement);
|
||||
}
|
||||
if (serializerElement.serializerImplElement != null) {
|
||||
serializerClassName = ClassName.get(serializerElement.serializerImplElement);
|
||||
}
|
||||
@ -169,10 +193,12 @@ final class SchemaElement {
|
||||
return new ToStringBuilder(this)
|
||||
.append("element", element)
|
||||
.append("tableElement", tableElement)
|
||||
.append("injectorElement", injectorElement)
|
||||
.append("injectorClassName", injectorClassName)
|
||||
.append("serializerElement", serializerElement)
|
||||
.append("serializerClassName", serializerClassName)
|
||||
.append("ParserElement", parserElement)
|
||||
.append("serializerClassName", serializerClassName)
|
||||
.append("parserElement", parserElement)
|
||||
.append("parserClassName", parserClassName)
|
||||
.append("primaryKeyFieldElement", primaryKeyFieldElement)
|
||||
.toString();
|
||||
}
|
||||
|
@ -1,67 +1,142 @@
|
||||
package com.riiablo.table.annotation;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.squareup.javapoet.ArrayTypeName;
|
||||
import com.squareup.javapoet.CodeBlock;
|
||||
import com.squareup.javapoet.ClassName;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.JavaFile;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.Processor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import org.apache.commons.collections4.SetUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
import static com.riiablo.table.annotation.Constants.FOREIGN_KEY;
|
||||
import static com.riiablo.table.annotation.Constants.PRIMARY_KEY;
|
||||
import static com.riiablo.table.annotation.Constants.PRIMARY_KEY_TYPES;
|
||||
|
||||
@AutoService(Processor.class)
|
||||
public class SchemaProcessor extends AbstractProcessor {
|
||||
private final Set<String> schemas = new HashSet<>();
|
||||
private final List<SchemaElement> schemas = new ArrayList<>();
|
||||
private final Map<ClassName, FieldSpec> tables = new HashMap<>();
|
||||
private final Set<TypeMirror> tableTypes = new HashSet<>();
|
||||
private Context context;
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnv) {
|
||||
super.init(processingEnv);
|
||||
context = new Context(processingEnv);
|
||||
public synchronized void init(ProcessingEnvironment p) {
|
||||
super.init(p);
|
||||
Validate.validState(context == null, "context already configured");
|
||||
context = new Context(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
if (roundEnv.processingOver()) {
|
||||
generateManifest();
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment r) {
|
||||
if (r.processingOver()) {
|
||||
ClassName tableManifest = generateManifest();
|
||||
if (tableManifest != null) {
|
||||
generateInjectors(tableManifest);
|
||||
}
|
||||
} else {
|
||||
processAnnotations(roundEnv);
|
||||
processPrimaryKeyAnnotations(r);
|
||||
processSchemaAnnotations(r);
|
||||
processForeignKeyAnnotations(r);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void processAnnotations(RoundEnvironment roundEnv) {
|
||||
private void processPrimaryKeyAnnotations(RoundEnvironment r) {
|
||||
for (Element element : r.getElementsAnnotatedWith(PrimaryKey.class)) {
|
||||
AnnotationMirror annotationMirror = context.getAnnotationMirror(element, PRIMARY_KEY);
|
||||
if (element.getKind() != ElementKind.FIELD) {
|
||||
context.error(element, annotationMirror,
|
||||
"{} can only be applied to fields",
|
||||
PrimaryKey.class);
|
||||
}
|
||||
|
||||
for (Element element : roundEnv.getElementsAnnotatedWith(PrimaryKey.class)) {
|
||||
VariableElement variableElement = (VariableElement) element;
|
||||
if (!Constants.isPrimaryKey(variableElement)) {
|
||||
context.error(variableElement, "{} must be one of {}",
|
||||
PrimaryKey.class, Constants.PRIMARY_KEY_TYPES);
|
||||
if (!Constants.isPrimaryKeyType(element)) {
|
||||
context.error(element, annotationMirror,
|
||||
"{} must be one of {}",
|
||||
PrimaryKey.class, PRIMARY_KEY_TYPES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processForeignKeyAnnotations(RoundEnvironment r) {
|
||||
for (Element element : r.getElementsAnnotatedWith(ForeignKey.class)) {
|
||||
AnnotationMirror annotationMirror = context.getAnnotationMirror(element, FOREIGN_KEY);
|
||||
if (element.getKind() != ElementKind.FIELD) {
|
||||
context.error(element, annotationMirror,
|
||||
"{} can only be applied to fields",
|
||||
ForeignKey.class);
|
||||
}
|
||||
|
||||
// validates that foreign key field type matches an existing table type
|
||||
TypeMirror mirror = element.asType();
|
||||
if (!tableTypes.contains(mirror)) {
|
||||
context.error(element, annotationMirror,
|
||||
"cannot locate table of type {} for {element}",
|
||||
mirror);
|
||||
}
|
||||
|
||||
// finds schema element of this foreign key element
|
||||
SchemaElement schemaElement = null;
|
||||
ForeignKeyElement foreignKey = null;
|
||||
finder:
|
||||
for (SchemaElement e : schemas) {
|
||||
for (FieldElement f : e.fields) {
|
||||
if (f.element == element) {
|
||||
schemaElement = e;
|
||||
foreignKey = f.foreignKeyElement;
|
||||
break finder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validates that foreign key column name matches an existing column name
|
||||
if (schemaElement != null) {
|
||||
boolean found = false;
|
||||
for (FieldElement f : schemaElement.fields) {
|
||||
if (f.name().contentEquals(foreignKey.annotation.value())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
context.error(element, foreignKey.mirror, foreignKey.value("value"),
|
||||
"{} does not contain any field named '{}'",
|
||||
schemaElement.element.getQualifiedName(), foreignKey.annotation.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processSchemaAnnotations(RoundEnvironment r) {
|
||||
TableCodeGenerator tableCodeGenerator = new TableCodeGenerator(
|
||||
context, "com.riiablo.table.table");
|
||||
SerializerCodeGenerator serializerCodeGenerator = new SerializerCodeGenerator(
|
||||
context, "com.riiablo.table.serializer");
|
||||
ParserCodeGenerator parserCodeGenerator = new ParserCodeGenerator(
|
||||
context, "com.riiablo.table.parser");
|
||||
for (Element element : roundEnv.getElementsAnnotatedWith(Schema.class)) {
|
||||
for (Element element : r.getElementsAnnotatedWith(Schema.class)) {
|
||||
if (element.getKind() != ElementKind.CLASS) {
|
||||
context.error(element, "{} can only be applied to classes", Schema.class);
|
||||
continue;
|
||||
@ -71,7 +146,8 @@ public class SchemaProcessor extends AbstractProcessor {
|
||||
if (schemaElement == null) continue;
|
||||
if (schemaElement.serializerElement.declaredType != null) {
|
||||
try {
|
||||
serializerCodeGenerator.generate(schemaElement)
|
||||
serializerCodeGenerator
|
||||
.generate(schemaElement)
|
||||
.writeTo(processingEnv.getFiler());
|
||||
} catch (Throwable t) {
|
||||
context.error(ExceptionUtils.getRootCauseMessage(t));
|
||||
@ -81,7 +157,8 @@ public class SchemaProcessor extends AbstractProcessor {
|
||||
|
||||
if (schemaElement.parserElement.declaredType != null) {
|
||||
try {
|
||||
parserCodeGenerator.generate(schemaElement)
|
||||
parserCodeGenerator
|
||||
.generate(schemaElement)
|
||||
.writeTo(processingEnv.getFiler());
|
||||
} catch (Throwable t) {
|
||||
context.error(ExceptionUtils.getRootCauseMessage(t));
|
||||
@ -93,7 +170,8 @@ public class SchemaProcessor extends AbstractProcessor {
|
||||
// Depends on parserElement to generate Parser impl
|
||||
if (schemaElement.tableElement.declaredType != null) {
|
||||
try {
|
||||
tableCodeGenerator.generate(schemaElement)
|
||||
tableCodeGenerator
|
||||
.generate(schemaElement)
|
||||
.writeTo(processingEnv.getFiler());
|
||||
} catch (Throwable t) {
|
||||
context.error(ExceptionUtils.getRootCauseMessage(t));
|
||||
@ -101,33 +179,62 @@ public class SchemaProcessor extends AbstractProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
schemas.add(CodeBlock.of("$S", schemaElement.element).toString());
|
||||
schemas.add(schemaElement);
|
||||
tableTypes.add(schemaElement.element.asType());
|
||||
}
|
||||
}
|
||||
|
||||
private void generateManifest() {
|
||||
private ClassName generateManifest() {
|
||||
try {
|
||||
JavaFile.builder("com.riiablo.table",
|
||||
TypeSpec
|
||||
.classBuilder("TableManifest")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
|
||||
.addMethod(MethodSpec
|
||||
.constructorBuilder()
|
||||
.addModifiers(Modifier.PRIVATE)
|
||||
.build())
|
||||
.addMethod(MethodSpec
|
||||
.methodBuilder("names")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.returns(ArrayTypeName.of(String.class))
|
||||
.addStatement("return new String[] {\n$L\n}", StringUtils
|
||||
.join(schemas, ",\n"))
|
||||
.build())
|
||||
ClassName manifestName = ClassName.get("com.riiablo.table", "TableManifest");
|
||||
TypeSpec.Builder tableManifest = TypeSpec
|
||||
.classBuilder(manifestName)
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
|
||||
.addMethod(MethodSpec
|
||||
.constructorBuilder()
|
||||
.addModifiers(Modifier.PRIVATE)
|
||||
.build())
|
||||
.build()
|
||||
;
|
||||
|
||||
for (SchemaElement schema : schemas) {
|
||||
ClassName schemaName = ClassName.get(schema.element);
|
||||
FieldSpec tableFieldSpec = FieldSpec
|
||||
.builder(
|
||||
schema.tableClassName,
|
||||
schemaName.simpleName().toLowerCase(),
|
||||
Modifier.PUBLIC, Modifier.FINAL)
|
||||
.initializer("new $T()", schema.tableClassName)
|
||||
.build();
|
||||
tableManifest.addField(tableFieldSpec);
|
||||
tables.put(schemaName, tableFieldSpec);
|
||||
}
|
||||
|
||||
JavaFile
|
||||
.builder(manifestName.packageName(), tableManifest.build()).build()
|
||||
.writeTo(processingEnv.getFiler());
|
||||
return manifestName;
|
||||
} catch (Throwable t) {
|
||||
context.error(ExceptionUtils.getRootCauseMessage(t));
|
||||
t.printStackTrace(System.err);
|
||||
context.error(ExceptionUtils.getRootCauseMessage(t));
|
||||
t.printStackTrace(System.err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void generateInjectors(ClassName tableManifest) {
|
||||
InjectorCodeGenerator injectorCodeGenerator = new InjectorCodeGenerator(
|
||||
context, "com.riiablo.table.injector", tableManifest, tables);
|
||||
for (SchemaElement schemaElement : schemas) {
|
||||
if (schemaElement.foreignKeys.isEmpty()) continue;
|
||||
if (schemaElement.parserElement.declaredType != null) {
|
||||
try {
|
||||
injectorCodeGenerator
|
||||
.generate(schemaElement)
|
||||
.writeTo(processingEnv.getFiler());
|
||||
} catch (Throwable t) {
|
||||
context.error(ExceptionUtils.getRootCauseMessage(t));
|
||||
t.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,6 +243,7 @@ public class SchemaProcessor extends AbstractProcessor {
|
||||
Set<String> set = new LinkedHashSet<>();
|
||||
set.add(Schema.class.getCanonicalName());
|
||||
set.add(PrimaryKey.class.getCanonicalName());
|
||||
set.add(ForeignKey.class.getCanonicalName());
|
||||
return SetUtils.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,7 @@ class SerializerCodeGenerator extends CodeGenerator {
|
||||
final ParameterSpec in = method.parameters.get(1);
|
||||
for (FieldElement field : schemaElement.fields) {
|
||||
if (field.isTransient()) continue;
|
||||
if (field.isForeignKey()) continue;
|
||||
final TypeName fieldTypeName = TypeName.get(field.element());
|
||||
final CodeBlock fqFieldName = qualify(record, field.name());
|
||||
if (field.isArray()) {
|
||||
@ -97,6 +98,7 @@ class SerializerCodeGenerator extends CodeGenerator {
|
||||
final ParameterSpec out = method.parameters.get(1);
|
||||
for (FieldElement field : schemaElement.fields) {
|
||||
if (field.isTransient()) continue;
|
||||
if (field.isForeignKey()) continue;
|
||||
final TypeName fieldTypeName = TypeName.get(field.element());
|
||||
final CodeBlock fqFieldName = qualify(record, field.name());
|
||||
if (field.isArray()) {
|
||||
@ -135,6 +137,7 @@ class SerializerCodeGenerator extends CodeGenerator {
|
||||
final ParameterSpec e2 = method.parameters.get(1);
|
||||
for (FieldElement field : schemaElement.fields) {
|
||||
if (field.isTransient()) continue;
|
||||
if (field.isForeignKey()) continue;
|
||||
final Name fieldName = field.name();
|
||||
final CodeBlock e1FqFieldName = qualify(e1, fieldName);
|
||||
final CodeBlock e2FqFieldName = qualify(e2, fieldName);
|
||||
@ -180,6 +183,7 @@ class SerializerCodeGenerator extends CodeGenerator {
|
||||
|
||||
for (FieldElement field : schemaElement.fields) {
|
||||
if (field.isTransient()) continue;
|
||||
if (field.isForeignKey()) continue;
|
||||
final Name fieldName = field.name();
|
||||
final CodeBlock e1FqFieldName = qualify(e1, fieldName);
|
||||
final CodeBlock e2FqFieldName = qualify(e2, fieldName);
|
||||
@ -231,7 +235,6 @@ class SerializerCodeGenerator extends CodeGenerator {
|
||||
}
|
||||
|
||||
static CodeBlock defaultString(Object var) {
|
||||
// return CodeBlock.of("$T.$N($L)", StringUtils.class, "defaultString", var);
|
||||
return CodeBlock.of("$1L == null ? $2S : $1L", var, "");
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,9 @@ class TableCodeGenerator extends CodeGenerator {
|
||||
|
||||
@Override
|
||||
ClassName formatName(String packageName, SchemaElement schemaElement) {
|
||||
return ClassName.get(
|
||||
packageName,
|
||||
schemaElement.element.getSimpleName() + Table.class.getSimpleName());
|
||||
return schemaElement.tableClassName = ClassName.get(
|
||||
packageName,
|
||||
schemaElement.element.getSimpleName() + Table.class.getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,19 @@
|
||||
package com.riiablo.table.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that the field is a reference to a record in another
|
||||
* {@link Schema schema} of field's type using the field in this schema
|
||||
* specified by {@link #value() value} as the foreign key.
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ForeignKey {
|
||||
String value();
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.riiablo.table.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that the specified {@link Schema schema} should use the given
|
||||
* {@link #value() injector} in lieu of generating one. The injector
|
||||
* implementation should have the schema set as its generic parameter.
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Injector {
|
||||
/**
|
||||
* A injector implementation that should be used by this
|
||||
* {@link Schema schema} in lieu of generating one.
|
||||
*/
|
||||
Class<? extends com.riiablo.table.Injector<?, ?>> value();
|
||||
}
|
12
table/core/src/main/java/com/riiablo/table/Injector.java
Normal file
12
table/core/src/main/java/com/riiablo/table/Injector.java
Normal file
@ -0,0 +1,12 @@
|
||||
package com.riiablo.table;
|
||||
|
||||
/**
|
||||
* Defines behaviors necessary to inject a record with its required
|
||||
* dependencies.
|
||||
*
|
||||
* @param <R> record type
|
||||
* @param <M> manifest
|
||||
*/
|
||||
public interface Injector<R, M> {
|
||||
R inject(M manifest, R record);
|
||||
}
|
@ -20,6 +20,7 @@ public abstract class Table<R> implements Iterable<R> {
|
||||
protected IntMap<R> records;
|
||||
protected Array<R> ordered;
|
||||
|
||||
protected Injector<R, ?> injector;
|
||||
protected Parser<R> parser;
|
||||
|
||||
protected Table(Class<R> recordClass) {
|
||||
@ -41,6 +42,10 @@ public abstract class Table<R> implements Iterable<R> {
|
||||
protected abstract Parser<R> newParser(ParserInput parser);
|
||||
protected abstract Serializer<R> newSerializer();
|
||||
|
||||
protected Injector<R, ?> newInjector() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Class<R> recordClass() {
|
||||
return recordClass;
|
||||
}
|
||||
@ -77,6 +82,12 @@ public abstract class Table<R> implements Iterable<R> {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected R inject(R record) {
|
||||
if (injector == null) injector = newInjector();
|
||||
if (injector != null) return injector.inject(null, record);
|
||||
return record;
|
||||
}
|
||||
|
||||
protected Parser<R> parser() {
|
||||
return parser;
|
||||
}
|
||||
@ -94,7 +105,9 @@ public abstract class Table<R> implements Iterable<R> {
|
||||
public R get(int id) {
|
||||
R record = records.get(id);
|
||||
if (record == null && parser != null) {
|
||||
records.put(id, record = parser.parseRecord(id, newRecord()));
|
||||
record = parser.parseRecord(id, newRecord());
|
||||
record = inject(record);
|
||||
records.put(id, record);
|
||||
}
|
||||
|
||||
return record;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.riiablo.table.schema;
|
||||
|
||||
import com.riiablo.table.annotation.ForeignKey;
|
||||
import com.riiablo.table.annotation.Format;
|
||||
import com.riiablo.table.annotation.PrimaryKey;
|
||||
import com.riiablo.table.annotation.Schema;
|
||||
@ -12,6 +13,9 @@ public class MonStats {
|
||||
return NameStr;
|
||||
}
|
||||
|
||||
@ForeignKey("MonStatsEx")
|
||||
public MonStats2 monstats2;
|
||||
|
||||
@PrimaryKey
|
||||
public String Id;
|
||||
public int hcIdx;
|
||||
|
@ -1,89 +1,125 @@
|
||||
package com.riiablo.table.schema;
|
||||
|
||||
import com.riiablo.table.annotation.Format;
|
||||
import com.riiablo.table.annotation.PrimaryKey;
|
||||
import com.riiablo.table.annotation.Schema;
|
||||
|
||||
@Schema
|
||||
public class MonStats2 {
|
||||
// @Override
|
||||
// public String toString() {
|
||||
// return Id;
|
||||
// }
|
||||
//
|
||||
// @Key
|
||||
// @Column public String Id;
|
||||
// @Column public int Height;
|
||||
// @Column public int OverlayHeight;
|
||||
// @Column public int pixHeight;
|
||||
// @Column public int SizeX;
|
||||
// @Column public int SizeY;
|
||||
// @Column public int spawnCol;
|
||||
// @Column public int MeleeRng;
|
||||
// @Column public String BaseW;
|
||||
// @Column public int HitClass;
|
||||
// @Column(format = "%sv", endIndex = 16, values = {
|
||||
// "HD", "TR", "LG", "RA", "LA", "RH", "LH", "SH", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"
|
||||
// })
|
||||
// public String ComponentV[];
|
||||
// @Column(endIndex = 16, values = {
|
||||
// "HD", "TR", "LG", "RA", "LA", "RH", "LH", "SH", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"
|
||||
// })
|
||||
// public boolean Components[];
|
||||
// @Column public int TotalPieces;
|
||||
// @Column(format = "m%s", endIndex = 16, values = {
|
||||
// "DT", "NU", "WL", "GH", "A1", "A2", "BL", "SC", "S1", "S2", "S3", "S4", "DD", "KB", "SQ", "RN"
|
||||
// })
|
||||
// public boolean mMode[];
|
||||
// @Column(format = "d%s", endIndex = 16, values = {
|
||||
// "DT", "NU", "WL", "GH", "A1", "A2", "BL", "SC", "S1", "S2", "S3", "S4", "DD", "KB", "SQ", "RN"
|
||||
// })
|
||||
// public int dMode[];
|
||||
// @Column(format = "%smv", endIndex = 16, values = {
|
||||
// "DT", "NU", "WL", "GH", "A1", "A2", "BL", "SC", "S1", "S2", "S3", "S4", "DD", "KB", "SQ", "RN"
|
||||
// })
|
||||
// public boolean Modemv[];
|
||||
// //@Column public int A1mv;
|
||||
// //@Column public int A2mv;
|
||||
// //@Column public int SCmv;
|
||||
// //@Column public int S1mv;
|
||||
// //@Column public int S2mv;
|
||||
// //@Column public int S3mv;
|
||||
// //@Column public int S4mv;
|
||||
// @Column public boolean noGfxHitTest;
|
||||
// @Column public int htTop;
|
||||
// @Column public int htLeft;
|
||||
// @Column public int htWidth;
|
||||
// @Column public int htHeight;
|
||||
// @Column public int restore;
|
||||
// @Column public int automapCel;
|
||||
// @Column public boolean noMap;
|
||||
// @Column public boolean noOvly;
|
||||
// @Column public boolean isSel;
|
||||
// @Column public boolean alSel;
|
||||
// @Column public boolean noSel;
|
||||
// @Column public boolean shiftSel;
|
||||
// @Column public boolean corpseSel;
|
||||
// @Column public boolean isAtt;
|
||||
// @Column public boolean revive;
|
||||
// @Column public boolean critter;
|
||||
// @Column public boolean small;
|
||||
// @Column public boolean large;
|
||||
// @Column public boolean soft;
|
||||
// @Column public boolean inert;
|
||||
// @Column public boolean objCol;
|
||||
// @Column public boolean deadCol;
|
||||
// @Column public boolean unflatDead;
|
||||
// @Column public boolean Shadow;
|
||||
// @Column public boolean noUniqueShift;
|
||||
// @Column public boolean compositeDeath;
|
||||
// @Column public int localBlood;
|
||||
// @Column public int Bleed;
|
||||
// @Column public int Light;
|
||||
// @Column(format = "light-%s", values = {"r", "g", "b"}, endIndex = 3)
|
||||
// public int light[];
|
||||
// @Column(format = "Utrans%s", values = {"", "(N)", "(H)"}, endIndex = 3)
|
||||
// public int Utrans[];
|
||||
// @Column public String Heart;
|
||||
// @Column public String BodyPart;
|
||||
// @Column public int InfernoLen;
|
||||
// @Column public int InfernoAnim;
|
||||
// @Column public int InfernoRollback;
|
||||
// @Column public String ResurrectMode;
|
||||
// @Column public String ResurrectSkill;
|
||||
@Override
|
||||
public String toString() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@PrimaryKey
|
||||
public String Id;
|
||||
|
||||
public int Height;
|
||||
public int OverlayHeight;
|
||||
public int pixHeight;
|
||||
public int SizeX;
|
||||
public int SizeY;
|
||||
public int spawnCol;
|
||||
public int MeleeRng;
|
||||
public String BaseW;
|
||||
public int HitClass;
|
||||
|
||||
@Format(
|
||||
format = "%sv",
|
||||
endIndex = 16,
|
||||
values = {
|
||||
"HD", "TR", "LG", "RA", "LA", "RH", "LH", "SH", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"
|
||||
})
|
||||
public String ComponentV[];
|
||||
|
||||
@Format(
|
||||
endIndex = 16,
|
||||
values = {
|
||||
"HD", "TR", "LG", "RA", "LA", "RH", "LH", "SH", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"
|
||||
})
|
||||
public boolean Components[];
|
||||
|
||||
public int TotalPieces;
|
||||
|
||||
@Format(
|
||||
format = "m%s",
|
||||
endIndex = 16,
|
||||
values = {
|
||||
"DT", "NU", "WL", "GH", "A1", "A2", "BL", "SC", "S1", "S2", "S3", "S4", "DD", "KB", "SQ", "RN"
|
||||
})
|
||||
public boolean mMode[];
|
||||
|
||||
@Format(
|
||||
format = "d%s",
|
||||
endIndex = 16,
|
||||
values = {
|
||||
"DT", "NU", "WL", "GH", "A1", "A2", "BL", "SC", "S1", "S2", "S3", "S4", "DD", "KB", "SQ", "RN"
|
||||
})
|
||||
public int dMode[];
|
||||
|
||||
@Format(
|
||||
format = "%smv",
|
||||
endIndex = 16,
|
||||
values = {
|
||||
"DT", "NU", "WL", "GH", "A1", "A2", "BL", "SC", "S1", "S2", "S3", "S4", "DD", "KB", "SQ", "RN"
|
||||
})
|
||||
public boolean Modemv[];
|
||||
|
||||
//public int A1mv;
|
||||
//public int A2mv;
|
||||
//public int SCmv;
|
||||
//public int S1mv;
|
||||
//public int S2mv;
|
||||
//public int S3mv;
|
||||
//public int S4mv;
|
||||
public boolean noGfxHitTest;
|
||||
public int htTop;
|
||||
public int htLeft;
|
||||
public int htWidth;
|
||||
public int htHeight;
|
||||
public int restore;
|
||||
public int automapCel;
|
||||
public boolean noMap;
|
||||
public boolean noOvly;
|
||||
public boolean isSel;
|
||||
public boolean alSel;
|
||||
public boolean noSel;
|
||||
public boolean shiftSel;
|
||||
public boolean corpseSel;
|
||||
public boolean isAtt;
|
||||
public boolean revive;
|
||||
public boolean critter;
|
||||
public boolean small;
|
||||
public boolean large;
|
||||
public boolean soft;
|
||||
public boolean inert;
|
||||
public boolean objCol;
|
||||
public boolean deadCol;
|
||||
public boolean unflatDead;
|
||||
public boolean Shadow;
|
||||
public boolean noUniqueShift;
|
||||
public boolean compositeDeath;
|
||||
public int localBlood;
|
||||
public int Bleed;
|
||||
public int Light;
|
||||
|
||||
@Format(
|
||||
format = "light-%s",
|
||||
values = {"r", "g", "b"},
|
||||
endIndex = 3)
|
||||
public int light[];
|
||||
|
||||
@Format(
|
||||
format = "Utrans%s",
|
||||
values = {"", "(N)", "(H)"},
|
||||
endIndex = 3)
|
||||
public int Utrans[];
|
||||
|
||||
public String Heart;
|
||||
public String BodyPart;
|
||||
public int InfernoLen;
|
||||
public int InfernoAnim;
|
||||
public int InfernoRollback;
|
||||
public String ResurrectMode;
|
||||
public String ResurrectSkill;
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
package com.riiablo.table.schema;
|
||||
|
||||
import com.riiablo.table.Injector;
|
||||
|
||||
public class MonStatsInjectorImpl implements Injector<MonStats, Object> {
|
||||
@Override
|
||||
public MonStats inject(Object manifest, MonStats record) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user