From 7cb185a446df5fb3debb74aefb5b6b085012eaca Mon Sep 17 00:00:00 2001
From: Ross Bamford
Date: Tue, 2 Jul 2013 02:12:53 +0100
Subject: [PATCH] Address issue #10: * Give more meaningful exceptions from
Entity#load() * Remove default type mapping (it doesn't work with the new
load API) * Add mapping for java.util.Date.
Thanks to rothschild86 for this bug report.
---
.../roscopeco/ormdroid/DateTypeMapping.java | 53 +++++++++++++++++++
src/com/roscopeco/ormdroid/Entity.java | 17 ++++--
src/com/roscopeco/ormdroid/TypeMapper.java | 17 +++---
3 files changed, 74 insertions(+), 13 deletions(-)
create mode 100644 src/com/roscopeco/ormdroid/DateTypeMapping.java
diff --git a/src/com/roscopeco/ormdroid/DateTypeMapping.java b/src/com/roscopeco/ormdroid/DateTypeMapping.java
new file mode 100644
index 0000000..12a68fa
--- /dev/null
+++ b/src/com/roscopeco/ormdroid/DateTypeMapping.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Ross Bamford
+ *
+ * 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.roscopeco.ormdroid;
+
+import java.util.Date;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+/*
+ * Map java.util.Date to the database.
+ *
+ * This implementation just stashes the number of seconds
+ * since the epoch in a BIGINT.
+ */
+public class DateTypeMapping implements TypeMapping {
+ private Class> mJavaType;
+ private String mSqlType;
+
+ public DateTypeMapping() {
+ mJavaType = Date.class;
+ mSqlType = "BIGINT";
+ }
+
+ public Class> javaType() {
+ return mJavaType;
+ }
+
+ public String sqlType(Class> concreteType) {
+ return mSqlType;
+ }
+
+ public String encodeValue(SQLiteDatabase db, Object value) {
+ return "\"" + ((Date)value).getTime() + "\"";
+ }
+
+ public Object decodeValue(SQLiteDatabase db, Class> expectedType, Cursor c, int columnIndex) {
+ return new Date(c.getInt(columnIndex));
+ }
+}
\ No newline at end of file
diff --git a/src/com/roscopeco/ormdroid/Entity.java b/src/com/roscopeco/ormdroid/Entity.java
index cb6591a..3d60a41 100644
--- a/src/com/roscopeco/ormdroid/Entity.java
+++ b/src/com/roscopeco/ormdroid/Entity.java
@@ -174,7 +174,6 @@ static final class EntityMapping {
static EntityMapping build(Class extends Entity> clz) {
EntityMapping mapping = new EntityMapping();
mapping.mMappedClass = clz;
-
Table table = clz.getAnnotation(Table.class);
if (table != null) {
mapping.mTableName = table.name();
@@ -207,6 +206,15 @@ static EntityMapping build(Class extends Entity> clz) {
(!Modifier.isPrivate(modifiers) || force) &&
!seenFields.contains(f.getName()) &&
!inverse) {
+
+ // Check we can map this type - if not, let's fail fast.
+ // This will save us wierd exceptions somewhere down the line...
+ if (TypeMapper.getMapping(f.getType()) == null) {
+ throw new TypeMappingException("Model " +
+ clz.getName() +
+ " has unmappable field: " + f);
+ }
+
Column col = f.getAnnotation(Column.class);
String name;
@@ -466,12 +474,15 @@ T load(SQLiteDatabase db, Cursor c) {
}
return model;
- } catch (Exception e) {
+ } catch (InstantiationException e) {
throw new ORMDroidException(
"Failed to instantiate model class - does it have a public null constructor?",
e);
+ } catch (IllegalAccessException e) {
+ throw new ORMDroidException(
+ "Access denied. Is your model's constructor non-public?",
+ e);
}
-
}
/*
diff --git a/src/com/roscopeco/ormdroid/TypeMapper.java b/src/com/roscopeco/ormdroid/TypeMapper.java
index 6f5675f..f1a8de5 100644
--- a/src/com/roscopeco/ormdroid/TypeMapper.java
+++ b/src/com/roscopeco/ormdroid/TypeMapper.java
@@ -22,8 +22,9 @@
*
* By default, ORMDroid provides a mapping for all primitive types
* and Entity classes. All other types are mapped by the default
- * mapping, which simply stores their toString
results
- * in a VARCHAR
column.
+ * mapping (if configured with {@link #setDefaultMapping(TypeMapping)}).
+ * By default, there is no default mapping - attempting to use a model class
+ * with a field of an unmapped type will throw an exception.
*
* Custom types may be mapped by registering an instance of
* {@link TypeMapping} via the {@link #mapType(TypeMapping) mapType} method.
@@ -41,7 +42,7 @@
*/
public final class TypeMapper {
private static final MappingList TYPEMAPS = new MappingList();
- private static TypeMapping mDefaultMapping = new StringTypeMapping(Object.class, "VARCHAR");
+ private static TypeMapping mDefaultMapping = null;
public static String sqlType(Class> type) {
return getMapping(type).sqlType(type);
@@ -51,19 +52,14 @@ public static String sqlType(Class> type) {
* Obtain the configured mapping the the specified Java type.
*
* @param type the Java type.
- * @return the configured mapping.
+ * @return the configured mapping, or null
if none.
*/
public static TypeMapping getMapping(Class> type) {
TypeMapping r = TYPEMAPS.findMapping(type);
if (r != null) {
return r;
} else {
- TypeMapping def = mDefaultMapping;
- if (def != null) {
- return def;
- } else {
- throw new TypeMappingException("No mapping found for type `" + type + "'");
- }
+ return mDefaultMapping;
}
}
@@ -115,6 +111,7 @@ public static void setDefaultMapping(TypeMapping mapping) {
mapType(new NumericTypeMapping(boolean.class, "TINYINT"));
mapType(new NumericTypeMapping(Long.class, "BIGINT"));
mapType(new NumericTypeMapping(long.class, "BIGINT"));
+ mapType(new DateTypeMapping());
mapType(new EntityTypeMapping());
mapType(new NumericTypeMapping(Integer.class, "INTEGER"));
mapType(new NumericTypeMapping(int.class, "INTEGER"));