From 65167eb6722aa13b4104681c2b5ee7a68dfefd74 Mon Sep 17 00:00:00 2001 From: Sai Pullabhotla Date: Fri, 4 Nov 2016 18:27:38 -0500 Subject: [PATCH] Remove restriction on what data types can exist in a model class #79 --- .../java/com/jmethods/catatumbo/Entity.java | 11 +- .../com/jmethods/catatumbo/MapperFactory.java | 8 +- .../com/jmethods/catatumbo/impl/DataType.java | 188 ------------------ .../catatumbo/impl/DatastoreUtils.java | 1 + .../catatumbo/impl/EntityIntrospector.java | 51 +---- .../catatumbo/impl/FieldMetadata.java | 53 ++--- .../catatumbo/impl/IdentifierMetadata.java | 78 +++++++- .../catatumbo/impl/IntrospectionUtils.java | 21 +- .../jmethods/catatumbo/impl/KeyMetadata.java | 2 +- .../jmethods/catatumbo/impl/Marshaller.java | 1 + .../catatumbo/impl/PropertyMetadata.java | 6 +- .../jmethods/catatumbo/CustomTypeTest.java | 51 +++++ .../catatumbo/entities/CustomTypeEntity.java | 63 ++++++ .../catatumbo/mappers/ByteMapper.java | 55 +++++ 14 files changed, 286 insertions(+), 303 deletions(-) delete mode 100644 src/main/java/com/jmethods/catatumbo/impl/DataType.java create mode 100644 src/test/java/com/jmethods/catatumbo/CustomTypeTest.java create mode 100644 src/test/java/com/jmethods/catatumbo/entities/CustomTypeEntity.java create mode 100644 src/test/java/com/jmethods/catatumbo/mappers/ByteMapper.java diff --git a/src/main/java/com/jmethods/catatumbo/Entity.java b/src/main/java/com/jmethods/catatumbo/Entity.java index 6350f15..3aad842 100644 --- a/src/main/java/com/jmethods/catatumbo/Entity.java +++ b/src/main/java/com/jmethods/catatumbo/Entity.java @@ -20,8 +20,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import com.jmethods.catatumbo.impl.DataType; - /** * Specifies that a class is an entity. Classes with this annotation can be * managed by the {@link EntityManager} to save objects to the Cloud Datastore @@ -48,8 +46,13 @@ * will hold the full key to the parent entity. *
  • Field annotated with @{@link ParentKey} must have a data type of * {@link DatastoreKey}.
  • - *
  • All other fields can be any of the types defined in the {@link DataType} - * enum and may have @{@link Property} annotation.
  • + *
  • May have zero or more embedded objects with an annotation + * of @{@link Embedded}. + *
  • All other fields can be any of the types for which a {@link Mapper} + * exists. The framework provides Mappers for various commonly used types. + * CustomMappers can be specified using + * {@link MapperFactory#setDefaultMapper(java.lang.reflect.Type, Mapper)} or + * {@link PropertyMapper} annotation.
  • * * * @author Sai Pullabhotla diff --git a/src/main/java/com/jmethods/catatumbo/MapperFactory.java b/src/main/java/com/jmethods/catatumbo/MapperFactory.java index f5b6187..e417943 100644 --- a/src/main/java/com/jmethods/catatumbo/MapperFactory.java +++ b/src/main/java/com/jmethods/catatumbo/MapperFactory.java @@ -197,11 +197,7 @@ private Mapper createMapper(Type type) { */ private Mapper createMapper(Class clazz) { Mapper mapper; - if (Date.class.isAssignableFrom(clazz)) { - mapper = new DateMapper(); - } else if (Calendar.class.isAssignableFrom(clazz)) { - mapper = new CalendarMapper(); - } else if (Enum.class.isAssignableFrom(clazz)) { + if (Enum.class.isAssignableFrom(clazz)) { mapper = new EnumMapper(clazz); } else if (List.class.isAssignableFrom(clazz)) { mapper = new ListMapper(clazz); @@ -269,6 +265,8 @@ private void createDefaultMappers() { cache.put(BigDecimal.class, new BigDecimalMapper()); cache.put(byte[].class, new ByteArrayMapper()); cache.put(char[].class, new CharArrayMapper()); + cache.put(Date.class, new DateMapper()); + cache.put(Calendar.class, new CalendarMapper()); cache.put(GeoLocation.class, new GeoLocationMapper()); cache.put(DatastoreKey.class, new KeyMapper()); } diff --git a/src/main/java/com/jmethods/catatumbo/impl/DataType.java b/src/main/java/com/jmethods/catatumbo/impl/DataType.java deleted file mode 100644 index 3ba1d08..0000000 --- a/src/main/java/com/jmethods/catatumbo/impl/DataType.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2016 Sai Pullabhotla. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.jmethods.catatumbo.impl; - -import java.math.BigDecimal; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.jmethods.catatumbo.DatastoreKey; -import com.jmethods.catatumbo.GeoLocation; - -/** - * Various data types supported by the Cloud Datastore and/or framework. - * - * @author Sai Pullabhotla - */ -public enum DataType { - /** - * Primitive boolean (Stored as Boolean in the Cloud Datastore). - */ - BOOLEAN(boolean.class), - /** - * Primitive char (Stored as String int he Cloud Datastore) - */ - CHARACTER(char.class), - /** - * Java Enum (Stored as String in the Cloud Datastore, by calling the - * toString() method. - */ - ENUM(Enum.class), - /** - * Primitive short (Stored as Integer in the Cloud Datastore). - */ - SHORT(short.class), - /** - * Primitive int (stored as Integer in the Cloud Datastore). - */ - INTEGER(int.class), - /** - * Primitive long (Stored as Integer in the Cloud Datastore). - */ - LONG(long.class), - /** - * Primitive float (Stored as Floating point number in the Cloud Datastore). - */ - FLOAT(float.class), - /** - * Primitive double (stored as Floating point number in the Cloud - * Datastore). - */ - DOUBLE(double.class), - /** - * Boolean wrapper (Stored as Boolean in the Cloud Datastore). - */ - BOOLEAN_OBJECT(Boolean.class), - /** - * Character wrapper (Stored as String in the Cloud Datastore). - */ - CHARACTER_OBJECT(Character.class), - /** - * Short wrapper (stored as Integer in the Cloud Datastore). - */ - SHORT_OBJECT(Short.class), - /** - * Integer wrapper (stored as Integer in the Cloud Datastore). - */ - INTEGER_OBJECT(Integer.class), - /** - * Long wrapper (stored as Integer in the Cloud Datastore). - */ - LONG_OBJECT(Long.class), - /** - * Float wrapper (stored as Floating point number in the Cloud Datastore). - */ - FLOAT_OBJECT(Float.class), - /** - * Double wrapper (stored as Floating point number in the Cloud Datastore). - */ - DOUBLE_OBJECT(Double.class), - /** - * java.lang.String (stored as String in the Cloud Datastore). - */ - STRING(String.class), - /** - * java.util.Date (stored as Date and Time in the Cloud Datastore). - */ - DATE(Date.class), - /** - * java.util.Calendar (stored as Date and Time in the Cloud Datastore). - */ - CALENDAR(Calendar.class), - /** - * Array of bytes (stored as Binary or Blob in the Cloud Datastore). - */ - BYTE_ARRAY(byte[].class), - /** - * Array of characters (stored as String in the Cloud Datastore). - */ - CHAR_ARRAY(char[].class), - - /** - * List object - */ - LIST(List.class), - - /** - * Set object - */ - SET(Set.class), - - /** - * Map object - */ - MAP(Map.class), - - /** - * Key (for an entity's full key, parent key, key references to other - * entities). - */ - KEY(DatastoreKey.class), - - /** - * Geo Location - */ - GEO_LOCATION(GeoLocation.class), - - /** - * BigDecimal - Maps to a Floating Point number in the Cloud Datastore. - */ - BIG_DECIMAL(BigDecimal.class); - - /** - * The Java class type - */ - private final Class dataClass; - - /** - * Creates a new instance of DataType. - * - * @param dataClass - * the Java class type. - */ - private DataType(Class dataClass) { - this.dataClass = dataClass; - } - - /** - * Returns the Java class type. - * - * @return the Java class type. - */ - public Class getDataClass() { - return dataClass; - } - - /** - * Returns the DataType for the given Java class type. - * - * @param dataClass - * the Java class type - * @return the corresponding DataType object - */ - public static DataType forClass(Class dataClass) { - for (DataType propertyType : values()) { - if (propertyType.getDataClass().isAssignableFrom(dataClass)) { - return propertyType; - } - } - return null; - } - -} diff --git a/src/main/java/com/jmethods/catatumbo/impl/DatastoreUtils.java b/src/main/java/com/jmethods/catatumbo/impl/DatastoreUtils.java index 8cc085a..a03167f 100644 --- a/src/main/java/com/jmethods/catatumbo/impl/DatastoreUtils.java +++ b/src/main/java/com/jmethods/catatumbo/impl/DatastoreUtils.java @@ -29,6 +29,7 @@ import com.jmethods.catatumbo.DatastoreKey; import com.jmethods.catatumbo.DefaultDatastoreKey; import com.jmethods.catatumbo.EntityManagerException; +import com.jmethods.catatumbo.impl.IdentifierMetadata.DataType; /** * Utility methods. diff --git a/src/main/java/com/jmethods/catatumbo/impl/EntityIntrospector.java b/src/main/java/com/jmethods/catatumbo/impl/EntityIntrospector.java index 9bd1b90..837eb21 100644 --- a/src/main/java/com/jmethods/catatumbo/impl/EntityIntrospector.java +++ b/src/main/java/com/jmethods/catatumbo/impl/EntityIntrospector.java @@ -18,9 +18,9 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import com.jmethods.catatumbo.DatastoreKey; import com.jmethods.catatumbo.Embedded; import com.jmethods.catatumbo.Entity; import com.jmethods.catatumbo.EntityManagerException; @@ -51,16 +51,6 @@ public class EntityIntrospector { */ private static Cache, EntityMetadata> cache = new Cache<>(64); - /** - * Data types that are valid for identifiers - */ - private static final DataType[] VALID_IDENTIFIER_TYPES = { DataType.LONG, DataType.LONG_OBJECT, DataType.STRING }; - - static { - // Sort the valid types so we can do binary search. - Arrays.sort(VALID_IDENTIFIER_TYPES); - } - /** * The class to introspect */ @@ -224,20 +214,10 @@ private List getAllFields() { * the identifier field */ private void processIdentifierField(Field field) { - String fieldName = field.getName(); Identifier identifier = field.getAnnotation(Identifier.class); boolean autoGenerated = identifier.autoGenerated(); Class type = field.getType(); - DataType dataType = DataType.forClass(type); - if (dataType == null) { - String message = String.format("Unknown or unsupported type, %s, for field %s in class %s. ", type, - fieldName, entityClass.getName()); - throw new EntityManagerException(message); - } - if (!isValidIdentifierType(dataType)) { - throw new EntityManagerException(String.format("Invalid identifier type: %s. ", type)); - } - IdentifierMetadata identifierMetadata = new IdentifierMetadata(field, dataType, autoGenerated); + IdentifierMetadata identifierMetadata = new IdentifierMetadata(field, autoGenerated); String readMethodName = IntrospectionUtils.getReadMethodName(field); Method readMethod = IntrospectionUtils.getReadMethod(entityClass, readMethodName, type); identifierMetadata.setReadMethod(readMethod); @@ -258,8 +238,7 @@ private void processIdentifierField(Field field) { private void processKeyField(Field field) { String fieldName = field.getName(); Class type = field.getType(); - DataType dataType = DataType.KEY; - if (!type.equals(dataType.getDataClass())) { + if (!type.equals(DatastoreKey.class)) { String message = String.format("Invalid type, %s, for Key field %s in class %s. ", type, fieldName, entityClass); throw new EntityManagerException(message); @@ -285,8 +264,7 @@ private void processKeyField(Field field) { private void processParentKeyField(Field field) { String fieldName = field.getName(); Class type = field.getType(); - DataType dataType = DataType.KEY; - if (!type.equals(dataType.getDataClass())) { + if (!type.equals(DatastoreKey.class)) { String message = String.format("Invalid type, %s, for ParentKey field %s in class %s. ", type, fieldName, entityClass); throw new EntityManagerException(message); @@ -331,11 +309,11 @@ private void processField(Field field) { * the metadata of the field that has the Version annotation. */ private void processVersionField(PropertyMetadata propertyMetadata) { - DataType dataType = propertyMetadata.getDataType(); - if (dataType != DataType.LONG) { + Class dataClass = propertyMetadata.getDeclaredType(); + if (!long.class.equals(dataClass)) { String messageFormat = "Field %s in class %s must be of type %s"; - throw new EntityManagerException(String.format(messageFormat, propertyMetadata.getField(), entityClass, - DataType.LONG.getDataClass())); + throw new EntityManagerException( + String.format(messageFormat, propertyMetadata.getField().getName(), entityClass, long.class)); } entityMetadata.setVersionMetadata(propertyMetadata); } @@ -375,19 +353,6 @@ private void processEmbeddedField(Field field) { entityMetadata.putEmbeddedMetadata(embeddedMetadata); } - /** - * Checks to see if the given data type is valid to be used as an - * identifier. - * - * @param dataType - * the data type to check - * @return true, if the given data type is valid to be used as an - * identifier; false, otherwise. - */ - private static boolean isValidIdentifierType(DataType dataType) { - return Arrays.binarySearch(VALID_IDENTIFIER_TYPES, dataType) >= 0; - } - /** * Convenient method for getting the metadata of the field used for * optimistic locking. diff --git a/src/main/java/com/jmethods/catatumbo/impl/FieldMetadata.java b/src/main/java/com/jmethods/catatumbo/impl/FieldMetadata.java index a5f6951..222ba8a 100644 --- a/src/main/java/com/jmethods/catatumbo/impl/FieldMetadata.java +++ b/src/main/java/com/jmethods/catatumbo/impl/FieldMetadata.java @@ -18,6 +18,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import com.jmethods.catatumbo.EntityManagerException; import com.jmethods.catatumbo.Mapper; import com.jmethods.catatumbo.MapperFactory; @@ -34,11 +35,6 @@ public abstract class FieldMetadata { */ protected Field field; - /** - * Data type - */ - protected DataType dataType; - /** * Read method (or getter method) for this field */ @@ -60,13 +56,10 @@ public abstract class FieldMetadata { * @param field * the field * - * @param dataType - * the data type */ - public FieldMetadata(Field field, DataType dataType) { + public FieldMetadata(Field field) { this.field = field; - this.dataType = dataType; - this.mapper = MapperFactory.getInstance().getMapper(field); + initializeMapper(); } /** @@ -87,25 +80,6 @@ public String getName() { return field.getName(); } - /** - * Returns the data type of the field. - * - * @return the data type of the field. - */ - public DataType getDataType() { - return dataType; - } - - /** - * Sets the feild's data type. - * - * @param dataType - * the data type. - */ - public void setDataType(DataType dataType) { - this.dataType = dataType; - } - /** * Returns the read method (or getter method) for this field. * @@ -144,15 +118,6 @@ public void setWriteMethod(Method writeMethod) { this.writeMethod = writeMethod; } - /** - * Returns the field's class (or type). - * - * @return the field's class (or type). - */ - public Class getDataClass() { - return dataType.getDataClass(); - } - /** * Returns the declared type of the field to which this metadata belongs. * @@ -174,4 +139,16 @@ public Mapper getMapper() { return mapper; } + private void initializeMapper() { + try { + this.mapper = MapperFactory.getInstance().getMapper(field); + } catch (Exception exp) { + String message = String.format( + "No suitable mapper found or error occurred creating a mapper for field %s in class %s", + field.getName(), field.getDeclaringClass().getName()); + throw new EntityManagerException(message, exp); + + } + } + } diff --git a/src/main/java/com/jmethods/catatumbo/impl/IdentifierMetadata.java b/src/main/java/com/jmethods/catatumbo/impl/IdentifierMetadata.java index a508b7a..8f95ccf 100644 --- a/src/main/java/com/jmethods/catatumbo/impl/IdentifierMetadata.java +++ b/src/main/java/com/jmethods/catatumbo/impl/IdentifierMetadata.java @@ -17,6 +17,8 @@ import java.lang.reflect.Field; +import com.jmethods.catatumbo.EntityManagerException; + /** * Objects of this class contain metadata about identifier field of an entity. * @@ -24,24 +26,85 @@ */ public class IdentifierMetadata extends FieldMetadata { + /** + * Valid identifier types. + * + * @author Sai Pullabhotla + * + */ + public enum DataType { + /** + * Primitive long + */ + LONG(long.class), + /** + * Wrapper Long + */ + LONG_OBJECT(Long.class), + /** + * String + */ + STRING(String.class); + + /** + * Data class + */ + private final Class dataClass; + + /** + * Creates a new instance of DataType. + * + * @param dataClass + * the type/class for the data type + */ + private DataType(Class dataClass) { + this.dataClass = dataClass; + } + + /** + * Returns the DataType for the given class. + * + * @param dataClass + * the class + * @return the DataType for the given class. + */ + public static DataType forClass(Class dataClass) { + for (DataType dataType : DataType.values()) { + if (dataType.dataClass.equals(dataClass)) { + return dataType; + } + } + return null; + } + } + /** * If identifier is to be auto generated or not */ private boolean autoGenerated; + /** + * Data type of the identifier + */ + private final DataType dataType; + /** * Creates a new instance of IdentifierMetadata. * * @param field * the field - * @param dataType - * the identifier data type * @param autoGenerated * if the identifier is to be generated automatically */ - public IdentifierMetadata(Field field, DataType dataType, boolean autoGenerated) { - super(field, dataType); + public IdentifierMetadata(Field field, boolean autoGenerated) { + super(field); this.autoGenerated = autoGenerated; + this.dataType = DataType.forClass(field.getType()); + if (this.dataType == null) { + String message = String.format("Invalid identifier type %s for field %s in class %s", + field.getType().getName(), field.getName(), field.getDeclaringClass().getName()); + throw new EntityManagerException(message); + } } /** @@ -65,4 +128,11 @@ public void setAutoGenerated(boolean autoGenerated) { this.autoGenerated = autoGenerated; } + /** + * @return the dataType + */ + public DataType getDataType() { + return dataType; + } + } diff --git a/src/main/java/com/jmethods/catatumbo/impl/IntrospectionUtils.java b/src/main/java/com/jmethods/catatumbo/impl/IntrospectionUtils.java index 79c28ef..74dd6fc 100644 --- a/src/main/java/com/jmethods/catatumbo/impl/IntrospectionUtils.java +++ b/src/main/java/com/jmethods/catatumbo/impl/IntrospectionUtils.java @@ -57,14 +57,6 @@ public static PropertyMetadata getPropertyMetadata(Field field) { String fieldName = field.getName(); String mappedName = null; boolean indexed = true; - Class fieldType = field.getType(); - - DataType dataType = DataType.forClass(fieldType); - if (dataType == null) { - String message = String.format("Unknown or unsupported type, %s, for field %s in class %s. ", fieldType, - fieldName, field.getDeclaringClass().getName()); - throw new EntityManagerException(message); - } Property property = field.getAnnotation(Property.class); if (property != null) { @@ -75,7 +67,7 @@ public static PropertyMetadata getPropertyMetadata(Field field) { mappedName = fieldName; } - PropertyMetadata propertyMetadata = new PropertyMetadata(field, mappedName, dataType, indexed); + PropertyMetadata propertyMetadata = new PropertyMetadata(field, mappedName, indexed); // For fields that have @Property annotation, we expect both setter and // getter methods. For all other fields, we only treat them as @@ -102,20 +94,17 @@ public static PropertyMetadata getPropertyMetadata(Field field) { public static Method getReadMethod(PropertyMetadata propertyMetadata) { Field field = propertyMetadata.getField(); Method readMethod; - switch (propertyMetadata.getDataType()) { - case BOOLEAN: + if (boolean.class.equals(propertyMetadata.getDeclaredType())) { String booleanReadMethodName = getReadMethodNameForBoolean(field); try { readMethod = getReadMethod(propertyMetadata, booleanReadMethodName); - break; + return readMethod; } catch (EntityManagerException exp) { // Do nothing... try the default option - getXXX method. } - default: - String readMethodName = getReadMethodName(field); - readMethod = getReadMethod(propertyMetadata, readMethodName); - break; } + String readMethodName = getReadMethodName(field); + readMethod = getReadMethod(propertyMetadata, readMethodName); return readMethod; } diff --git a/src/main/java/com/jmethods/catatumbo/impl/KeyMetadata.java b/src/main/java/com/jmethods/catatumbo/impl/KeyMetadata.java index ea0603f..cc1e503 100644 --- a/src/main/java/com/jmethods/catatumbo/impl/KeyMetadata.java +++ b/src/main/java/com/jmethods/catatumbo/impl/KeyMetadata.java @@ -31,7 +31,7 @@ public class KeyMetadata extends FieldMetadata { * the field. */ public KeyMetadata(Field field) { - super(field, DataType.KEY); + super(field); } } diff --git a/src/main/java/com/jmethods/catatumbo/impl/Marshaller.java b/src/main/java/com/jmethods/catatumbo/impl/Marshaller.java index 197f91c..cb1bb67 100644 --- a/src/main/java/com/jmethods/catatumbo/impl/Marshaller.java +++ b/src/main/java/com/jmethods/catatumbo/impl/Marshaller.java @@ -31,6 +31,7 @@ import com.google.cloud.datastore.ValueBuilder; import com.jmethods.catatumbo.DatastoreKey; import com.jmethods.catatumbo.EntityManagerException; +import com.jmethods.catatumbo.impl.IdentifierMetadata.DataType; /** * Converts application's entities (POJOs) to the format needed for the diff --git a/src/main/java/com/jmethods/catatumbo/impl/PropertyMetadata.java b/src/main/java/com/jmethods/catatumbo/impl/PropertyMetadata.java index ca3d123..d1f505b 100644 --- a/src/main/java/com/jmethods/catatumbo/impl/PropertyMetadata.java +++ b/src/main/java/com/jmethods/catatumbo/impl/PropertyMetadata.java @@ -41,13 +41,11 @@ public class PropertyMetadata extends FieldMetadata { * the field * @param mappedName * the property name in the Cloud Datastore - * @param dataType - * the data type. * @param indexed * whether or not to index */ - public PropertyMetadata(Field field, String mappedName, DataType dataType, boolean indexed) { - super(field, dataType); + public PropertyMetadata(Field field, String mappedName, boolean indexed) { + super(field); this.mappedName = mappedName; this.indexed = indexed; } diff --git a/src/test/java/com/jmethods/catatumbo/CustomTypeTest.java b/src/test/java/com/jmethods/catatumbo/CustomTypeTest.java new file mode 100644 index 0000000..0138af0 --- /dev/null +++ b/src/test/java/com/jmethods/catatumbo/CustomTypeTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016 Sai Pullabhotla. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.jmethods.catatumbo; + +import static org.junit.Assert.*; + +import org.junit.BeforeClass; +import org.junit.Test; + +import com.jmethods.catatumbo.entities.CustomTypeEntity; +import com.jmethods.catatumbo.mappers.ByteMapper; + +/** + * @author Sai Pullabhotla + * + */ + +public class CustomTypeTest { + + private static EntityManager em = null; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + MapperFactory.getInstance().setDefaultMapper(byte.class, new ByteMapper()); + em = TestUtils.getEntityManager(); + em.deleteAll(CustomTypeEntity.class); + } + + @Test + public void testInsert() { + CustomTypeEntity entity = new CustomTypeEntity(); + entity.setByteField((byte) 65); + entity = em.insert(entity); + assertEquals((byte) 65, entity.getByteField()); + } + +} diff --git a/src/test/java/com/jmethods/catatumbo/entities/CustomTypeEntity.java b/src/test/java/com/jmethods/catatumbo/entities/CustomTypeEntity.java new file mode 100644 index 0000000..47c59b9 --- /dev/null +++ b/src/test/java/com/jmethods/catatumbo/entities/CustomTypeEntity.java @@ -0,0 +1,63 @@ +/* + * Copyright 2016 Sai Pullabhotla. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.jmethods.catatumbo.entities; + +import com.jmethods.catatumbo.Entity; +import com.jmethods.catatumbo.Identifier; + +/** + * @author Sai Pullabhotla + * + */ +@Entity +public class CustomTypeEntity { + @Identifier + private Long id; + + private byte byteField; + + /** + * @return the id + */ + public Long getId() { + return id; + } + + /** + * @param id + * the id to set + */ + public void setId(Long id) { + this.id = id; + } + + /** + * @return the byteField + */ + public byte getByteField() { + return byteField; + } + + /** + * @param byteField + * the byteField to set + */ + public void setByteField(byte byteField) { + this.byteField = byteField; + } + +} diff --git a/src/test/java/com/jmethods/catatumbo/mappers/ByteMapper.java b/src/test/java/com/jmethods/catatumbo/mappers/ByteMapper.java new file mode 100644 index 0000000..e250c3d --- /dev/null +++ b/src/test/java/com/jmethods/catatumbo/mappers/ByteMapper.java @@ -0,0 +1,55 @@ +/* + * Copyright 2016 Sai Pullabhotla. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.jmethods.catatumbo.mappers; + +import com.google.cloud.datastore.LongValue; +import com.google.cloud.datastore.NullValue; +import com.google.cloud.datastore.Value; +import com.google.cloud.datastore.ValueBuilder; +import com.jmethods.catatumbo.Mapper; +import com.jmethods.catatumbo.MappingException; + +/** + * An implementation of {@link Mapper} for mapping primitive and wrapper byte + * types to/from the Cloud Datastore. + * + * @author Sai Pullabhotla + * + */ +public class ByteMapper implements Mapper { + + @Override + public ValueBuilder toDatastore(Object input) { + if (input == null) { + return NullValue.newBuilder(); + } + return LongValue.newBuilder((byte) input); + } + + @Override + public Object toModel(Value input) { + if (input instanceof NullValue) { + return null; + } + Long l = ((LongValue) input).get(); + if (l < Byte.MIN_VALUE || l > Byte.MAX_VALUE) { + throw new MappingException(String.format("Value %d is out of range for byte type", l)); + } + return l.byteValue(); + } + +}