Skip to content

Commit

Permalink
Added Realm.copyFromRealm()
Browse files Browse the repository at this point in the history
  • Loading branch information
cmelchior committed Dec 3, 2015
1 parent 71b7b40 commit f028bf9
Show file tree
Hide file tree
Showing 18 changed files with 598 additions and 21 deletions.
3 changes: 3 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
0.87.0
* Added Realm.copyFromRealm() for creating detached copies of Realm objects.

0.86.0
* BREAKING CHANGE: The Migration API has been replaced with a new API.
* BREAKING CHANGE: RealmResults.SORT_ORDER_ASCENDING and RealmResults.SORT_ORDER_DESCENDING constants have been replaced by Sort.ASCENDING and Sort.DESCENDING enums.
Expand Down
2 changes: 1 addition & 1 deletion examples/kotlinExample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}"
compile "org.jetbrains.kotlin:kotlin-reflect:${kotlin_version}"
compile 'org.jetbrains.anko:anko-sdk15:0.7.2'
compile "io.realm:realm-android:${version}@aar"
compile "io.realm:realm-android-library:${version}"
compile "io.realm:realm-annotations:${version}"
kapt "io.realm:realm-annotations-processor:${version}"
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public void generate() throws IOException, UnsupportedOperationException {
emitCreateUsingJsonStream(writer);
emitCopyOrUpdateMethod(writer);
emitCopyMethod(writer);
emitCreateDetachedCopyMethod(writer);
emitUpdateMethod(writer);
emitToStringMethod(writer);
emitHashcodeMethod(writer);
Expand Down Expand Up @@ -683,6 +684,70 @@ private void emitCopyMethod(JavaWriter writer) throws IOException {
writer.emitEmptyLine();
}

private void emitCreateDetachedCopyMethod(JavaWriter writer) throws IOException {
writer.beginMethod(
className, // Return type
"createDetachedCopy", // Method name
EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), // Modifiers
className, "realmObject", "int", "currentDepth", "int", "maxDepth", "Map<RealmObject, CacheData<RealmObject>>", "cache");
writer
.beginControlFlow("if (currentDepth > maxDepth || realmObject == null)")
.emitStatement("return null")
.endControlFlow()
.emitStatement("CacheData<%s> cachedObject = (CacheData) cache.get(realmObject)", className)
.emitStatement("%s standaloneObject", className)
.beginControlFlow("if (cachedObject != null)")
.emitSingleLineComment("Reuse cached object or recreate it because it was encountered at a lower depth.")
.beginControlFlow("if (currentDepth >= cachedObject.minDepth)")
.emitStatement("return cachedObject.object")
.nextControlFlow("else")
.emitStatement("standaloneObject = cachedObject.object")
.emitStatement("cachedObject.minDepth = currentDepth")
.endControlFlow()
.nextControlFlow("else")
.emitStatement("standaloneObject = new %s()", className)
.emitStatement("cache.put(realmObject, new RealmObjectProxy.CacheData<RealmObject>(currentDepth, standaloneObject))")
.endControlFlow();

for (VariableElement field : metadata.getFields()) {
String fieldName = field.getSimpleName().toString();
String setter = metadata.getSetter(fieldName);
String getter = metadata.getGetter(fieldName);

if (Utils.isRealmObject(field)) {
writer
.emitEmptyLine()
.emitSingleLineComment("Deep copy of %s", fieldName)
.emitStatement("standaloneObject.%s(%s.createDetachedCopy(realmObject.%s(), currentDepth + 1, maxDepth, cache))",
setter, Utils.getProxyClassSimpleName(field), getter);
} else if (Utils.isRealmList(field)) {
writer
.emitEmptyLine()
.emitSingleLineComment("Deep copy of %s", fieldName)
.beginControlFlow("if (currentDepth == maxDepth)")
.emitStatement("standaloneObject.%s(null)", setter)
.nextControlFlow("else")
.emitStatement("RealmList<%s> managed%sList = realmObject.%s()", Utils.getGenericType(field), fieldName, getter)
.emitStatement("RealmList<%1$s> standalone%2$sList = new RealmList<%1$s>()", Utils.getGenericType(field), fieldName)
.emitStatement("standaloneObject.%s(standalone%sList)", setter, fieldName)
.emitStatement("int nextDepth = currentDepth + 1")
.emitStatement("int size = managed%sList.size()", fieldName)
.beginControlFlow("for (int i = 0; i < size; i++)")
.emitStatement("%s item = %s.createDetachedCopy(managed%sList.get(i), nextDepth, maxDepth, cache)",
Utils.getGenericType(field), Utils.getProxyClassSimpleName(field), fieldName)
.emitStatement("standalone%sList.add(item)", fieldName)
.endControlFlow()
.endControlFlow();
} else {
writer.emitStatement("standaloneObject.%s(realmObject.%s())", metadata.getSetter(fieldName), getter);
}
}

writer.emitStatement("return standaloneObject");
writer.endMethod();
writer.emitEmptyLine();
}

