From ae0a82eec28f70c9b63d1b9108d8ef52e5d00e08 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Mon, 13 Oct 2014 20:33:31 +0200 Subject: [PATCH 01/33] Bumped Gradle and Android build tools to 2.1 and 0.13 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index e4b66f446f..03835145eb 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:0.12.2' + classpath 'com.android.tools.build:gradle:0.13.+' classpath 'de.undercouch:gradle-download-task:1.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:0.5' // Version 0.6 seems to require gradle 2.0+ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0e1f2d976d..097aec6d5e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-1.12-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip diff --git a/settings.gradle b/settings.gradle index 898f839d2c..a789dae24a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,5 @@ -include 'realm', ':introExample', ':gridViewExample', ':concurrencyExample', ':encryptionExample', ':migrationExample' +include ':realm', ':introExample', ':gridViewExample', ':concurrencyExample', ':encryptionExample', ':migrationExample' +//include ':realm-annotations-processor' include ':performance' project(':performance').projectDir = new File('examples/performance') From a636ce199ef481e896655bc88758952d5ad74802 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Mon, 13 Oct 2014 20:34:53 +0200 Subject: [PATCH 02/33] Basic JSON import of simple data types. --- .../realm/processor/RealmJsonTypeHelper.java | 96 +++++++++++++++++++ .../processor/RealmProxyClassGenerator.java | 44 ++++++++- .../java/io/realm/RealmJsonTest.java | 78 +++++++++++++++ realm/src/main/java/io/realm/Realm.java | 52 +++++++++- realm/src/main/java/io/realm/RealmObject.java | 16 +++- .../io/realm/exceptions/RealmException.java | 5 + 6 files changed, 280 insertions(+), 11 deletions(-) create mode 100644 realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java create mode 100644 realm/src/androidTest/java/io/realm/RealmJsonTest.java diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java new file mode 100644 index 0000000000..f440d78608 --- /dev/null +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java @@ -0,0 +1,96 @@ +package io.realm.processor; + +import com.squareup.javawriter.JavaWriter; + +import java.io.IOException; +import java.util.HashMap; + +/** + * Helper class for converting between Json types and data types in Java that are supported by Realm. + */ +public class RealmJsonTypeHelper { + + private static final HashMap JAVA_TO_JSON_TYPES; + + static { + JAVA_TO_JSON_TYPES = new HashMap(); + + JAVA_TO_JSON_TYPES.put("byte", new SimpleTypeConverter("byte", "Int")); + JAVA_TO_JSON_TYPES.put("short", new SimpleTypeConverter("short", "Int")); + JAVA_TO_JSON_TYPES.put("int", new SimpleTypeConverter("int", "Int")); + JAVA_TO_JSON_TYPES.put("long", new SimpleTypeConverter("long", "Long")); + JAVA_TO_JSON_TYPES.put("float", new SimpleTypeConverter("float", "Double")); + JAVA_TO_JSON_TYPES.put("double", new SimpleTypeConverter("double", "Double")); + JAVA_TO_JSON_TYPES.put("boolean", new SimpleTypeConverter("boolean", "Boolean")); + JAVA_TO_JSON_TYPES.put("Byte", new SimpleTypeConverter("Byte", "Int")); + JAVA_TO_JSON_TYPES.put("Short", new SimpleTypeConverter("Short", "Int")); + JAVA_TO_JSON_TYPES.put("Integer", new SimpleTypeConverter("Integer", "Int")); + JAVA_TO_JSON_TYPES.put("Long", new SimpleTypeConverter("Long", "Long")); + JAVA_TO_JSON_TYPES.put("Float", new SimpleTypeConverter("Float", "Double")); + JAVA_TO_JSON_TYPES.put("Double", new SimpleTypeConverter("Double", "Double")); + JAVA_TO_JSON_TYPES.put("Boolean", new SimpleTypeConverter("Boolean", "Boolean")); + JAVA_TO_JSON_TYPES.put("java.lang.String", new SimpleTypeConverter("String", "String")); + + + JAVA_TO_JSON_TYPES.put("java.util.Date", new JsonToRealmTypeConverter() { + @Override + public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { +// throw new RuntimeException("Date not supported yet"); + } + }); // ISO-8601 encoded or Unix timestamp or /Date(unix timestamp)/ + + + JAVA_TO_JSON_TYPES.put("byte[]", new JsonToRealmTypeConverter() { + @Override + public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { +// throw new RuntimeException("byte[] not supported yet"); + } + }); // Hex 64 encoded + } + + public static void emitFillFieldWithJsonValue(String fieldName, String fieldType, JavaWriter writer) throws IOException { + if (JAVA_TO_JSON_TYPES.containsKey(fieldType)) { + writer.beginControlFlow("if (json.has(\"%s\"))", fieldName); + JAVA_TO_JSON_TYPES.get(fieldType).emitTypeConversion(fieldName, fieldType, writer); + writer.endControlFlow(); + + } else { +// throw new IllegalArgumentException("Java type not support in Json import: " + fieldType); + } + } + + private static String capitaliseFirstChar(String input) { + return input.substring(0, 1).toUpperCase() + input.substring(1); + } + + static class SimpleTypeConverter implements JsonToRealmTypeConverter { + + private final String castType; + private final String jsonType; + + /** + * Create a conversion between simple types that can be expressed of the form + * RealmObject.setFieldName(() json.get) + * + * @param castType Java type to cast to. + * @param jsonType JsonType to get data from. + */ + private SimpleTypeConverter(String castType, String jsonType) { + this.castType = castType; + this.jsonType = jsonType; + } + + @Override + public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { + writer.emitStatement("set%s((%s) json.get%s(\"%s\"))", + capitaliseFirstChar(fieldName), + castType, + jsonType, + fieldName); + } + } + + private interface JsonToRealmTypeConverter { + public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException; + } +} diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java index 0e15569362..d9f7d75e7b 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java @@ -18,6 +18,16 @@ import com.squareup.javawriter.JavaWriter; +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Modifier; import javax.lang.model.element.VariableElement; @@ -26,10 +36,6 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.JavaFileObject; -import java.io.BufferedWriter; -import java.io.IOException; -import java.lang.String; -import java.util.*; public class RealmProxyClassGenerator { private ProcessingEnvironment processingEnvironment; @@ -211,7 +217,6 @@ public void generate() throws IOException, UnsupportedOperationException { writer.emitPackage(REALM_PACKAGE_NAME) .emitEmptyLine(); - writer.emitImports( "io.realm.internal.ColumnType", "io.realm.internal.Table", @@ -220,6 +225,8 @@ public void generate() throws IOException, UnsupportedOperationException { "io.realm.internal.LinkView", "io.realm.RealmList", "io.realm.RealmObject", + "org.json.JSONObject", + "org.json.JSONException", "java.util.*", packageName + ".*") .emitEmptyLine(); @@ -604,11 +611,38 @@ else if (typeUtils.isAssignable(field.asType(), realmObject) || typeUtils.isAssi writer.endMethod(); writer.emitEmptyLine(); + // Add JSON methods + emitPopulateFromJsonObjectMethod(writer); +// emitPopulateFromJsonStreamMethod(writer); + // End the class definition writer.endType(); writer.close(); } + private void emitPopulateFromJsonObjectMethod(JavaWriter writer) throws IOException { + writer.emitAnnotation(Override.class); + writer.beginMethod( + "void", + "populateFromJsonObject", + EnumSet.of(Modifier.PROTECTED), + Arrays.asList("JSONObject", "json"), + Arrays.asList("JSONException")); + + for (VariableElement field : fields) { + String fieldName = field.getSimpleName().toString(); + String fieldTypeCanonicalName = field.asType().toString(); + RealmJsonTypeHelper.emitFillFieldWithJsonValue(fieldName, fieldTypeCanonicalName, writer); + } + + writer.endMethod(); + writer.emitEmptyLine(); + } + + private void emitPopulateFromJsonStreamMethod(JavaWriter writer) { +// throw new RuntimeException("Not implemented"); + } + private static String capitaliseFirstChar(String input) { return input.substring(0, 1).toUpperCase() + input.substring(1); } diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java new file mode 100644 index 0000000000..60a22b8026 --- /dev/null +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -0,0 +1,78 @@ +package io.realm; + +import android.test.AndroidTestCase; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import io.realm.entities.AllTypes; + +public class RealmJsonTest extends AndroidTestCase { + + protected Realm testRealm; + + @Override + protected void setUp() throws Exception { + Realm.deleteRealmFile(getContext()); + testRealm = Realm.getInstance(getContext()); + } + + public void testImportJSonNullObject() { + testRealm.createFromJson(AllTypes.class, (JSONObject) null); + assertEquals(0, testRealm.allObjects(AllTypes.class).size()); + } + + public void testImportJSonNullArray() { + testRealm.addFromJson(AllTypes.class, (JSONArray) null); + assertEquals(0, testRealm.allObjects(AllTypes.class).size()); + + } + + public void testImportJSonAllSimpSimpleObjectAllTypes() throws JSONException { + JSONObject json = new JSONObject(); + json.put("columnString", "String"); + json.put("columnLong", 1l); + json.put("columnFloat", 1.23f); + json.put("columnDouble", 1.23d); + json.put("columnBoolean", true); +// json.put("columnDate", new Date(100).toString()); // ISO 8601 encoded +// json.put("columnBinary", Base64.encode(new byte[] {1, 2, 3}, Base64.DEFAULT)); // Base 64 encoded + + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, json); + testRealm.commitTransaction(); + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + + // Check that all primative types are imported correctly + assertEquals("String", obj.getColumnString()); + assertEquals(1l, obj.getColumnLong()); + assertEquals(1.23f, obj.getColumnFloat()); + assertEquals(1.23d, obj.getColumnDouble()); + assertEquals(true, obj.isColumnBoolean()); +// assertEquals(new Date(100), obj.getColumnDate()); +// assertEquals(new byte[] {1, 2, 3}, obj.getColumnBinary()); + } + + public void testImportJSonNestedObjects() { + fail("Not implemented."); + } + + + public void testRemoveObjetsIfImportFail() { + fail("Test if objects created by the json import is removed if something fail during decoding"); + } + + public void testImportJsonNullStream() { + testRealm.addFromJson(AllTypes.class, (JSONArray) null); + assertEquals(0, testRealm.allObjects(AllTypes.class).size()); + } + + public void testImportJsonStreamAllTypes() { + fail("Not implemented."); + } + + public void testImportJsonStreamNestedTypes() { + fail("Not implemented."); + } +} diff --git a/realm/src/main/java/io/realm/Realm.java b/realm/src/main/java/io/realm/Realm.java index 1692b86929..142e4bc78f 100644 --- a/realm/src/main/java/io/realm/Realm.java +++ b/realm/src/main/java/io/realm/Realm.java @@ -22,7 +22,11 @@ import android.os.Message; import android.util.Log; +import org.json.JSONArray; +import org.json.JSONObject; + import java.io.File; +import java.io.InputStream; import java.lang.ref.SoftReference; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -69,8 +73,8 @@ public class Realm { private final LooperThread looperThread = LooperThread.getInstance(); private final SharedGroup sharedGroup; private final ImplicitTransaction transaction; - private final Map, String> simpleClassNames = new HashMap, String>(); - private final Map> generatedClasses = new HashMap>(); + private final Map, String> simpleClassNames = new HashMap, String>(); // Map between original class and their class name + private final Map> generatedClasses = new HashMap>(); // Map between generated class names and their implementation private final Map, Constructor> constructors = new HashMap, Constructor>(); private final Map, Method> initTableMethods = new HashMap, Method>(); private final Map, Constructor> generatedConstructors = new HashMap, Constructor>(); @@ -263,7 +267,7 @@ private static Realm createAndValidate(String absolutePath, byte[] key, boolean for (String className : proxyClasses) { String[] splitted = className.split("\\."); String modelClassName = splitted[splitted.length - 1]; - String generatedClassName = "io.realm." + modelClassName + "RealmProxy"; + String generatedClassName = getProxyClassName(modelClassName); Class generatedClass; try { generatedClass = Class.forName(generatedClassName); @@ -344,6 +348,40 @@ private static Realm createAndValidate(String absolutePath, byte[] key, boolean return realm; } + public void addFromJson(Class clazz, JSONArray json) { + } + + /** + * Add a JsonObject to the Realm as a new object. This must be done inside a transaction. + * + * @param clazz Class of object the json will map to. + * @param json JsonObject that can map to the chosen clazz. Properties not in the class are ignored. + * @return Object with data or null if no json data was provided. + * + * @throws RealmException if the mapping fail. + */ + + public E createFromJson(Class clazz, JSONObject json) { + if (json == null) return null; + + E obj = createObject(clazz); + try { + obj.populateFromJsonObject(json); + } catch (Exception e) { + // TODO Remove object from realm + throw new RealmException("Could not map Json", e); + } + + return obj; + } + + public void addFromJson(Class clazz, InputStream inputStream) { + + } + + + + // This class stores soft-references to realm objects per thread per realm file private static class ThreadRealm extends ThreadLocal> { private String absolutePath; @@ -377,7 +415,7 @@ public E createObject(Class clazz) { simpleClassName = clazz.getSimpleName(); simpleClassNames.put(clazz, simpleClassName); } - String generatedClassName = "io.realm." + simpleClassName + "RealmProxy"; + String generatedClassName = getProxyClassName(simpleClassName); Class generatedClass = generatedClasses.get(generatedClassName); if (generatedClass == null) { @@ -442,7 +480,7 @@ E get(Class clazz, long rowIndex) { simpleClassName = clazz.getSimpleName(); simpleClassNames.put(clazz, simpleClassName); } - String generatedClassName = "io.realm." + simpleClassName + "RealmProxy"; + String generatedClassName = getProxyClassName(simpleClassName); Class generatedClass = generatedClasses.get(generatedClassName); @@ -484,6 +522,10 @@ E get(Class clazz, long rowIndex) { return result; } + private static String getProxyClassName(String simpleClassName) { + return "io.realm." + simpleClassName + "RealmProxy"; + } + boolean contains(Class clazz) { String simpleClassName = simpleClassNames.get(clazz); if (simpleClassName == null) { diff --git a/realm/src/main/java/io/realm/RealmObject.java b/realm/src/main/java/io/realm/RealmObject.java index 85019d24ec..88a03262f8 100644 --- a/realm/src/main/java/io/realm/RealmObject.java +++ b/realm/src/main/java/io/realm/RealmObject.java @@ -16,8 +16,14 @@ package io.realm; -import io.realm.internal.Row; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.io.InputStream; + import io.realm.annotations.RealmClass; +import io.realm.internal.Row; /** * In Realm you define your model classes by subclassing RealmObject and adding fields to be @@ -52,4 +58,12 @@ protected Row realmGetRow() { protected void realmSetRow(Row row) { this.row = row; } + + protected void populateFromJsonObject(JSONObject json) throws JSONException { + throw new IllegalStateException("Only use this method on objects created or fetched in a Realm. Realm.createObject() or Realm.where()"); + } + + protected void populateFromJsonStream(InputStream inputStream) throws IOException { + throw new IllegalStateException("Only use this method on objects created or fetched in a Realm. Realm.createObject() or Realm.where()"); + } } diff --git a/realm/src/main/java/io/realm/exceptions/RealmException.java b/realm/src/main/java/io/realm/exceptions/RealmException.java index 04873de473..ce8057fd66 100644 --- a/realm/src/main/java/io/realm/exceptions/RealmException.java +++ b/realm/src/main/java/io/realm/exceptions/RealmException.java @@ -20,7 +20,12 @@ * RealmException is Realm specific exceptions. */ public class RealmException extends RuntimeException { + public RealmException(String detailMessage) { super(detailMessage); } + + public RealmException(String detailMessage, Throwable exception) { + super(detailMessage, exception); + } } From 2e512fa1aa5d62ca3d3d5d1936cb02ca58f25b20 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Tue, 14 Oct 2014 20:10:29 +0200 Subject: [PATCH 03/33] Added annotation processor project to global gradle settings. --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index a789dae24a..a2764840e4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ include ':realm', ':introExample', ':gridViewExample', ':concurrencyExample', ':encryptionExample', ':migrationExample' -//include ':realm-annotations-processor' +include ':realm-annotations-processor' include ':performance' project(':performance').projectDir = new File('examples/performance') From 50e3e08693809844bb333159192ca6f85d25b609 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Thu, 16 Oct 2014 08:48:15 +0200 Subject: [PATCH 04/33] Fixed references to version.txt. --- build.gradle | 3 +-- realm-annotations-processor/build.gradle | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 03835145eb..09b85fe924 100644 --- a/build.gradle +++ b/build.gradle @@ -12,8 +12,7 @@ buildscript { allprojects { group = 'io.realm' - version = new File('version.txt').text - + version = new File("${rootDir}/version.txt").text repositories { jcenter() } diff --git a/realm-annotations-processor/build.gradle b/realm-annotations-processor/build.gradle index 62307f5bfa..d6f06caf6a 100644 --- a/realm-annotations-processor/build.gradle +++ b/realm-annotations-processor/build.gradle @@ -1,6 +1,6 @@ apply plugin: 'java' -version = new File("${projectDir}/../version.txt").text +version = new File("${rootDir}/version.txt").text sourceCompatibility = '1.6' targetCompatibility = '1.6' From f9bcd2b5bd9006a2e9188fb7dc91538c6e99b0dc Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Thu, 16 Oct 2014 09:00:39 +0200 Subject: [PATCH 05/33] Support for importing single object references. --- .../realm/processor/RealmJsonTypeHelper.java | 33 ++++++++---- .../processor/RealmProxyClassGenerator.java | 22 ++++++-- .../java/io/realm/RealmJsonTest.java | 53 ++++++++++++++++--- 3 files changed, 87 insertions(+), 21 deletions(-) diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java index f440d78608..19ff8a25cc 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java @@ -30,15 +30,24 @@ public class RealmJsonTypeHelper { JAVA_TO_JSON_TYPES.put("Double", new SimpleTypeConverter("Double", "Double")); JAVA_TO_JSON_TYPES.put("Boolean", new SimpleTypeConverter("Boolean", "Boolean")); JAVA_TO_JSON_TYPES.put("java.lang.String", new SimpleTypeConverter("String", "String")); - - JAVA_TO_JSON_TYPES.put("java.util.Date", new JsonToRealmTypeConverter() { @Override public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { -// throw new RuntimeException("Date not supported yet"); + // TODO Add support for ISO 8601, but apparently there is a lot of edge cases. + // Currently support for long timestamp and "/Date(long)/" + writer .emitStatement("long timestamp = json.optLong(\"%s\", -1)", fieldName) + .beginControlFlow("if (timestamp > -1)") + .emitStatement("set%s(new Date(timestamp))", capitaliseFirstChar(fieldName)) + .nextControlFlow("else") + .emitStatement("String jsonDate = json.getString(\"%s\")", fieldName) + .beginControlFlow("if (!(jsonDate == null || jsonDate.length() == 0))") + .emitStatement("jsonDate = jsonDate.substring(6, jsonDate.length() - 2)") + .emitStatement("timestamp = Long.parseLong(jsonDate)") + .emitStatement("set%s(new Date(timestamp))", capitaliseFirstChar(fieldName)) + .endControlFlow() + .endControlFlow(); } - }); // ISO-8601 encoded or Unix timestamp or /Date(unix timestamp)/ - + }); JAVA_TO_JSON_TYPES.put("byte[]", new JsonToRealmTypeConverter() { @Override @@ -48,14 +57,11 @@ public void emitTypeConversion(String fieldName, String fieldType, JavaWriter wr }); // Hex 64 encoded } - public static void emitFillFieldWithJsonValue(String fieldName, String fieldType, JavaWriter writer) throws IOException { + public static void emitFillJavaTypeFieldWithJsonValue(String fieldName, String fieldType, JavaWriter writer) throws IOException { if (JAVA_TO_JSON_TYPES.containsKey(fieldType)) { writer.beginControlFlow("if (json.has(\"%s\"))", fieldName); JAVA_TO_JSON_TYPES.get(fieldType).emitTypeConversion(fieldName, fieldType, writer); writer.endControlFlow(); - - } else { -// throw new IllegalArgumentException("Java type not support in Json import: " + fieldType); } } @@ -63,6 +69,15 @@ private static String capitaliseFirstChar(String input) { return input.substring(0, 1).toUpperCase() + input.substring(1); } + public static void emitFillRealmObjectWithJsonValue(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { + writer + .beginControlFlow("if (json.has(\"%s\"))", fieldName) + .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) + .emitStatement("obj.populateFromJsonObject(json.getJSONObject(\"%s\"))", fieldName) + .emitStatement("set%s(obj)", capitaliseFirstChar(fieldName)) + .endControlFlow(); + } + static class SimpleTypeConverter implements JsonToRealmTypeConverter { private final String castType; diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java index d9f7d75e7b..ea34f16ace 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java @@ -47,6 +47,11 @@ public class RealmProxyClassGenerator { private static final String TABLE_PREFIX = "class_"; private static final String PROXY_SUFFIX = "RealmProxy"; + private Elements elementUtils; + private Types typeUtils; + private TypeMirror realmObject; + private DeclaredType realmList; + public RealmProxyClassGenerator(ProcessingEnvironment processingEnvironment, String className, String packageName, List fields, List fieldsToIndex) { this.processingEnvironment = processingEnvironment; this.className = className; @@ -206,11 +211,11 @@ public void generate() throws IOException, UnsupportedOperationException { JavaFileObject sourceFile = processingEnvironment.getFiler().createSourceFile(qualifiedGeneratedClassName); JavaWriter writer = new JavaWriter(new BufferedWriter(sourceFile.openWriter())); - Elements elementUtils = processingEnvironment.getElementUtils(); - Types typeUtils = processingEnvironment.getTypeUtils(); + elementUtils = processingEnvironment.getElementUtils(); + typeUtils = processingEnvironment.getTypeUtils(); - TypeMirror realmObject = elementUtils.getTypeElement("io.realm.RealmObject").asType(); - DeclaredType realmList = typeUtils.getDeclaredType(elementUtils.getTypeElement("io.realm.RealmList"), typeUtils.getWildcardType(null, null)); + realmObject = elementUtils.getTypeElement("io.realm.RealmObject").asType(); + realmList = typeUtils.getDeclaredType(elementUtils.getTypeElement("io.realm.RealmList"), typeUtils.getWildcardType(null, null)); // Set source code indent to 4 spaces writer.setIndent(" "); @@ -632,7 +637,14 @@ private void emitPopulateFromJsonObjectMethod(JavaWriter writer) throws IOExcept for (VariableElement field : fields) { String fieldName = field.getSimpleName().toString(); String fieldTypeCanonicalName = field.asType().toString(); - RealmJsonTypeHelper.emitFillFieldWithJsonValue(fieldName, fieldTypeCanonicalName, writer); + if (typeUtils.isAssignable(field.asType(), realmObject)) { + RealmJsonTypeHelper.emitFillRealmObjectWithJsonValue(fieldName, fieldTypeCanonicalName, writer); + } else if (typeUtils.isAssignable(field.asType(), realmList)) { + + } else { + RealmJsonTypeHelper.emitFillJavaTypeFieldWithJsonValue(fieldName, fieldTypeCanonicalName, writer); + } + } writer.endMethod(); diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index 60a22b8026..f7d5296f0a 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -1,11 +1,14 @@ package io.realm; import android.test.AndroidTestCase; +import android.util.Base64; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.util.Date; + import io.realm.entities.AllTypes; public class RealmJsonTest extends AndroidTestCase { @@ -16,6 +19,9 @@ public class RealmJsonTest extends AndroidTestCase { protected void setUp() throws Exception { Realm.deleteRealmFile(getContext()); testRealm = Realm.getInstance(getContext()); + testRealm.beginTransaction(); + testRealm.clear(AllTypes.class); + testRealm.commitTransaction(); } public void testImportJSonNullObject() { @@ -36,26 +42,59 @@ public void testImportJSonAllSimpSimpleObjectAllTypes() throws JSONException { json.put("columnFloat", 1.23f); json.put("columnDouble", 1.23d); json.put("columnBoolean", true); -// json.put("columnDate", new Date(100).toString()); // ISO 8601 encoded -// json.put("columnBinary", Base64.encode(new byte[] {1, 2, 3}, Base64.DEFAULT)); // Base 64 encoded + json.put("columnBinary", Base64.encode(new byte[] {1, 2, 3}, Base64.DEFAULT)); testRealm.beginTransaction(); testRealm.createFromJson(AllTypes.class, json); testRealm.commitTransaction(); AllTypes obj = testRealm.allObjects(AllTypes.class).first(); - // Check that all primative types are imported correctly + // Check that all primitive types are imported correctly assertEquals("String", obj.getColumnString()); assertEquals(1l, obj.getColumnLong()); assertEquals(1.23f, obj.getColumnFloat()); assertEquals(1.23d, obj.getColumnDouble()); assertEquals(true, obj.isColumnBoolean()); -// assertEquals(new Date(100), obj.getColumnDate()); -// assertEquals(new byte[] {1, 2, 3}, obj.getColumnBinary()); + assertEquals(new byte[]{1, 2, 3}, obj.getColumnBinary()); + } - public void testImportJSonNestedObjects() { - fail("Not implemented."); + public void testImportJSonDateAsLong() throws JSONException { + JSONObject json = new JSONObject(); + json.put("columnDate", 1000L); // Realm operates at seconds level granularity + + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, json); + testRealm.commitTransaction(); + + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals(new Date(1000), obj.getColumnDate()); + } + + public void testImportJSonDateAsString() throws JSONException { + JSONObject json = new JSONObject(); + json.put("columnDate", "/Date(1000)/"); + + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, json); + testRealm.commitTransaction(); + + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals(new Date(1000), obj.getColumnDate()); + } + + public void testImportJSonNestedObjects() throws JSONException { + JSONObject allTypesObject = new JSONObject(); + JSONObject dogObject = new JSONObject(); + dogObject.put("name", "Fido"); + allTypesObject.put("columnRealmObject", dogObject); + + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, allTypesObject); + testRealm.commitTransaction(); + + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals("Fido", obj.getColumnRealmObject().getName()); } From 725e85414857db09a12999cba600c288e1c3942c Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Thu, 16 Oct 2014 21:30:55 +0200 Subject: [PATCH 06/33] Support for importing RealmLists. --- .../realm/processor/RealmJsonTypeHelper.java | 14 +++++++- .../processor/RealmProxyClassGenerator.java | 16 +++++++-- .../java/io/realm/RealmJsonTest.java | 36 ++++++++++++++++++- .../java/io/realm/entities/AllTypes.java | 10 ++++++ 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java index 19ff8a25cc..1665e972c5 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java @@ -57,7 +57,7 @@ public void emitTypeConversion(String fieldName, String fieldType, JavaWriter wr }); // Hex 64 encoded } - public static void emitFillJavaTypeFieldWithJsonValue(String fieldName, String fieldType, JavaWriter writer) throws IOException { + public static void emitFillJavaTypeWithJsonValue(String fieldName, String fieldType, JavaWriter writer) throws IOException { if (JAVA_TO_JSON_TYPES.containsKey(fieldType)) { writer.beginControlFlow("if (json.has(\"%s\"))", fieldName); JAVA_TO_JSON_TYPES.get(fieldType).emitTypeConversion(fieldName, fieldType, writer); @@ -78,6 +78,18 @@ public static void emitFillRealmObjectWithJsonValue(String fieldName, String fie .endControlFlow(); } + public static void emitFillRealmListWithJsonValue(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { + writer + .beginControlFlow("if (json.has(\"%s\"))", fieldName) + .emitStatement("JSONArray array = json.getJSONArray(\"%s\")", fieldName) + .beginControlFlow("for (int i = 0; i < array.length(); i++)") + .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) + .emitStatement("obj.populateFromJsonObject(array.getJSONObject(i))") + .emitStatement("get%s().add(obj)", capitaliseFirstChar(fieldName)) + .endControlFlow() + .endControlFlow(); + } + static class SimpleTypeConverter implements JsonToRealmTypeConverter { private final String castType; diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java index ea34f16ace..7cf2ef6840 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java @@ -232,6 +232,7 @@ public void generate() throws IOException, UnsupportedOperationException { "io.realm.RealmObject", "org.json.JSONObject", "org.json.JSONException", + "org.json.JSONArray", "java.util.*", packageName + ".*") .emitEmptyLine(); @@ -638,11 +639,22 @@ private void emitPopulateFromJsonObjectMethod(JavaWriter writer) throws IOExcept String fieldName = field.getSimpleName().toString(); String fieldTypeCanonicalName = field.asType().toString(); if (typeUtils.isAssignable(field.asType(), realmObject)) { - RealmJsonTypeHelper.emitFillRealmObjectWithJsonValue(fieldName, fieldTypeCanonicalName, writer); + RealmJsonTypeHelper.emitFillRealmObjectWithJsonValue( + fieldName, + fieldTypeCanonicalName, + writer); + } else if (typeUtils.isAssignable(field.asType(), realmList)) { + RealmJsonTypeHelper.emitFillRealmListWithJsonValue( + fieldName, + ((DeclaredType) field.asType()).getTypeArguments().get(0).toString(), + writer); } else { - RealmJsonTypeHelper.emitFillJavaTypeFieldWithJsonValue(fieldName, fieldTypeCanonicalName, writer); + RealmJsonTypeHelper.emitFillJavaTypeWithJsonValue( + fieldName, + fieldTypeCanonicalName, + writer); } } diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index f7d5296f0a..02c9c6106d 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -83,7 +83,7 @@ public void testImportJSonDateAsString() throws JSONException { assertEquals(new Date(1000), obj.getColumnDate()); } - public void testImportJSonNestedObjects() throws JSONException { + public void testImportJSonChildObject() throws JSONException { JSONObject allTypesObject = new JSONObject(); JSONObject dogObject = new JSONObject(); dogObject.put("name", "Fido"); @@ -97,6 +97,40 @@ public void testImportJSonNestedObjects() throws JSONException { assertEquals("Fido", obj.getColumnRealmObject().getName()); } + public void testImportJSonChildObjectList() throws JSONException { + JSONObject allTypesObject = new JSONObject(); + JSONObject dog1 = new JSONObject(); dog1.put("name", "Fido-1"); + JSONObject dog2 = new JSONObject(); dog2.put("name", "Fido-2"); + JSONObject dog3 = new JSONObject(); dog3.put("name", "Fido-3"); + JSONArray dogList = new JSONArray(); + dogList.put(dog1); + dogList.put(dog2); + dogList.put(dog3); + + allTypesObject.put("columnRealmObjectList", dogList); + + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, allTypesObject); + testRealm.commitTransaction(); + + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals(3, obj.getColumnRealmObjectList().size()); + assertEquals("Fido-3", obj.getColumnRealmObjectList().get(2).getName()); + } + + public void testImportJSonChildObjectList_empty() throws JSONException { + JSONObject allTypesObject = new JSONObject(); + JSONArray dogList = new JSONArray(); + + allTypesObject.put("columnRealmObjectList", dogList); + + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, allTypesObject); + testRealm.commitTransaction(); + + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals(0, obj.getColumnRealmObjectList().size()); + } public void testRemoveObjetsIfImportFail() { fail("Test if objects created by the json import is removed if something fail during decoding"); diff --git a/realm/src/androidTest/java/io/realm/entities/AllTypes.java b/realm/src/androidTest/java/io/realm/entities/AllTypes.java index 14984fc9d0..f168773890 100644 --- a/realm/src/androidTest/java/io/realm/entities/AllTypes.java +++ b/realm/src/androidTest/java/io/realm/entities/AllTypes.java @@ -18,6 +18,7 @@ import java.util.Date; +import io.realm.RealmList; import io.realm.RealmObject; public class AllTypes extends RealmObject { @@ -29,6 +30,7 @@ public class AllTypes extends RealmObject { private Date columnDate; private byte[] columnBinary; private Dog columnRealmObject; + private RealmList columnRealmObjectList; public String getColumnString() { return columnString; @@ -93,4 +95,12 @@ public Dog getColumnRealmObject() { public void setColumnRealmObject(Dog columnRealmObject) { this.columnRealmObject = columnRealmObject; } + + public RealmList getColumnRealmObjectList() { + return columnRealmObjectList; + } + + public void setColumnRealmObjectList(RealmList columnRealmObjectList) { + this.columnRealmObjectList = columnRealmObjectList; + } } From 1ec4fb628711195c9a5c73d7e2097cb7856ab33d Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Thu, 16 Oct 2014 22:08:20 +0200 Subject: [PATCH 07/33] Added support for importing lists of objects. --- .../java/io/realm/RealmJsonTest.java | 32 +++++++++++++++++-- realm/src/main/java/io/realm/Realm.java | 23 +++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index 02c9c6106d..c95f98a6d3 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -7,9 +7,11 @@ import org.json.JSONException; import org.json.JSONObject; +import java.io.InputStream; import java.util.Date; import io.realm.entities.AllTypes; +import io.realm.entities.Dog; public class RealmJsonTest extends AndroidTestCase { @@ -56,7 +58,6 @@ public void testImportJSonAllSimpSimpleObjectAllTypes() throws JSONException { assertEquals(1.23d, obj.getColumnDouble()); assertEquals(true, obj.isColumnBoolean()); assertEquals(new byte[]{1, 2, 3}, obj.getColumnBinary()); - } public void testImportJSonDateAsLong() throws JSONException { @@ -132,12 +133,39 @@ public void testImportJSonChildObjectList_empty() throws JSONException { assertEquals(0, obj.getColumnRealmObjectList().size()); } + public void testImportJsonArray_empty() throws JSONException { + JSONArray array = new JSONArray(); + testRealm.beginTransaction(); + testRealm.addFromJson(AllTypes.class, array); + testRealm.commitTransaction(); + + assertEquals(0, testRealm.allObjects(AllTypes.class).size()); + } + + public void testImportJsonArray() throws JSONException { + JSONObject dog1 = new JSONObject(); dog1.put("name", "Fido-1"); + JSONObject dog2 = new JSONObject(); dog2.put("name", "Fido-2"); + JSONObject dog3 = new JSONObject(); dog3.put("name", "Fido-3"); + JSONArray dogList = new JSONArray(); + dogList.put(dog1); + dogList.put(dog2); + dogList.put(dog3); + + testRealm.beginTransaction(); + testRealm.addFromJson(Dog.class, dogList); + testRealm.commitTransaction(); + + assertEquals(3, testRealm.allObjects(Dog.class).size()); + assertEquals("Fido-3", testRealm.allObjects(Dog.class).get(2).getName()); + } + + public void testRemoveObjetsIfImportFail() { fail("Test if objects created by the json import is removed if something fail during decoding"); } public void testImportJsonNullStream() { - testRealm.addFromJson(AllTypes.class, (JSONArray) null); + testRealm.addFromJson(AllTypes.class, (InputStream) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); } diff --git a/realm/src/main/java/io/realm/Realm.java b/realm/src/main/java/io/realm/Realm.java index 142e4bc78f..b00fa25b07 100644 --- a/realm/src/main/java/io/realm/Realm.java +++ b/realm/src/main/java/io/realm/Realm.java @@ -348,7 +348,17 @@ private static Realm createAndValidate(String absolutePath, byte[] key, boolean return realm; } - public void addFromJson(Class clazz, JSONArray json) { + public void addFromJson(Class clazz, JSONArray json) { + if (json == null) return; + for (int i = 0; i < json.length(); i++) { + E obj = createObject(clazz); + try { + obj.populateFromJsonObject(json.getJSONObject(i)); + } catch (Exception e) { + // TODO Remove object from realm + throw new RealmException("Could not map Json", e); + } + } } /** @@ -375,8 +385,17 @@ public E createFromJson(Class clazz, JSONObject json) return obj; } - public void addFromJson(Class clazz, InputStream inputStream) { + public E addFromJson(Class clazz, InputStream inputStream) { + if (inputStream == null) return null; + + E obj = createObject(clazz); + try { + } catch (Exception e) { + + } + + return obj; } From 88d4329955f9c5314ae548c0734198c585244cac Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Fri, 17 Oct 2014 16:26:18 +0200 Subject: [PATCH 08/33] Streamlined API methods. Added basic tests for ison streams. --- .../realm/processor/RealmJsonTypeHelper.java | 67 +++++++++++++---- .../processor/RealmProxyClassGenerator.java | 41 +++++++++- .../androidTest/assets/all_simple_types.json | 8 ++ realm/src/androidTest/assets/dates.json | 4 + realm/src/androidTest/assets/realmlist.json | 7 ++ .../assets/single_child_object.json | 3 + .../java/io/realm/RealmJsonTest.java | 74 +++++++++++++++---- realm/src/main/java/io/realm/Realm.java | 53 +++++++++++-- realm/src/main/java/io/realm/RealmObject.java | 6 +- 9 files changed, 218 insertions(+), 45 deletions(-) create mode 100644 realm/src/androidTest/assets/all_simple_types.json create mode 100644 realm/src/androidTest/assets/dates.json create mode 100644 realm/src/androidTest/assets/realmlist.json create mode 100644 realm/src/androidTest/assets/single_child_object.json diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java index 1665e972c5..088098c920 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java @@ -35,17 +35,18 @@ public class RealmJsonTypeHelper { public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { // TODO Add support for ISO 8601, but apparently there is a lot of edge cases. // Currently support for long timestamp and "/Date(long)/" - writer .emitStatement("long timestamp = json.optLong(\"%s\", -1)", fieldName) - .beginControlFlow("if (timestamp > -1)") + writer + .emitStatement("long timestamp = json.optLong(\"%s\", -1)", fieldName) + .beginControlFlow("if (timestamp > -1)") + .emitStatement("set%s(new Date(timestamp))", capitaliseFirstChar(fieldName)) + .nextControlFlow("else") + .emitStatement("String jsonDate = json.getString(\"%s\")", fieldName) + .beginControlFlow("if (!(jsonDate == null || jsonDate.length() == 0))") + .emitStatement("jsonDate = jsonDate.substring(6, jsonDate.length() - 2)") + .emitStatement("timestamp = Long.parseLong(jsonDate)") .emitStatement("set%s(new Date(timestamp))", capitaliseFirstChar(fieldName)) - .nextControlFlow("else") - .emitStatement("String jsonDate = json.getString(\"%s\")", fieldName) - .beginControlFlow("if (!(jsonDate == null || jsonDate.length() == 0))") - .emitStatement("jsonDate = jsonDate.substring(6, jsonDate.length() - 2)") - .emitStatement("timestamp = Long.parseLong(jsonDate)") - .emitStatement("set%s(new Date(timestamp))", capitaliseFirstChar(fieldName)) - .endControlFlow() - .endControlFlow(); + .endControlFlow() + .endControlFlow(); } }); @@ -65,15 +66,11 @@ public static void emitFillJavaTypeWithJsonValue(String fieldName, String fieldT } } - private static String capitaliseFirstChar(String input) { - return input.substring(0, 1).toUpperCase() + input.substring(1); - } - public static void emitFillRealmObjectWithJsonValue(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { writer .beginControlFlow("if (json.has(\"%s\"))", fieldName) .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) - .emitStatement("obj.populateFromJsonObject(json.getJSONObject(\"%s\"))", fieldName) + .emitStatement("obj.populateUsingJsonObject(json.getJSONObject(\"%s\"))", fieldName) .emitStatement("set%s(obj)", capitaliseFirstChar(fieldName)) .endControlFlow(); } @@ -84,12 +81,50 @@ public static void emitFillRealmListWithJsonValue(String fieldName, String field .emitStatement("JSONArray array = json.getJSONArray(\"%s\")", fieldName) .beginControlFlow("for (int i = 0; i < array.length(); i++)") .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) - .emitStatement("obj.populateFromJsonObject(array.getJSONObject(i))") + .emitStatement("obj.populateUsingJsonObject(array.getJSONObject(i))") .emitStatement("get%s().add(obj)", capitaliseFirstChar(fieldName)) .endControlFlow() .endControlFlow(); } + + public static void emitFillJavaTypeFromStream(String fieldName, String fieldType, JavaWriter writer) throws IOException { + if (JAVA_TO_JSON_TYPES.containsKey(fieldType)) { + writer.beginControlFlow("if (json.has(\"%s\"))", fieldName); + JAVA_TO_JSON_TYPES.get(fieldType).emitTypeConversion(fieldName, fieldType, writer); + writer.endControlFlow(); + } + } + + public static void emitFillRealmObjectFromStream(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { + writer + .beginControlFlow("if (json.has(\"%s\"))", fieldName) + .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) + .emitStatement("obj.populateUsingJsonObject(json.getJSONObject(\"%s\"))", fieldName) + .emitStatement("set%s(obj)", capitaliseFirstChar(fieldName)) + .endControlFlow(); + } + + public static void emitFillRealmListFromStream(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { + writer + .beginControlFlow("if (json.has(\"%s\"))", fieldName) + .emitStatement("JSONArray array = json.getJSONArray(\"%s\")", fieldName) + .beginControlFlow("for (int i = 0; i < array.length(); i++)") + .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) + .emitStatement("obj.populateUsingJsonObject(array.getJSONObject(i))") + .emitStatement("get%s().add(obj)", capitaliseFirstChar(fieldName)) + .endControlFlow() + .endControlFlow(); + } + + + + + + private static String capitaliseFirstChar(String input) { + return input.substring(0, 1).toUpperCase() + input.substring(1); + } + static class SimpleTypeConverter implements JsonToRealmTypeConverter { private final String castType; diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java index 7cf2ef6840..27aa4f4bd9 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java @@ -233,6 +233,7 @@ public void generate() throws IOException, UnsupportedOperationException { "org.json.JSONObject", "org.json.JSONException", "org.json.JSONArray", + "android.util.JsonReader", "java.util.*", packageName + ".*") .emitEmptyLine(); @@ -619,7 +620,7 @@ else if (typeUtils.isAssignable(field.asType(), realmObject) || typeUtils.isAssi // Add JSON methods emitPopulateFromJsonObjectMethod(writer); -// emitPopulateFromJsonStreamMethod(writer); + emitPopulateFromJsonStreamMethod(writer); // End the class definition writer.endType(); @@ -630,7 +631,7 @@ private void emitPopulateFromJsonObjectMethod(JavaWriter writer) throws IOExcept writer.emitAnnotation(Override.class); writer.beginMethod( "void", - "populateFromJsonObject", + "populateUsingJsonObject", EnumSet.of(Modifier.PROTECTED), Arrays.asList("JSONObject", "json"), Arrays.asList("JSONException")); @@ -663,8 +664,40 @@ private void emitPopulateFromJsonObjectMethod(JavaWriter writer) throws IOExcept writer.emitEmptyLine(); } - private void emitPopulateFromJsonStreamMethod(JavaWriter writer) { -// throw new RuntimeException("Not implemented"); + private void emitPopulateFromJsonStreamMethod(JavaWriter writer) throws IOException { + writer.emitAnnotation(Override.class); + writer.beginMethod( + "void", + "populateUsingJsonStream", + EnumSet.of(Modifier.PROTECTED), + "JsonReader", "json"); + +// for (VariableElement field : fields) { +// String fieldName = field.getSimpleName().toString(); +// String fieldTypeCanonicalName = field.asType().toString(); +// if (typeUtils.isAssignable(field.asType(), realmObject)) { +// RealmJsonTypeHelper.emitFillRealmObjectWithJsonValue( +// fieldName, +// fieldTypeCanonicalName, +// writer); +// +// } else if (typeUtils.isAssignable(field.asType(), realmList)) { +// RealmJsonTypeHelper.emitFillRealmListWithJsonValue( +// fieldName, +// ((DeclaredType) field.asType()).getTypeArguments().get(0).toString(), +// writer); +// +// } else { +// RealmJsonTypeHelper.emitFillJavaTypeWithJsonValue( +// fieldName, +// fieldTypeCanonicalName, +// writer); +// } +// +// } + + writer.endMethod(); + writer.emitEmptyLine(); } private static String capitaliseFirstChar(String input) { diff --git a/realm/src/androidTest/assets/all_simple_types.json b/realm/src/androidTest/assets/all_simple_types.json new file mode 100644 index 0000000000..72bdd124c4 --- /dev/null +++ b/realm/src/androidTest/assets/all_simple_types.json @@ -0,0 +1,8 @@ +{ + "columnString" : "String", + "columnLong" : 1, + "columnFloat" : 1.23, + "columnDouble" : 1.23, + "columnBoolean" : true, + "columnBinary" : "" +} \ No newline at end of file diff --git a/realm/src/androidTest/assets/dates.json b/realm/src/androidTest/assets/dates.json new file mode 100644 index 0000000000..d5ca5f132f --- /dev/null +++ b/realm/src/androidTest/assets/dates.json @@ -0,0 +1,4 @@ +{ + "columnDateLong" : 1000, + "columnDateString" : 1000 +} \ No newline at end of file diff --git a/realm/src/androidTest/assets/realmlist.json b/realm/src/androidTest/assets/realmlist.json new file mode 100644 index 0000000000..98c1ee90a7 --- /dev/null +++ b/realm/src/androidTest/assets/realmlist.json @@ -0,0 +1,7 @@ +{ + "columnRealmList" : [ + { "name" : "Fido-1" }, + { "name" : "Fido-2" }, + { "name" : "Fido-3" }, + ] +} \ No newline at end of file diff --git a/realm/src/androidTest/assets/single_child_object.json b/realm/src/androidTest/assets/single_child_object.json new file mode 100644 index 0000000000..635ab0a693 --- /dev/null +++ b/realm/src/androidTest/assets/single_child_object.json @@ -0,0 +1,3 @@ +{ + "columnObject" : { "name" : "Fido" } +} \ No newline at end of file diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index c95f98a6d3..f2e6d45976 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -1,5 +1,6 @@ package io.realm; +import android.content.res.AssetManager; import android.test.AndroidTestCase; import android.util.Base64; @@ -7,6 +8,7 @@ import org.json.JSONException; import org.json.JSONObject; +import java.io.IOException; import java.io.InputStream; import java.util.Date; @@ -26,18 +28,30 @@ protected void setUp() throws Exception { testRealm.commitTransaction(); } - public void testImportJSonNullObject() { + private InputStream loadJsonFromAssets(String file) { + AssetManager assetManager = getContext().getAssets(); + InputStream input = null; + try { + input = assetManager.open(file); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + return input; + } + } + + public void testImportJSon_nullObject() { testRealm.createFromJson(AllTypes.class, (JSONObject) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); } - public void testImportJSonNullArray() { - testRealm.addFromJson(AllTypes.class, (JSONArray) null); + public void testImportJSon_nullArray() { + testRealm.createAllFromJson(AllTypes.class, (JSONArray) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); } - public void testImportJSonAllSimpSimpleObjectAllTypes() throws JSONException { + public void testImportJSon_allSimpSimpleObjectAllTypes() throws JSONException { JSONObject json = new JSONObject(); json.put("columnString", "String"); json.put("columnLong", 1l); @@ -60,7 +74,7 @@ public void testImportJSonAllSimpSimpleObjectAllTypes() throws JSONException { assertEquals(new byte[]{1, 2, 3}, obj.getColumnBinary()); } - public void testImportJSonDateAsLong() throws JSONException { + public void testImportJSon_dateAsLong() throws JSONException { JSONObject json = new JSONObject(); json.put("columnDate", 1000L); // Realm operates at seconds level granularity @@ -72,7 +86,7 @@ public void testImportJSonDateAsLong() throws JSONException { assertEquals(new Date(1000), obj.getColumnDate()); } - public void testImportJSonDateAsString() throws JSONException { + public void testImportJSon_dateAsString() throws JSONException { JSONObject json = new JSONObject(); json.put("columnDate", "/Date(1000)/"); @@ -84,7 +98,7 @@ public void testImportJSonDateAsString() throws JSONException { assertEquals(new Date(1000), obj.getColumnDate()); } - public void testImportJSonChildObject() throws JSONException { + public void testImportJSon_childObject() throws JSONException { JSONObject allTypesObject = new JSONObject(); JSONObject dogObject = new JSONObject(); dogObject.put("name", "Fido"); @@ -98,7 +112,7 @@ public void testImportJSonChildObject() throws JSONException { assertEquals("Fido", obj.getColumnRealmObject().getName()); } - public void testImportJSonChildObjectList() throws JSONException { + public void testImportJSon_childObjectList() throws JSONException { JSONObject allTypesObject = new JSONObject(); JSONObject dog1 = new JSONObject(); dog1.put("name", "Fido-1"); JSONObject dog2 = new JSONObject(); dog2.put("name", "Fido-2"); @@ -119,7 +133,7 @@ public void testImportJSonChildObjectList() throws JSONException { assertEquals("Fido-3", obj.getColumnRealmObjectList().get(2).getName()); } - public void testImportJSonChildObjectList_empty() throws JSONException { + public void testImportJSon_emptyChildObjectList() throws JSONException { JSONObject allTypesObject = new JSONObject(); JSONArray dogList = new JSONArray(); @@ -136,7 +150,7 @@ public void testImportJSonChildObjectList_empty() throws JSONException { public void testImportJsonArray_empty() throws JSONException { JSONArray array = new JSONArray(); testRealm.beginTransaction(); - testRealm.addFromJson(AllTypes.class, array); + testRealm.createAllFromJson(AllTypes.class, array); testRealm.commitTransaction(); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); @@ -152,7 +166,7 @@ public void testImportJsonArray() throws JSONException { dogList.put(dog3); testRealm.beginTransaction(); - testRealm.addFromJson(Dog.class, dogList); + testRealm.createAllFromJson(Dog.class, dogList); testRealm.commitTransaction(); assertEquals(3, testRealm.allObjects(Dog.class).size()); @@ -164,16 +178,46 @@ public void testRemoveObjetsIfImportFail() { fail("Test if objects created by the json import is removed if something fail during decoding"); } - public void testImportJsonNullStream() { - testRealm.addFromJson(AllTypes.class, (InputStream) null); + public void testImportStream_null() { + testRealm.createAllFromJson(AllTypes.class, (InputStream) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); } - public void testImportJsonStreamAllTypes() { + public void testImportStream_allSimpleTypes() throws IOException { + InputStream in = loadJsonFromAssets("all_simple_types.json"); + testRealm.beginTransaction(); + testRealm.createAllFromJson(AllTypes.class, in); + testRealm.commitTransaction(); + in.close(); + + // Check that all primitive types are imported correctly + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals("String", obj.getColumnString()); + assertEquals(1l, obj.getColumnLong()); + assertEquals(1.23f, obj.getColumnFloat()); + assertEquals(1.23d, obj.getColumnDouble()); + assertEquals(true, obj.isColumnBoolean()); + assertEquals(new byte[]{1, 2, 3}, obj.getColumnBinary()); + } + + public void testImportStream_DateAsLong() { + fail("Not implemented."); + } + + public void testImportStream_DateAsString() { + fail("Not implemented."); + } + + public void testImportStream_childObject() { + fail("Not implemented."); + } + + public void testImportStream_emptyChildObjectList() { fail("Not implemented."); } - public void testImportJsonStreamNestedTypes() { + public void testImportStream_childObjectList() { fail("Not implemented."); } + } diff --git a/realm/src/main/java/io/realm/Realm.java b/realm/src/main/java/io/realm/Realm.java index b00fa25b07..1c1fde0a57 100644 --- a/realm/src/main/java/io/realm/Realm.java +++ b/realm/src/main/java/io/realm/Realm.java @@ -348,12 +348,22 @@ private static Realm createAndValidate(String absolutePath, byte[] key, boolean return realm; } - public void addFromJson(Class clazz, JSONArray json) { + + + /** + * Add an array of of JsonObjects to the Realm as a new object. This must be done inside a transaction. + * + * @param clazz Class of object the json will map to. All Objects in the array must be of the same type. + * @param json Array of JsonObject's that can map to the chosen clazz. Properties not in the class are ignored. + * + * @throws RealmException if the mapping fail. + */ + public void createAllFromJson(Class clazz, JSONArray json) { if (json == null) return; for (int i = 0; i < json.length(); i++) { E obj = createObject(clazz); try { - obj.populateFromJsonObject(json.getJSONObject(i)); + obj.populateUsingJsonObject(json.getJSONObject(i)); } catch (Exception e) { // TODO Remove object from realm throw new RealmException("Could not map Json", e); @@ -361,6 +371,28 @@ public void addFromJson(Class clazz, JSONArray json) } } + + /** + * Add a Json InputStream to the Realm as new objects. This must be done inside a transaction. + * + * @param clazz Class of object the json will map to. All Objects in the array must be of the same type. + * @param inputStream A JSON InputStream of objects of type clazz. All objects must be of the chosen clazz. Properties not in the class are ignored. + * + * @throws RealmException if the mapping fail. + */ + public void createAllFromJson(Class clazz, InputStream inputStream) { + if (inputStream == null) return; + + E obj = createObject(clazz); + try { + + } catch (Exception e) { + + } + } + + + /** * Add a JsonObject to the Realm as a new object. This must be done inside a transaction. * @@ -370,13 +402,12 @@ public void addFromJson(Class clazz, JSONArray json) * * @throws RealmException if the mapping fail. */ - public E createFromJson(Class clazz, JSONObject json) { if (json == null) return null; E obj = createObject(clazz); try { - obj.populateFromJsonObject(json); + obj.populateUsingJsonObject(json); } catch (Exception e) { // TODO Remove object from realm throw new RealmException("Could not map Json", e); @@ -385,7 +416,16 @@ public E createFromJson(Class clazz, JSONObject json) return obj; } - public E addFromJson(Class clazz, InputStream inputStream) { + /** + * Add a JsonObject from a InputStream to the Realm as a new object. This must be done inside a transaction. + * + * @param clazz Class of object the json will map to. + * @param inputStream JSONObject as a input stream of the chosen clazz. Properties not in the class are ignored. + * @return Object with data or null if no json data was provided. + * + * @throws RealmException if the mapping fail. + */ + public E createFromJson(Class clazz, InputStream inputStream) { if (inputStream == null) return null; E obj = createObject(clazz); @@ -398,9 +438,6 @@ public E addFromJson(Class clazz, InputStream inputSt return obj; } - - - // This class stores soft-references to realm objects per thread per realm file private static class ThreadRealm extends ThreadLocal> { private String absolutePath; diff --git a/realm/src/main/java/io/realm/RealmObject.java b/realm/src/main/java/io/realm/RealmObject.java index 88a03262f8..86977b8140 100644 --- a/realm/src/main/java/io/realm/RealmObject.java +++ b/realm/src/main/java/io/realm/RealmObject.java @@ -16,6 +16,8 @@ package io.realm; +import android.util.JsonReader; + import org.json.JSONException; import org.json.JSONObject; @@ -59,11 +61,11 @@ protected void realmSetRow(Row row) { this.row = row; } - protected void populateFromJsonObject(JSONObject json) throws JSONException { + protected void populateUsingJsonObject(JSONObject json) throws JSONException { throw new IllegalStateException("Only use this method on objects created or fetched in a Realm. Realm.createObject() or Realm.where()"); } - protected void populateFromJsonStream(InputStream inputStream) throws IOException { + protected void populateUsingJsonStream(JsonReader json) throws IOException { throw new IllegalStateException("Only use this method on objects created or fetched in a Realm. Realm.createObject() or Realm.where()"); } } From 0c41a3eef325ba1a672672284219623a7dc9ca11 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Sat, 18 Oct 2014 07:07:39 +0200 Subject: [PATCH 09/33] Support for stream simple types added. --- .../realm/processor/RealmJsonTypeHelper.java | 72 ++++++++++++------- .../processor/RealmProxyClassGenerator.java | 68 +++++++++++------- .../androidTest/assets/all_simple_types.json | 2 +- .../src/androidTest/assets/date_as_long.json | 3 + .../androidTest/assets/date_as_string.json | 3 + .../java/io/realm/RealmJsonTest.java | 39 +++++++--- .../io/realm/entities/AnnotationTypes.java | 2 + .../io/realm/internal/test/ExtraTests.java | 8 ++- realm/src/main/java/io/realm/Realm.java | 37 +++++----- .../io/realm/internal/json/JsonUtils.java | 42 +++++++++++ realm_version_check.timestamp | 1 + 11 files changed, 196 insertions(+), 81 deletions(-) create mode 100644 realm/src/androidTest/assets/date_as_long.json create mode 100644 realm/src/androidTest/assets/date_as_string.json create mode 100644 realm/src/main/java/io/realm/internal/json/JsonUtils.java create mode 100644 realm_version_check.timestamp diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java index 088098c920..2b018db4cc 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java @@ -33,19 +33,26 @@ public class RealmJsonTypeHelper { JAVA_TO_JSON_TYPES.put("java.util.Date", new JsonToRealmTypeConverter() { @Override public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { - // TODO Add support for ISO 8601, but apparently there is a lot of edge cases. - // Currently support for long timestamp and "/Date(long)/" writer .emitStatement("long timestamp = json.optLong(\"%s\", -1)", fieldName) .beginControlFlow("if (timestamp > -1)") .emitStatement("set%s(new Date(timestamp))", capitaliseFirstChar(fieldName)) .nextControlFlow("else") .emitStatement("String jsonDate = json.getString(\"%s\")", fieldName) - .beginControlFlow("if (!(jsonDate == null || jsonDate.length() == 0))") - .emitStatement("jsonDate = jsonDate.substring(6, jsonDate.length() - 2)") - .emitStatement("timestamp = Long.parseLong(jsonDate)") + .emitStatement("set%s(JsonUtils.stringToDate(jsonDate))", capitaliseFirstChar(fieldName)) + .endControlFlow(); + } + + @Override + public void emitStreamTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { + writer + .beginControlFlow("if (reader.peek() == JsonToken.NUMBER)") + .emitStatement("long timestamp = reader.nextLong()", fieldName) + .beginControlFlow("if (timestamp > -1)") .emitStatement("set%s(new Date(timestamp))", capitaliseFirstChar(fieldName)) .endControlFlow() + .nextControlFlow("else") + .emitStatement("set%s(JsonUtils.stringToDate(reader.nextString()))", capitaliseFirstChar(fieldName)) .endControlFlow(); } }); @@ -53,9 +60,14 @@ public void emitTypeConversion(String fieldName, String fieldType, JavaWriter wr JAVA_TO_JSON_TYPES.put("byte[]", new JsonToRealmTypeConverter() { @Override public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { -// throw new RuntimeException("byte[] not supported yet"); + writer.emitStatement("set%s(JsonUtils.stringToBytes(json.getString(\"%s\")))", capitaliseFirstChar(fieldName), fieldName); + } + + @Override + public void emitStreamTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { + writer.emitStatement("set%s(JsonUtils.stringToBytes(reader.nextString()))", capitaliseFirstChar(fieldName)); } - }); // Hex 64 encoded + }); } public static void emitFillJavaTypeWithJsonValue(String fieldName, String fieldType, JavaWriter writer) throws IOException { @@ -90,31 +102,29 @@ public static void emitFillRealmListWithJsonValue(String fieldName, String field public static void emitFillJavaTypeFromStream(String fieldName, String fieldType, JavaWriter writer) throws IOException { if (JAVA_TO_JSON_TYPES.containsKey(fieldType)) { - writer.beginControlFlow("if (json.has(\"%s\"))", fieldName); - JAVA_TO_JSON_TYPES.get(fieldType).emitTypeConversion(fieldName, fieldType, writer); - writer.endControlFlow(); + JAVA_TO_JSON_TYPES.get(fieldType).emitStreamTypeConversion(fieldName, fieldType, writer); } } public static void emitFillRealmObjectFromStream(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { - writer - .beginControlFlow("if (json.has(\"%s\"))", fieldName) - .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) - .emitStatement("obj.populateUsingJsonObject(json.getJSONObject(\"%s\"))", fieldName) - .emitStatement("set%s(obj)", capitaliseFirstChar(fieldName)) - .endControlFlow(); +// writer +// .beginControlFlow("if (json.has(\"%s\"))", fieldName) +// .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) +// .emitStatement("obj.populateUsingJsonObject(json.getJSONObject(\"%s\"))", fieldName) +// .emitStatement("set%s(obj)", capitaliseFirstChar(fieldName)) +// .endControlFlow(); } public static void emitFillRealmListFromStream(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { - writer - .beginControlFlow("if (json.has(\"%s\"))", fieldName) - .emitStatement("JSONArray array = json.getJSONArray(\"%s\")", fieldName) - .beginControlFlow("for (int i = 0; i < array.length(); i++)") - .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) - .emitStatement("obj.populateUsingJsonObject(array.getJSONObject(i))") - .emitStatement("get%s().add(obj)", capitaliseFirstChar(fieldName)) - .endControlFlow() - .endControlFlow(); +// writer +// .beginControlFlow("if (json.has(\"%s\"))", fieldName) +// .emitStatement("JSONArray array = json.getJSONArray(\"%s\")", fieldName) +// .beginControlFlow("for (int i = 0; i < array.length(); i++)") +// .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) +// .emitStatement("obj.populateUsingJsonObject(array.getJSONObject(i))") +// .emitStatement("get%s().add(obj)", capitaliseFirstChar(fieldName)) +// .endControlFlow() +// .endControlFlow(); } @@ -132,7 +142,8 @@ static class SimpleTypeConverter implements JsonToRealmTypeConverter { /** * Create a conversion between simple types that can be expressed of the form - * RealmObject.setFieldName(() json.get) + * RealmObject.setFieldName(() json.get) or + * RealmObject.setFieldName(() reader.next * * @param castType Java type to cast to. * @param jsonType JsonType to get data from. @@ -150,9 +161,18 @@ public void emitTypeConversion(String fieldName, String fieldType, JavaWriter wr jsonType, fieldName); } + + @Override + public void emitStreamTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { + writer.emitStatement("set%s((%s) reader.next%s())", + capitaliseFirstChar(fieldName), + castType, + jsonType); + } } private interface JsonToRealmTypeConverter { public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException; + public void emitStreamTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException; } } diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java index 27aa4f4bd9..357fee2d3f 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java @@ -228,12 +228,15 @@ public void generate() throws IOException, UnsupportedOperationException { "io.realm.internal.ImplicitTransaction", "io.realm.internal.Row", "io.realm.internal.LinkView", + "io.realm.internal.json.JsonUtils", "io.realm.RealmList", "io.realm.RealmObject", "org.json.JSONObject", "org.json.JSONException", "org.json.JSONArray", "android.util.JsonReader", + "android.util.JsonToken", + "java.io.IOException", "java.util.*", packageName + ".*") .emitEmptyLine(); @@ -670,32 +673,49 @@ private void emitPopulateFromJsonStreamMethod(JavaWriter writer) throws IOExcept "void", "populateUsingJsonStream", EnumSet.of(Modifier.PROTECTED), - "JsonReader", "json"); - -// for (VariableElement field : fields) { -// String fieldName = field.getSimpleName().toString(); -// String fieldTypeCanonicalName = field.asType().toString(); -// if (typeUtils.isAssignable(field.asType(), realmObject)) { -// RealmJsonTypeHelper.emitFillRealmObjectWithJsonValue( -// fieldName, -// fieldTypeCanonicalName, -// writer); -// -// } else if (typeUtils.isAssignable(field.asType(), realmList)) { -// RealmJsonTypeHelper.emitFillRealmListWithJsonValue( -// fieldName, -// ((DeclaredType) field.asType()).getTypeArguments().get(0).toString(), -// writer); -// -// } else { -// RealmJsonTypeHelper.emitFillJavaTypeWithJsonValue( -// fieldName, -// fieldTypeCanonicalName, -// writer); -// } + Arrays.asList("JsonReader", "reader"), + Arrays.asList("IOException")); + + writer.emitStatement("reader.beginObject()"); + writer.beginControlFlow("while (reader.hasNext())"); + writer.emitStatement("String name = reader.nextName()"); // -// } + for (int i = 0; i < fields.size(); i++) { + VariableElement field = fields.get(i); + String fieldName = field.getSimpleName().toString(); + String fieldTypeCanonicalName = field.asType().toString(); + + if (i == 0) { + writer.beginControlFlow("if (name.equals(\"%s\"))", fieldName); + } else { + writer.nextControlFlow("else if (name.equals(\"%s\"))", fieldName); + } + if (typeUtils.isAssignable(field.asType(), realmObject)) { + RealmJsonTypeHelper.emitFillRealmObjectFromStream( + fieldName, + fieldTypeCanonicalName, + writer); + + } else if (typeUtils.isAssignable(field.asType(), realmList)) { + RealmJsonTypeHelper.emitFillRealmListFromStream( + fieldName, + ((DeclaredType) field.asType()).getTypeArguments().get(0).toString(), + writer); + + } else { + RealmJsonTypeHelper.emitFillJavaTypeFromStream( + fieldName, + fieldTypeCanonicalName, + writer); + } + } + + writer.nextControlFlow("else"); + writer.emitStatement("reader.skipValue()"); + writer.endControlFlow(); + writer.endControlFlow(); + writer.emitStatement("reader.endObject()"); writer.endMethod(); writer.emitEmptyLine(); } diff --git a/realm/src/androidTest/assets/all_simple_types.json b/realm/src/androidTest/assets/all_simple_types.json index 72bdd124c4..f21ddd70e1 100644 --- a/realm/src/androidTest/assets/all_simple_types.json +++ b/realm/src/androidTest/assets/all_simple_types.json @@ -4,5 +4,5 @@ "columnFloat" : 1.23, "columnDouble" : 1.23, "columnBoolean" : true, - "columnBinary" : "" + "columnBinary" : "AQID" } \ No newline at end of file diff --git a/realm/src/androidTest/assets/date_as_long.json b/realm/src/androidTest/assets/date_as_long.json new file mode 100644 index 0000000000..51ee039a6a --- /dev/null +++ b/realm/src/androidTest/assets/date_as_long.json @@ -0,0 +1,3 @@ +{ + "columnDate" : 1000 +} \ No newline at end of file diff --git a/realm/src/androidTest/assets/date_as_string.json b/realm/src/androidTest/assets/date_as_string.json new file mode 100644 index 0000000000..2892658ef4 --- /dev/null +++ b/realm/src/androidTest/assets/date_as_string.json @@ -0,0 +1,3 @@ +{ + "columnDate" : "/Date(1000)/" +} \ No newline at end of file diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index f2e6d45976..9dce6cf880 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -15,6 +15,8 @@ import io.realm.entities.AllTypes; import io.realm.entities.Dog; +import static io.realm.internal.test.ExtraTests.assertArrayEquals; + public class RealmJsonTest extends AndroidTestCase { protected Realm testRealm; @@ -25,6 +27,7 @@ protected void setUp() throws Exception { testRealm = Realm.getInstance(getContext()); testRealm.beginTransaction(); testRealm.clear(AllTypes.class); + testRealm.clear(Dog.class); testRealm.commitTransaction(); } @@ -58,7 +61,7 @@ public void testImportJSon_allSimpSimpleObjectAllTypes() throws JSONException { json.put("columnFloat", 1.23f); json.put("columnDouble", 1.23d); json.put("columnBoolean", true); - json.put("columnBinary", Base64.encode(new byte[] {1, 2, 3}, Base64.DEFAULT)); + json.put("columnBinary", new String(Base64.encode(new byte[] {1,2,3}, Base64.DEFAULT))); testRealm.beginTransaction(); testRealm.createFromJson(AllTypes.class, json); @@ -71,7 +74,7 @@ public void testImportJSon_allSimpSimpleObjectAllTypes() throws JSONException { assertEquals(1.23f, obj.getColumnFloat()); assertEquals(1.23d, obj.getColumnDouble()); assertEquals(true, obj.isColumnBoolean()); - assertEquals(new byte[]{1, 2, 3}, obj.getColumnBinary()); + assertArrayEquals(new byte[]{1, 2, 3}, obj.getColumnBinary()); } public void testImportJSon_dateAsLong() throws JSONException { @@ -170,7 +173,7 @@ public void testImportJsonArray() throws JSONException { testRealm.commitTransaction(); assertEquals(3, testRealm.allObjects(Dog.class).size()); - assertEquals("Fido-3", testRealm.allObjects(Dog.class).get(2).getName()); + assertEquals(1, testRealm.where(Dog.class).equalTo("name", "Fido-3").findAll().size()); } @@ -178,7 +181,7 @@ public void testRemoveObjetsIfImportFail() { fail("Test if objects created by the json import is removed if something fail during decoding"); } - public void testImportStream_null() { + public void testImportStream_null() throws IOException { testRealm.createAllFromJson(AllTypes.class, (InputStream) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); } @@ -186,7 +189,7 @@ public void testImportStream_null() { public void testImportStream_allSimpleTypes() throws IOException { InputStream in = loadJsonFromAssets("all_simple_types.json"); testRealm.beginTransaction(); - testRealm.createAllFromJson(AllTypes.class, in); + testRealm.createFromJson(AllTypes.class, in); testRealm.commitTransaction(); in.close(); @@ -197,15 +200,31 @@ public void testImportStream_allSimpleTypes() throws IOException { assertEquals(1.23f, obj.getColumnFloat()); assertEquals(1.23d, obj.getColumnDouble()); assertEquals(true, obj.isColumnBoolean()); - assertEquals(new byte[]{1, 2, 3}, obj.getColumnBinary()); + assertArrayEquals(new byte[]{1, 2, 3}, obj.getColumnBinary()); } - public void testImportStream_DateAsLong() { - fail("Not implemented."); + public void testImportStream_DateAsLong() throws IOException { + InputStream in = loadJsonFromAssets("date_as_long.json"); + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, in); + testRealm.commitTransaction(); + in.close(); + + // Check that all primitive types are imported correctly + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals(new Date(1000), obj.getColumnDate()); } - public void testImportStream_DateAsString() { - fail("Not implemented."); + public void testImportStream_DateAsString() throws IOException { + InputStream in = loadJsonFromAssets("date_as_string.json"); + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, in); + testRealm.commitTransaction(); + in.close(); + + // Check that all primitive types are imported correctly + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals(new Date(1000), obj.getColumnDate()); } public void testImportStream_childObject() { diff --git a/realm/src/androidTest/java/io/realm/entities/AnnotationTypes.java b/realm/src/androidTest/java/io/realm/entities/AnnotationTypes.java index 35b731e749..95d3e9ac35 100644 --- a/realm/src/androidTest/java/io/realm/entities/AnnotationTypes.java +++ b/realm/src/androidTest/java/io/realm/entities/AnnotationTypes.java @@ -50,4 +50,6 @@ public String getIgnoreString() { public void setIgnoreString(String ignoreString) { this.ignoreString = ignoreString; } + + } diff --git a/realm/src/androidTest/java/io/realm/internal/test/ExtraTests.java b/realm/src/androidTest/java/io/realm/internal/test/ExtraTests.java index a3b8e15796..77660e48ec 100644 --- a/realm/src/androidTest/java/io/realm/internal/test/ExtraTests.java +++ b/realm/src/androidTest/java/io/realm/internal/test/ExtraTests.java @@ -7,8 +7,12 @@ import static junit.framework.Assert.fail; public class ExtraTests { - public static void assertArrayEquals(Object[] expecteds, Object[] actuals) - { + + public static void assertArrayEquals(Object[] expecteds, Object[] actuals) { + new ExactComparisonCriteria().arrayEquals(null, expecteds, actuals); + } + + public static void assertArrayEquals(byte[] expecteds, byte[] actuals) { new ExactComparisonCriteria().arrayEquals(null, expecteds, actuals); } diff --git a/realm/src/main/java/io/realm/Realm.java b/realm/src/main/java/io/realm/Realm.java index 1c1fde0a57..f634b22cf0 100644 --- a/realm/src/main/java/io/realm/Realm.java +++ b/realm/src/main/java/io/realm/Realm.java @@ -16,17 +16,22 @@ package io.realm; +import android.annotation.TargetApi; import android.content.Context; +import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.util.JsonReader; import android.util.Log; import org.json.JSONArray; import org.json.JSONObject; import java.io.File; +import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.ref.SoftReference; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -379,16 +384,11 @@ public void createAllFromJson(Class clazz, JSONArray * @param inputStream A JSON InputStream of objects of type clazz. All objects must be of the chosen clazz. Properties not in the class are ignored. * * @throws RealmException if the mapping fail. + * @throws IOException if something is wrong with the input stream. */ - public void createAllFromJson(Class clazz, InputStream inputStream) { - if (inputStream == null) return; - - E obj = createObject(clazz); - try { - - } catch (Exception e) { - - } + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void createAllFromJson(Class clazz, InputStream inputStream) throws IOException { + // TODO } @@ -424,18 +424,19 @@ public E createFromJson(Class clazz, JSONObject json) * @return Object with data or null if no json data was provided. * * @throws RealmException if the mapping fail. + * @throws IOException if something is wrong with the input stream. */ - public E createFromJson(Class clazz, InputStream inputStream) { - if (inputStream == null) return null; - - E obj = createObject(clazz); - try { - - } catch (Exception e) { - + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public E createFromJson(Class clazz, InputStream inputStream) throws IOException { + if (inputStream != null && clazz != null) { + JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8")); + E obj = createObject(clazz); + obj.populateUsingJsonStream(reader); + reader.close(); + return obj; } - return obj; + return null; } // This class stores soft-references to realm objects per thread per realm file diff --git a/realm/src/main/java/io/realm/internal/json/JsonUtils.java b/realm/src/main/java/io/realm/internal/json/JsonUtils.java new file mode 100644 index 0000000000..350b45402b --- /dev/null +++ b/realm/src/main/java/io/realm/internal/json/JsonUtils.java @@ -0,0 +1,42 @@ +package io.realm.internal.json; + +import android.util.Base64; +import java.util.Date; + +/** + * Created by Christian Melchior on 17/10/14. + */ +public class JsonUtils { + + /** + * Converts a Json string to a Java Date object. Currently supports 2 types: + * - "" + * - "/Date()/" + * - TODO ISO 8601 String + * + * @param str String input of the supported types. + * @return Date object or null if invalid input. + * + * @throws NumberFormatException If timestamp is not a proper long + * @throws IndexOutOfBoundsException if dates of type /Date(x)/ does not have a proper format. + */ + public static Date stringToDate(String str) { + if (str == null || str.length() == 0) return null; + if (str.startsWith("/Date")) { + return new Date(Long.parseLong(str.substring(6, str.length() - 2))); + } else { + return new Date(Long.parseLong(str)); + } + } + + /** + * Converts a Json string to byte[]. String must be Base64 encoded. + * + * @param str Base 64 encoded bytes. + * @return Byte array or empty byte array + */ + public static byte[] stringToBytes(String str) { + if (str == null || str.length() == 0) return new byte[0]; + return Base64.decode(str, Base64.DEFAULT); + } +} diff --git a/realm_version_check.timestamp b/realm_version_check.timestamp new file mode 100644 index 0000000000..5afa2a9e5e --- /dev/null +++ b/realm_version_check.timestamp @@ -0,0 +1 @@ +1413573872243 From 420dfc0a4a640777d660bd98f3e538f30062d5a3 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Sat, 18 Oct 2014 09:21:52 +0200 Subject: [PATCH 10/33] Support for json stream arrays. --- .../realm/processor/RealmJsonTypeHelper.java | 31 +++++------- realm/src/androidTest/assets/realmlist.json | 2 +- .../androidTest/assets/realmlist_empty.json | 3 ++ .../assets/single_child_object.json | 2 +- .../java/io/realm/RealmJsonTest.java | 49 ++++++++++++------- .../java/io/realm/entities/AllTypes.java | 11 +++-- 6 files changed, 55 insertions(+), 43 deletions(-) create mode 100644 realm/src/androidTest/assets/realmlist_empty.json diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java index 2b018db4cc..992ac2a4b6 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java @@ -107,30 +107,23 @@ public static void emitFillJavaTypeFromStream(String fieldName, String fieldType } public static void emitFillRealmObjectFromStream(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { -// writer -// .beginControlFlow("if (json.has(\"%s\"))", fieldName) -// .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) -// .emitStatement("obj.populateUsingJsonObject(json.getJSONObject(\"%s\"))", fieldName) -// .emitStatement("set%s(obj)", capitaliseFirstChar(fieldName)) -// .endControlFlow(); + writer + .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) + .emitStatement("obj.populateUsingJsonStream(reader)", fieldName) + .emitStatement("set%s(obj)", capitaliseFirstChar(fieldName)); } public static void emitFillRealmListFromStream(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { -// writer -// .beginControlFlow("if (json.has(\"%s\"))", fieldName) -// .emitStatement("JSONArray array = json.getJSONArray(\"%s\")", fieldName) -// .beginControlFlow("for (int i = 0; i < array.length(); i++)") -// .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) -// .emitStatement("obj.populateUsingJsonObject(array.getJSONObject(i))") -// .emitStatement("get%s().add(obj)", capitaliseFirstChar(fieldName)) -// .endControlFlow() -// .endControlFlow(); + writer + .emitStatement("reader.beginArray()") + .beginControlFlow("while (reader.hasNext())") + .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) + .emitStatement("obj.populateUsingJsonStream(reader)") + .emitStatement("get%s().add(obj)", capitaliseFirstChar(fieldName)) + .endControlFlow() + .emitStatement("reader.endArray()"); } - - - - private static String capitaliseFirstChar(String input) { return input.substring(0, 1).toUpperCase() + input.substring(1); } diff --git a/realm/src/androidTest/assets/realmlist.json b/realm/src/androidTest/assets/realmlist.json index 98c1ee90a7..c0fc1c7e00 100644 --- a/realm/src/androidTest/assets/realmlist.json +++ b/realm/src/androidTest/assets/realmlist.json @@ -2,6 +2,6 @@ "columnRealmList" : [ { "name" : "Fido-1" }, { "name" : "Fido-2" }, - { "name" : "Fido-3" }, + { "name" : "Fido-3" } ] } \ No newline at end of file diff --git a/realm/src/androidTest/assets/realmlist_empty.json b/realm/src/androidTest/assets/realmlist_empty.json new file mode 100644 index 0000000000..b9b4587203 --- /dev/null +++ b/realm/src/androidTest/assets/realmlist_empty.json @@ -0,0 +1,3 @@ +{ + "columnRealmList" : [] +} \ No newline at end of file diff --git a/realm/src/androidTest/assets/single_child_object.json b/realm/src/androidTest/assets/single_child_object.json index 635ab0a693..b714b8073a 100644 --- a/realm/src/androidTest/assets/single_child_object.json +++ b/realm/src/androidTest/assets/single_child_object.json @@ -1,3 +1,3 @@ { - "columnObject" : { "name" : "Fido" } + "columnRealmObject" : { "name" : "Fido" } } \ No newline at end of file diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index 9dce6cf880..05e9757ed8 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -125,29 +125,29 @@ public void testImportJSon_childObjectList() throws JSONException { dogList.put(dog2); dogList.put(dog3); - allTypesObject.put("columnRealmObjectList", dogList); + allTypesObject.put("columnRealmList", dogList); testRealm.beginTransaction(); testRealm.createFromJson(AllTypes.class, allTypesObject); testRealm.commitTransaction(); AllTypes obj = testRealm.allObjects(AllTypes.class).first(); - assertEquals(3, obj.getColumnRealmObjectList().size()); - assertEquals("Fido-3", obj.getColumnRealmObjectList().get(2).getName()); + assertEquals(3, obj.getColumnRealmList().size()); + assertEquals("Fido-3", obj.getColumnRealmList().get(2).getName()); } public void testImportJSon_emptyChildObjectList() throws JSONException { JSONObject allTypesObject = new JSONObject(); JSONArray dogList = new JSONArray(); - allTypesObject.put("columnRealmObjectList", dogList); + allTypesObject.put("columnRealmList", dogList); testRealm.beginTransaction(); testRealm.createFromJson(AllTypes.class, allTypesObject); testRealm.commitTransaction(); AllTypes obj = testRealm.allObjects(AllTypes.class).first(); - assertEquals(0, obj.getColumnRealmObjectList().size()); + assertEquals(0, obj.getColumnRealmList().size()); } public void testImportJsonArray_empty() throws JSONException { @@ -176,11 +176,6 @@ public void testImportJsonArray() throws JSONException { assertEquals(1, testRealm.where(Dog.class).equalTo("name", "Fido-3").findAll().size()); } - - public void testRemoveObjetsIfImportFail() { - fail("Test if objects created by the json import is removed if something fail during decoding"); - } - public void testImportStream_null() throws IOException { testRealm.createAllFromJson(AllTypes.class, (InputStream) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); @@ -227,16 +222,36 @@ public void testImportStream_DateAsString() throws IOException { assertEquals(new Date(1000), obj.getColumnDate()); } - public void testImportStream_childObject() { - fail("Not implemented."); - } + public void testImportStream_childObject() throws IOException { + InputStream in = loadJsonFromAssets("single_child_object.json"); + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, in); + testRealm.commitTransaction(); + in.close(); - public void testImportStream_emptyChildObjectList() { - fail("Not implemented."); + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals("Fido", obj.getColumnRealmObject().getName()); } - public void testImportStream_childObjectList() { - fail("Not implemented."); + public void testImportStream_emptyChildObjectList() throws IOException { + InputStream in = loadJsonFromAssets("realmlist_empty.json"); + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, in); + testRealm.commitTransaction(); + in.close(); + + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals(0, obj.getColumnRealmList().size()); } + public void testImportStream_childObjectList() throws IOException { + InputStream in = loadJsonFromAssets("realmlist.json"); + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, in); + testRealm.commitTransaction(); + in.close(); + + assertEquals(3, testRealm.allObjects(Dog.class).size()); + assertEquals(1, testRealm.where(Dog.class).equalTo("name", "Fido-3").findAll().size()); + } } diff --git a/realm/src/androidTest/java/io/realm/entities/AllTypes.java b/realm/src/androidTest/java/io/realm/entities/AllTypes.java index f168773890..e79b44e6f9 100644 --- a/realm/src/androidTest/java/io/realm/entities/AllTypes.java +++ b/realm/src/androidTest/java/io/realm/entities/AllTypes.java @@ -30,7 +30,8 @@ public class AllTypes extends RealmObject { private Date columnDate; private byte[] columnBinary; private Dog columnRealmObject; - private RealmList columnRealmObjectList; + + private RealmList columnRealmList; public String getColumnString() { return columnString; @@ -96,11 +97,11 @@ public void setColumnRealmObject(Dog columnRealmObject) { this.columnRealmObject = columnRealmObject; } - public RealmList getColumnRealmObjectList() { - return columnRealmObjectList; + public RealmList getColumnRealmList() { + return columnRealmList; } - public void setColumnRealmObjectList(RealmList columnRealmObjectList) { - this.columnRealmObjectList = columnRealmObjectList; + public void setColumnRealmList(RealmList columnRealmList) { + this.columnRealmList = columnRealmList; } } From e4d78e0ee35dc3d78c446a0916d603f8666ed6dc Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Sat, 18 Oct 2014 09:47:45 +0200 Subject: [PATCH 11/33] Support for null JSON values added. --- .../processor/RealmProxyClassGenerator.java | 4 +- .../java/io/realm/RealmJsonTest.java | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java index 357fee2d3f..9c93911916 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java @@ -686,9 +686,9 @@ private void emitPopulateFromJsonStreamMethod(JavaWriter writer) throws IOExcept String fieldTypeCanonicalName = field.asType().toString(); if (i == 0) { - writer.beginControlFlow("if (name.equals(\"%s\"))", fieldName); + writer.beginControlFlow("if (name.equals(\"%s\") && reader.peek() != JsonToken.NULL)", fieldName); } else { - writer.nextControlFlow("else if (name.equals(\"%s\"))", fieldName); + writer.nextControlFlow("else if (name.equals(\"%s\") && reader.peek() != JsonToken.NULL)", fieldName); } if (typeUtils.isAssignable(field.asType(), realmObject)) { diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index 05e9757ed8..abc26d5529 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -176,6 +176,35 @@ public void testImportJsonArray() throws JSONException { assertEquals(1, testRealm.where(Dog.class).equalTo("name", "Fido-3").findAll().size()); } + public void testImportJson_nullValues() throws JSONException { + JSONObject json = new JSONObject(); + json.put("columnString", null); + json.put("columnLong", null); + json.put("columnFloat", null); + json.put("columnDouble", null); + json.put("columnBoolean", null); + json.put("columnBinary", null); + json.put("columnDate", null); + json.put("columnRealmObject", null); + json.put("columnRealmList", null); + + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, json); + testRealm.commitTransaction(); + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + + // Check that all primitive types are imported correctly + assertEquals("", obj.getColumnString()); + assertEquals(0L, obj.getColumnLong()); + assertEquals(0f, obj.getColumnFloat()); + assertEquals(0d, obj.getColumnDouble()); + assertEquals(false, obj.isColumnBoolean()); + assertArrayEquals(new byte[0], obj.getColumnBinary()); + assertNull(obj.getColumnRealmObject()); + assertEquals(0, obj.getColumnRealmList().size()); + } + + public void testImportStream_null() throws IOException { testRealm.createAllFromJson(AllTypes.class, (InputStream) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); @@ -254,4 +283,24 @@ public void testImportStream_childObjectList() throws IOException { assertEquals(3, testRealm.allObjects(Dog.class).size()); assertEquals(1, testRealm.where(Dog.class).equalTo("name", "Fido-3").findAll().size()); } + + public void testImportStream_nullValues() throws IOException { + InputStream in = loadJsonFromAssets("all_types_null.json"); + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, in); + testRealm.commitTransaction(); + in.close(); + + // Check that all primitive types are imported correctly + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals("", obj.getColumnString()); + assertEquals(0L, obj.getColumnLong()); + assertEquals(0f, obj.getColumnFloat()); + assertEquals(0d, obj.getColumnDouble()); + assertEquals(false, obj.isColumnBoolean()); + assertArrayEquals(new byte[0], obj.getColumnBinary()); + assertNull(obj.getColumnRealmObject()); + assertEquals(0, obj.getColumnRealmList().size()); + } + } From 097e862c0062752e96ecbe3df739ed88cb1fe3bc Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Sat, 18 Oct 2014 11:01:39 +0200 Subject: [PATCH 12/33] Cleanup test json. --- realm/src/androidTest/assets/all_types_null.json | 11 +++++++++++ realm/src/androidTest/assets/dates.json | 4 ---- 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 realm/src/androidTest/assets/all_types_null.json delete mode 100644 realm/src/androidTest/assets/dates.json diff --git a/realm/src/androidTest/assets/all_types_null.json b/realm/src/androidTest/assets/all_types_null.json new file mode 100644 index 0000000000..3b4cbdeb88 --- /dev/null +++ b/realm/src/androidTest/assets/all_types_null.json @@ -0,0 +1,11 @@ +{ + "columnString" : null, + "columnLong" : null, + "columnFloat" : null, + "columnDouble" : null, + "columnBoolean" : null, + "columnBinary" : null, + "columnDate" : null, + "columnRealmObject" : null, + "columnRealmList" : null +} \ No newline at end of file diff --git a/realm/src/androidTest/assets/dates.json b/realm/src/androidTest/assets/dates.json deleted file mode 100644 index d5ca5f132f..0000000000 --- a/realm/src/androidTest/assets/dates.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "columnDateLong" : 1000, - "columnDateString" : 1000 -} \ No newline at end of file From 9a6b36d2f22e2bb45ac860098b6a065e14a253ea Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Sat, 18 Oct 2014 21:04:09 +0200 Subject: [PATCH 13/33] Cleanup --- .../java/io/realm/processor/RealmJsonTypeHelper.java | 4 ++-- .../io/realm/processor/RealmProxyClassGenerator.java | 10 +++++----- realm/src/androidTest/java/io/realm/RealmJsonTest.java | 3 ++- .../main/java/io/realm/internal/json/JsonUtils.java | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java index 992ac2a4b6..16c8721bc3 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java @@ -73,7 +73,7 @@ public void emitStreamTypeConversion(String fieldName, String fieldType, JavaWri public static void emitFillJavaTypeWithJsonValue(String fieldName, String fieldType, JavaWriter writer) throws IOException { if (JAVA_TO_JSON_TYPES.containsKey(fieldType)) { writer.beginControlFlow("if (json.has(\"%s\"))", fieldName); - JAVA_TO_JSON_TYPES.get(fieldType).emitTypeConversion(fieldName, fieldType, writer); + JAVA_TO_JSON_TYPES.get(fieldType).emitTypeConversion(fieldName, fieldType, writer); writer.endControlFlow(); } } @@ -128,7 +128,7 @@ private static String capitaliseFirstChar(String input) { return input.substring(0, 1).toUpperCase() + input.substring(1); } - static class SimpleTypeConverter implements JsonToRealmTypeConverter { + private static class SimpleTypeConverter implements JsonToRealmTypeConverter { private final String castType; private final String jsonType; diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java index 9c93911916..7ee2c2ab41 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java @@ -622,15 +622,15 @@ else if (typeUtils.isAssignable(field.asType(), realmObject) || typeUtils.isAssi writer.emitEmptyLine(); // Add JSON methods - emitPopulateFromJsonObjectMethod(writer); - emitPopulateFromJsonStreamMethod(writer); + emitPopulateUsingJsonObjectMethod(writer); + emitPopulateUsingJsonStreamMethod(writer); // End the class definition writer.endType(); writer.close(); } - private void emitPopulateFromJsonObjectMethod(JavaWriter writer) throws IOException { + private void emitPopulateUsingJsonObjectMethod(JavaWriter writer) throws IOException { writer.emitAnnotation(Override.class); writer.beginMethod( "void", @@ -667,7 +667,7 @@ private void emitPopulateFromJsonObjectMethod(JavaWriter writer) throws IOExcept writer.emitEmptyLine(); } - private void emitPopulateFromJsonStreamMethod(JavaWriter writer) throws IOException { + private void emitPopulateUsingJsonStreamMethod(JavaWriter writer) throws IOException { writer.emitAnnotation(Override.class); writer.beginMethod( "void", @@ -679,7 +679,7 @@ private void emitPopulateFromJsonStreamMethod(JavaWriter writer) throws IOExcept writer.emitStatement("reader.beginObject()"); writer.beginControlFlow("while (reader.hasNext())"); writer.emitStatement("String name = reader.nextName()"); -// + for (int i = 0; i < fields.size(); i++) { VariableElement field = fields.get(i); String fieldName = field.getSimpleName().toString(); diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index abc26d5529..dd38c93203 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -199,6 +199,7 @@ public void testImportJson_nullValues() throws JSONException { assertEquals(0f, obj.getColumnFloat()); assertEquals(0d, obj.getColumnDouble()); assertEquals(false, obj.isColumnBoolean()); + assertEquals(new Date(0), obj.getColumnDate()); assertArrayEquals(new byte[0], obj.getColumnBinary()); assertNull(obj.getColumnRealmObject()); assertEquals(0, obj.getColumnRealmList().size()); @@ -298,9 +299,9 @@ public void testImportStream_nullValues() throws IOException { assertEquals(0f, obj.getColumnFloat()); assertEquals(0d, obj.getColumnDouble()); assertEquals(false, obj.isColumnBoolean()); + assertEquals(new Date(0), obj.getColumnDate()); assertArrayEquals(new byte[0], obj.getColumnBinary()); assertNull(obj.getColumnRealmObject()); assertEquals(0, obj.getColumnRealmList().size()); } - } diff --git a/realm/src/main/java/io/realm/internal/json/JsonUtils.java b/realm/src/main/java/io/realm/internal/json/JsonUtils.java index 350b45402b..bfa00a78c5 100644 --- a/realm/src/main/java/io/realm/internal/json/JsonUtils.java +++ b/realm/src/main/java/io/realm/internal/json/JsonUtils.java @@ -14,11 +14,11 @@ public class JsonUtils { * - "/Date()/" * - TODO ISO 8601 String * - * @param str String input of the supported types. + * @param str String input of date of the the supported types. * @return Date object or null if invalid input. * * @throws NumberFormatException If timestamp is not a proper long - * @throws IndexOutOfBoundsException if dates of type /Date(x)/ does not have a proper format. + * @throws IndexOutOfBoundsException if dates of type /Date(x)/ does not have the proper format. */ public static Date stringToDate(String str) { if (str == null || str.length() == 0) return null; From ee960fd1d6ee2baa64e779381c1f55786fa4b007 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Sat, 18 Oct 2014 21:59:29 +0200 Subject: [PATCH 14/33] Added support for importing json array stream to Realm. --- realm/src/androidTest/assets/array.json | 5 +++++ .../java/io/realm/RealmJsonTest.java | 13 ++++++++++++- realm/src/main/java/io/realm/Realm.java | 17 ++++++++++------- 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 realm/src/androidTest/assets/array.json diff --git a/realm/src/androidTest/assets/array.json b/realm/src/androidTest/assets/array.json new file mode 100644 index 0000000000..1c3c18ef97 --- /dev/null +++ b/realm/src/androidTest/assets/array.json @@ -0,0 +1,5 @@ +[ + { "name" : "Fido-1" }, + { "name" : "Fido-2" }, + { "name" : "Fido-3" } +] \ No newline at end of file diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index dd38c93203..ecb445d0e7 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -205,7 +205,6 @@ public void testImportJson_nullValues() throws JSONException { assertEquals(0, obj.getColumnRealmList().size()); } - public void testImportStream_null() throws IOException { testRealm.createAllFromJson(AllTypes.class, (InputStream) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); @@ -285,6 +284,18 @@ public void testImportStream_childObjectList() throws IOException { assertEquals(1, testRealm.where(Dog.class).equalTo("name", "Fido-3").findAll().size()); } + public void testImportStream_array() throws IOException { + InputStream in = loadJsonFromAssets("array.json"); + + testRealm.beginTransaction(); + testRealm.createAllFromJson(Dog.class, in); + testRealm.commitTransaction(); + + assertEquals(3, testRealm.allObjects(Dog.class).size()); + assertEquals(1, testRealm.where(Dog.class).equalTo("name", "Fido-3").findAll().size()); + } + + public void testImportStream_nullValues() throws IOException { InputStream in = loadJsonFromAssets("all_types_null.json"); testRealm.beginTransaction(); diff --git a/realm/src/main/java/io/realm/Realm.java b/realm/src/main/java/io/realm/Realm.java index f634b22cf0..4def8886b3 100644 --- a/realm/src/main/java/io/realm/Realm.java +++ b/realm/src/main/java/io/realm/Realm.java @@ -353,8 +353,6 @@ private static Realm createAndValidate(String absolutePath, byte[] key, boolean return realm; } - - /** * Add an array of of JsonObjects to the Realm as a new object. This must be done inside a transaction. * @@ -370,13 +368,11 @@ public void createAllFromJson(Class clazz, JSONArray try { obj.populateUsingJsonObject(json.getJSONObject(i)); } catch (Exception e) { - // TODO Remove object from realm throw new RealmException("Could not map Json", e); } } } - /** * Add a Json InputStream to the Realm as new objects. This must be done inside a transaction. * @@ -388,11 +384,18 @@ public void createAllFromJson(Class clazz, JSONArray */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void createAllFromJson(Class clazz, InputStream inputStream) throws IOException { - // TODO + if (inputStream != null && clazz != null) { + JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8")); + reader.beginArray(); + while (reader.hasNext()) { + E obj = createObject(clazz); + obj.populateUsingJsonStream(reader); + } + reader.endArray(); + reader.close(); + } } - - /** * Add a JsonObject to the Realm as a new object. This must be done inside a transaction. * From 69db6028b20d0955438f56e328eb9277de923a67 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Sat, 18 Oct 2014 22:04:42 +0200 Subject: [PATCH 15/33] Added unit test for objects saved despite exception. --- .../java/io/realm/RealmJsonTest.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index ecb445d0e7..e11d2ed054 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -205,7 +205,27 @@ public void testImportJson_nullValues() throws JSONException { assertEquals(0, obj.getColumnRealmList().size()); } - public void testImportStream_null() throws IOException { + // Test that given an exception everything up to the exception is saved + public void testImportJson_jsonexception() throws JSONException { + JSONObject json = new JSONObject(); + json.put("columnString", "Foo"); + json.put("columnDate", "Boom"); + + try { + testRealm.beginTransaction(); + testRealm.createFromJson(AllTypes.class, json); + } catch (Exception e) { + // Ignore + } finally { + testRealm.commitTransaction(); + } + + AllTypes obj = testRealm.allObjects(AllTypes.class).first(); + assertEquals("Foo", obj.getColumnString()); + assertEquals(new Date(0), obj.getColumnDate()); + } + + public void testImportStream_null() throws IOException { testRealm.createAllFromJson(AllTypes.class, (InputStream) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); } @@ -315,4 +335,5 @@ public void testImportStream_nullValues() throws IOException { assertNull(obj.getColumnRealmObject()); assertEquals(0, obj.getColumnRealmList().size()); } + } From c5a4b234ca8456a912d51f91d310a5a3da4645c8 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Tue, 21 Oct 2014 21:03:31 +0200 Subject: [PATCH 16/33] Added example project + unit tests for ignored properties. --- .gitignore | 2 + examples/jsonImportExample/.gitignore | 1 + examples/jsonImportExample/build.gradle | 28 ++++++ examples/jsonImportExample/proguard-rules.pro | 17 ++++ .../src/main/AndroidManifest.xml | 21 ++++ .../src/main/assets/cities.json | 64 +++++++++++++ .../io/realm/examples/realmgridview/City.java | 42 ++++++++ .../examples/realmgridview/CityAdapter.java | 86 +++++++++++++++++ .../RealmJsonExampleActivity.java | 90 ++++++++++++++++++ .../main/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 9397 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 5237 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 14383 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 19388 bytes .../res/layout/activity_realm_example.xml | 25 +++++ .../src/main/res/layout/city_listitem.xml | 21 ++++ .../src/main/res/menu/options_menu.xml | 11 +++ .../src/main/res/values-w820dp/dimens.xml | 6 ++ .../src/main/res/values/dimens.xml | 5 + .../src/main/res/values/strings.xml | 7 ++ .../src/main/res/values/styles.xml | 8 ++ .../java/io/realm/RealmJsonTest.java | 19 +++- settings.gradle | 3 +- 22 files changed, 453 insertions(+), 3 deletions(-) create mode 100644 examples/jsonImportExample/.gitignore create mode 100644 examples/jsonImportExample/build.gradle create mode 100644 examples/jsonImportExample/proguard-rules.pro create mode 100644 examples/jsonImportExample/src/main/AndroidManifest.xml create mode 100644 examples/jsonImportExample/src/main/assets/cities.json create mode 100644 examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/City.java create mode 100644 examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/CityAdapter.java create mode 100644 examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/RealmJsonExampleActivity.java create mode 100644 examples/jsonImportExample/src/main/res/drawable-hdpi/ic_launcher.png create mode 100644 examples/jsonImportExample/src/main/res/drawable-mdpi/ic_launcher.png create mode 100644 examples/jsonImportExample/src/main/res/drawable-xhdpi/ic_launcher.png create mode 100644 examples/jsonImportExample/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100644 examples/jsonImportExample/src/main/res/layout/activity_realm_example.xml create mode 100755 examples/jsonImportExample/src/main/res/layout/city_listitem.xml create mode 100644 examples/jsonImportExample/src/main/res/menu/options_menu.xml create mode 100644 examples/jsonImportExample/src/main/res/values-w820dp/dimens.xml create mode 100644 examples/jsonImportExample/src/main/res/values/dimens.xml create mode 100644 examples/jsonImportExample/src/main/res/values/strings.xml create mode 100644 examples/jsonImportExample/src/main/res/values/styles.xml diff --git a/.gitignore b/.gitignore index acec2b9127..5af6c9cc59 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ core-* */.DS_Store # JNI libs +realm_version_check.timestamp /realm/src/main/jniLibs # Build artifacts @@ -44,3 +45,4 @@ distribution/version.txt distribution/RealmGridViewExample/app/src distribution/RealmIntroExample/app/src distribution/RealmMigrationExample/app/src + diff --git a/examples/jsonImportExample/.gitignore b/examples/jsonImportExample/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/examples/jsonImportExample/.gitignore @@ -0,0 +1 @@ +/build diff --git a/examples/jsonImportExample/build.gradle b/examples/jsonImportExample/build.gradle new file mode 100644 index 0000000000..1f5f413e9f --- /dev/null +++ b/examples/jsonImportExample/build.gradle @@ -0,0 +1,28 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 19 + buildToolsVersion "20.0.0" + defaultConfig { + applicationId 'io.realm.examples.jsonimport' + minSdkVersion 15 + targetSdkVersion 19 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + runProguard false + } + } + productFlavors { + } +} + +tasks.preBuild { + dependsOn ":realm:androidJar" +} + +dependencies { + compile files("../../realm/build/libs/realm-${version}.jar") +} diff --git a/examples/jsonImportExample/proguard-rules.pro b/examples/jsonImportExample/proguard-rules.pro new file mode 100644 index 0000000000..200fdffd58 --- /dev/null +++ b/examples/jsonImportExample/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /usr/local/Cellar/android-sdk/22.6.2/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/examples/jsonImportExample/src/main/AndroidManifest.xml b/examples/jsonImportExample/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..e5615233cf --- /dev/null +++ b/examples/jsonImportExample/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + diff --git a/examples/jsonImportExample/src/main/assets/cities.json b/examples/jsonImportExample/src/main/assets/cities.json new file mode 100644 index 0000000000..db4e522255 --- /dev/null +++ b/examples/jsonImportExample/src/main/assets/cities.json @@ -0,0 +1,64 @@ +[ + { "name" : "Barcelona", + "votes" : 23 }, + + { "name" : "San Francisco", + "votes" : 21 }, + + { "name" : "Venice", + "votes" : 19 }, + + { "name" : "Melbourne", + "votes" : 18 }, + + { "name" : "Paris", + "votes" : 17 }, + + { "name" : "Seville", + "votes" : 14 }, + + { "name" : "Sydney", + "votes" : 12 }, + + { "name" : "New York", + "votes" : 11 }, + + { "name" : "Krakow", + "votes" : 9 }, + + { "name" : "Peoria", + "votes" : 72 }, + + { "name" : "Springfield", + "votes" : 88 }, + + { "name" : "Kansas City", + "votes" : 100 }, + + { "name" : "Tokyo", + "votes" : 20 }, + + { "name" : "Boise", + "votes" : 17 }, + + { "name" : "Los Angeles", + "votes" : 14 }, + + { "name" : "Newark", + "votes" : 12 }, + + { "name" : "Chicago", + "votes" : 11 }, + + { "name" : "Detroit", + "votes" : 91 }, + + { "name" : "Florence", + "votes" : 4 }, + + { "name" : "Madrid", + "votes" : 1 }, + + { "name" : "London", + "votes" : 9 } +] \ No newline at end of file diff --git a/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/City.java b/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/City.java new file mode 100644 index 0000000000..f0aec3c61c --- /dev/null +++ b/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/City.java @@ -0,0 +1,42 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.realmgridview; + +import io.realm.RealmObject; + +public class City extends RealmObject { + + private String name; + private long votes; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getVotes() { + return votes; + } + + public void setVotes(long votes) { + this.votes = votes; + } + +} diff --git a/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/CityAdapter.java b/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/CityAdapter.java new file mode 100644 index 0000000000..1b5050abfd --- /dev/null +++ b/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/CityAdapter.java @@ -0,0 +1,86 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.realmgridview; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import java.util.List; + +// This adapter is strictly to interface with the GridView and doesn't +// particular show much interesting Realm functionality. + +// Alternatively from this example, +// a developer could update the getView() to pull items from the Realm. + +public class CityAdapter extends BaseAdapter { + + public static final String TAG = RealmJsonExampleActivity.class.getName(); + + private LayoutInflater inflater; + + private List cities = null; + + public CityAdapter(Context context) { + inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + public void setData(List details) { + this.cities = details; + } + + @Override + public int getCount() { + if (cities == null) { + return 0; + } + return cities.size(); + } + + @Override + public Object getItem(int position) { + if (cities == null || cities.get(position) == null) { + return null; + } + return cities.get(position); + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public View getView(int position, View currentView, ViewGroup parent) { + if (currentView == null) { + currentView = inflater.inflate(R.layout.city_listitem, parent, false); + } + + City city = cities.get(position); + + if (city != null) { + ((TextView) currentView.findViewById(R.id.name)).setText(city.getName()); + ((TextView) currentView.findViewById(R.id.votes)).setText(Long.toString(city.getVotes())); + } + + return currentView; + } +} diff --git a/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/RealmJsonExampleActivity.java b/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/RealmJsonExampleActivity.java new file mode 100644 index 0000000000..214d436dbd --- /dev/null +++ b/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/RealmJsonExampleActivity.java @@ -0,0 +1,90 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.realmgridview; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.GridView; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import io.realm.Realm; + +public class RealmJsonExampleActivity extends Activity { + + public static final String TAG = RealmJsonExampleActivity.class.getName(); + + private GridView mGridView; + private CityAdapter mAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_realm_example); + } + + @Override + public void onResume() { + super.onResume(); + + // Load from file "cities.json" first time + if(mAdapter == null) { + List cities = loadCities(); + + //This is the GridView adapter + mAdapter = new CityAdapter(this); + mAdapter.setData(cities); + + //This is the GridView which will display the list of cities + mGridView = (GridView) findViewById(R.id.cities_list); + mGridView.setAdapter(mAdapter); + mAdapter.notifyDataSetChanged(); + mGridView.invalidate(); + } + } + + public List loadCities() { + + // In this case we're loading from local assets. + // NOTE: could alternatively easily load from network + InputStream stream = null; + try { + stream = getAssets().open("cities.json"); + } catch (IOException e) { + return null; + } + + Realm.deleteRealmFile(this); + + // Store the retrieved items to the Realm + Realm realm = Realm.getInstance(this); + + // Open a transaction to store items into the realm + try { + realm.beginTransaction(); + realm.createAllFromJson(City.class, stream); + realm.commitTransaction(); + stream.close(); + } catch (IOException e) { + // Ignore + } + + return realm.allObjects(City.class); + } +} diff --git a/examples/jsonImportExample/src/main/res/drawable-hdpi/ic_launcher.png b/examples/jsonImportExample/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..96a442e5b8e9394ccf50bab9988cb2316026245d GIT binary patch literal 9397 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 literal 0 HcmV?d00001 diff --git a/examples/jsonImportExample/src/main/res/drawable-xhdpi/ic_launcher.png b/examples/jsonImportExample/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..71c6d760f05183ef8a47c614d8d13380c8528499 GIT binary patch literal 14383 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 literal 0 HcmV?d00001 diff --git a/examples/jsonImportExample/src/main/res/drawable-xxhdpi/ic_launcher.png b/examples/jsonImportExample/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4df18946442ed763bd52cf3adca31617848656fa GIT binary patch literal 19388 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 literal 0 HcmV?d00001 diff --git a/examples/jsonImportExample/src/main/res/layout/activity_realm_example.xml b/examples/jsonImportExample/src/main/res/layout/activity_realm_example.xml new file mode 100644 index 0000000000..a1a15a17a3 --- /dev/null +++ b/examples/jsonImportExample/src/main/res/layout/activity_realm_example.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/examples/jsonImportExample/src/main/res/layout/city_listitem.xml b/examples/jsonImportExample/src/main/res/layout/city_listitem.xml new file mode 100755 index 0000000000..84a41ed2dc --- /dev/null +++ b/examples/jsonImportExample/src/main/res/layout/city_listitem.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/examples/jsonImportExample/src/main/res/menu/options_menu.xml b/examples/jsonImportExample/src/main/res/menu/options_menu.xml new file mode 100644 index 0000000000..c713700d24 --- /dev/null +++ b/examples/jsonImportExample/src/main/res/menu/options_menu.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/examples/jsonImportExample/src/main/res/values-w820dp/dimens.xml b/examples/jsonImportExample/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000000..63fc816444 --- /dev/null +++ b/examples/jsonImportExample/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/examples/jsonImportExample/src/main/res/values/dimens.xml b/examples/jsonImportExample/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..47c8224673 --- /dev/null +++ b/examples/jsonImportExample/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/examples/jsonImportExample/src/main/res/values/strings.xml b/examples/jsonImportExample/src/main/res/values/strings.xml new file mode 100644 index 0000000000..5e6e5ad0ad --- /dev/null +++ b/examples/jsonImportExample/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + + + Realm Example + Reset + + diff --git a/examples/jsonImportExample/src/main/res/values/styles.xml b/examples/jsonImportExample/src/main/res/values/styles.xml new file mode 100644 index 0000000000..ff6c9d2c0f --- /dev/null +++ b/examples/jsonImportExample/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index e11d2ed054..73df5e40e6 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -13,6 +13,7 @@ import java.util.Date; import io.realm.entities.AllTypes; +import io.realm.entities.AnnotationTypes; import io.realm.entities.Dog; import static io.realm.internal.test.ExtraTests.assertArrayEquals; @@ -23,11 +24,11 @@ public class RealmJsonTest extends AndroidTestCase { @Override protected void setUp() throws Exception { - Realm.deleteRealmFile(getContext()); testRealm = Realm.getInstance(getContext()); testRealm.beginTransaction(); testRealm.clear(AllTypes.class); testRealm.clear(Dog.class); + testRealm.clear(AnnotationTypes.class); testRealm.commitTransaction(); } @@ -225,6 +226,21 @@ public void testImportJson_jsonexception() throws JSONException { assertEquals(new Date(0), obj.getColumnDate()); } + public void testImportJson_respectIgnoredFields() throws JSONException { + JSONObject json = new JSONObject(); + json.put("indexString", "Foo"); + json.put("notIndexString", "Bar"); + json.put("ignoreString", "Baz"); + + testRealm.beginTransaction(); + testRealm.createFromJson(AnnotationTypes.class, json); + testRealm.commitTransaction(); + + AnnotationTypes annotationsObject = testRealm.allObjects(AnnotationTypes.class).first(); + assertEquals("Foo", annotationsObject.getIndexString()); + assertEquals(null, annotationsObject.getIgnoreString()); + } + public void testImportStream_null() throws IOException { testRealm.createAllFromJson(AllTypes.class, (InputStream) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); @@ -335,5 +351,4 @@ public void testImportStream_nullValues() throws IOException { assertNull(obj.getColumnRealmObject()); assertEquals(0, obj.getColumnRealmList().size()); } - } diff --git a/settings.gradle b/settings.gradle index a2764840e4..1ce1a8d57a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -include ':realm', ':introExample', ':gridViewExample', ':concurrencyExample', ':encryptionExample', ':migrationExample' +include ':realm', ':introExample', ':gridViewExample', ':concurrencyExample', ':encryptionExample', ':migrationExample', ':jsonImportExample' include ':realm-annotations-processor' include ':performance' project(':performance').projectDir = new File('examples/performance') @@ -9,3 +9,4 @@ project(':gridViewExample').projectDir = new File('examples/gridViewExample') project(':concurrencyExample').projectDir = new File('examples/concurrencyExample') project(':encryptionExample').projectDir = new File('examples/encryptionExample') project(':migrationExample').projectDir = new File('examples/migrationExample') +project(':jsonImportExample').projectDir = new File('examples/jsonImportExample') From ae3bba1e029673958f167a1d7dfee2cf96e5f2ae Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Wed, 22 Oct 2014 19:21:29 +0200 Subject: [PATCH 17/33] PR review fixes. --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- realm-annotations-processor/build.gradle | 2 +- .../java/io/realm/RealmJsonTest.java | 15 +++++++++++++ .../io/realm/internal/json/JsonUtils.java | 21 +++++++++++++++---- realm_version_check.timestamp | 1 - settings.gradle | 1 - 7 files changed, 35 insertions(+), 9 deletions(-) delete mode 100644 realm_version_check.timestamp diff --git a/build.gradle b/build.gradle index 09b85fe924..41a5c1bbe5 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:0.13.+' + classpath 'com.android.tools.build:gradle:0.12.2' classpath 'de.undercouch:gradle-download-task:1.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:0.5' // Version 0.6 seems to require gradle 2.0+ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 097aec6d5e..0e1f2d976d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-1.12-all.zip diff --git a/realm-annotations-processor/build.gradle b/realm-annotations-processor/build.gradle index d6f06caf6a..62307f5bfa 100644 --- a/realm-annotations-processor/build.gradle +++ b/realm-annotations-processor/build.gradle @@ -1,6 +1,6 @@ apply plugin: 'java' -version = new File("${rootDir}/version.txt").text +version = new File("${projectDir}/../version.txt").text sourceCompatibility = '1.6' targetCompatibility = '1.6' diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index 73df5e40e6..3224d8c4c1 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm; import android.content.res.AssetManager; diff --git a/realm/src/main/java/io/realm/internal/json/JsonUtils.java b/realm/src/main/java/io/realm/internal/json/JsonUtils.java index bfa00a78c5..ebbde5b8e5 100644 --- a/realm/src/main/java/io/realm/internal/json/JsonUtils.java +++ b/realm/src/main/java/io/realm/internal/json/JsonUtils.java @@ -1,18 +1,31 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.internal.json; import android.util.Base64; import java.util.Date; -/** - * Created by Christian Melchior on 17/10/14. - */ public class JsonUtils { /** * Converts a Json string to a Java Date object. Currently supports 2 types: * - "" * - "/Date()/" - * - TODO ISO 8601 String + * - TODO ISO 8601 String, * * @param str String input of date of the the supported types. * @return Date object or null if invalid input. diff --git a/realm_version_check.timestamp b/realm_version_check.timestamp deleted file mode 100644 index 5afa2a9e5e..0000000000 --- a/realm_version_check.timestamp +++ /dev/null @@ -1 +0,0 @@ -1413573872243 diff --git a/settings.gradle b/settings.gradle index 1ce1a8d57a..d6a2d9d6c5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,4 @@ include ':realm', ':introExample', ':gridViewExample', ':concurrencyExample', ':encryptionExample', ':migrationExample', ':jsonImportExample' -include ':realm-annotations-processor' include ':performance' project(':performance').projectDir = new File('examples/performance') From 63d1f2a5c2c6c078f6aba1ea577c22f90b294896 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Wed, 22 Oct 2014 20:10:27 +0200 Subject: [PATCH 18/33] Added file with notes about API. --- json_api.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 json_api.md diff --git a/json_api.md b/json_api.md new file mode 100644 index 0000000000..70751423dd --- /dev/null +++ b/json_api.md @@ -0,0 +1,51 @@ +## API Methods + + +### Android + + Realm.createObjectFromJson(Class realmObject, JSONObject json) + Realm.createAllFromJson(Class realmObject, JSONArray json) + + Realm.createObjectFromJson(Class realmObject, InputStream json); + Realm.createAllFromJson(Class realmObject, InputStream json); + + +### Java + +*Not implemented yet* + + +## Notes about the JSON API + + +- Import using InputStream only works for API 11+. Methods are annotated as such. + +- JSON property names need to match java variabel names. + +- Import API does not work in standard Java. JSON is only supported from Java 7, and with a + different API. + +- If import fails, everything up to that point is put in the database (if Realm.commitTransaction() + is called). We need a Realm.cancelWriteTransaction() to change this behavior. + +- createObject() returns the created Object, createAll() doesn't. Returning it matches the current + API, but not being able to for Arrays is annoying. Does it make sense to return created object + anyway? As it is already filled. + +- Currently two methods are added to RealmObject: populateUsingJsonObject() and + populateUsingJsonStream(). They are overridden in the proxy objects with the proper + implementations. It would perhaps be better to move these to a separate class, but then it makes + it harder to hook it into the Realm object. + + +### Enhancements + +- adding Realm.rollback() or Realm.cancelWriteTransaction() will make it possible to abort a faulty + import. + +- Java 6 does not have Json parsing capabilities. We need to add them ourselves. Perhaps using GSON. + +- Realm.createOrUpdateFromJson() would be a natural extension but requires primary key support. + +- Consider adding mapping annotations between Json and Java, but is this a Realm responsibility? + From ab851d1518d0c98b9ae26c3d6ef993b92d4ca8ea Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Wed, 22 Oct 2014 20:21:18 +0200 Subject: [PATCH 19/33] Fixed missing PR comment. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 41a5c1bbe5..e19c35cb35 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { allprojects { group = 'io.realm' - version = new File("${rootDir}/version.txt").text + version = new File("version.txt").text repositories { jcenter() } From 2326d0cc5ceaf79677f58257642b520d95963a72 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Wed, 22 Oct 2014 20:25:46 +0200 Subject: [PATCH 20/33] Added note about dates. --- json_api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/json_api.md b/json_api.md index 70751423dd..142de19542 100644 --- a/json_api.md +++ b/json_api.md @@ -49,3 +49,4 @@ - Consider adding mapping annotations between Json and Java, but is this a Realm responsibility? +- Consider which date representations we should support. There exist a multitude of options. From db92dcb8318b267bcaa6804448c781910771ea02 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Sat, 25 Oct 2014 21:40:12 +0200 Subject: [PATCH 21/33] Added support for importing JSON directly as Strings. Streamlined method names and JavaDoc. --- changelog.txt | 1 + json_api.md | 7 +- .../java/io/realm/RealmJsonTest.java | 100 ++++++++++--- realm/src/main/java/io/realm/Realm.java | 134 ++++++++++++------ 4 files changed, 181 insertions(+), 61 deletions(-) diff --git a/changelog.txt b/changelog.txt index 00969f33ad..7cc3dbb79d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,7 @@ 0.72.0 (??) * Extending sorting to more types: boolean, short, int, long, float, double, Date, and String fields are now supported + * Support for directly adding data to Realm using Json. 0.71.0 (07 Oct 2014) * Simplified the release artifact to a single Jar file diff --git a/json_api.md b/json_api.md index 142de19542..9317554d33 100644 --- a/json_api.md +++ b/json_api.md @@ -9,6 +9,9 @@ Realm.createObjectFromJson(Class realmObject, InputStream json); Realm.createAllFromJson(Class realmObject, InputStream json); + Realm.createObjectFromJson(Class realmObject, String json); + Realm.createAllFromJson(Class realmObject, String json); + ### Java @@ -20,7 +23,7 @@ - Import using InputStream only works for API 11+. Methods are annotated as such. -- JSON property names need to match java variabel names. +- JSON property names need to match java variable names. - Import API does not work in standard Java. JSON is only supported from Java 7, and with a different API. @@ -49,4 +52,4 @@ - Consider adding mapping annotations between Json and Java, but is this a Realm responsibility? -- Consider which date representations we should support. There exist a multitude of options. +- Consider which date representations we should support. There exist a multitude of options. \ No newline at end of file diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index 3224d8c4c1..1df1027670 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -30,6 +30,7 @@ import io.realm.entities.AllTypes; import io.realm.entities.AnnotationTypes; import io.realm.entities.Dog; +import io.realm.exceptions.RealmException; import static io.realm.internal.test.ExtraTests.assertArrayEquals; @@ -60,7 +61,7 @@ private InputStream loadJsonFromAssets(String file) { } public void testImportJSon_nullObject() { - testRealm.createFromJson(AllTypes.class, (JSONObject) null); + testRealm.createObjectFromJson(AllTypes.class, (JSONObject) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); } @@ -80,7 +81,7 @@ public void testImportJSon_allSimpSimpleObjectAllTypes() throws JSONException { json.put("columnBinary", new String(Base64.encode(new byte[] {1,2,3}, Base64.DEFAULT))); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, json); + testRealm.createObjectFromJson(AllTypes.class, json); testRealm.commitTransaction(); AllTypes obj = testRealm.allObjects(AllTypes.class).first(); @@ -98,7 +99,7 @@ public void testImportJSon_dateAsLong() throws JSONException { json.put("columnDate", 1000L); // Realm operates at seconds level granularity testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, json); + testRealm.createObjectFromJson(AllTypes.class, json); testRealm.commitTransaction(); AllTypes obj = testRealm.allObjects(AllTypes.class).first(); @@ -110,7 +111,7 @@ public void testImportJSon_dateAsString() throws JSONException { json.put("columnDate", "/Date(1000)/"); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, json); + testRealm.createObjectFromJson(AllTypes.class, json); testRealm.commitTransaction(); AllTypes obj = testRealm.allObjects(AllTypes.class).first(); @@ -124,7 +125,7 @@ public void testImportJSon_childObject() throws JSONException { allTypesObject.put("columnRealmObject", dogObject); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, allTypesObject); + testRealm.createObjectFromJson(AllTypes.class, allTypesObject); testRealm.commitTransaction(); AllTypes obj = testRealm.allObjects(AllTypes.class).first(); @@ -144,7 +145,7 @@ public void testImportJSon_childObjectList() throws JSONException { allTypesObject.put("columnRealmList", dogList); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, allTypesObject); + testRealm.createObjectFromJson(AllTypes.class, allTypesObject); testRealm.commitTransaction(); AllTypes obj = testRealm.allObjects(AllTypes.class).first(); @@ -159,13 +160,47 @@ public void testImportJSon_emptyChildObjectList() throws JSONException { allTypesObject.put("columnRealmList", dogList); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, allTypesObject); + testRealm.createObjectFromJson(AllTypes.class, allTypesObject); testRealm.commitTransaction(); AllTypes obj = testRealm.allObjects(AllTypes.class).first(); assertEquals(0, obj.getColumnRealmList().size()); } + public void testImportJsonString_simpleObject() { + testRealm.beginTransaction(); + Dog dog = testRealm.createObjectFromJson(Dog.class, "{ name: \"Foo\" }"); + testRealm. commitTransaction(); + + assertEquals("Foo", dog.getName()); + assertEquals("Foo", testRealm.allObjects(Dog.class).first().getName()); + } + + + public void testImportJsonString_faultyJson() { + testRealm.beginTransaction(); + try { + Dog dog = testRealm.createObjectFromJson(Dog.class, "{ name \"Foo\" }"); + } catch (RealmException e) { + assertTrue(true); + return; + } finally { + testRealm.commitTransaction(); + } + + fail("Doesn't handle faulty JSON properly"); + } + + + public void testImportJsonString_null() { + testRealm.beginTransaction(); + Dog dog = testRealm.createObjectFromJson(Dog.class, (String) null); + testRealm.commitTransaction(); + + assertNull(dog); + assertEquals(0, testRealm.allObjects(Dog.class).size()); + } + public void testImportJsonArray_empty() throws JSONException { JSONArray array = new JSONArray(); testRealm.beginTransaction(); @@ -205,7 +240,7 @@ public void testImportJson_nullValues() throws JSONException { json.put("columnRealmList", null); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, json); + testRealm.createObjectFromJson(AllTypes.class, json); testRealm.commitTransaction(); AllTypes obj = testRealm.allObjects(AllTypes.class).first(); @@ -229,7 +264,7 @@ public void testImportJson_jsonexception() throws JSONException { try { testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, json); + testRealm.createObjectFromJson(AllTypes.class, json); } catch (Exception e) { // Ignore } finally { @@ -248,7 +283,7 @@ public void testImportJson_respectIgnoredFields() throws JSONException { json.put("ignoreString", "Baz"); testRealm.beginTransaction(); - testRealm.createFromJson(AnnotationTypes.class, json); + testRealm.createObjectFromJson(AnnotationTypes.class, json); testRealm.commitTransaction(); AnnotationTypes annotationsObject = testRealm.allObjects(AnnotationTypes.class).first(); @@ -256,6 +291,37 @@ public void testImportJson_respectIgnoredFields() throws JSONException { assertEquals(null, annotationsObject.getIgnoreString()); } + public void testImportJsonArrayString_simpleArray() { + testRealm.beginTransaction(); + testRealm.createAllFromJson(Dog.class, "[{ name: \"Foo\" }, { name: \"Bar\" }]"); + testRealm. commitTransaction(); + + assertEquals(2, testRealm.allObjects(Dog.class).size()); + } + + public void testImportJsonArrayString_faultyJson() { + testRealm.beginTransaction(); + try { + testRealm.createAllFromJson(Dog.class, "[{ name : \"Foo\" ]"); + } catch (RealmException e) { + assertTrue(true); + return; + } finally { + testRealm.commitTransaction(); + } + + fail("Doesn't handle faulty JSON properly"); + } + + + public void testImportJsonArrayString_null() { + testRealm.beginTransaction(); + testRealm.createAllFromJson(Dog.class, (String) null); + testRealm.commitTransaction(); + + assertEquals(0, testRealm.allObjects(Dog.class).size()); + } + public void testImportStream_null() throws IOException { testRealm.createAllFromJson(AllTypes.class, (InputStream) null); assertEquals(0, testRealm.allObjects(AllTypes.class).size()); @@ -264,7 +330,7 @@ public void testImportStream_null() throws IOException { public void testImportStream_allSimpleTypes() throws IOException { InputStream in = loadJsonFromAssets("all_simple_types.json"); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, in); + testRealm.createObjectFromJson(AllTypes.class, in); testRealm.commitTransaction(); in.close(); @@ -281,7 +347,7 @@ public void testImportStream_allSimpleTypes() throws IOException { public void testImportStream_DateAsLong() throws IOException { InputStream in = loadJsonFromAssets("date_as_long.json"); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, in); + testRealm.createObjectFromJson(AllTypes.class, in); testRealm.commitTransaction(); in.close(); @@ -293,7 +359,7 @@ public void testImportStream_DateAsLong() throws IOException { public void testImportStream_DateAsString() throws IOException { InputStream in = loadJsonFromAssets("date_as_string.json"); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, in); + testRealm.createObjectFromJson(AllTypes.class, in); testRealm.commitTransaction(); in.close(); @@ -305,7 +371,7 @@ public void testImportStream_DateAsString() throws IOException { public void testImportStream_childObject() throws IOException { InputStream in = loadJsonFromAssets("single_child_object.json"); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, in); + testRealm.createObjectFromJson(AllTypes.class, in); testRealm.commitTransaction(); in.close(); @@ -316,7 +382,7 @@ public void testImportStream_childObject() throws IOException { public void testImportStream_emptyChildObjectList() throws IOException { InputStream in = loadJsonFromAssets("realmlist_empty.json"); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, in); + testRealm.createObjectFromJson(AllTypes.class, in); testRealm.commitTransaction(); in.close(); @@ -327,7 +393,7 @@ public void testImportStream_emptyChildObjectList() throws IOException { public void testImportStream_childObjectList() throws IOException { InputStream in = loadJsonFromAssets("realmlist.json"); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, in); + testRealm.createObjectFromJson(AllTypes.class, in); testRealm.commitTransaction(); in.close(); @@ -350,7 +416,7 @@ public void testImportStream_array() throws IOException { public void testImportStream_nullValues() throws IOException { InputStream in = loadJsonFromAssets("all_types_null.json"); testRealm.beginTransaction(); - testRealm.createFromJson(AllTypes.class, in); + testRealm.createObjectFromJson(AllTypes.class, in); testRealm.commitTransaction(); in.close(); diff --git a/realm/src/main/java/io/realm/Realm.java b/realm/src/main/java/io/realm/Realm.java index 2546a711a0..9965d41a7e 100644 --- a/realm/src/main/java/io/realm/Realm.java +++ b/realm/src/main/java/io/realm/Realm.java @@ -361,15 +361,17 @@ private static Realm createAndValidate(String absolutePath, byte[] key, boolean } /** - * Add an array of of JsonObjects to the Realm as a new object. This must be done inside a transaction. + * Create a matching Realm object for each object in a JSON array. Unknown JSON properties are + * ignored. This must be done inside a transaction. * - * @param clazz Class of object the json will map to. All Objects in the array must be of the same type. - * @param json Array of JsonObject's that can map to the chosen clazz. Properties not in the class are ignored. + * @param clazz Type of Realm objects to create. + * @param json Array where each JSONObject must map to the chosen class. * - * @throws RealmException if the mapping fail. + * @throws RealmException if mapping from JSON fail. */ public void createAllFromJson(Class clazz, JSONArray json) { - if (json == null) return; + if (clazz == null || json == null) return; + for (int i = 0; i < json.length(); i++) { E obj = createObject(clazz); try { @@ -381,45 +383,69 @@ public void createAllFromJson(Class clazz, JSONArray } /** - * Add a Json InputStream to the Realm as new objects. This must be done inside a transaction. + * Create a matching Realm object for each object in a JSON array. Unknown JSON properties are + * ignored. This must be done inside a transaction. * - * @param clazz Class of object the json will map to. All Objects in the array must be of the same type. - * @param inputStream A JSON InputStream of objects of type clazz. All objects must be of the chosen clazz. Properties not in the class are ignored. + * @param clazz Type of Realm objects to create. + * @param json JSON array as a String where each object can map to the chosen class. * - * @throws RealmException if the mapping fail. - * @throws IOException if something is wrong with the input stream. + * @throws RealmException if mapping from JSON failed. + */ + public void createAllFromJson(Class clazz, String json) { + if (clazz == null || json == null || json.length() == 0) return; + + JSONArray arr; + try { + arr = new JSONArray(json); + } catch (Exception e) { + throw new RealmException("Could not create JSON array from string", e); + } + + createAllFromJson(clazz, arr); + } + + /** + * Create a Realm object for each object in a JSON array. Unknown properties are + * ignored. This must be done inside a transaction. + * + * @param clazz Type of Realm objects created. + * @param inputStream JSON array as a InputStream. All objects in the array must be of the + * chosen class. + * + * @throws RealmException if the mapping from JSON failed. + * @throws IOException if something was wrong with the input stream. */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void createAllFromJson(Class clazz, InputStream inputStream) throws IOException { - if (inputStream != null && clazz != null) { - JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8")); - reader.beginArray(); - while (reader.hasNext()) { - E obj = createObject(clazz); - obj.populateUsingJsonStream(reader); - } - reader.endArray(); - reader.close(); + if (clazz == null || inputStream == null) return; + + JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8")); + reader.beginArray(); + while (reader.hasNext()) { + E obj = createObject(clazz); + obj.populateUsingJsonStream(reader); } + reader.endArray(); + reader.close(); } /** - * Add a JsonObject to the Realm as a new object. This must be done inside a transaction. + * Create a Realm object prefilled with data from a JSON object. Unknown JSON properties are + * ignored. This must be done inside a transaction. * - * @param clazz Class of object the json will map to. - * @param json JsonObject that can map to the chosen clazz. Properties not in the class are ignored. - * @return Object with data or null if no json data was provided. + * @param clazz Type of Realm object to create. + * @param json JSONObject with object data. + * @return Created object or null if no json data was provided. * - * @throws RealmException if the mapping fail. + * @throws RealmException if the mapping from JSON fails. */ - public E createFromJson(Class clazz, JSONObject json) { - if (json == null) return null; + public E createObjectFromJson(Class clazz, JSONObject json) { + if (clazz == null || json == null) return null; E obj = createObject(clazz); try { obj.populateUsingJsonObject(json); } catch (Exception e) { - // TODO Remove object from realm throw new RealmException("Could not map Json", e); } @@ -427,26 +453,50 @@ public E createFromJson(Class clazz, JSONObject json) } /** - * Add a JsonObject from a InputStream to the Realm as a new object. This must be done inside a transaction. + * Create a Realm object prefilled with data from a JSON object. Unknown JSON properties are + * ignored. This must be done inside a transaction. * - * @param clazz Class of object the json will map to. - * @param inputStream JSONObject as a input stream of the chosen clazz. Properties not in the class are ignored. - * @return Object with data or null if no json data was provided. + * @param clazz Type of Realm object to create. + * @param json JSON string with object data. + * @return Created object or null if json string was empty or null. * - * @throws RealmException if the mapping fail. - * @throws IOException if something is wrong with the input stream. + * @throws RealmException if mapping to json failed. */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public E createFromJson(Class clazz, InputStream inputStream) throws IOException { - if (inputStream != null && clazz != null) { - JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8")); - E obj = createObject(clazz); - obj.populateUsingJsonStream(reader); - reader.close(); - return obj; + public E createObjectFromJson(Class clazz, String json) { + if (clazz == null || json == null || json.length() == 0) return null; + + JSONObject obj; + try { + obj = new JSONObject(json); + } catch (Exception e) { + throw new RealmException("Could not create Json object from string", e); } - return null; + return createObjectFromJson(clazz, obj); + } + + + + /** + * Create a Realm object prefilled with data from a JSON object. Unknown JSON properties are + * ignored. This must be done inside a transaction. + * + * @param clazz Type of Realm object to create. + * @param inputStream JSON object data as a InputStream. + * @return Created object or null if json string was empty or null. + * + * @throws RealmException if the mapping from JSON failed. + * @throws IOException if something was wrong with the input stream. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public E createObjectFromJson(Class clazz, InputStream inputStream) throws IOException { + if (inputStream == null || clazz == null) return null; + + JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8")); + E obj = createObject(clazz); + obj.populateUsingJsonStream(reader); + reader.close(); + return obj; } /** From b978254d9054de16942905c08e17d959e60d368d Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Wed, 12 Nov 2014 09:48:47 +0100 Subject: [PATCH 22/33] Updated design document. --- json_api.md | 199 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 168 insertions(+), 31 deletions(-) diff --git a/json_api.md b/json_api.md index 9317554d33..1f53f6a416 100644 --- a/json_api.md +++ b/json_api.md @@ -1,55 +1,192 @@ -## API Methods +# JSON API design document +This document is work-in-progress and describe the vision and proposed API's for working with JSON and Realm on Android. It was moved from the Wiki to here to ease discussion. -### Android +The current prototype can be found here: https://github.com/realm/realm-java/pull/489 - Realm.createObjectFromJson(Class realmObject, JSONObject json) - Realm.createAllFromJson(Class realmObject, JSONArray json) +## Vision +Realm should understand JSON so well that ~80% of all use-cases can be supported without needing 3rd party tools. For the last 20% people should either do it by hand or using GSON/Jackson. - Realm.createObjectFromJson(Class realmObject, InputStream json); - Realm.createAllFromJson(Class realmObject, InputStream json); +The primary use case is dumping data from the network into Realm, this should be as easy as possible. - Realm.createObjectFromJson(Class realmObject, String json); - Realm.createAllFromJson(Class realmObject, String json); +## Requirements -### Java +**High priority** +- Map between JSON naming and RealmObject naming. +- Select JSON nodes from a nested structure. +- Map many different date formats. +- Ignore JSON properties even though they exist in the model class. +- Handle different casing rules. +- Serialize RealmObject back to JSON (Core already has some native support for this?) -*Not implemented yet* +**Low priority** +- Select a single array node from a nested structure +- Value transformations: + - Combine two strings with custom separator + - Add two numbers together + - Subtract two numbers + - Others? -## Notes about the JSON API +## 3rd party tools +The primary JSON libraries are GSON and Jackson. The primary blocker for not supporting them is that people are not able to add standalone objects to Realm. Once we fix this, I don't see any reason for not supporting both very easily. -- Import using InputStream only works for API 11+. Methods are annotated as such. -- JSON property names need to match java variable names. +## API -- Import API does not work in standard Java. JSON is only supported from Java 7, and with a - different API. +The proposed API consists of 2 ways of configuring the JSON mapping: Through annotations and/or through a RealmJSONMapper class. Annotations should cover most use cases while RealmJSONMapper should only be needed for special requirements. -- If import fails, everything up to that point is put in the database (if Realm.commitTransaction() - is called). We need a Realm.cancelWriteTransaction() to change this behavior. +This is the same approach that GSON has. -- createObject() returns the created Object, createAll() doesn't. Returning it matches the current - API, but not being able to for Arrays is annoying. Does it make sense to return created object - anyway? As it is already filled. +I will discuss the API from an example. So code first, explanations later: -- Currently two methods are added to RealmObject: populateUsingJsonObject() and - populateUsingJsonStream(). They are overridden in the proxy objects with the proper - implementations. It would perhaps be better to move these to a separate class, but then it makes - it harder to hook it into the Realm object. +**Input JSON** +```` +{ + "status" : { + "code" : 200 + }, + data : { + "fullName" : "John Doe", + "address" "Founders House" + "birth" : "2014-11-04T10:15:16+02:00", + "animals: { + "dogs" : [ + { "name" : "Fido" }, + { "name" : "Blackie" } + ], + "cats" : [] + } + } +} +```` -### Enhancements +**Model class** +``` +@NodePath("data") +public class Person extends RealmObject { -- adding Realm.rollback() or Realm.cancelWriteTransaction() will make it possible to abort a faulty - import. + @SerializedName("fullName") + private String name; -- Java 6 does not have Json parsing capabilities. We need to add them ourselves. Perhaps using GSON. + @SerializedDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + private Date birth; -- Realm.createOrUpdateFromJson() would be a natural extension but requires primary key support. + @NodePath("animals.dogs") + private RealmList dogs; -- Consider adding mapping annotations between Json and Java, but is this a Realm responsibility? + @IgnoreNode + private String address; -- Consider which date representations we should support. There exist a multitude of options. \ No newline at end of file + // Standard getters and setters + +} +```` + +**RealmJSONMapper** + +Corresponding mapper for the above annotations. + +```` +RealmJSONMapper mapper = new RealmJSONMapper.Builder() + .nameComparisonPolicy(NameComparisonPolicy.LOWERCASE) + .startFrom("data") + .mapField("name", "fullName") + .mapDate("birth", yyyy-MM-dd'T'HH:mm:ssZ) + .useChildNode("dogs", "animals.dogs") + .ignoreField("address") + .build() +```` + +**Realm API Methods** + +```` +// It looks like a lot of methods, but is essentially the combination of 3 dimensions +// +// Support for JSONObject, JSONArray, String and InputStream as input +// Support for create\* and createOrUpdate\*. +// Optional RealmJSONMapper + +Realm.createObjectFromJson(Class realmObject, JSONObject json) +Realm.createObjectFromJson(Class realmObject, JSONObject json, RealmJSONMapper mapper) + +Realm.createAllFromJson(Class realmObject, JSONArray json) +Realm.createAllFromJson(Class realmObject, JSONArray json, RealmJSONMapper mapper) + +Realm.createObjectFromJson(Class realmObject, String json) +Realm.createObjectFromJson(Class realmObject, String json, RealmJSONMapper mapper) + +Realm.createAllFromJson(Class realmObject, String json) +Realm.createAllFromJson(Class realmObject, String json, RealmJSONMapper mapper) + + +// Uses JsonReader for stream parsing. Only for API 11+ +Realm.createObjectFromJson(Class realmObject, InputStream json); +Realm.createObjectFromJson(Class realmObject, InputStream json, RealmJSONMapper mapper); + +Realm.createAllFromJson(Class realmObject, InputStream json); +Realm.createAllFromJson(Class realmObject, InputStream json, RealmJSONMapper mapper); + +```` + + +## Naming Policy + +The default casing rule should either be STRICT or LOWERCASE ie, either json names and field names has to match or the comparison is done after lowercasing both. I would suggest LOWERCASE as that is the most forgiving. + +GSON has a lot more options in a special class called FieldNamingPolicy. We should add something something similar in the RealmJSONMapper. I have called our NameComparisonPolicy as that seemed more appropriate. + + +## Mapping names + +Mapping between JSON names and Java names should just follow our default naming policy, unless overriden by a @SerializedName annotation.This allow for fine grained control of different naming schemes. + +@SerializedName is also used by GSON. + + +## Parsing dates + +We want to support two types of dates: longs and Strings that can be parsed by a date specification. + +Dates of type long are supported implicitly (ie. we do the conversion automatically). String dates are supported through the @SerializedDateFormat annotation. + +We should allow all the same arguments as those specified here: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html + +@SerializedDateFormat is not used by GSON, but the similar name should hopefully make sense for people. + + +## Extracting child nodes + +Used to extract a different node in the JSON hierarchy. This is useful if incoming JSON is not formatted nicely. I have seen JSON like my example in the wild, and by having this, people don't have to create unnecessary model classes, like they do with GSON currently. + +The name @NodePath is a bit arbitrary and could probably use some iterations. Feedback welcome. + +Perhaps it could be combined with @SerializedName in order to reduce the number of annotations. + + +## Ignore JSON fields + +Could be a useful feature. I havn't seen a use case though. @Ignore annotation also prevents importing the JSON value. + +@IgnoreNode should feel similar to @NodePath but it is still a bit arbitrary. + + +## RealmJSONMapper + +Should use the Builder pattern to build all the mapping rules used. Behind the scenes we should probably convert annotations to this as well, but by exposing it we can hide all kind of crazy options without polluting the rest of the API. + +Names differ a bit from the annotations but I feel they flow better that way. Feedback on style/naming very welcome. + + +## Some discussion points + +- Is it JSON or Json in API names? + +- createAll vs. createObjects: createAll is similar to addAll from Lists in Java. I feel that createObject and createObjects are to similar, but input on this would be appreciated. + +- Support for child JSONMappers on sub nodes? Ie. should it be possible to set a another Mapper class on any one childNode? Probably low priority. + +- Any other requirements. iOS has had time to gather more feedback about their current solution? \ No newline at end of file From 6bb6a597e1d820bb85bd1bdef02fd40eeeaa5f77 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Wed, 12 Nov 2014 09:51:44 +0100 Subject: [PATCH 23/33] Fixed bug in settings.gradle. --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index aac2cd4ac4..e8b39658cc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,7 +11,7 @@ project(':encryptionExample').projectDir = new File('examples/encryptionExample' project(':migrationExample').projectDir = new File('examples/migrationExample') project(':adapterExample').projectDir = new File('examples/adapterExample') project(':jsonImportExample').projectDir = new File('examples/jsonImportExample') -s + // Experimental projects include ':pathExperiment' From 58f610c6de36b9ac6701a1dfb3df1351602af53b Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Wed, 17 Dec 2014 20:13:29 +0100 Subject: [PATCH 24/33] Fixed merge mistakes. --- examples/introExample/build.gradle | 2 +- examples/jsonImportExample/build.gradle | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/introExample/build.gradle b/examples/introExample/build.gradle index c89d178977..1ed7fc0eac 100644 --- a/examples/introExample/build.gradle +++ b/examples/introExample/build.gradle @@ -4,7 +4,7 @@ android { compileSdkVersion 19 buildToolsVersion "20.0.0" defaultConfig { - applicationId 'examples.realm.io.introExample' + applicationId 'io.realm.examples.intro' minSdkVersion 15 targetSdkVersion 19 versionCode 1 diff --git a/examples/jsonImportExample/build.gradle b/examples/jsonImportExample/build.gradle index 1f5f413e9f..6a64fd589f 100644 --- a/examples/jsonImportExample/build.gradle +++ b/examples/jsonImportExample/build.gradle @@ -4,7 +4,7 @@ android { compileSdkVersion 19 buildToolsVersion "20.0.0" defaultConfig { - applicationId 'io.realm.examples.jsonimport' + applicationId 'io.realm.examples.json' minSdkVersion 15 targetSdkVersion 19 versionCode 1 @@ -12,7 +12,8 @@ android { } buildTypes { release { - runProguard false + minifyEnabled false + //proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } productFlavors { From bd11aab9a72a35937705bd9969372932a031904d Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Thu, 18 Dec 2014 10:37:51 +0100 Subject: [PATCH 25/33] Added support for new getters/setters. populateFromJson* methods have been made package protected. Cleanup --- .../realm/processor/RealmJsonTypeHelper.java | 82 +++++++++---------- .../processor/RealmProxyClassGenerator.java | 35 ++++---- .../java/io/realm/RealmJsonTest.java | 8 +- .../java/io/realm/RealmObjectTest.java | 2 +- realm/src/main/java/io/realm/RealmObject.java | 8 +- 5 files changed, 65 insertions(+), 70 deletions(-) diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java index 16c8721bc3..4faf75b4a1 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmJsonTypeHelper.java @@ -32,102 +32,98 @@ public class RealmJsonTypeHelper { JAVA_TO_JSON_TYPES.put("java.lang.String", new SimpleTypeConverter("String", "String")); JAVA_TO_JSON_TYPES.put("java.util.Date", new JsonToRealmTypeConverter() { @Override - public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { + public void emitTypeConversion(String setter, String fieldName, String fieldType, JavaWriter writer) throws IOException { writer .emitStatement("long timestamp = json.optLong(\"%s\", -1)", fieldName) .beginControlFlow("if (timestamp > -1)") - .emitStatement("set%s(new Date(timestamp))", capitaliseFirstChar(fieldName)) + .emitStatement("%s(new Date(timestamp))", setter) .nextControlFlow("else") .emitStatement("String jsonDate = json.getString(\"%s\")", fieldName) - .emitStatement("set%s(JsonUtils.stringToDate(jsonDate))", capitaliseFirstChar(fieldName)) + .emitStatement("%s(JsonUtils.stringToDate(jsonDate))", setter) .endControlFlow(); } @Override - public void emitStreamTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { + public void emitStreamTypeConversion(String setter, String fieldName, String fieldType, JavaWriter writer) throws IOException { writer .beginControlFlow("if (reader.peek() == JsonToken.NUMBER)") .emitStatement("long timestamp = reader.nextLong()", fieldName) .beginControlFlow("if (timestamp > -1)") - .emitStatement("set%s(new Date(timestamp))", capitaliseFirstChar(fieldName)) + .emitStatement("%s(new Date(timestamp))", setter) .endControlFlow() .nextControlFlow("else") - .emitStatement("set%s(JsonUtils.stringToDate(reader.nextString()))", capitaliseFirstChar(fieldName)) + .emitStatement("%s(JsonUtils.stringToDate(reader.nextString()))", setter) .endControlFlow(); } }); JAVA_TO_JSON_TYPES.put("byte[]", new JsonToRealmTypeConverter() { @Override - public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { - writer.emitStatement("set%s(JsonUtils.stringToBytes(json.getString(\"%s\")))", capitaliseFirstChar(fieldName), fieldName); + public void emitTypeConversion(String setter, String fieldName, String fieldType, JavaWriter writer) throws IOException { + writer.emitStatement("%s(JsonUtils.stringToBytes(json.getString(\"%s\")))", setter, fieldName); } @Override - public void emitStreamTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { - writer.emitStatement("set%s(JsonUtils.stringToBytes(reader.nextString()))", capitaliseFirstChar(fieldName)); + public void emitStreamTypeConversion(String setter, String fieldName, String fieldType, JavaWriter writer) throws IOException { + writer.emitStatement("%s(JsonUtils.stringToBytes(reader.nextString()))", setter); } }); } - public static void emitFillJavaTypeWithJsonValue(String fieldName, String fieldType, JavaWriter writer) throws IOException { - if (JAVA_TO_JSON_TYPES.containsKey(fieldType)) { + public static void emitFillJavaTypeWithJsonValue(String setter, String fieldName, String qualifiedFieldType, JavaWriter writer) throws IOException { + if (JAVA_TO_JSON_TYPES.containsKey(qualifiedFieldType)) { writer.beginControlFlow("if (json.has(\"%s\"))", fieldName); - JAVA_TO_JSON_TYPES.get(fieldType).emitTypeConversion(fieldName, fieldType, writer); + JAVA_TO_JSON_TYPES.get(qualifiedFieldType).emitTypeConversion(setter, fieldName, qualifiedFieldType, writer); writer.endControlFlow(); } } - public static void emitFillRealmObjectWithJsonValue(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { + public static void emitFillRealmObjectWithJsonValue(String setter, String fieldName, String qualifiedFieldType, JavaWriter writer) throws IOException { writer .beginControlFlow("if (json.has(\"%s\"))", fieldName) - .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) - .emitStatement("obj.populateUsingJsonObject(json.getJSONObject(\"%s\"))", fieldName) - .emitStatement("set%s(obj)", capitaliseFirstChar(fieldName)) + .emitStatement("%s obj = realm.createObject(%s.class)", qualifiedFieldType, qualifiedFieldType) + .emitStatement("((RealmObject) obj).populateUsingJsonObject(json.getJSONObject(\"%s\"))", fieldName) + .emitStatement("%s(obj)", setter) .endControlFlow(); } - public static void emitFillRealmListWithJsonValue(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { + public static void emitFillRealmListWithJsonValue(String getter, String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { writer .beginControlFlow("if (json.has(\"%s\"))", fieldName) .emitStatement("JSONArray array = json.getJSONArray(\"%s\")", fieldName) .beginControlFlow("for (int i = 0; i < array.length(); i++)") - .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) - .emitStatement("obj.populateUsingJsonObject(array.getJSONObject(i))") - .emitStatement("get%s().add(obj)", capitaliseFirstChar(fieldName)) + .emitStatement("%s obj = realm.createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) + .emitStatement("((RealmObject) obj).populateUsingJsonObject(array.getJSONObject(i))") + .emitStatement("%s().add(obj)", getter) .endControlFlow() .endControlFlow(); } - public static void emitFillJavaTypeFromStream(String fieldName, String fieldType, JavaWriter writer) throws IOException { + public static void emitFillJavaTypeFromStream(String setter, String fieldName, String fieldType, JavaWriter writer) throws IOException { if (JAVA_TO_JSON_TYPES.containsKey(fieldType)) { - JAVA_TO_JSON_TYPES.get(fieldType).emitStreamTypeConversion(fieldName, fieldType, writer); + JAVA_TO_JSON_TYPES.get(fieldType).emitStreamTypeConversion(setter, fieldName, fieldType, writer); } } - public static void emitFillRealmObjectFromStream(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { + public static void emitFillRealmObjectFromStream(String setter, String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { writer - .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) - .emitStatement("obj.populateUsingJsonStream(reader)", fieldName) - .emitStatement("set%s(obj)", capitaliseFirstChar(fieldName)); + .emitStatement("%s obj = realm.createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) + .emitStatement("((RealmObject) obj).populateUsingJsonStream(reader)", fieldName) + .emitStatement("%s(obj)", setter); } - public static void emitFillRealmListFromStream(String fieldName, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { + public static void emitFillRealmListFromStream(String getter, String fieldTypeCanonicalName, JavaWriter writer) throws IOException { writer .emitStatement("reader.beginArray()") .beginControlFlow("while (reader.hasNext())") - .emitStatement("%s obj = getRealm().createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) - .emitStatement("obj.populateUsingJsonStream(reader)") - .emitStatement("get%s().add(obj)", capitaliseFirstChar(fieldName)) + .emitStatement("%s obj = realm.createObject(%s.class)", fieldTypeCanonicalName, fieldTypeCanonicalName) + .emitStatement("((RealmObject) obj).populateUsingJsonStream(reader)") + .emitStatement("%s().add(obj)", getter) .endControlFlow() .emitStatement("reader.endArray()"); } - private static String capitaliseFirstChar(String input) { - return input.substring(0, 1).toUpperCase() + input.substring(1); - } - private static class SimpleTypeConverter implements JsonToRealmTypeConverter { private final String castType; @@ -147,25 +143,25 @@ private SimpleTypeConverter(String castType, String jsonType) { } @Override - public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { - writer.emitStatement("set%s((%s) json.get%s(\"%s\"))", - capitaliseFirstChar(fieldName), + public void emitTypeConversion(String setter, String fieldName, String fieldType, JavaWriter writer) throws IOException { + writer.emitStatement("%s((%s) json.get%s(\"%s\"))", + setter, castType, jsonType, fieldName); } @Override - public void emitStreamTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException { - writer.emitStatement("set%s((%s) reader.next%s())", - capitaliseFirstChar(fieldName), + public void emitStreamTypeConversion(String setter, String fieldName, String fieldType, JavaWriter writer) throws IOException { + writer.emitStatement("%s((%s) reader.next%s())", + setter, castType, jsonType); } } private interface JsonToRealmTypeConverter { - public void emitTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException; - public void emitStreamTypeConversion(String fieldName, String fieldType, JavaWriter writer) throws IOException; + public void emitTypeConversion(String setter, String fieldName, String fieldType, JavaWriter writer) throws IOException; + public void emitStreamTypeConversion(String setter, String fieldName, String fieldType, JavaWriter writer) throws IOException; } } diff --git a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java index 80012c9d93..163be419cc 100644 --- a/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java +++ b/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -36,9 +37,6 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.JavaFileObject; -import java.io.BufferedWriter; -import java.io.IOException; -import java.util.*; public class RealmProxyClassGenerator { private ProcessingEnvironment processingEnvironment; @@ -512,33 +510,35 @@ private void emitEqualsMethod(JavaWriter writer) throws IOException { } private void emitPopulateUsingJsonObjectMethod(JavaWriter writer) throws IOException { - writer.emitAnnotation(Override.class); writer.beginMethod( "void", "populateUsingJsonObject", - EnumSet.of(Modifier.PROTECTED), + Collections.emptySet(), Arrays.asList("JSONObject", "json"), Arrays.asList("JSONException")); for (VariableElement field : fields) { String fieldName = field.getSimpleName().toString(); - String fieldTypeCanonicalName = field.asType().toString(); + String qualifiedFieldType = field.asType().toString(); if (typeUtils.isAssignable(field.asType(), realmObject)) { RealmJsonTypeHelper.emitFillRealmObjectWithJsonValue( + setters.get(fieldName), fieldName, - fieldTypeCanonicalName, + qualifiedFieldType, writer); } else if (typeUtils.isAssignable(field.asType(), realmList)) { RealmJsonTypeHelper.emitFillRealmListWithJsonValue( + getters.get(fieldName), fieldName, ((DeclaredType) field.asType()).getTypeArguments().get(0).toString(), writer); } else { RealmJsonTypeHelper.emitFillJavaTypeWithJsonValue( + setters.get(fieldName), fieldName, - fieldTypeCanonicalName, + qualifiedFieldType, writer); } @@ -549,11 +549,10 @@ private void emitPopulateUsingJsonObjectMethod(JavaWriter writer) throws IOExcep } private void emitPopulateUsingJsonStreamMethod(JavaWriter writer) throws IOException { - writer.emitAnnotation(Override.class); writer.beginMethod( "void", "populateUsingJsonStream", - EnumSet.of(Modifier.PROTECTED), + Collections.emptySet(), Arrays.asList("JsonReader", "reader"), Arrays.asList("IOException")); @@ -564,7 +563,7 @@ private void emitPopulateUsingJsonStreamMethod(JavaWriter writer) throws IOExcep for (int i = 0; i < fields.size(); i++) { VariableElement field = fields.get(i); String fieldName = field.getSimpleName().toString(); - String fieldTypeCanonicalName = field.asType().toString(); + String qualifiedFieldType = field.asType().toString(); if (i == 0) { writer.beginControlFlow("if (name.equals(\"%s\") && reader.peek() != JsonToken.NULL)", fieldName); @@ -574,20 +573,22 @@ private void emitPopulateUsingJsonStreamMethod(JavaWriter writer) throws IOExcep if (typeUtils.isAssignable(field.asType(), realmObject)) { RealmJsonTypeHelper.emitFillRealmObjectFromStream( + setters.get(fieldName), fieldName, - fieldTypeCanonicalName, + qualifiedFieldType, writer); } else if (typeUtils.isAssignable(field.asType(), realmList)) { RealmJsonTypeHelper.emitFillRealmListFromStream( - fieldName, + getters.get(fieldName), ((DeclaredType) field.asType()).getTypeArguments().get(0).toString(), writer); } else { RealmJsonTypeHelper.emitFillJavaTypeFromStream( + setters.get(fieldName), fieldName, - fieldTypeCanonicalName, + qualifiedFieldType, writer); } } @@ -599,12 +600,6 @@ private void emitPopulateUsingJsonStreamMethod(JavaWriter writer) throws IOExcep writer.emitStatement("reader.endObject()"); writer.endMethod(); writer.emitEmptyLine(); - - // End the class definition - writer.endType(); - writer.close(); - } - } public static String joinStringList(List strings, String separator) { diff --git a/realm/src/androidTest/java/io/realm/RealmJsonTest.java b/realm/src/androidTest/java/io/realm/RealmJsonTest.java index 1df1027670..4c0a61d720 100644 --- a/realm/src/androidTest/java/io/realm/RealmJsonTest.java +++ b/realm/src/androidTest/java/io/realm/RealmJsonTest.java @@ -40,6 +40,7 @@ public class RealmJsonTest extends AndroidTestCase { @Override protected void setUp() throws Exception { + Realm.deleteRealmFile(getContext()); testRealm = Realm.getInstance(getContext()); testRealm.beginTransaction(); testRealm.clear(AllTypes.class); @@ -48,6 +49,11 @@ protected void setUp() throws Exception { testRealm.commitTransaction(); } + @Override + protected void tearDown() throws Exception { + testRealm.close(); + } + private InputStream loadJsonFromAssets(String file) { AssetManager assetManager = getContext().getAssets(); InputStream input = null; @@ -182,7 +188,6 @@ public void testImportJsonString_faultyJson() { try { Dog dog = testRealm.createObjectFromJson(Dog.class, "{ name \"Foo\" }"); } catch (RealmException e) { - assertTrue(true); return; } finally { testRealm.commitTransaction(); @@ -304,7 +309,6 @@ public void testImportJsonArrayString_faultyJson() { try { testRealm.createAllFromJson(Dog.class, "[{ name : \"Foo\" ]"); } catch (RealmException e) { - assertTrue(true); return; } finally { testRealm.commitTransaction(); diff --git a/realm/src/androidTest/java/io/realm/RealmObjectTest.java b/realm/src/androidTest/java/io/realm/RealmObjectTest.java index bc8557330c..94fd87a9aa 100644 --- a/realm/src/androidTest/java/io/realm/RealmObjectTest.java +++ b/realm/src/androidTest/java/io/realm/RealmObjectTest.java @@ -63,7 +63,7 @@ public void testRealmGetRowReturnsValidRow() { testRealm.commitTransaction(); assertNotNull("RealmObject.realmGetRow returns zero ", row); - assertEquals(8, row.getColumnCount()); + assertEquals(9, row.getColumnCount()); } public void testStringEncoding() { diff --git a/realm/src/main/java/io/realm/RealmObject.java b/realm/src/main/java/io/realm/RealmObject.java index 9051bd80fa..5d6da2c518 100644 --- a/realm/src/main/java/io/realm/RealmObject.java +++ b/realm/src/main/java/io/realm/RealmObject.java @@ -61,11 +61,11 @@ public void removeFromRealm() { row.getTable().moveLastOver(row.getIndex()); } - protected void populateUsingJsonObject(JSONObject json) throws JSONException { - throw new IllegalStateException("Only use this method on objects created or fetched in a Realm. Realm.createObject() or Realm.where()"); + void populateUsingJsonObject(JSONObject json) throws JSONException { + throw new IllegalStateException("Only use this method on objects created or fetched in a Realm, Realm.createObject() or Realm.where()"); } - protected void populateUsingJsonStream(JsonReader json) throws IOException { - throw new IllegalStateException("Only use this method on objects created or fetched in a Realm. Realm.createObject() or Realm.where()"); + void populateUsingJsonStream(JsonReader json) throws IOException { + throw new IllegalStateException("Only use this method on objects created or fetched in a Realm, Realm.createObject() or Realm.where()"); } } From 29ab93bf7aeaa5cd048cbf468a228046496ecb13 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Thu, 18 Dec 2014 12:36:33 +0100 Subject: [PATCH 26/33] Cleanup Json examples. Updated changelog. --- .../app/src/main/AndroidManifest.xml | 23 +++ .../realmadapters/AdapterExampleActivity.java | 81 +++++++++ .../examples/realmadapters/MyAdapter.java | 59 ++++++ .../realmadapters/models/TimeStamp.java | 30 +++ .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 6983 bytes .../app/src/main/res/layout/activity_my.xml | 20 ++ .../app/src/main/res/menu/my.xml | 8 + .../src/main/res/values-w820dp/dimens.xml | 0 .../app}/src/main/res/values/dimens.xml | 0 .../app/src/main/res/values/strings.xml | 8 + .../app}/src/main/res/values/styles.xml | 0 distribution/RealmJsonExample/.gitignore | 6 + .../RealmJsonExample/app}/.gitignore | 0 .../RealmJsonExample/app/build.gradle | 24 +++ .../RealmJsonExample/app/proguard-rules.pro | 17 ++ .../app}/src/main/AndroidManifest.xml | 4 +- .../app}/src/main/assets/cities.json | 0 .../java/io/realm/examples/json}/City.java | 2 +- .../io/realm/examples/json}/CityAdapter.java | 2 +- .../json}/RealmJsonExampleActivity.java | 62 ++++++- .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 6983 bytes .../res/layout/activity_realm_example.xml | 0 .../src/main/res/layout/city_listitem.xml | 0 .../app/src/main/res/values-w820dp/dimens.xml | 6 + .../app/src/main/res/values/dimens.xml | 5 + .../app/src/main/res/values/strings.xml | 4 + .../app/src/main/res/values/styles.xml | 8 + distribution/RealmJsonExample/build.gradle | 16 ++ .../RealmJsonExample/gradle.properties | 18 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + distribution/RealmJsonExample/gradlew | 164 +++++++++++++++++ distribution/RealmJsonExample/gradlew.bat | 90 +++++++++ distribution/RealmJsonExample/settings.gradle | 1 + .../app/src/main/AndroidManifest.xml | 21 +++ .../examples/threads/AsyncTaskFragment.java | 129 +++++++++++++ .../threads/ThreadExampleActivity.java | 109 +++++++++++ .../examples/threads/ThreadFragment.java | 171 ++++++++++++++++++ .../io/realm/examples/threads/model/Dot.java | 59 ++++++ .../realm/examples/threads/model/Score.java | 41 +++++ .../examples/threads/widget/DotsView.java | 86 +++++++++ .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 6983 bytes .../app/src/main/res/layout/activity_main.xml | 22 +++ .../main/res/layout/fragment_asynctask.xml | 50 +++++ .../src/main/res/layout/fragment_thread.xml | 29 +++ .../main/res/menu/menu_backgroundthread.xml | 32 ++++ .../app/src/main/res/values-w820dp/dimens.xml | 6 + .../app/src/main/res/values/colors.xml | 6 + .../app/src/main/res/values/dimens.xml | 5 + .../app/src/main/res/values/strings.xml | 11 ++ .../app/src/main/res/values/styles.xml | 8 + distribution/changelog.txt | 85 +++++++++ examples/jsonExample/.gitignore | 1 + .../build.gradle | 0 .../proguard-rules.pro | 0 .../jsonExample/src/main/AndroidManifest.xml | 21 +++ .../jsonExample/src/main/assets/cities.json | 64 +++++++ .../java/io/realm/examples/json/City.java | 42 +++++ .../io/realm/examples/json/CityAdapter.java | 86 +++++++++ .../json/RealmJsonExampleActivity.java | 132 ++++++++++++++ .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 6983 bytes .../res/layout/activity_realm_example.xml | 25 +++ .../src/main/res/layout/city_listitem.xml | 21 +++ .../src/main/res/values-w820dp/dimens.xml | 6 + .../src/main/res/values/dimens.xml | 5 + .../src/main/res/values/strings.xml | 4 + .../src/main/res/values/styles.xml | 8 + .../main/res/drawable-hdpi/ic_launcher.png | Bin 9397 -> 0 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 5237 -> 0 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 14383 -> 0 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 19388 -> 0 bytes .../src/main/res/menu/options_menu.xml | 11 -- .../src/main/res/values/strings.xml | 7 - .../processor/RealmProxyClassGenerator.java | 4 - settings.gradle | 4 +- tools/build-distribution.sh | 2 + 76 files changed, 1939 insertions(+), 38 deletions(-) create mode 100644 distribution/RealmAdapterExample/app/src/main/AndroidManifest.xml create mode 100644 distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/AdapterExampleActivity.java create mode 100644 distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/MyAdapter.java create mode 100644 distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/models/TimeStamp.java create mode 100644 distribution/RealmAdapterExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100644 distribution/RealmAdapterExample/app/src/main/res/layout/activity_my.xml create mode 100644 distribution/RealmAdapterExample/app/src/main/res/menu/my.xml rename {examples/jsonImportExample => distribution/RealmAdapterExample/app}/src/main/res/values-w820dp/dimens.xml (100%) rename {examples/jsonImportExample => distribution/RealmAdapterExample/app}/src/main/res/values/dimens.xml (100%) create mode 100644 distribution/RealmAdapterExample/app/src/main/res/values/strings.xml rename {examples/jsonImportExample => distribution/RealmAdapterExample/app}/src/main/res/values/styles.xml (100%) create mode 100644 distribution/RealmJsonExample/.gitignore rename {examples/jsonImportExample => distribution/RealmJsonExample/app}/.gitignore (100%) create mode 100644 distribution/RealmJsonExample/app/build.gradle create mode 100644 distribution/RealmJsonExample/app/proguard-rules.pro rename {examples/jsonImportExample => distribution/RealmJsonExample/app}/src/main/AndroidManifest.xml (85%) rename {examples/jsonImportExample => distribution/RealmJsonExample/app}/src/main/assets/cities.json (100%) rename {examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview => distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json}/City.java (95%) rename {examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview => distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json}/CityAdapter.java (98%) rename {examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview => distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json}/RealmJsonExampleActivity.java (62%) create mode 100644 distribution/RealmJsonExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png rename {examples/jsonImportExample => distribution/RealmJsonExample/app}/src/main/res/layout/activity_realm_example.xml (100%) rename {examples/jsonImportExample => distribution/RealmJsonExample/app}/src/main/res/layout/city_listitem.xml (100%) create mode 100644 distribution/RealmJsonExample/app/src/main/res/values-w820dp/dimens.xml create mode 100644 distribution/RealmJsonExample/app/src/main/res/values/dimens.xml create mode 100644 distribution/RealmJsonExample/app/src/main/res/values/strings.xml create mode 100644 distribution/RealmJsonExample/app/src/main/res/values/styles.xml create mode 100644 distribution/RealmJsonExample/build.gradle create mode 100644 distribution/RealmJsonExample/gradle.properties create mode 100644 distribution/RealmJsonExample/gradle/wrapper/gradle-wrapper.jar create mode 100644 distribution/RealmJsonExample/gradle/wrapper/gradle-wrapper.properties create mode 100755 distribution/RealmJsonExample/gradlew create mode 100644 distribution/RealmJsonExample/gradlew.bat create mode 100644 distribution/RealmJsonExample/settings.gradle create mode 100644 distribution/RealmThreadExample/app/src/main/AndroidManifest.xml create mode 100644 distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/AsyncTaskFragment.java create mode 100644 distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/ThreadExampleActivity.java create mode 100644 distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/ThreadFragment.java create mode 100644 distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/model/Dot.java create mode 100644 distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/model/Score.java create mode 100644 distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/widget/DotsView.java create mode 100644 distribution/RealmThreadExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100644 distribution/RealmThreadExample/app/src/main/res/layout/activity_main.xml create mode 100644 distribution/RealmThreadExample/app/src/main/res/layout/fragment_asynctask.xml create mode 100644 distribution/RealmThreadExample/app/src/main/res/layout/fragment_thread.xml create mode 100644 distribution/RealmThreadExample/app/src/main/res/menu/menu_backgroundthread.xml create mode 100644 distribution/RealmThreadExample/app/src/main/res/values-w820dp/dimens.xml create mode 100644 distribution/RealmThreadExample/app/src/main/res/values/colors.xml create mode 100644 distribution/RealmThreadExample/app/src/main/res/values/dimens.xml create mode 100644 distribution/RealmThreadExample/app/src/main/res/values/strings.xml create mode 100644 distribution/RealmThreadExample/app/src/main/res/values/styles.xml create mode 100644 distribution/changelog.txt create mode 100644 examples/jsonExample/.gitignore rename examples/{jsonImportExample => jsonExample}/build.gradle (100%) rename examples/{jsonImportExample => jsonExample}/proguard-rules.pro (100%) create mode 100644 examples/jsonExample/src/main/AndroidManifest.xml create mode 100644 examples/jsonExample/src/main/assets/cities.json create mode 100644 examples/jsonExample/src/main/java/io/realm/examples/json/City.java create mode 100644 examples/jsonExample/src/main/java/io/realm/examples/json/CityAdapter.java create mode 100644 examples/jsonExample/src/main/java/io/realm/examples/json/RealmJsonExampleActivity.java create mode 100644 examples/jsonExample/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100644 examples/jsonExample/src/main/res/layout/activity_realm_example.xml create mode 100755 examples/jsonExample/src/main/res/layout/city_listitem.xml create mode 100644 examples/jsonExample/src/main/res/values-w820dp/dimens.xml create mode 100644 examples/jsonExample/src/main/res/values/dimens.xml create mode 100644 examples/jsonExample/src/main/res/values/strings.xml create mode 100644 examples/jsonExample/src/main/res/values/styles.xml delete mode 100644 examples/jsonImportExample/src/main/res/drawable-hdpi/ic_launcher.png delete mode 100644 examples/jsonImportExample/src/main/res/drawable-mdpi/ic_launcher.png delete mode 100644 examples/jsonImportExample/src/main/res/drawable-xhdpi/ic_launcher.png delete mode 100644 examples/jsonImportExample/src/main/res/drawable-xxhdpi/ic_launcher.png delete mode 100644 examples/jsonImportExample/src/main/res/menu/options_menu.xml delete mode 100644 examples/jsonImportExample/src/main/res/values/strings.xml diff --git a/distribution/RealmAdapterExample/app/src/main/AndroidManifest.xml b/distribution/RealmAdapterExample/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..06c055c5e6 --- /dev/null +++ b/distribution/RealmAdapterExample/app/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + diff --git a/distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/AdapterExampleActivity.java b/distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/AdapterExampleActivity.java new file mode 100644 index 0000000000..4d3e0f7ba3 --- /dev/null +++ b/distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/AdapterExampleActivity.java @@ -0,0 +1,81 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.realmadapters; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; + +import io.realm.Realm; +import io.realm.RealmResults; +import io.realm.examples.realmadapters.models.TimeStamp; + + +public class AdapterExampleActivity extends Activity { + + private Realm realm; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_my); + + Realm.deleteRealmFile(this); + realm = Realm.getInstance(this); + RealmResults timeStamps = realm.where(TimeStamp.class).findAll(); + final MyAdapter adapter = new MyAdapter(this, R.id.listView, timeStamps, true); + ListView listView = (ListView) findViewById(R.id.listView); + listView.setAdapter(adapter); + listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView adapterView, View view, int i, long l) { + realm.beginTransaction(); + adapter.getRealmResults().remove(i); + realm.commitTransaction(); + return true; + } + }); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + realm.close(); // Remember to close Realm when done. + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.my, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if (id == R.id.action_add) { + realm.beginTransaction(); + TimeStamp timeStamp = realm.createObject(TimeStamp.class); + timeStamp.setTimeStamp(Long.toString(System.currentTimeMillis())); + realm.commitTransaction(); + } + return true; + } +} diff --git a/distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/MyAdapter.java b/distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/MyAdapter.java new file mode 100644 index 0000000000..1dfc7e3cb2 --- /dev/null +++ b/distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/MyAdapter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.realmadapters; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListAdapter; +import android.widget.TextView; + +import io.realm.RealmBaseAdapter; +import io.realm.RealmResults; +import io.realm.examples.realmadapters.models.TimeStamp; + +public class MyAdapter extends RealmBaseAdapter implements ListAdapter { + + private static class ViewHolder { + TextView timestamp; + } + + public MyAdapter(Context context, int resId, RealmResults realmResults, boolean automaticUpdate) { + super(context, realmResults, automaticUpdate); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder viewHolder; + if (convertView == null) { + convertView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false); + viewHolder = new ViewHolder(); + viewHolder.timestamp = (TextView) convertView.findViewById(android.R.id.text1); + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + + TimeStamp item = realmResults.get(position); + viewHolder.timestamp.setText(item.getTimeStamp()); + return convertView; + } + + public RealmResults getRealmResults() { + return realmResults; + } +} diff --git a/distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/models/TimeStamp.java b/distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/models/TimeStamp.java new file mode 100644 index 0000000000..8945954fa2 --- /dev/null +++ b/distribution/RealmAdapterExample/app/src/main/java/io/realm/examples/realmadapters/models/TimeStamp.java @@ -0,0 +1,30 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.realmadapters.models; + +import io.realm.RealmObject; + +public class TimeStamp extends RealmObject { + private String timeStamp; + + public String getTimeStamp() { + return timeStamp; + } + + public void setTimeStamp(String timeStamp) { + this.timeStamp = timeStamp; + } +} diff --git a/distribution/RealmAdapterExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/distribution/RealmAdapterExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..433021180bcdf217165afa00f3054ba6f00185d7 GIT binary patch literal 6983 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 + + + + diff --git a/distribution/RealmAdapterExample/app/src/main/res/menu/my.xml b/distribution/RealmAdapterExample/app/src/main/res/menu/my.xml new file mode 100644 index 0000000000..bd4fcccd3e --- /dev/null +++ b/distribution/RealmAdapterExample/app/src/main/res/menu/my.xml @@ -0,0 +1,8 @@ + + + diff --git a/examples/jsonImportExample/src/main/res/values-w820dp/dimens.xml b/distribution/RealmAdapterExample/app/src/main/res/values-w820dp/dimens.xml similarity index 100% rename from examples/jsonImportExample/src/main/res/values-w820dp/dimens.xml rename to distribution/RealmAdapterExample/app/src/main/res/values-w820dp/dimens.xml diff --git a/examples/jsonImportExample/src/main/res/values/dimens.xml b/distribution/RealmAdapterExample/app/src/main/res/values/dimens.xml similarity index 100% rename from examples/jsonImportExample/src/main/res/values/dimens.xml rename to distribution/RealmAdapterExample/app/src/main/res/values/dimens.xml diff --git a/distribution/RealmAdapterExample/app/src/main/res/values/strings.xml b/distribution/RealmAdapterExample/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..d5d49bd286 --- /dev/null +++ b/distribution/RealmAdapterExample/app/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + + + Adapter example + Settings + Add + + \ No newline at end of file diff --git a/examples/jsonImportExample/src/main/res/values/styles.xml b/distribution/RealmAdapterExample/app/src/main/res/values/styles.xml similarity index 100% rename from examples/jsonImportExample/src/main/res/values/styles.xml rename to distribution/RealmAdapterExample/app/src/main/res/values/styles.xml diff --git a/distribution/RealmJsonExample/.gitignore b/distribution/RealmJsonExample/.gitignore new file mode 100644 index 0000000000..afbdab33e9 --- /dev/null +++ b/distribution/RealmJsonExample/.gitignore @@ -0,0 +1,6 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build diff --git a/examples/jsonImportExample/.gitignore b/distribution/RealmJsonExample/app/.gitignore similarity index 100% rename from examples/jsonImportExample/.gitignore rename to distribution/RealmJsonExample/app/.gitignore diff --git a/distribution/RealmJsonExample/app/build.gradle b/distribution/RealmJsonExample/app/build.gradle new file mode 100644 index 0000000000..c29ed99168 --- /dev/null +++ b/distribution/RealmJsonExample/app/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 20 + buildToolsVersion "20.0.0" + + defaultConfig { + applicationId "io.realm.examples.json" + minSdkVersion 14 + targetSdkVersion 20 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile 'io.realm:realm-android:0.75.1' +} diff --git a/distribution/RealmJsonExample/app/proguard-rules.pro b/distribution/RealmJsonExample/app/proguard-rules.pro new file mode 100644 index 0000000000..bb65c6fe88 --- /dev/null +++ b/distribution/RealmJsonExample/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/examples/jsonImportExample/src/main/AndroidManifest.xml b/distribution/RealmJsonExample/app/src/main/AndroidManifest.xml similarity index 85% rename from examples/jsonImportExample/src/main/AndroidManifest.xml rename to distribution/RealmJsonExample/app/src/main/AndroidManifest.xml index e5615233cf..2a1063ff0e 100644 --- a/examples/jsonImportExample/src/main/AndroidManifest.xml +++ b/distribution/RealmJsonExample/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="io.realm.examples.json"> diff --git a/examples/jsonImportExample/src/main/assets/cities.json b/distribution/RealmJsonExample/app/src/main/assets/cities.json similarity index 100% rename from examples/jsonImportExample/src/main/assets/cities.json rename to distribution/RealmJsonExample/app/src/main/assets/cities.json diff --git a/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/City.java b/distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json/City.java similarity index 95% rename from examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/City.java rename to distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json/City.java index f0aec3c61c..a583df868f 100644 --- a/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/City.java +++ b/distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json/City.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.realm.examples.realmgridview; +package io.realm.examples.json; import io.realm.RealmObject; diff --git a/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/CityAdapter.java b/distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json/CityAdapter.java similarity index 98% rename from examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/CityAdapter.java rename to distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json/CityAdapter.java index 1b5050abfd..d94c8372f9 100644 --- a/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/CityAdapter.java +++ b/distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json/CityAdapter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.realm.examples.realmgridview; +package io.realm.examples.json; import android.content.Context; import android.view.LayoutInflater; diff --git a/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/RealmJsonExampleActivity.java b/distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json/RealmJsonExampleActivity.java similarity index 62% rename from examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/RealmJsonExampleActivity.java rename to distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json/RealmJsonExampleActivity.java index 214d436dbd..79b84b72bb 100644 --- a/examples/jsonImportExample/src/main/java/io/realm/examples/realmgridview/RealmJsonExampleActivity.java +++ b/distribution/RealmJsonExample/app/src/main/java/io/realm/examples/json/RealmJsonExampleActivity.java @@ -14,15 +14,19 @@ * limitations under the License. */ -package io.realm.examples.realmgridview; +package io.realm.examples.json; import android.app.Activity; import android.os.Bundle; import android.widget.GridView; +import org.json.JSONObject; + import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; import java.util.List; +import java.util.Map; import io.realm.Realm; @@ -32,11 +36,15 @@ public class RealmJsonExampleActivity extends Activity { private GridView mGridView; private CityAdapter mAdapter; + private Realm realm; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_realm_example); + + Realm.deleteRealmFile(this); + realm = Realm.getInstance(this); } @Override @@ -59,32 +67,66 @@ public void onResume() { } } + @Override + protected void onDestroy() { + super.onDestroy(); + realm.close(); + } + public List loadCities() { - // In this case we're loading from local assets. - // NOTE: could alternatively easily load from network + loadJsonFromStream(); + loadJsonFromJsonObject(); + loadJsonFromString(); + + return realm.allObjects(City.class); + } + + private void loadJsonFromStream() { + // Use streams if you are worried about the size of the JSON whether it was persisted on disk + // or received from the network. InputStream stream = null; try { stream = getAssets().open("cities.json"); } catch (IOException e) { - return null; + e.printStackTrace(); } - Realm.deleteRealmFile(this); - // Store the retrieved items to the Realm Realm realm = Realm.getInstance(this); // Open a transaction to store items into the realm + realm.beginTransaction(); try { - realm.beginTransaction(); realm.createAllFromJson(City.class, stream); realm.commitTransaction(); - stream.close(); } catch (IOException e) { - // Ignore + realm.cancelTransaction(); + } finally { + try { + stream.close(); + } catch (IOException ignore) { + // Ignore + } } + } - return realm.allObjects(City.class); + private void loadJsonFromJsonObject() { + Map city = new HashMap(); + city.put("name", "København"); + city.put("votes", "9"); + JSONObject json = new JSONObject(city); + + realm.beginTransaction(); + realm.createObjectFromJson(City.class, json); + realm.commitTransaction(); + } + + private void loadJsonFromString() { + String json = "{ name: \"Aarhus\", votes: 99 }"; + + realm.beginTransaction(); + realm.createObjectFromJson(City.class, json); + realm.commitTransaction(); } } diff --git a/distribution/RealmJsonExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/distribution/RealmJsonExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..433021180bcdf217165afa00f3054ba6f00185d7 GIT binary patch literal 6983 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 + + 64dp + diff --git a/distribution/RealmJsonExample/app/src/main/res/values/dimens.xml b/distribution/RealmJsonExample/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..47c8224673 --- /dev/null +++ b/distribution/RealmJsonExample/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/distribution/RealmJsonExample/app/src/main/res/values/strings.xml b/distribution/RealmJsonExample/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..ba6b396ac7 --- /dev/null +++ b/distribution/RealmJsonExample/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + JSON Example + diff --git a/distribution/RealmJsonExample/app/src/main/res/values/styles.xml b/distribution/RealmJsonExample/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..ff6c9d2c0f --- /dev/null +++ b/distribution/RealmJsonExample/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/distribution/RealmJsonExample/build.gradle b/distribution/RealmJsonExample/build.gradle new file mode 100644 index 0000000000..2027f3b578 --- /dev/null +++ b/distribution/RealmJsonExample/build.gradle @@ -0,0 +1,16 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.0.0' + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/distribution/RealmJsonExample/gradle.properties b/distribution/RealmJsonExample/gradle.properties new file mode 100644 index 0000000000..5d08ba75bb --- /dev/null +++ b/distribution/RealmJsonExample/gradle.properties @@ -0,0 +1,18 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Settings specified in this file will override any Gradle settings +# configured through the IDE. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true \ No newline at end of file diff --git a/distribution/RealmJsonExample/gradle/wrapper/gradle-wrapper.jar b/distribution/RealmJsonExample/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/distribution/RealmJsonExample/gradlew.bat b/distribution/RealmJsonExample/gradlew.bat new file mode 100644 index 0000000000..aec99730b4 --- /dev/null +++ b/distribution/RealmJsonExample/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/distribution/RealmJsonExample/settings.gradle b/distribution/RealmJsonExample/settings.gradle new file mode 100644 index 0000000000..e7b4def49c --- /dev/null +++ b/distribution/RealmJsonExample/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/distribution/RealmThreadExample/app/src/main/AndroidManifest.xml b/distribution/RealmThreadExample/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..44b5bfca94 --- /dev/null +++ b/distribution/RealmThreadExample/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/AsyncTaskFragment.java b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/AsyncTaskFragment.java new file mode 100644 index 0000000000..8ef12c8692 --- /dev/null +++ b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/AsyncTaskFragment.java @@ -0,0 +1,129 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.threads; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.SeekBar; +import android.widget.TextView; + +import io.realm.Realm; +import io.realm.examples.threads.model.Score; + +/** + * This fragment demonstrates how Realm can work with AsyncTasks. + */ +public class AsyncTaskFragment extends Fragment { + + private static final String TAG = AsyncTaskFragment.class.getName(); + private static final int TEST_OBJECTS = 100; + + private LinearLayout logsView; + private SeekBar seekBar; + private ImportAsyncTask asyncTask; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_asynctask, container, false); + logsView = (LinearLayout) rootView.findViewById(R.id.logs); + seekBar = (SeekBar) rootView.findViewById(R.id.seekBar); + seekBar.setMax(TEST_OBJECTS); + rootView.findViewById(R.id.start_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (asyncTask != null) { + asyncTask.cancel(true); + } + + asyncTask = new ImportAsyncTask(); + asyncTask.execute(); + } + }); + + return rootView; + } + + private void showStatus(String txt) { + Log.i(TAG, txt); + TextView tv = new TextView(getActivity()); + tv.setText(txt); + tv.setTextColor(getResources().getColor(android.R.color.white)); + logsView.addView(tv); + } + + // ASyncTask that imports Realm data while providing progress and returns the value of an + // aggregate function in the end. + // + // Note: + // doInBackground() runs in its own background thread while all other methods are executed on the + // UI thread. This means that it is not possible to reuse RealmObjects or RealmResults created + // in doInBackground() in the other methods. Nor is it possible to use RealmObjects as Progress + // or Result objects. + private class ImportAsyncTask extends AsyncTask { + + @Override + protected Integer doInBackground(Void... params) { + Realm realm = Realm.getInstance(getActivity()); + + realm.beginTransaction(); + realm.clear(Score.class); + for (int i = 0; i < TEST_OBJECTS; i++) { + if (isCancelled()) break; + Score score = realm.createObject(Score.class); + score.setName("Name" + i); + score.setScore(i); + publishProgress(i); + + // Slow down import so seekBar doesn't move too fast + try { + Thread.sleep(50); + } catch (InterruptedException e) { + // Ignore. Task is cancelled and will be aborted. + } + } + realm.commitTransaction(); + + Number sum = realm.allObjects(Score.class).sum("score"); + realm.close(); + return sum.intValue(); + } + + @Override + protected void onPreExecute() { + logsView.removeAllViews(); + seekBar.setProgress(0); + showStatus("Starting import"); + } + + @Override + protected void onProgressUpdate(Integer... progress) { + seekBar.setProgress(progress[0]); + } + + @Override + protected void onPostExecute(Integer sum) { + showStatus(TEST_OBJECTS + " objects imported."); + showStatus("The total score is : " + sum); + } + } +} diff --git a/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/ThreadExampleActivity.java b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/ThreadExampleActivity.java new file mode 100644 index 0000000000..6c085cfcdf --- /dev/null +++ b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/ThreadExampleActivity.java @@ -0,0 +1,109 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.threads; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; + +import java.util.Locale; + + +public class ThreadExampleActivity extends ActionBarActivity implements android.support.v7.app.ActionBar.TabListener { + + private SectionsPagerAdapter pageAdapter; + private ViewPager viewPager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + // Set up the action bar. + final android.support.v7.app.ActionBar actionBar = getSupportActionBar(); + actionBar.setNavigationMode(android.support.v7.app.ActionBar.NAVIGATION_MODE_TABS); + + pageAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); + viewPager = (ViewPager) findViewById(R.id.pager); + viewPager.setAdapter(pageAdapter); + + viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + actionBar.setSelectedNavigationItem(position); + } + }); + + for (int i = 0; i < pageAdapter.getCount(); i++) { + getSupportActionBar().addTab(getSupportActionBar().newTab() + .setText(pageAdapter.getPageTitle(i)) + .setTabListener(this)); + } + } + + @Override + public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + viewPager.setCurrentItem(tab.getPosition()); + } + + @Override + public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + + } + + @Override + public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + + } + + + public class SectionsPagerAdapter extends FragmentPagerAdapter { + + public SectionsPagerAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public Fragment getItem(int position) { + switch(position) { + case 0: return new ThreadFragment(); + case 1: return new AsyncTaskFragment(); + default: return null; + } + } + + @Override + public int getCount() { + return 2; + } + + @Override + public CharSequence getPageTitle(int position) { + Locale l = Locale.getDefault(); + switch (position) { + case 0: return getString(R.string.title_section1).toUpperCase(l); + case 1: return getString(R.string.title_section2).toUpperCase(l); + default: return null; + } + } + } +} \ No newline at end of file diff --git a/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/ThreadFragment.java b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/ThreadFragment.java new file mode 100644 index 0000000000..d58bce7a1c --- /dev/null +++ b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/ThreadFragment.java @@ -0,0 +1,171 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.threads; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; + +import java.util.Random; + +import io.realm.Realm; +import io.realm.RealmChangeListener; +import io.realm.examples.threads.model.Dot; +import io.realm.examples.threads.widget.DotsView; + +/** + * This fragment demonstrates how Realm can interact with a background thread. + */ +public class ThreadFragment extends Fragment { + + private Realm realm; + private Random random = new Random(); + private Thread backgroundThread; + private DotsView dotsView; + + // Realm change listener that refreshes the UI when there is changes to Realm. + private RealmChangeListener realmListener = new RealmChangeListener() { + @Override + public void onChange() { + dotsView.invalidate(); + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + // Create Realm instance for the UI thread + realm = Realm.getInstance(getActivity()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_thread, container, false); + dotsView = (DotsView) rootView.findViewById(R.id.dots); + + // Create a RealmQuery on the UI thread and send the results to the custom view. The + // RealmResults will automatically be updated whenever the Realm data is changed. + // We still need to invalidate the UI to show the changes however. See the RealmChangeListener. + // + // Note that the query gets updated by rerunning it on the thread it was + // created. This can negatively effect frame rates if it is a complicated query or a very + // large data set. + dotsView.setRealmResults(realm.allObjects(Dot.class)); + + return rootView; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.menu_backgroundthread, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch(item.getItemId()) { + case R.id.action_add_dot: + // Add blue dot from the UI thread + realm.beginTransaction(); + Dot dot = realm.createObject(Dot.class); + dot.setX(random.nextInt(100)); + dot.setY(random.nextInt(100)); + dot.setColor(getResources().getColor(R.color.realm_blue)); + realm.commitTransaction(); + return true; + + case R.id.action_clear: + realm.beginTransaction(); + realm.clear(Dot.class); + realm.commitTransaction(); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onResume() { + super.onResume(); + + // Enable UI refresh while the fragment is active. + realm.addChangeListener(realmListener); + + // Create background thread that add a new dot every 0.5 second. + backgroundThread = new Thread() { + + @Override + public void run() { + // Realm instances cannot be shared between threads, so we need to create a new + // instance on the background thread. + Realm backgroundThreadRealm = Realm.getInstance(getActivity()); + while (!backgroundThread.isInterrupted()) { + backgroundThreadRealm.beginTransaction(); + + // Add red dot from the background thread + Dot dot = backgroundThreadRealm.createObject(Dot.class); + dot.setX(random.nextInt(100)); + dot.setY(random.nextInt(100)); + dot.setColor(getResources().getColor(R.color.realm_red)); + backgroundThreadRealm.commitTransaction(); + + // Wait 0.5 sec. before adding the next dot. + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + // Also close Realm instances used in background threads. + backgroundThreadRealm.close(); + } + }; + backgroundThread.start(); + } + + @Override + public void onPause() { + super.onPause(); + + // Disable UI refresh while the fragment is no longer active. + realm.removeChangeListener(realmListener); + backgroundThread.interrupt(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + // Remember to close the Realm instance when done with it. + realm.close(); + } +} diff --git a/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/model/Dot.java b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/model/Dot.java new file mode 100644 index 0000000000..4cd087febd --- /dev/null +++ b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/model/Dot.java @@ -0,0 +1,59 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.threads.model; + +import io.realm.RealmObject; + +public class Dot extends RealmObject{ + + private int x; + private int y; + private int color; + private long timestamp; + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public int getColor() { + return color; + } + + public void setColor(int color) { + this.color = color; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } +} diff --git a/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/model/Score.java b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/model/Score.java new file mode 100644 index 0000000000..3ea777d17a --- /dev/null +++ b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/model/Score.java @@ -0,0 +1,41 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.threads.model; + +import io.realm.RealmObject; + +public class Score extends RealmObject { + + private String name; + private int score; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getScore() { + return score; + } + + public void setScore(int score) { + this.score = score; + } +} diff --git a/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/widget/DotsView.java b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/widget/DotsView.java new file mode 100644 index 0000000000..e3fcbea651 --- /dev/null +++ b/distribution/RealmThreadExample/app/src/main/java/io/realm/examples/threads/widget/DotsView.java @@ -0,0 +1,86 @@ +/* + * Copyright 2014 Realm Inc. + * + * 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 io.realm.examples.threads.widget; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.View; + +import io.realm.RealmResults; +import io.realm.examples.threads.model.Dot; + +/** + * Custom view that plot (x,y) coordinates from a RealmQuery + */ +public class DotsView extends View { + + // RealmResults will automatically be up to date. + private RealmResults results; + + private Paint circlePaint; + private float circleRadius; + private float pixelsPrWidth; + private float pixelsPrHeight; + + + public DotsView(Context context) { + super(context); + init(); + } + + public DotsView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public DotsView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + circlePaint.setStyle(Paint.Style.FILL); + circlePaint.setColor(Color.RED); + circleRadius = 10; + } + + public void setRealmResults(RealmResults results) { + this.results = results; + invalidate(); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + pixelsPrWidth = w/100f; + pixelsPrHeight = h/100f; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.drawColor(Color.TRANSPARENT); + for (Dot dot : results) { + circlePaint.setColor(dot.getColor()); + canvas.drawCircle(dot.getX()*pixelsPrWidth, dot.getY()*pixelsPrHeight, circleRadius, circlePaint); + } + } +} diff --git a/distribution/RealmThreadExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/distribution/RealmThreadExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..433021180bcdf217165afa00f3054ba6f00185d7 GIT binary patch literal 6983 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 + + diff --git a/distribution/RealmThreadExample/app/src/main/res/layout/fragment_asynctask.xml b/distribution/RealmThreadExample/app/src/main/res/layout/fragment_asynctask.xml new file mode 100644 index 0000000000..c9ef9beb48 --- /dev/null +++ b/distribution/RealmThreadExample/app/src/main/res/layout/fragment_asynctask.xml @@ -0,0 +1,50 @@ + + + + +