diff --git a/changelog.txt b/changelog.txt index 703038214f..a946f9dc4b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ * BREAKING CHANGE: DynamicRealm.executeTransaction() now throws IllegalArgumentException instead of silently accepting a null Transaction object. * Fixed an error occurring during test and connectedCheck of unit test example (#1934). * Added RealmQuery.isNotEmpty() (#2025). (Thank you @stk1m1) + * Added Realm.clear() and RealmList.removeAllFromRealm() (#1560). * Improved .so loading by using ReLinker (https://github.com/KeepSafe/ReLinker). * Improved performance of RealmList#contains() (#897). diff --git a/realm/realm-jni/src/io_realm_internal_LinkView.cpp b/realm/realm-jni/src/io_realm_internal_LinkView.cpp index 6349e14494..cc9aa38b2e 100644 --- a/realm/realm-jni/src/io_realm_internal_LinkView.cpp +++ b/realm/realm-jni/src/io_realm_internal_LinkView.cpp @@ -190,4 +190,14 @@ JNIEXPORT jlong JNICALL Java_io_realm_internal_LinkView_nativeFind return to_jlong_or_not_found(ndx); } CATCH_STD() return -1; -} \ No newline at end of file +} + +JNIEXPORT void JNICALL Java_io_realm_internal_LinkView_nativeRemoveAllTargetRows + (JNIEnv *env, jobject, jlong nativeLinkViewPtr) { + TR_ENTER_PTR(nativeLinkViewPtr) + try { + LinkView *lv = LV(nativeLinkViewPtr); + lv->remove_all_target_rows(); + } CATCH_STD() + } + diff --git a/realm/realm-jni/src/io_realm_internal_LinkView.h b/realm/realm-jni/src/io_realm_internal_LinkView.h index e65c9781a2..9be9eae086 100644 --- a/realm/realm-jni/src/io_realm_internal_LinkView.h +++ b/realm/realm-jni/src/io_realm_internal_LinkView.h @@ -109,7 +109,7 @@ JNIEXPORT jlong JNICALL Java_io_realm_internal_LinkView_nativeWhere * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_io_realm_internal_LinkView_nativeIsAttached - (JNIEnv *, jobject, jlong); + (JNIEnv *, jobject, jlong); /* * Class: io_realm_internal_LinkView @@ -117,7 +117,15 @@ JNIEXPORT jboolean JNICALL Java_io_realm_internal_LinkView_nativeIsAttached * Signature: (JJ)J */ JNIEXPORT jlong JNICALL Java_io_realm_internal_LinkView_nativeFind - (JNIEnv *, jobject, jlong, jlong); + (JNIEnv *, jobject, jlong, jlong); + +/* + * Class: io_realm_internal_LinkView + * Method: nativeRemoveAllTargetRows + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_io_realm_internal_LinkView_nativeRemoveAllTargetRows + (JNIEnv *, jobject, jlong); #ifdef __cplusplus } diff --git a/realm/realm-library/src/androidTest/java/io/realm/DynamicRealmTests.java b/realm/realm-library/src/androidTest/java/io/realm/DynamicRealmTests.java index ad3e490ad2..282d27a114 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/DynamicRealmTests.java +++ b/realm/realm-library/src/androidTest/java/io/realm/DynamicRealmTests.java @@ -32,6 +32,8 @@ import io.realm.entities.AllTypes; import io.realm.entities.AnnotationIndexTypes; +import io.realm.entities.Cat; +import io.realm.entities.Dog; import io.realm.entities.DogPrimaryKey; import io.realm.entities.Owner; import io.realm.internal.log.RealmLog; @@ -87,6 +89,8 @@ private void populateTestRealm(DynamicRealm realm, int objects) { allTypes.setFloat(AllTypes.FIELD_FLOAT, 1.234567F + i); allTypes.setString(AllTypes.FIELD_STRING, "test data " + i); allTypes.setLong(AllTypes.FIELD_LONG, i); + allTypes.getList(AllTypes.FIELD_REALMLIST).add(realm.createObject(Dog.CLASS_NAME)); + allTypes.getList(AllTypes.FIELD_REALMLIST).add(realm.createObject(Dog.CLASS_NAME)); } realm.commitTransaction(); } @@ -748,4 +752,45 @@ public void run() { .between(AllTypes.FIELD_LONG, 4, 9) .findFirstAsync(); } + + @Test + public void clear_all() { + realm.beginTransaction(); + realm.createObject(AllTypes.CLASS_NAME); + DynamicRealmObject cat = realm.createObject(Cat.CLASS_NAME); + DynamicRealmObject owner = realm.createObject(Owner.CLASS_NAME); + owner.setObject("cat", cat); + realm.getSchema().create("TestRemoveAll").addField("Field1", String.class); + realm.createObject("TestRemoveAll"); + realm.commitTransaction(); + + assertEquals(1, realm.where(AllTypes.CLASS_NAME).count()); + assertEquals(1, realm.where(Owner.CLASS_NAME).count()); + assertEquals(1, realm.where(Cat.CLASS_NAME).count()); + assertEquals(1, realm.where("TestRemoveAll").count()); + + realm.beginTransaction(); + realm.clear(); + realm.commitTransaction(); + + assertEquals(0, realm.where(AllTypes.CLASS_NAME).count()); + assertEquals(0, realm.where(Owner.CLASS_NAME).count()); + assertEquals(0, realm.where(Cat.CLASS_NAME).count()); + assertEquals(0, realm.where("TestRemoveAll").count()); + assertTrue(realm.isEmpty()); + } + + @Test + public void realmListRemoveAllFromRealm() { + populateTestRealm(realm, 1); + RealmList list = realm.where(AllTypes.CLASS_NAME).findFirst().getList(AllTypes.FIELD_REALMLIST); + assertEquals(2, list.size()); + + realm.beginTransaction(); + list.removeAllFromRealm(); + realm.commitTransaction(); + + assertEquals(0, list.size()); + assertEquals(0, realm.where(Dog.CLASS_NAME).count()); + } } diff --git a/realm/realm-library/src/androidTest/java/io/realm/RealmListTest.java b/realm/realm-library/src/androidTest/java/io/realm/RealmListTest.java index a789f3111d..73234c7660 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/RealmListTest.java +++ b/realm/realm-library/src/androidTest/java/io/realm/RealmListTest.java @@ -72,8 +72,10 @@ public void setUp() throws Exception { @After public void tearDown() throws Exception { - testRealm.close(); - Realm.deleteRealm(testRealm.getConfiguration()); + if (testRealm != null) { + testRealm.close(); + Realm.deleteRealm(testRealm.getConfiguration()); + } } private RealmList createNonManagedDogList() { @@ -979,4 +981,110 @@ public void testSettingListClearsOldItems() { assertEquals(1, two.getObjects().size()); } + + @Test + public void testRemoveAllFromRealm() { + Owner owner = testRealm.where(Owner.class).findFirst(); + RealmList dogs = owner.getDogs(); + assertEquals(TEST_OBJECTS, dogs.size()); + + testRealm.beginTransaction(); + dogs.removeAllFromRealm(); + testRealm.commitTransaction(); + assertEquals(0, dogs.size()); + assertEquals(0, testRealm.where(Dog.class).count()); + } + + @Test + public void testRealmRemoveAllNotManagedList() { + Owner owner = testRealm.where(Owner.class).findFirst(); + RealmList dogs = owner.getDogs(); + assertEquals(TEST_OBJECTS, dogs.size()); + + RealmList notManagedDogs = new RealmList(); + for (Dog dog : dogs) { + notManagedDogs.add(dog); + } + + testRealm.beginTransaction(); + notManagedDogs.removeAllFromRealm(); + testRealm.commitTransaction(); + assertEquals(0, dogs.size()); + assertEquals(0, notManagedDogs.size()); + assertEquals(0, testRealm.where(Dog.class).count()); + } + + @Test + public void testRealmRemoveAllOutsideTransaction() { + Owner owner = testRealm.where(Owner.class).findFirst(); + RealmList dogs = owner.getDogs(); + try { + dogs.removeAllFromRealm(); + fail("removeAllFromRealm should be called in a transaction."); + } catch (IllegalStateException e) { + assertEquals("Changing Realm data can only be done from inside a transaction.", e.getMessage()); + } + } + + @Test + public void testRemoveAllFromListStandaloneObjectShouldThrow() { + final RealmList list = new RealmList(); + + testRealm.beginTransaction(); + Dog dog1 = testRealm.where(Dog.class).findFirst(); + testRealm.commitTransaction(); + Dog dog2 = new Dog(); + + list.add(dog1); + list.add(dog2); + + testRealm.beginTransaction(); + try { + list.removeAllFromRealm(); + fail("Cannot remove a list with a standalone object in it!"); + } catch (IllegalStateException e) { + assertEquals("Object malformed: missing object in Realm. Make sure to instantiate RealmObjects with" + + " Realm.createObject()", e.getMessage()); + } finally { + testRealm.cancelTransaction(); + } + + assertEquals(TEST_OBJECTS, testRealm.where(Dog.class).count()); + assertEquals(2, list.size()); + } + + @Test + public void testRemoveAllFromRealmEmptyList() { + RealmList dogs = testRealm.where(Owner.class).findFirst().getDogs(); + assertEquals(TEST_OBJECTS, dogs.size()); + + testRealm.beginTransaction(); + dogs.removeAllFromRealm(); + testRealm.commitTransaction(); + assertEquals(0, dogs.size()); + assertEquals(0, testRealm.where(Dog.class).count()); + + // The dogs is empty now. + testRealm.beginTransaction(); + dogs.removeAllFromRealm(); + testRealm.commitTransaction(); + assertEquals(0, dogs.size()); + assertEquals(0, testRealm.where(Dog.class).count()); + + } + + @Test + public void testRemoveAllFromRealmInvalidListShouldThrow() { + RealmList dogs = testRealm.where(Owner.class).findFirst().getDogs(); + assertEquals(TEST_OBJECTS, dogs.size()); + testRealm.close(); + testRealm = null; + + try { + dogs.removeAllFromRealm(); + fail("dogs is invalid and it should throw an exception"); + } catch (IllegalStateException e) { + assertEquals("This Realm instance has already been closed, making it unusable.", e.getMessage()); + } + } } diff --git a/realm/realm-library/src/androidTest/java/io/realm/RealmTest.java b/realm/realm-library/src/androidTest/java/io/realm/RealmTest.java index f2c2e87c5d..c814e3bd6f 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/RealmTest.java +++ b/realm/realm-library/src/androidTest/java/io/realm/RealmTest.java @@ -2906,4 +2906,54 @@ public void run() { thread.interrupt(); } } + + @Test + public void testClearAll() { + testRealm.beginTransaction(); + testRealm.createObject(AllTypes.class); + testRealm.createObject(Owner.class).setCat(testRealm.createObject(Cat.class)); + testRealm.commitTransaction(); + + assertEquals(1, testRealm.where(AllTypes.class).count()); + assertEquals(1, testRealm.where(Owner.class).count()); + assertEquals(1, testRealm.where(Cat.class).count()); + + testRealm.beginTransaction(); + testRealm.clear(); + testRealm.commitTransaction(); + + assertEquals(0, testRealm.where(AllTypes.class).count()); + assertEquals(0, testRealm.where(Owner.class).count()); + assertEquals(0, testRealm.where(Cat.class).count()); + assertTrue(testRealm.isEmpty()); + } + + @Test + public void testClearAllInWrongThreadShouldThrow() { + final CountDownLatch signalTestFinished = new CountDownLatch(1); + + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + try { + testRealm.clear(); + fail("testRealm cannot be used in another thread!"); + } catch (IllegalStateException ignored) { + signalTestFinished.countDown(); + } + } + }); + thread.start(); + + TestHelper.awaitOrFail(signalTestFinished); + } + + @Test + public void testClearAllCalledOutsideTransaction() { + try { + testRealm.clear(); + fail("clear can only be called in transaction block!"); + } catch (IllegalStateException ignored) { + } + } } diff --git a/realm/realm-library/src/androidTest/java/io/realm/entities/Cat.java b/realm/realm-library/src/androidTest/java/io/realm/entities/Cat.java index bb7a911601..03e961f055 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/entities/Cat.java +++ b/realm/realm-library/src/androidTest/java/io/realm/entities/Cat.java @@ -21,6 +21,9 @@ import io.realm.RealmObject; public class Cat extends RealmObject { + + public static final String CLASS_NAME = "Cat"; + private String name; private long age; private float height; diff --git a/realm/realm-library/src/androidTest/java/io/realm/entities/Dog.java b/realm/realm-library/src/androidTest/java/io/realm/entities/Dog.java index 91d754b658..f27ba08519 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/entities/Dog.java +++ b/realm/realm-library/src/androidTest/java/io/realm/entities/Dog.java @@ -24,6 +24,8 @@ public class Dog extends RealmObject { + public static final String CLASS_NAME = "Dog"; + @Index private String name; private long age; diff --git a/realm/realm-library/src/main/java/io/realm/BaseRealm.java b/realm/realm-library/src/main/java/io/realm/BaseRealm.java index 45d3c7ceba..2ce9b3ec3a 100644 --- a/realm/realm-library/src/main/java/io/realm/BaseRealm.java +++ b/realm/realm-library/src/main/java/io/realm/BaseRealm.java @@ -21,7 +21,6 @@ import java.io.Closeable; import java.io.File; -import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -539,6 +538,18 @@ E get(Class clazz, String dynamicClassName, long rowI return result; } + /** + * Removes all objects from this Realm. + * + * @throws IllegalStateException if the corresponding Realm is closed or on an incorrect thread. + */ + public void clear() { + checkIfValid(); + for (RealmObjectSchema objectSchema : schema.getAll()) { + schema.getTable(objectSchema.getClassName()).clear(); + } + } + /** * Deletes the Realm file defined by the given configuration. */ diff --git a/realm/realm-library/src/main/java/io/realm/RealmList.java b/realm/realm-library/src/main/java/io/realm/RealmList.java index 50507c1f29..014db93928 100644 --- a/realm/realm-library/src/main/java/io/realm/RealmList.java +++ b/realm/realm-library/src/main/java/io/realm/RealmList.java @@ -263,11 +263,12 @@ public void move(int oldPos, int newPos) { } /** - * Removes all elements from this list, leaving it empty. + * Removes all elements from this list, leaving it empty. This method doesn't remove the objects from the Realm. * * @throws IllegalStateException if Realm instance has been closed or parent object has been removed. * @see List#isEmpty * @see List#size + * @see #removeAllFromRealm() */ @Override public void clear() { @@ -299,6 +300,26 @@ public E remove(int location) { } } + /** + * Removes all elements from this list and delete them from the corresponding Realm. This method can be called on a + * non-managed {@link RealmList} if all of the RealmObjects in the list are managed by Realm. + * + * @throws IllegalStateException if the Realm instance has been closed, the parent object has been removed, the + * method is called in a wrong thread or any RealmObject in the list is not managed by Realm. + * @see #clear() + */ + public void removeAllFromRealm() { + if (managedMode) { + checkValidView(); + view.removeAllTargetRows(); + } else { + for (RealmObject object : nonManagedList) { + object.removeFromRealm(); + } + nonManagedList.clear(); + } + } + /** * Returns the element at the specified location in this list. * diff --git a/realm/realm-library/src/main/java/io/realm/internal/LinkView.java b/realm/realm-library/src/main/java/io/realm/internal/LinkView.java index fb7322bc5e..a0f2eefd30 100644 --- a/realm/realm-library/src/main/java/io/realm/internal/LinkView.java +++ b/realm/realm-library/src/main/java/io/realm/internal/LinkView.java @@ -145,6 +145,14 @@ public Table getTable() { return parent; } + /** + * Remove all target rows pointed to by links in this link view, and clear this link view. + */ + public void removeAllTargetRows() { + checkImmutable(); + nativeRemoveAllTargetRows(nativePointer); + } + private void checkImmutable() { if (parent.isImmutable()) { throw new IllegalStateException("Changing Realm data can only be done from inside a transaction."); @@ -165,4 +173,5 @@ private void checkImmutable() { protected native long nativeWhere(long nativeLinkViewPtr); private native boolean nativeIsAttached(long nativeLinkViewPtr); private native long nativeFind(long nativeLinkViewPtr, long targetRowIndex); + private native void nativeRemoveAllTargetRows(long nativeLinkViewPtr); }