private void emitUpdateMethod(JavaWriter writer) throws IOException {
if (!metadata.hasPrimaryKey()) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public void generate() throws IOException {
"android.util.JsonReader",
"java.io.IOException",
"java.util.Collections",
"java.util.HashMap",
"java.util.HashSet",
"java.util.List",
"java.util.Map",
Expand Down Expand Up @@ -102,7 +103,7 @@ public void generate() throws IOException {
emitCopyToRealmMethod(writer);
emitCreteOrUpdateUsingJsonObject(writer);
emitCreateUsingJsonStream(writer);

emitCreateDetachedCopyMethod(writer);
writer.endType();
writer.close();
}
Expand Down Expand Up @@ -277,6 +278,29 @@ public void emitStatement(int i, JavaWriter writer) throws IOException {
writer.emitEmptyLine();
}

private void emitCreateDetachedCopyMethod(JavaWriter writer) throws IOException {
writer.emitAnnotation("Override");
writer.beginMethod(
"<E extends RealmObject> E",
"createDetachedCopy",
EnumSet.of(Modifier.PUBLIC),
"E", "realmObject", "int", "maxDepth", "Map<RealmObject, RealmObjectProxy.CacheData<RealmObject>>", "cache"
);
writer.emitSingleLineComment("This cast is correct because obj is either");
writer.emitSingleLineComment("generated by RealmProxy or the original type extending directly from RealmObject");
writer.emitStatement("@SuppressWarnings(\"unchecked\") Class<E> clazz = (Class<E>) realmObject.getClass().getSuperclass()");
writer.emitEmptyLine();
emitMediatorSwitch(new ProxySwitchStatement() {
@Override
public void emitStatement(int i, JavaWriter writer) throws IOException {
writer.emitStatement("return clazz.cast(%s.createDetachedCopy((%s) realmObject, 0, maxDepth, cache))",
proxyClasses.get(i), simpleModelClasses.get(i));
}
}, writer, false);
writer.endMethod();
writer.emitEmptyLine();
}

// Emits the control flow for selecting the appropriate proxy class based on the model class
// Currently it is just if..else, which is inefficient for large amounts amounts of model classes.
// Consider switching to HashMap or similar.
Expand Down Expand Up @@ -310,6 +334,6 @@ private String getProxyClassName(String clazz) {
}

private interface ProxySwitchStatement {
public void emitStatement(int i, JavaWriter writer) throws IOException;
void emitStatement(int i, JavaWriter writer) throws IOException;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,52 @@ public static AllTypes copy(Realm realm, AllTypes newObject, boolean update, Map
return realmObject;
}

public static AllTypes createDetachedCopy(AllTypes realmObject, int currentDepth, int maxDepth, Map<RealmObject, CacheData<RealmObject>> cache) {
if (currentDepth > maxDepth || realmObject == null) {
return null;
}
CacheData<AllTypes> cachedObject = (CacheData) cache.get(realmObject);
AllTypes standaloneObject;
if (cachedObject != null) {
// Reuse cached object or recreate it because it was encountered at a lower depth.
if (currentDepth >= cachedObject.minDepth) {
return cachedObject.object;
} else {
standaloneObject = cachedObject.object;
cachedObject.minDepth = currentDepth;
}
} else {
standaloneObject = new AllTypes();
cache.put(realmObject, new RealmObjectProxy.CacheData<RealmObject>(currentDepth, standaloneObject));
}
standaloneObject.setColumnString(realmObject.getColumnString());
standaloneObject.setColumnLong(realmObject.getColumnLong());
standaloneObject.setColumnFloat(realmObject.getColumnFloat());
standaloneObject.setColumnDouble(realmObject.getColumnDouble());
standaloneObject.setColumnBoolean(realmObject.isColumnBoolean());
standaloneObject.setColumnDate(realmObject.getColumnDate());
standaloneObject.setColumnBinary(realmObject.getColumnBinary());

// Deep copy of columnObject
standaloneObject.setColumnObject(AllTypesRealmProxy.createDetachedCopy(realmObject.getColumnObject(), currentDepth + 1, maxDepth, cache));

// Deep copy of columnRealmList
if (currentDepth == maxDepth) {
standaloneObject.setColumnRealmList(null);
} else {
RealmList<AllTypes> managedcolumnRealmListList = realmObject.getColumnRealmList();
RealmList<AllTypes> standalonecolumnRealmListList = new RealmList<AllTypes>();
standaloneObject.setColumnRealmList(standalonecolumnRealmListList);
int nextDepth = currentDepth + 1;
int size = managedcolumnRealmListList.size();
for (int i = 0; i < size; i++) {
AllTypes item = AllTypesRealmProxy.createDetachedCopy(managedcolumnRealmListList.get(i), nextDepth, maxDepth, cache);
standalonecolumnRealmListList.add(item);
}
}
return standaloneObject;
}

static AllTypes update(Realm realm, AllTypes realmObject, AllTypes newObject, Map<RealmObject, RealmObjectProxy> cache) {
realmObject.setColumnLong(newObject.getColumnLong());
realmObject.setColumnFloat(newObject.getColumnFloat());
Expand Down Expand Up @@ -772,4 +818,4 @@ public boolean equals(Object o) {
return true;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,31 @@ public static Booleans copy(Realm realm, Booleans newObject, boolean update, Map
return realmObject;
}

public static Booleans createDetachedCopy(Booleans realmObject, int currentDepth, int maxDepth, Map<RealmObject, CacheData<RealmObject>> cache) {
if (currentDepth > maxDepth || realmObject == null) {
return null;
}
CacheData<Booleans> cachedObject = (CacheData) cache.get(realmObject);
Booleans standaloneObject;
if (cachedObject != null) {
// Reuse cached object or recreate it because it was encountered at a lower depth.
if (currentDepth >= cachedObject.minDepth) {
return cachedObject.object;
} else {
standaloneObject = cachedObject.object;
cachedObject.minDepth = currentDepth;
}
} else {
standaloneObject = new Booleans();
cache.put(realmObject, new RealmObjectProxy.CacheData<RealmObject>(currentDepth, standaloneObject));
}
standaloneObject.setDone(realmObject.isDone());
standaloneObject.setReady(realmObject.isReady());
standaloneObject.setmCompleted(realmObject.ismCompleted());
standaloneObject.setAnotherBoolean(realmObject.getAnotherBoolean());
return standaloneObject;
}

@Override
public String toString() {
if (!isValid()) {
Expand Down Expand Up @@ -347,4 +372,4 @@ public boolean equals(Object o) {
return true;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,50 @@ public static NullTypes copy(Realm realm, NullTypes newObject, boolean update, M
return realmObject;
}

public static NullTypes createDetachedCopy(NullTypes realmObject, int currentDepth, int maxDepth, Map<RealmObject, CacheData<RealmObject>> cache) {
if (currentDepth > maxDepth || realmObject == null) {
return null;
}
CacheData<NullTypes> cachedObject = (CacheData) cache.get(realmObject);
NullTypes standaloneObject;
if (cachedObject != null) {
// Reuse cached object or recreate it because it was encountered at a lower depth.
if (currentDepth >= cachedObject.minDepth) {
return cachedObject.object;
} else {
standaloneObject = cachedObject.object;
cachedObject.minDepth = currentDepth;
}
} else {
standaloneObject = new NullTypes();
cache.put(realmObject, new RealmObjectProxy.CacheData<RealmObject>(currentDepth, standaloneObject));
}
standaloneObject.setFieldStringNotNull(realmObject.getFieldStringNotNull());
standaloneObject.setFieldStringNull(realmObject.getFieldStringNull());
standaloneObject.setFieldBooleanNotNull(realmObject.getFieldBooleanNotNull());
standaloneObject.setFieldBooleanNull(realmObject.getFieldBooleanNull());
standaloneObject.setFieldBytesNotNull(realmObject.getFieldBytesNotNull());
standaloneObject.setFieldBytesNull(realmObject.getFieldBytesNull());
standaloneObject.setFieldByteNotNull(realmObject.getFieldByteNotNull());
standaloneObject.setFieldByteNull(realmObject.getFieldByteNull());
standaloneObject.setFieldShortNotNull(realmObject.getFieldShortNotNull());
standaloneObject.setFieldShortNull(realmObject.getFieldShortNull());
standaloneObject.setFieldIntegerNotNull(realmObject.getFieldIntegerNotNull());
standaloneObject.setFieldIntegerNull(realmObject.getFieldIntegerNull());
standaloneObject.setFieldLongNotNull(realmObject.getFieldLongNotNull());
standaloneObject.setFieldLongNull(realmObject.getFieldLongNull());
standaloneObject.setFieldFloatNotNull(realmObject.getFieldFloatNotNull());
standaloneObject.setFieldFloatNull(realmObject.getFieldFloatNull());
standaloneObject.setFieldDoubleNotNull(realmObject.getFieldDoubleNotNull());
standaloneObject.setFieldDoubleNull(realmObject.getFieldDoubleNull());
standaloneObject.setFieldDateNotNull(realmObject.getFieldDateNotNull());
standaloneObject.setFieldDateNull(realmObject.getFieldDateNull());

// Deep copy of fieldObjectNull
standaloneObject.setFieldObjectNull(NullTypesRealmProxy.createDetachedCopy(realmObject.getFieldObjectNull(), currentDepth + 1, maxDepth, cache));
return standaloneObject;
}

@Override
public String toString() {
if (!isValid()) {
Expand Down Expand Up @@ -1292,4 +1336,4 @@ public boolean equals(Object o) {
return true;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.realm.internal.Table;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -124,4 +125,17 @@ public <E extends RealmObject> E createUsingJsonStream(Class<E> clazz, Realm rea
}
}

}
@Override
public <E extends RealmObject> E createDetachedCopy(E realmObject, int maxDepth, Map<RealmObject, RealmObjectProxy.CacheData<RealmObject>> cache) {
// This cast is correct because obj is either
// generated by RealmProxy or the original type extending directly from RealmObject
@SuppressWarnings("unchecked") Class<E> clazz = (Class<E>) realmObject.getClass().getSuperclass();

if (clazz.equals(AllTypes.class)) {
return clazz.cast(AllTypesRealmProxy.createDetachedCopy((AllTypes) realmObject, 0, maxDepth, cache));
} else {
throw getMissingProxyClassException(clazz);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,29 @@ public static Simple copy(Realm realm, Simple newObject, boolean update, Map<Rea
return realmObject;
}

public static Simple createDetachedCopy(Simple realmObject, int currentDepth, int maxDepth, Map<RealmObject, CacheData<RealmObject>> cache) {
if (currentDepth > maxDepth || realmObject == null) {
return null;
}
CacheData<Simple> cachedObject = (CacheData) cache.get(realmObject);
Simple standaloneObject;
if (cachedObject != null) {
// Reuse cached object or recreate it because it was encountered at a lower depth.
if (currentDepth >= cachedObject.minDepth) {
return cachedObject.object;
} else {
standaloneObject = cachedObject.object;
cachedObject.minDepth = currentDepth;
}
} else {
standaloneObject = new Simple();
cache.put(realmObject, new RealmObjectProxy.CacheData<RealmObject>(currentDepth, standaloneObject));
}
standaloneObject.setName(realmObject.getName());
standaloneObject.setAge(realmObject.getAge());
return standaloneObject;
}

@Override
public String toString() {
if (!isValid()) {
Expand Down
1 change: 0 additions & 1 deletion realm/realm-library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ android {
}

dependencies {
compile 'com.intellij:annotations:+@jar'
compile project(':realm-annotations')

androidTestApt project(':realm-annotations-processor')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,7 @@ public void testCyclicToString() {
testRealm.beginTransaction();
CyclicType foo = createCyclicData();
testRealm.commitTransaction();

String expected = "CyclicType = [{name:Foo},{object:CyclicType},{objects:RealmList<CyclicType>[0]}]";
String expected = "CyclicType = [{name:Foo},{object:CyclicType},{otherObject:null},{objects:RealmList<CyclicType>[0]}]";
assertEquals(expected, foo.toString());
}

Expand Down

0 comments on commit f028bf9

Please sign in to comment.