-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add missing support for object properties having Enum array type.
- Loading branch information
1 parent
575c2bb
commit 6b73f18
Showing
11 changed files
with
419 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
permazen-main/src/main/java/io/permazen/AbstractEnumJSimpleField.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
|
||
/* | ||
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved. | ||
*/ | ||
|
||
package io.permazen; | ||
|
||
import com.google.common.base.Converter; | ||
import com.google.common.base.Preconditions; | ||
import com.google.common.reflect.TypeToken; | ||
|
||
import io.permazen.core.FieldType; | ||
import io.permazen.schema.AbstractEnumSchemaField; | ||
import io.permazen.schema.SimpleSchemaField; | ||
|
||
import java.lang.reflect.Method; | ||
|
||
import org.dellroad.stuff.java.EnumUtil; | ||
|
||
/** | ||
* Support superclass for {@link JSimpleField}'s that involve an {@link Enum} type. | ||
*/ | ||
abstract class AbstractEnumJSimpleField<A, B> extends ConvertedJSimpleField<A, B> { | ||
|
||
final Class<? extends Enum<?>> enumType; | ||
|
||
AbstractEnumJSimpleField(Permazen jdb, String name, int storageId, TypeToken<A> typeToken, FieldType<B> fieldType, | ||
Class<? extends Enum<?>> enumType, boolean indexed, io.permazen.annotation.JField annotation, String description, | ||
Method getter, Method setter, Converter<A, B> converter) { | ||
super(jdb, name, storageId, typeToken, fieldType, indexed, annotation, description, getter, setter, converter); | ||
Preconditions.checkArgument(enumType != null, "null enumType"); | ||
this.enumType = enumType; | ||
} | ||
|
||
@Override | ||
@SuppressWarnings("unchecked") | ||
void initialize(Permazen jdb, SimpleSchemaField schemaField0) { | ||
super.initialize(jdb, schemaField0); | ||
final AbstractEnumSchemaField schemaField = (AbstractEnumSchemaField)schemaField0; | ||
schemaField.setType(null); // core API ignores "type" of Enum array fields | ||
schemaField.getIdentifiers().clear(); | ||
for (Enum<?> value : EnumUtil.getValues(this.enumType)) | ||
schemaField.getIdentifiers().add(value.name()); | ||
} | ||
|
||
@Override | ||
@SuppressWarnings("unchecked") | ||
Class<? extends Enum<?>> getEnumType() { | ||
return this.enumType; | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
permazen-main/src/main/java/io/permazen/ArrayConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
|
||
/* | ||
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved. | ||
*/ | ||
|
||
package io.permazen; | ||
|
||
import com.google.common.base.Converter; | ||
import com.google.common.base.Preconditions; | ||
|
||
import java.lang.reflect.Array; | ||
|
||
class ArrayConverter<A, B> extends Converter<A[], B[]> { | ||
|
||
private final Converter<A, B> converter; | ||
private final Class<A> aType; | ||
private final Class<B> bType; | ||
|
||
@SuppressWarnings("unchecked") | ||
ArrayConverter(Class<A> aType, Class<B> bType, Converter<A, B> converter) { | ||
Preconditions.checkArgument(aType != null, "null aType"); | ||
Preconditions.checkArgument(bType != null, "null bType"); | ||
Preconditions.checkArgument(converter != null, "null converter"); | ||
this.aType = aType; | ||
this.bType = bType; | ||
this.converter = converter; | ||
} | ||
|
||
@Override | ||
protected B[] doForward(A[] value) { | ||
return ArrayConverter.convert(this.aType, this.bType, this.converter, value); | ||
} | ||
|
||
@Override | ||
protected A[] doBackward(B[] value) { | ||
return ArrayConverter.convert(this.bType, this.aType, this.converter.reverse(), value); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
private static <X, Y> Y[] convert(Class<X> xType, Class<Y> yType, Converter<X, Y> converter, X[] value) { | ||
if (value == null) | ||
return null; | ||
final int length = Array.getLength(value); | ||
final Y[] result = (Y[])Array.newInstance(yType, length); | ||
for (int i = 0; i < length; i++) | ||
result[i] = converter.convert(value[i]); | ||
return result; | ||
} | ||
|
||
// Object | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (obj == this) | ||
return true; | ||
if (obj == null || obj.getClass() != this.getClass()) | ||
return false; | ||
final ArrayConverter<?, ?> that = (ArrayConverter<?, ?>)obj; | ||
return this.aType == that.aType | ||
&& this.bType == that.bType | ||
&& this.converter.equals(that.converter); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return this.aType.hashCode() | ||
^ this.bType.hashCode() | ||
^ this.converter.hashCode(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return this.getClass().getSimpleName() + "[" + this.aType.getName() + "[]->" + this.bType.getName() + "[]]"; | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 118 additions & 0 deletions
118
permazen-main/src/main/java/io/permazen/ConvertedJSimpleField.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
|
||
/* | ||
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved. | ||
*/ | ||
|
||
package io.permazen; | ||
|
||
import com.google.common.base.Converter; | ||
import com.google.common.base.Preconditions; | ||
import com.google.common.reflect.TypeToken; | ||
|
||
import io.permazen.core.FieldType; | ||
|
||
import java.lang.reflect.Method; | ||
|
||
import org.objectweb.asm.ClassWriter; | ||
import org.objectweb.asm.FieldVisitor; | ||
import org.objectweb.asm.MethodVisitor; | ||
import org.objectweb.asm.Opcodes; | ||
import org.objectweb.asm.Type; | ||
|
||
/** | ||
* Support superclass for {@link JSimpleField}'s that require convertion between Java and core API values. | ||
*/ | ||
abstract class ConvertedJSimpleField<A, B> extends JSimpleField { | ||
|
||
final Converter<A, B> converter; // converts Java value -> Core API value in the forward direction | ||
|
||
ConvertedJSimpleField(Permazen jdb, String name, int storageId, TypeToken<A> typeToken, FieldType<B> fieldType, | ||
boolean indexed, io.permazen.annotation.JField annotation, String description, Method getter, Method setter, | ||
Converter<A, B> converter) { | ||
super(jdb, name, storageId, typeToken, fieldType, indexed, annotation, description, getter, setter); | ||
Preconditions.checkArgument(converter != null, "null converter"); | ||
this.converter = converter; | ||
} | ||
|
||
@Override | ||
public Converter<B, A> getConverter(JTransaction jtx) { | ||
return this.converter.reverse(); | ||
} | ||
|
||
@Override | ||
boolean isSameAs(JField that0) { | ||
if (!super.isSameAs(that0)) | ||
return false; | ||
final ConvertedJSimpleField<?, ?> that = (ConvertedJSimpleField)that0; | ||
if (!this.converter.equals(that.converter)) | ||
return false; | ||
return true; | ||
} | ||
|
||
// POJO import/export | ||
|
||
@SuppressWarnings({ "rawtypes", "unchecked" }) | ||
Object importCoreValue(ImportContext context, Object value) { | ||
return this.converter.convert((A)value); | ||
} | ||
|
||
@SuppressWarnings({ "rawtypes", "unchecked" }) | ||
Object exportCoreValue(ExportContext context, Object value) { | ||
return this.converter.reverse().convert((B)value); | ||
} | ||
|
||
// Bytecode generation | ||
|
||
@Override | ||
void outputFields(ClassGenerator<?> generator, ClassWriter cw) { | ||
final FieldVisitor valueField = cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, | ||
ClassGenerator.CONVERTER_FIELD_PREFIX + this.storageId, Type.getDescriptor(Converter.class), null, null); | ||
valueField.visitEnd(); | ||
} | ||
|
||
@Override | ||
boolean hasClassInitializerBytecode() { | ||
return true; | ||
} | ||
|
||
@Override | ||
void outputClassInitializerBytecode(ClassGenerator<?> generator, MethodVisitor mv) { | ||
this.outputCreateConverterBytecode(generator, mv); | ||
mv.visitFieldInsn(Opcodes.PUTSTATIC, generator.getClassName(), | ||
ClassGenerator.CONVERTER_FIELD_PREFIX + this.storageId, Type.getDescriptor(Converter.class)); | ||
} | ||
|
||
abstract void outputCreateConverterBytecode(ClassGenerator<?> generator, MethodVisitor mv); | ||
|
||
@Override | ||
void outputMethods(final ClassGenerator<?> generator, ClassWriter cw) { | ||
|
||
// Getter | ||
MethodVisitor mv = cw.visitMethod( | ||
this.getter.getModifiers() & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED), | ||
this.getter.getName(), Type.getMethodDescriptor(this.getter), null, generator.getExceptionNames(this.getter)); | ||
mv.visitFieldInsn(Opcodes.GETSTATIC, generator.getClassName(), | ||
ClassGenerator.CONVERTER_FIELD_PREFIX + this.storageId, Type.getDescriptor(Converter.class)); | ||
this.outputReadCoreValueBytecode(generator, mv); | ||
generator.emitInvoke(mv, ClassGenerator.CONVERTER_CONVERT_METHOD); | ||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(this.getter.getReturnType())); | ||
mv.visitInsn(Opcodes.ARETURN); | ||
mv.visitMaxs(0, 0); | ||
mv.visitEnd(); | ||
|
||
// Setter | ||
mv = cw.visitMethod( | ||
this.setter.getModifiers() & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED), | ||
this.setter.getName(), Type.getMethodDescriptor(this.setter), null, generator.getExceptionNames(this.setter)); | ||
mv.visitFieldInsn(Opcodes.GETSTATIC, generator.getClassName(), | ||
ClassGenerator.CONVERTER_FIELD_PREFIX + this.storageId, Type.getDescriptor(Converter.class)); | ||
generator.emitInvoke(mv, ClassGenerator.CONVERTER_REVERSE_METHOD); | ||
mv.visitVarInsn(Opcodes.ALOAD, 1); | ||
generator.emitInvoke(mv, ClassGenerator.CONVERTER_CONVERT_METHOD); | ||
this.outputWriteCoreValueBytecode(generator, mv); | ||
mv.visitInsn(Opcodes.RETURN); | ||
mv.visitMaxs(0, 0); | ||
mv.visitEnd(); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.