diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f74bbe824..15707564a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,9 @@
* Destructive updates of a schema of a synced Realm will now consistently throw an `UnsupportedOperationException` instead of some methods throwing `IllegalArgumentException`. The affected methods are `RealmSchema.remove(String)`, `RealmSchema.rename(String, String)`, `RealmObjectSchema.setClassName(String)`, `RealmObjectSchema.removeField(String)`, `RealmObjectSchema.renameField(String, String)`, `RealmObjectSchema.removeIndex(String)`, `RealmObjectSchema.removePrimaryKey()`, `RealmObjectSchema.addPrimaryKey(String)` and `RealmObjectSchema.addField(String, Class>, FieldAttribute)`
### Enhancements
-* None.
+* Added support for `org.bson.types.Decimal128` and `org.bson.types.ObjectId` as supported fields in model classes.
+* Added support for `org.bson.types.ObjectId` as a primary key.
+* Added support for "Embedded Objects". They are enabled using `@RealmClass(embedded = true)`. An embedded object must have exactly one parent object linking to it and it will be deleted when the the parent is. Embedded objects can also be the parent of other embedded classes. Read more [here](https://realm.io/docs/java/latest/#embedded-objects). (Issue [#6713](https://github.com/realm/realm-java/issues/6713))
### Fixed
* None.
@@ -41,12 +43,12 @@ NOTE: This version bumps the Realm file format to version 10. Files created with
* [ObjectServer] `IncompatibleSyncedFileException` is removed and no longer thrown.
### Enhancements
-* Added support for `org.bson.types.Decimal128` and `org.bson.types.ObjectId` as supported fields in model classes.
-* Add support for `org.bson.types.ObjectId` as a primary key.
* Added `Realm.freeze()`, `RealmObject.freeze()`, `RealmResults.freeze()` and `RealmList.freeze()`. These methods will return a frozen version of the current Realm data. This data can be read from any thread without throwing an `IllegalStateException`, but will never change. All frozen Realms and data can be closed by calling `Realm.close()` on the frozen Realm, but fully closing all live Realms will also close the frozen ones. Frozen data can be queried as normal, but trying to mutate it in any way will throw an `IllegalStateException`. This includes all methods that attempt to refresh or add change listeners. (Issue [#6590](https://github.com/realm/realm-java/pull/6590))
* Added `Realm.isFrozen()`, `RealmObject.isFrozen()`, `RealmObject.isFrozen(RealmModel)`, `RealmResults.isFrozen()` and `RealmList.isFrozen()`, which returns whether or not the data is frozen.
* Added `RealmConfiguration.Builder.maxNumberOfActiveVersions(long number)`. Setting this will cause Realm to throw an `IllegalStateException` if too many versions of the Realm data are live at the same time. Having too many versions can dramatically increase the filesize of the Realm.
-* `RealmResults.asJSON()` is no longer `@Beta`.
+* Storing large binary blobs in Realm files no longer forces the file to be at least 8x the size of the largest blob.
+* Reduce the size of transaction logs stored inside the Realm file, reducing file size growth from large transactions.
+* `RealmResults.asJSON()` is no longer `@Beta`
* The default `toString()` for proxy objects now print the length of binary fields. (Issue [#6767](https://github.com/realm/realm-java/pull/6767))
### Fixes
diff --git a/realm-annotations/src/main/java/io/realm/annotations/PrimaryKey.java b/realm-annotations/src/main/java/io/realm/annotations/PrimaryKey.java
index daac8c110f..b654e69451 100644
--- a/realm-annotations/src/main/java/io/realm/annotations/PrimaryKey.java
+++ b/realm-annotations/src/main/java/io/realm/annotations/PrimaryKey.java
@@ -33,6 +33,9 @@
* It is allowed to apply this annotation on the following primitive types: byte, short, int, and long.
* String, Byte, Short, Integer, and Long are also allowed, and further permitted to have {@code null}
* as a primary key value.
+ *
+ * This annotation is not allowed inside Realm classes marked as {@code \@RealmClass(embedded = true)}.
+ *
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
diff --git a/realm-annotations/src/main/java/io/realm/annotations/RealmClass.java b/realm-annotations/src/main/java/io/realm/annotations/RealmClass.java
index 6f284714e8..396c5f9f95 100644
--- a/realm-annotations/src/main/java/io/realm/annotations/RealmClass.java
+++ b/realm-annotations/src/main/java/io/realm/annotations/RealmClass.java
@@ -50,6 +50,35 @@
*/
String name() default "";
+ /**
+ * Define objects of this type as "Embedded". Embedded objects have a slightly different behavior than
+ * normal objects:
+ *
+ */
+ boolean embedded() default false;
+
/**
* The naming policy applied to all fields in this class. The default policy is {@link RealmNamingPolicy#NO_POLICY}.
*
diff --git a/realm/kotlin-extensions/src/main/kotlin/io/realm/kotlin/RealmExtensions.kt b/realm/kotlin-extensions/src/main/kotlin/io/realm/kotlin/RealmExtensions.kt
index 1307e48a38..15382339c0 100644
--- a/realm/kotlin-extensions/src/main/kotlin/io/realm/kotlin/RealmExtensions.kt
+++ b/realm/kotlin-extensions/src/main/kotlin/io/realm/kotlin/RealmExtensions.kt
@@ -74,6 +74,23 @@ inline fun Realm.createObject(primaryKeyValue: Any?): T
return this.createObject(T::class.java, primaryKeyValue)
}
+/**
+ * Instantiates and adds a new embedded object to the Realm.
+ *
+ * This method should only be used to create objects of types marked as embedded.
+ *
+ * @param T the Class of the object to create. It must be marked with {@code \@RealmClass(embedded = true)}.
+ * @param parentObject The parent object which should hold a reference to the embedded object. If the parent property is a list
+ * the embedded object will be added to the end of that list.
+ * @param parentProperty the property in the parent class which holds the reference.
+ * @return the newly created embedded object.
+ * @throws IllegalArgumentException if {@code clazz} is not an embedded class or if the property
+ * in the parent class cannot hold objects of the appropriate type.
+ */
+inline fun Realm.createEmbeddedObject(parentObject: RealmModel, parentProperty: String): T {
+ return this.createEmbeddedObject(T::class.java, parentObject, parentProperty)
+}
+
/**
TODO: Figure out if we should include this is or not. Using this makes it possible to do
@@ -99,4 +116,4 @@ Missing functions. Consider these for inclusion later:
- createOrUpdateObjectFromJson(Class clazz, org.json.JSONObject json)
- createOrUpdateObjectFromJson(Class clazz, String json)
- createOrUpdateObjectFromJson(Class clazz, String json)
-*/
\ No newline at end of file
+*/
diff --git a/realm/realm-annotations-processor/src/main/java/io/realm/processor/Backlink.kt b/realm/realm-annotations-processor/src/main/java/io/realm/processor/Backlink.kt
index 862db60de5..415c52355a 100644
--- a/realm/realm-annotations-processor/src/main/java/io/realm/processor/Backlink.kt
+++ b/realm/realm-annotations-processor/src/main/java/io/realm/processor/Backlink.kt
@@ -36,11 +36,21 @@ import io.realm.annotations.Required
* To expose backlinks for use, create a declaration as follows:
*
* ```
+ * // For Normal top-level objects
* class TargetClass {
* // ...
* @LinkingObjects("sourceField")
* final RealmResults targetField = null;
* }
+ *
+ * // If the class is an embedded object, we know there is always one parent, so
+ * // backlinks in this case can also be defined this way:
+ * class TargetClass {
+ * // ...
+ * @LinkingObjects("sourceField")
+ * final SourceClass targetField;
+ * }
+ *
*```
*
* The `targetField`, the field annotated with the @LinkingObjects annotation must be final.
@@ -57,7 +67,7 @@ import io.realm.annotations.Required
* An unmanaged Model object will have, as the value of its backlink field, the value with which
* the field is initialized (typically null).
*/
-class Backlink(clazz: ClassMetaData, private val backlinkField: VariableElement) {
+class Backlink(private val clazz: ClassMetaData, private val backlinkField: VariableElement) {
/**
* The fully-qualified name of the class containing the `targetField`, which is the field
@@ -74,7 +84,7 @@ class Backlink(clazz: ClassMetaData, private val backlinkField: VariableElement)
/**
* The fully-qualified name of the class to which the backlinks, from `targetField`, point.
*/
- val sourceClass: QualifiedClassName? = Utils.getRealmResultsType(backlinkField)
+ val sourceClass: QualifiedClassName? = if (Utils.isRealmResults(backlinkField)) Utils.getRealmResultsType(backlinkField) else Utils.getModelClassQualifiedName(backlinkField)
/**
* The name of the field, in `SourceClass` that has a normal link to `targetClass`.
@@ -83,8 +93,13 @@ class Backlink(clazz: ClassMetaData, private val backlinkField: VariableElement)
*/
val sourceField: String? = backlinkField.getAnnotation(LinkingObjects::class.java)?.value
- val targetFieldType: String
- get() = backlinkField.asType().toString()
+ /**
+ * {@code true} if the parent link should be modeled as a RealmResults instead of a single link.
+ * Single links are only supported in classes that are embedded.
+ */
+ val exposeAsRealmResults: Boolean = Utils.isRealmResults(backlinkField)
+
+ val targetFieldType: String = backlinkField.asType().toString()
/**
* Validate the source side of the backlink.
@@ -92,17 +107,7 @@ class Backlink(clazz: ClassMetaData, private val backlinkField: VariableElement)
* @return true if the backlink source looks good.
*/
fun validateSource(): Boolean {
- // A @LinkingObjects cannot be @Required
- if (backlinkField.getAnnotation(Required::class.java) != null) {
- Utils.error(String.format(
- Locale.US,
- "The @LinkingObjects field \"%s.%s\" cannot be @Required.",
- targetClass,
- targetField))
- return false
- }
-
- // The annotation must have an argument, identifying the linked field
+ // The annotation must have an argument, identifying the linked field.
if (sourceField == null || sourceField == "") {
Utils.error(String.format(
Locale.US,
@@ -122,8 +127,17 @@ class Backlink(clazz: ClassMetaData, private val backlinkField: VariableElement)
return false
}
- // The annotated element must be a RealmResult
- if (!Utils.isRealmResults(backlinkField)) {
+ if (Utils.isRealmResults(backlinkField)) {
+ return validateBacklinksAsRealmResults(backlinkField)
+ } else {
+ return validateBacklinkAsObjectReference(backlinkField)
+ }
+ }
+
+ private fun validateBacklinkAsObjectReference(field: VariableElement): Boolean {
+
+ // Using @LinkingObjects as a single parent reference is only allowed in embedded classes
+ if (!clazz.embedded && !Utils.isRealmResults(backlinkField)) {
Utils.error(String.format(
Locale.US,
"The field \"%s.%s\" is a \"%s\". Fields annotated with @LinkingObjects must be RealmResults.",
@@ -133,6 +147,44 @@ class Backlink(clazz: ClassMetaData, private val backlinkField: VariableElement)
return false
}
+ // A @LinkingObjects can only be required if for the class being embedded there is
+ // only one @LinkingField field defined. And even in that case, it requires runtime
+ // schema validation since we need to know if only one other type is pointing to it.
+ // If multiple types point to it, we cannot keep the contract of @Required.
+ if (field.getAnnotation(Required::class.java) != null && clazz.backlinkFields.isNotEmpty()) {
+ Utils.error(String.format(
+ Locale.US,
+ "@Required cannot be used on @LinkingObjects field if multiple @LinkingParents are defined: \"%s.%s\".",
+ targetClass,
+ targetField))
+ return false
+ }
+
+ // A @LinkingObjects field must be final.
+ if (!field.modifiers.contains(Modifier.FINAL)) {
+ Utils.error(String.format(
+ Locale.US,
+ "The @LinkingObjects field \"%s.%s\" must be final.",
+ targetClass,
+ targetField))
+ return false
+ }
+
+ return true
+ }
+
+ private fun validateBacklinksAsRealmResults(field: VariableElement): Boolean {
+ // A @LinkingObjects on a RealmResults cannot be @Required as doesn't have any
+ // meaning.
+ if (field.getAnnotation(Required::class.java) != null) {
+ Utils.error(String.format(
+ Locale.US,
+ "The @LinkingObjects field \"%s.%s\" cannot be @Required.",
+ targetClass,
+ targetField))
+ return false
+ }
+
if (sourceClass == null) {
Utils.error(String.format(
Locale.US,
@@ -142,7 +194,7 @@ class Backlink(clazz: ClassMetaData, private val backlinkField: VariableElement)
return false
}
- // A @LinkingObjects field must be final
+ // A @LinkingObjects field must be final.
if (!backlinkField.modifiers.contains(Modifier.FINAL)) {
Utils.error(String.format(
Locale.US,
diff --git a/realm/realm-annotations-processor/src/main/java/io/realm/processor/ClassMetaData.kt b/realm/realm-annotations-processor/src/main/java/io/realm/processor/ClassMetaData.kt
index f8590ae51b..7c16b9177a 100644
--- a/realm/realm-annotations-processor/src/main/java/io/realm/processor/ClassMetaData.kt
+++ b/realm/realm-annotations-processor/src/main/java/io/realm/processor/ClassMetaData.kt
@@ -128,12 +128,13 @@ class ClassMetaData(env: ProcessingEnvironment, typeMirrors: TypeMirrors, privat
return type != "io.realm.DynamicRealmObject" && !type.endsWith(".RealmObject") && !type.endsWith("RealmProxy")
}
+ var embedded: Boolean = false
+ private set
+
val classElement: Element
get() = classType
init {
-
-
for (element in classType.enclosedElements) {
if (element is ExecutableElement) {
val name = element.getSimpleName()
@@ -309,6 +310,8 @@ class ClassMetaData(env: ProcessingEnvironment, typeMirrors: TypeMirrors, privat
defaultFieldNameFormatter = Utils.getNameFormatter(realmClassAnnotation.fieldNamingPolicy)
}
+ embedded = realmClassAnnotation.embedded
+
// Categorize and check the rest of the file
if (!categorizeClassElements()) {
return false
@@ -548,7 +551,7 @@ class ClassMetaData(env: ProcessingEnvironment, typeMirrors: TypeMirrors, privat
}
}
} else if (isRequiredField(field)) {
- if (!checkBasicRequiredAnnotationUsage(element, field)) {
+ if (!checkBasicRequiredAnnotationUsage(field)) {
return false
}
} else {
@@ -675,25 +678,30 @@ class ClassMetaData(env: ProcessingEnvironment, typeMirrors: TypeMirrors, privat
// The field has the @Required annotation
// Returns `true` if the field could be correctly validated, `false` if an error was reported.
- private fun checkBasicRequiredAnnotationUsage(element: Element, variableElement: VariableElement): Boolean {
- if (Utils.isPrimitiveType(variableElement)) {
+ private fun checkBasicRequiredAnnotationUsage(field: VariableElement): Boolean {
+ if (Utils.isPrimitiveType(field)) {
Utils.error(String.format(Locale.US,
- "@Required or @NotNull annotation is unnecessary for primitive field \"%s\".", element))
+ "@Required or @NotNull annotation is unnecessary for primitive field \"%s\".", field))
return false
}
- if (Utils.isRealmModel(variableElement)) {
- Utils.error(String.format(Locale.US,
- "Field \"%s\" with type \"%s\" cannot be @Required or @NotNull.", element, element.asType()))
- return false
+ if (Utils.isRealmModel(field)) {
+ /**
+ * Defer checking if @Required usage is valid when checking backlinks. See [categorizeBacklinkField]
+ */
+ if (!embedded || field.getAnnotation(LinkingObjects::class.java) == null) {
+ Utils.error(String.format(Locale.US,
+ "Field \"%s\" with type \"%s\" cannot be @Required or @NotNull.", field, field.asType()))
+ return false
+ }
}
// Should never get here - user should remove @Required
- if (nullableFields.contains(variableElement)) {
+ if (nullableFields.contains(field)) {
Utils.error(String.format(Locale.US,
"Field \"%s\" with type \"%s\" appears to be nullable. Consider removing @Required.",
- element,
- element.asType()))
+ field,
+ field.asType()))
return false
}
@@ -706,6 +714,15 @@ class ClassMetaData(env: ProcessingEnvironment, typeMirrors: TypeMirrors, privat
// From Core 6 String primary keys no longer needs to be indexed, and from Core 10
// none of the primary key types do.
private fun categorizePrimaryKeyField(fieldElement: RealmFieldElement): Boolean {
+ // Embedded Objects do not support primary keys at all
+ if (embedded) {
+ Utils.error(String.format(Locale.US,
+ "A model class marked as embedded cannot contain a @PrimaryKey. One was defined for: %s",
+ fieldElement.simpleName.toString()))
+ return false
+ }
+
+ // Only one primary key pr. class is allowed
if (primaryKey != null) {
Utils.error(String.format(Locale.US,
"A class cannot have more than one @PrimaryKey. Both \"%s\" and \"%s\" are annotated as @PrimaryKey.",
@@ -714,6 +731,7 @@ class ClassMetaData(env: ProcessingEnvironment, typeMirrors: TypeMirrors, privat
return false
}
+ // Check that the primary key is defined on a supported field
val fieldType = fieldElement.asType()
if (!isValidPrimaryKeyType(fieldType)) {
Utils.error(String.format(Locale.US,
diff --git a/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProcessor.kt b/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProcessor.kt
index 47123ba632..2d68ebee44 100644
--- a/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProcessor.kt
+++ b/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProcessor.kt
@@ -30,6 +30,7 @@ import javax.lang.model.element.TypeElement
import io.realm.annotations.RealmClass
import io.realm.annotations.RealmModule
import javax.lang.model.element.Name
+import javax.lang.model.type.TypeMirror
/**
@@ -115,6 +116,7 @@ import javax.lang.model.element.Name
inline class QualifiedClassName(val name: String) {
constructor(name: Name): this(name.toString())
+ constructor(name: TypeMirror) : this(name.toString())
fun getSimpleName(): SimpleClassName {
return SimpleClassName(Utils.stripPackage(name))
}
diff --git a/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.kt b/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.kt
index 26b97deab7..be04a300e0 100644
--- a/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.kt
+++ b/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.kt
@@ -106,6 +106,7 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
emitInsertOrUpdateListMethod(writer)
emitCreateDetachedCopyMethod(writer)
emitUpdateMethod(writer)
+ emitUpdateEmbeddedObjectMethod(writer)
emitToStringMethod(writer)
emitRealmObjectProxyImplementation(writer)
emitHashcodeMethod(writer)
@@ -368,15 +369,26 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
// Getter - End
// Setter - Start
+ val fieldType = QualifiedClassName(field.asType())
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(fieldType)
+ val linkedQualifiedClassName: QualifiedClassName = Utils.getFieldTypeQualifiedName(field)
+ val linkedProxyClass: SimpleClassName = Utils.getProxyClassSimpleName(field)
emitAnnotation("Override")
beginMethod("void", metadata.getInternalSetter(fieldName), EnumSet.of(Modifier.PUBLIC), fieldTypeCanonicalName, "value")
+ emitStatement("Realm realm = (Realm) proxyState.getRealm\$realm()")
emitCodeForUnderConstruction(writer, metadata.isPrimaryKey(field)) {
// check excludeFields
beginControlFlow("if (proxyState.getExcludeFields\$realm().contains(\"%1\$s\"))", field.simpleName.toString())
emitStatement("return")
endControlFlow()
beginControlFlow("if (value != null && !RealmObject.isManaged(value))")
- emitStatement("value = ((Realm) proxyState.getRealm\$realm()).copyToRealm(value)")
+ if (fieldTypeMetaData.embedded) {
+ emitStatement("%1\$s proxyObject = realm.createEmbeddedObject(%1\$s.class, this, \"%2\$s\")", linkedQualifiedClassName, fieldName)
+ emitStatement("%s.updateEmbeddedObject(realm, value, proxyObject, new HashMap(), Collections.EMPTY_SET)", linkedProxyClass)
+ emitStatement("value = proxyObject")
+ } else {
+ emitStatement("value = realm.copyToRealm(value)")
+ }
endControlFlow()
// set value as default value
@@ -395,8 +407,17 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
emitStatement("proxyState.getRow\$realm().nullifyLink(%s)", fieldColKeyVariableReference(field))
emitStatement("return")
endControlFlow()
- emitStatement("proxyState.checkValidObject(value)")
- emitStatement("proxyState.getRow\$realm().setLink(%s, ((RealmObjectProxy) value).realmGet\$proxyState().getRow\$realm().getObjectKey())", fieldColKeyVariableReference(field))
+
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (RealmObject.isManaged(value))")
+ emitStatement("proxyState.checkValidObject(value)")
+ endControlFlow()
+ emitStatement("%1\$s proxyObject = realm.createEmbeddedObject(%1\$s.class, this, \"%2\$s\")", linkedQualifiedClassName, fieldName)
+ emitStatement("%s.updateEmbeddedObject(realm, value, proxyObject, new HashMap(), Collections.EMPTY_SET)", linkedProxyClass)
+ } else {
+ emitStatement("proxyState.checkValidObject(value)")
+ emitStatement("proxyState.getRow\$realm().setLink(%s, ((RealmObjectProxy) value).realmGet\$proxyState().getRow\$realm().getObjectKey())", fieldColKeyVariableReference(field))
+ }
endMethod()
// Setter - End
}
@@ -598,19 +619,39 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
for (backlink in metadata.backlinkFields) {
val cacheFieldName = backlink.targetField + BACKLINKS_FIELD_EXTENSION
val realmResultsType = "RealmResults<" + backlink.sourceClass + ">"
- // Getter, no setter
- writer.apply {
- emitAnnotation("Override")
- beginMethod(realmResultsType, metadata.getInternalGetter(backlink.targetField), EnumSet.of(Modifier.PUBLIC))
- emitStatement("BaseRealm realm = proxyState.getRealm\$realm()")
- emitStatement("realm.checkIfValid()")
- emitStatement("proxyState.getRow\$realm().checkIfAttached()")
- beginControlFlow("if ($cacheFieldName == null)")
+ when (backlink.exposeAsRealmResults) {
+ true -> {
+ // Getter, no setter
+ writer.apply {
+ emitAnnotation("Override")
+ beginMethod(realmResultsType, metadata.getInternalGetter(backlink.targetField), EnumSet.of(Modifier.PUBLIC))
+ emitStatement("BaseRealm realm = proxyState.getRealm\$realm()")
+ emitStatement("realm.checkIfValid()")
+ emitStatement("proxyState.getRow\$realm().checkIfAttached()")
+ beginControlFlow("if ($cacheFieldName == null)")
emitStatement("$cacheFieldName = RealmResults.createBacklinkResults(realm, proxyState.getRow\$realm(), %s.class, \"%s\")", backlink.sourceClass, backlink.sourceField)
- endControlFlow()
- emitStatement("return $cacheFieldName")
- endMethod()
- emitEmptyLine()
+ endControlFlow()
+ emitStatement("return $cacheFieldName")
+ endMethod()
+ emitEmptyLine()
+ }
+ }
+ false -> {
+ // Getter, no setter
+ writer.apply {
+ emitAnnotation("Override")
+ beginMethod(backlink.sourceClass.toString(), metadata.getInternalGetter(backlink.targetField), EnumSet.of(Modifier.PUBLIC))
+ emitStatement("BaseRealm realm = proxyState.getRealm\$realm()")
+ emitStatement("realm.checkIfValid()")
+ emitStatement("proxyState.getRow\$realm().checkIfAttached()")
+ beginControlFlow("if ($cacheFieldName == null)")
+ emitStatement("$cacheFieldName = RealmResults.createBacklinkResults(realm, proxyState.getRow\$realm(), %s.class, \"%s\").first()", backlink.sourceClass, backlink.sourceField)
+ endControlFlow()
+ emitStatement("return $cacheFieldName") // TODO: Figure out the exact API for this
+ endMethod()
+ emitEmptyLine()
+ }
+ }
}
}
}
@@ -634,8 +675,9 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
// Used to prevent array resizing at runtime
val persistedFields = metadata.fields.size
val computedFields = metadata.backlinkFields.size
+ val embeddedClass = if (metadata.embedded) "true" else "false"
- emitStatement("OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder(\"%s\", %s, %s)", internalClassName, persistedFields, computedFields)
+ emitStatement("OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder(\"%s\", %s, %s, %s)", internalClassName, embeddedClass, persistedFields, computedFields)
// For each field generate corresponding table index constant
for (field in metadata.fields) {
@@ -742,7 +784,7 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
@Throws(IOException::class)
private fun emitNewProxyInstance(writer: JavaWriter) {
writer.apply {
- beginMethod(generatedClassName, "newProxyInstance", EnumSet.of(Modifier.PRIVATE, Modifier.STATIC), "BaseRealm", "realm", "Row", "row")
+ beginMethod(generatedClassName, "newProxyInstance", EnumSet.of(Modifier.STATIC), "BaseRealm", "realm", "Row", "row")
emitSingleLineComment("Ignore default values to avoid creating unexpected objects from RealmModel/RealmList fields")
emitStatement("final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get()")
emitStatement("objectContext.set(realm, row, realm.getSchema().getColumnInfo(%s.class), false, Collections.emptyList())", qualifiedJavaClassName)
@@ -985,12 +1027,25 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
@Throws(IOException::class)
private fun emitInsertMethod(writer: JavaWriter) {
writer.apply {
- beginMethod("long","insert", EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), "Realm", "realm", qualifiedJavaClassName.toString(), "object", "Map", "cache")
-
- // If object is already in the Realm there is nothing to update
- beginControlFlow("if (object instanceof RealmObjectProxy && !RealmObject.isFrozen(object) && ((RealmObjectProxy) object).realmGet\$proxyState().getRealm\$realm() != null && ((RealmObjectProxy) object).realmGet\$proxyState().getRealm\$realm().getPath().equals(realm.getPath()))")
- emitStatement("return ((RealmObjectProxy) object).realmGet\$proxyState().getRow\$realm().getObjectKey()")
- endControlFlow()
+ val topLevelArgs = arrayOf("Realm", "realm",
+ qualifiedJavaClassName.toString(), "object",
+ "Map", "cache")
+ val embeddedArgs = arrayOf("Realm", "realm",
+ "Table", "parentObjectTable",
+ "long", "parentColumnKey",
+ "long", "parentObjectKey",
+ qualifiedJavaClassName.toString(), "object",
+ "Map", "cache")
+ val args = if (metadata.embedded) embeddedArgs else topLevelArgs
+ beginMethod("long","insert", EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), *args)
+
+ // If object is already in the Realm there is nothing to update, unless it is an embedded
+ // object. In which case we always update the underlying object.
+ if (!metadata.embedded) {
+ beginControlFlow("if (object instanceof RealmObjectProxy && !RealmObject.isFrozen(object) && ((RealmObjectProxy) object).realmGet\$proxyState().getRealm\$realm() != null && ((RealmObjectProxy) object).realmGet\$proxyState().getRealm\$realm().getPath().equals(realm.getPath()))")
+ emitStatement("return ((RealmObjectProxy) object).realmGet\$proxyState().getRow\$realm().getObjectKey()")
+ endControlFlow()
+ }
emitStatement("Table table = realm.getTable(%s.class)", qualifiedJavaClassName)
emitStatement("long tableNativePtr = table.getNativePtr()")
@@ -1003,33 +1058,55 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
for (field in metadata.fields) {
val fieldName = field.simpleName.toString()
- val fieldType = field.asType().toString()
+ val fieldType = QualifiedClassName(field.asType().toString())
val getter = metadata.getInternalGetter(fieldName)
when {
Utils.isRealmModel(field) -> {
+ // FIXME: How to support types from other compilation units?
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(fieldType)
+
emitEmptyLine()
emitStatement("%s %sObj = ((%s) object).%s()", fieldType, fieldName, interfaceName, getter)
beginControlFlow("if (%sObj != null)", fieldName)
emitStatement("Long cache%1\$s = cache.get(%1\$sObj)", fieldName)
- beginControlFlow("if (cache%s == null)", fieldName)
- emitStatement("cache%s = %s.insert(realm, %sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field), fieldName)
- endControlFlow()
- emitStatement("Table.nativeSetLink(tableNativePtr, columnInfo.%1\$sColKey, objKey, cache%1\$s, false)", fieldName)
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cache%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: \" + cache%s.toString())", fieldName)
+ nextControlFlow("else")
+ emitStatement("cache%1\$s = %2\$s.insert(realm, table, columnInfo.%3\$sColKey, objKey, %3\$sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field), fieldName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (cache%s == null)", fieldName)
+ emitStatement("cache%s = %s.insert(realm, %sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field), fieldName)
+ endControlFlow()
+ emitStatement("Table.nativeSetLink(tableNativePtr, columnInfo.%1\$sColKey, objKey, cache%1\$s, false)", fieldName)
+ }
endControlFlow()
}
Utils.isRealmModelList(field) -> {
- val genericType = Utils.getGenericTypeQualifiedName(field)
+ val genericType = Utils.getGenericTypeQualifiedName(field)!!
+ // FIXME: How to support types from other compilation units?
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(genericType)
+
emitEmptyLine()
emitStatement("RealmList<%s> %sList = ((%s) object).%s()", genericType, fieldName, interfaceName, getter)
beginControlFlow("if (%sList != null)", fieldName)
emitStatement("OsList %1\$sOsList = new OsList(table.getUncheckedRow(objKey), columnInfo.%1\$sColKey)", fieldName)
beginControlFlow("for (%1\$s %2\$sItem : %2\$sList)", genericType, fieldName)
emitStatement("Long cacheItemIndex%1\$s = cache.get(%1\$sItem)", fieldName)
- beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
- emitStatement("cacheItemIndex%1\$s = %2\$s.insert(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
- endControlFlow()
- emitStatement("%1\$sOsList.addRow(cacheItemIndex%1\$s)", fieldName)
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cacheItemIndex%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: \" + cacheItemIndex%s.toString())", fieldName)
+ nextControlFlow("else")
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insert(realm, table, columnInfo.%3\$sColKey, objKey, %3\$sItem, cache)", fieldName, Utils.getProxyClassName(genericType), fieldName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insert(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
+ endControlFlow()
+ emitStatement("%1\$sOsList.addRow(cacheItemIndex%1\$s)", fieldName)
+ }
endControlFlow()
endControlFlow()
}
@@ -1051,7 +1128,7 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
}
else -> {
if (metadata.primaryKey !== field) {
- setTableValues(writer, fieldType, fieldName, interfaceName, getter, false)
+ setTableValues(writer, fieldType.toString(), fieldName, interfaceName, getter, false)
}
}
}
@@ -1066,7 +1143,18 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
@Throws(IOException::class)
private fun emitInsertListMethod(writer: JavaWriter) {
writer.apply {
- beginMethod("void", "insert", EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), "Realm", "realm", "Iterator extends RealmModel>", "objects", "Map", "cache")
+ val topLevelArgs = arrayOf("Realm", "realm",
+ "Iterator extends RealmModel>", "objects",
+ "Map", "cache")
+ val embeddedArgs = arrayOf("Realm", "realm",
+ "Table", "parentObjectTable",
+ "long", "parentColumnKey",
+ "long", "parentObjectKey",
+ "Iterator extends RealmModel>", "objects",
+ "Map", "cache")
+ val args = if (metadata.embedded) embeddedArgs else topLevelArgs
+
+ beginMethod("void", "insert", EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), *args)
emitStatement("Table table = realm.getTable(%s.class)", qualifiedJavaClassName)
emitStatement("long tableNativePtr = table.getNativePtr()")
emitStatement("%s columnInfo = (%s) realm.getSchema().getColumnInfo(%s.class)", columnInfoClassName(), columnInfoClassName(), qualifiedJavaClassName)
@@ -1089,31 +1177,53 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
for (field in metadata.fields) {
val fieldName = field.simpleName.toString()
- val fieldType = field.asType().toString()
+ val fieldType = QualifiedClassName(field.asType().toString())
val getter = metadata.getInternalGetter(fieldName)
if (Utils.isRealmModel(field)) {
+ // FIXME: How to support types from other compilation units?
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(fieldType)
+
emitEmptyLine()
emitStatement("%s %sObj = ((%s) object).%s()", fieldType, fieldName, interfaceName, getter)
beginControlFlow("if (%sObj != null)", fieldName)
emitStatement("Long cache%1\$s = cache.get(%1\$sObj)", fieldName)
- beginControlFlow("if (cache%s == null)", fieldName)
- emitStatement("cache%s = %s.insert(realm, %sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field), fieldName)
- endControlFlow()
- emitStatement("table.setLink(columnInfo.%1\$sColKey, objKey, cache%1\$s, false)", fieldName)
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cache%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: \" + cache%s.toString())", fieldName)
+ nextControlFlow("else")
+ emitStatement("cache%1\$s = %2\$s.insert(realm, table, columnInfo.%3\$sColKey, objKey, %3\$sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field), fieldName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (cache%s == null)", fieldName)
+ emitStatement("cache%s = %s.insert(realm, %sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field), fieldName)
+ endControlFlow()
+ emitStatement("table.setLink(columnInfo.%1\$sColKey, objKey, cache%1\$s, false)", fieldName)
+ }
endControlFlow()
} else if (Utils.isRealmModelList(field)) {
- val genericType = Utils.getGenericTypeQualifiedName(field)
+ val genericType = Utils.getGenericTypeQualifiedName(field)!!
+ // FIXME: How to support types from other compilation units?
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(genericType)
+
emitEmptyLine()
emitStatement("RealmList<%s> %sList = ((%s) object).%s()", genericType, fieldName, interfaceName, getter)
beginControlFlow("if (%sList != null)", fieldName)
emitStatement("OsList %1\$sOsList = new OsList(table.getUncheckedRow(objKey), columnInfo.%1\$sColKey)", fieldName)
beginControlFlow("for (%1\$s %2\$sItem : %2\$sList)", genericType, fieldName)
emitStatement("Long cacheItemIndex%1\$s = cache.get(%1\$sItem)", fieldName)
- beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
- emitStatement("cacheItemIndex%1\$s = %2\$s.insert(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
- endControlFlow()
- emitStatement("%1\$sOsList.addRow(cacheItemIndex%1\$s)", fieldName)
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cacheItemIndex%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: \" + cacheItemIndex%s.toString())", fieldName)
+ nextControlFlow("else")
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insert(realm, table, columnInfo.%3\$sColKey, objKey, %3\$sItem, cache)", fieldName, Utils.getProxyClassName(genericType), fieldName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insert(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
+ endControlFlow()
+ emitStatement("%1\$sOsList.addRow(cacheItemIndex%1\$s)", fieldName)
+ }
endControlFlow()
endControlFlow()
} else if (Utils.isRealmValueList(field)) {
@@ -1133,7 +1243,7 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
endControlFlow()
} else {
if (metadata.primaryKey !== field) {
- setTableValues(writer, fieldType, fieldName, interfaceName, getter, false)
+ setTableValues(writer, fieldType.toString(), fieldName, interfaceName, getter, false)
}
}
}
@@ -1146,7 +1256,17 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
@Throws(IOException::class)
private fun emitInsertOrUpdateMethod(writer: JavaWriter) {
writer.apply {
- beginMethod("long", "insertOrUpdate", EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), "Realm", "realm", qualifiedJavaClassName.toString(), "object", "Map", "cache")
+ val topLevelArgs = arrayOf("Realm", "realm",
+ qualifiedJavaClassName.toString(), "object",
+ "Map", "cache")
+ val embeddedArgs = arrayOf("Realm", "realm",
+ "Table", "parentObjectTable",
+ "long", "parentColumnKey",
+ "long", "parentObjectKey",
+ qualifiedJavaClassName.toString(), "object",
+ "Map", "cache")
+ val args = if (metadata.embedded) embeddedArgs else topLevelArgs
+ beginMethod("long", "insertOrUpdate", EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), *args)
// If object is already in the Realm there is nothing to update
beginControlFlow("if (object instanceof RealmObjectProxy && !RealmObject.isFrozen(object) && ((RealmObjectProxy) object).realmGet\$proxyState().getRealm\$realm() != null && ((RealmObjectProxy) object).realmGet\$proxyState().getRealm\$realm().getPath().equals(realm.getPath()))")
@@ -1163,24 +1283,38 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
for (field in metadata.fields) {
val fieldName = field.simpleName.toString()
- val fieldType = field.asType().toString()
+ val fieldType = QualifiedClassName(field.asType().toString())
val getter = metadata.getInternalGetter(fieldName)
if (Utils.isRealmModel(field)) {
+ // FIXME: How to support types from other compilation units?
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(fieldType)
+
emitEmptyLine()
emitStatement("%s %sObj = ((%s) object).%s()", fieldType, fieldName, interfaceName, getter)
beginControlFlow("if (%sObj != null)", fieldName)
emitStatement("Long cache%1\$s = cache.get(%1\$sObj)", fieldName)
- beginControlFlow("if (cache%s == null)", fieldName)
- emitStatement("cache%1\$s = %2\$s.insertOrUpdate(realm, %1\$sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field))
- endControlFlow()
- emitStatement("Table.nativeSetLink(tableNativePtr, columnInfo.%1\$sColKey, objKey, cache%1\$s, false)", fieldName)
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cache%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: \" + cache%s.toString())", fieldName)
+ nextControlFlow("else")
+ emitStatement("cache%1\$s = %2\$s.insertOrUpdate(realm, table, columnInfo.%3\$sColKey, objKey, %3\$sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field), fieldName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (cache%s == null)", fieldName)
+ emitStatement("cache%1\$s = %2\$s.insertOrUpdate(realm, %1\$sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field))
+ endControlFlow()
+ emitStatement("Table.nativeSetLink(tableNativePtr, columnInfo.%1\$sColKey, objKey, cache%1\$s, false)", fieldName)
+ }
nextControlFlow("else")
// No need to throw exception here if the field is not nullable. A exception will be thrown in setter.
emitStatement("Table.nativeNullifyLink(tableNativePtr, columnInfo.%sColKey, objKey)", fieldName)
endControlFlow()
} else if (Utils.isRealmModelList(field)) {
- val genericType = Utils.getGenericTypeQualifiedName(field)
+ val genericType = Utils.getGenericTypeQualifiedName(field)!!
+ // FIXME: How to support types from other compilation units?
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(genericType)
+
emitEmptyLine()
emitStatement("OsList %1\$sOsList = new OsList(table.getUncheckedRow(objKey), columnInfo.%1\$sColKey)", fieldName)
emitStatement("RealmList<%s> %sList = ((%s) object).%s()", genericType, fieldName, interfaceName, getter)
@@ -1190,20 +1324,36 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
beginControlFlow("for (int i = 0; i < objects; i++)")
emitStatement("%1\$s %2\$sItem = %2\$sList.get(i)", genericType, fieldName)
emitStatement("Long cacheItemIndex%1\$s = cache.get(%1\$sItem)", fieldName)
- beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
- emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
- endControlFlow()
- emitStatement("%1\$sOsList.setRow(i, cacheItemIndex%1\$s)", fieldName)
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cacheItemIndex%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: \" + cacheItemIndex%s.toString())", fieldName)
+ nextControlFlow("else")
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, table, columnInfo.%3\$sColKey, objKey, %3\$sItem, cache)", fieldName, Utils.getProxyClassName(genericType), fieldName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
+ endControlFlow()
+ emitStatement("%1\$sOsList.setRow(i, cacheItemIndex%1\$s)", fieldName)
+ }
endControlFlow()
nextControlFlow("else")
emitStatement("%1\$sOsList.removeAll()", fieldName)
beginControlFlow("if (%sList != null)", fieldName)
beginControlFlow("for (%1\$s %2\$sItem : %2\$sList)", genericType, fieldName)
emitStatement("Long cacheItemIndex%1\$s = cache.get(%1\$sItem)", fieldName)
- beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
- emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
- endControlFlow()
- emitStatement("%1\$sOsList.addRow(cacheItemIndex%1\$s)", fieldName)
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cacheItemIndex%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: \" + cacheItemIndex%s.toString())", fieldName)
+ nextControlFlow("else")
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, table, columnInfo.%3\$sColKey, objKey, %3\$sItem, cache)", fieldName, Utils.getProxyClassName(genericType), fieldName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
+ endControlFlow()
+ emitStatement("%1\$sOsList.addRow(cacheItemIndex%1\$s)", fieldName)
+ }
endControlFlow()
endControlFlow()
endControlFlow()
@@ -1227,7 +1377,7 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
emitEmptyLine()
} else {
if (metadata.primaryKey !== field) {
- setTableValues(writer, fieldType, fieldName, interfaceName, getter, true)
+ setTableValues(writer, fieldType.toString(), fieldName, interfaceName, getter, true)
}
}
}
@@ -1241,7 +1391,18 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
@Throws(IOException::class)
private fun emitInsertOrUpdateListMethod(writer: JavaWriter) {
writer.apply {
- beginMethod("void", "insertOrUpdate", EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), "Realm", "realm", "Iterator extends RealmModel>", "objects", "Map", "cache")
+ val topLevelArgs = arrayOf("Realm", "realm",
+ "Iterator extends RealmModel>", "objects",
+ "Map", "cache")
+ val embeddedArgs = arrayOf("Realm", "realm",
+ "Table", "parentObjectTable",
+ "long", "parentColumnKey",
+ "long", "parentObjectKey",
+ "Iterator extends RealmModel>", "objects",
+ "Map", "cache")
+ val args = if (metadata.embedded) embeddedArgs else topLevelArgs
+
+ beginMethod("void", "insertOrUpdate", EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), *args)
emitStatement("Table table = realm.getTable(%s.class)", qualifiedJavaClassName)
emitStatement("long tableNativePtr = table.getNativePtr()")
emitStatement("%s columnInfo = (%s) realm.getSchema().getColumnInfo(%s.class)", columnInfoClassName(), columnInfoClassName(), qualifiedJavaClassName)
@@ -1263,26 +1424,40 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
for (field in metadata.fields) {
val fieldName = field.simpleName.toString()
- val fieldType = field.asType().toString()
+ val fieldType = QualifiedClassName(field.asType().toString())
val getter = metadata.getInternalGetter(fieldName)
when {
Utils.isRealmModel(field) -> {
+ // FIXME: How to support types from other compilation units?
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(fieldType)
+
emitEmptyLine()
emitStatement("%s %sObj = ((%s) object).%s()", fieldType, fieldName, interfaceName, getter)
beginControlFlow("if (%sObj != null)", fieldName)
emitStatement("Long cache%1\$s = cache.get(%1\$sObj)", fieldName)
- beginControlFlow("if (cache%s == null)", fieldName)
- emitStatement("cache%1\$s = %2\$s.insertOrUpdate(realm, %1\$sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field))
- endControlFlow()
- emitStatement("Table.nativeSetLink(tableNativePtr, columnInfo.%1\$sColKey, objKey, cache%1\$s, false)", fieldName)
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cache%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: \" + cache%s.toString())", fieldName)
+ nextControlFlow("else")
+ emitStatement("cache%1\$s = %2\$s.insertOrUpdate(realm, table, columnInfo.%3\$sColKey, objKey, %3\$sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field), fieldName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (cache%s == null)", fieldName)
+ emitStatement("cache%1\$s = %2\$s.insertOrUpdate(realm, %1\$sObj, cache)", fieldName, Utils.getProxyClassSimpleName(field))
+ endControlFlow()
+ emitStatement("Table.nativeSetLink(tableNativePtr, columnInfo.%1\$sColKey, objKey, cache%1\$s, false)", fieldName)
+ }
nextControlFlow("else")
// No need to throw exception here if the field is not nullable. A exception will be thrown in setter.
emitStatement("Table.nativeNullifyLink(tableNativePtr, columnInfo.%sColKey, objKey)", fieldName)
endControlFlow()
}
Utils.isRealmModelList(field) -> {
- val genericType = Utils.getGenericTypeQualifiedName(field)
+ val genericType = Utils.getGenericTypeQualifiedName(field)!!
+ // FIXME: How to support types from other compilation units?
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(genericType)
+
emitEmptyLine()
emitStatement("OsList %1\$sOsList = new OsList(table.getUncheckedRow(objKey), columnInfo.%1\$sColKey)", fieldName)
emitStatement("RealmList<%s> %sList = ((%s) object).%s()", genericType, fieldName, interfaceName, getter)
@@ -1292,20 +1467,36 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
beginControlFlow("for (int i = 0; i < objectCount; i++)")
emitStatement("%1\$s %2\$sItem = %2\$sList.get(i)", genericType, fieldName)
emitStatement("Long cacheItemIndex%1\$s = cache.get(%1\$sItem)", fieldName)
- beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
- emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
- endControlFlow()
- emitStatement("%1\$sOsList.setRow(i, cacheItemIndex%1\$s)", fieldName)
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cacheItemIndex%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: \" + cacheItemIndex%s.toString())", fieldName)
+ nextControlFlow("else")
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, table, columnInfo.%3\$sColKey, objKey, %3\$sItem, cache)", fieldName, Utils.getProxyClassName(genericType), fieldName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
+ endControlFlow()
+ emitStatement("%1\$sOsList.setRow(i, cacheItemIndex%1\$s)", fieldName)
+ }
endControlFlow()
nextControlFlow("else")
emitStatement("%1\$sOsList.removeAll()", fieldName)
beginControlFlow("if (%sList != null)", fieldName)
beginControlFlow("for (%1\$s %2\$sItem : %2\$sList)", genericType, fieldName)
emitStatement("Long cacheItemIndex%1\$s = cache.get(%1\$sItem)", fieldName)
- beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
- emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
- endControlFlow()
- emitStatement("%1\$sOsList.addRow(cacheItemIndex%1\$s)", fieldName)
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cacheItemIndex%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: \" + cacheItemIndex%s.toString())", fieldName)
+ nextControlFlow("else")
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, table, columnInfo.%3\$sColKey, objKey, %3\$sItem, cache)", fieldName, Utils.getProxyClassName(genericType), fieldName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (cacheItemIndex%s == null)", fieldName)
+ emitStatement("cacheItemIndex%1\$s = %2\$s.insertOrUpdate(realm, %1\$sItem, cache)", fieldName, Utils.getProxyClassSimpleName(field))
+ endControlFlow()
+ emitStatement("%1\$sOsList.addRow(cacheItemIndex%1\$s)", fieldName)
+ }
endControlFlow()
endControlFlow()
endControlFlow()
@@ -1331,7 +1522,7 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
}
else -> {
if (metadata.primaryKey !== field) {
- setTableValues(writer, fieldType, fieldName, interfaceName, getter, true)
+ setTableValues(writer, fieldType.toString(), fieldName, interfaceName, getter, true)
}
}
}
@@ -1402,8 +1593,13 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
endControlFlow()
emitStatement("cache.put(object, objKey)")
} else {
- emitStatement("long objKey = OsObject.createRow(table)")
- emitStatement("cache.put(object, objKey)")
+ if (metadata.embedded) {
+ emitStatement("long objKey = OsObject.createEmbeddedObject(parentObjectTable, parentObjectKey, parentColumnKey)")
+ emitStatement("cache.put(object, objKey)")
+ } else {
+ emitStatement("long objKey = OsObject.createRow(table)")
+ emitStatement("cache.put(object, objKey)")
+ }
}
}
}
@@ -1424,7 +1620,7 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
emitStatement("return (%s) cachedRealmObject", qualifiedJavaClassName)
endControlFlow()
emitEmptyLine()
- emitStatement("%1\$s realmObjectSource = (%1\$s) newObject", interfaceName)
+ emitStatement("%1\$s unmanagedSource = (%1\$s) newObject", interfaceName)
emitEmptyLine()
emitStatement("Table table = realm.getTable(%s.class)", qualifiedJavaClassName)
emitStatement("OsObjectBuilder builder = new OsObjectBuilder(table, flags)")
@@ -1436,7 +1632,7 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
val fieldColKey = fieldColKeyVariableReference(field)
val fieldName = field.simpleName.toString()
val getter = metadata.getInternalGetter(fieldName)
- emitStatement("builder.%s(%s, realmObjectSource.%s())", OsObjectBuilderTypeHelper.getOsObjectBuilderName(field), fieldColKey, getter)
+ emitStatement("builder.%s(%s, unmanagedSource.%s())", OsObjectBuilderTypeHelper.getOsObjectBuilderName(field), fieldColKey, getter)
}
// Create the underlying object
@@ -1444,8 +1640,8 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
emitSingleLineComment("Create the underlying object and cache it before setting any object/objectlist references")
emitSingleLineComment("This will allow us to break any circular dependencies by using the object cache.")
emitStatement("Row row = builder.createNewObject()")
- emitStatement("%s realmObjectCopy = newProxyInstance(realm, row)", generatedClassName)
- emitStatement("cache.put(newObject, realmObjectCopy)")
+ emitStatement("%s managedCopy = newProxyInstance(realm, row)", generatedClassName)
+ emitStatement("cache.put(newObject, managedCopy)")
// Copy all object references or lists-of-objects
emitEmptyLine()
@@ -1453,42 +1649,82 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
emitSingleLineComment("Finally add all fields that reference other Realm Objects, either directly or through a list")
}
for (field in metadata.objectReferenceFields) {
- val fieldType = field.asType().toString()
- val fieldName = field.simpleName.toString()
- val getter = metadata.getInternalGetter(fieldName)
- val setter = metadata.getInternalSetter(fieldName)
+ val fieldType = QualifiedClassName(field.asType())
+ val fieldName: String = field.simpleName.toString()
+ val getter: String = metadata.getInternalGetter(fieldName)
+ val setter: String = metadata.getInternalSetter(fieldName)
when {
Utils.isRealmModel(field) -> {
- emitStatement("%s %sObj = realmObjectSource.%s()", fieldType, fieldName, getter)
+ // FIXME: How to support Embedded objects defined in another compilation unit?
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(fieldType)
+ val fieldColKey: String = fieldColKeyVariableReference(field)
+ val linkedQualifiedClassName: QualifiedClassName = Utils.getFieldTypeQualifiedName(field)
+ val linkedProxyClass: SimpleClassName = Utils.getProxyClassSimpleName(field)
+
+ emitStatement("%s %sObj = unmanagedSource.%s()", fieldType, fieldName, getter)
beginControlFlow("if (%sObj == null)", fieldName)
- emitStatement("realmObjectCopy.%s(null)", setter)
+ emitStatement("managedCopy.%s(null)", setter)
nextControlFlow("else")
emitStatement("%s cache%s = (%s) cache.get(%sObj)", fieldType, fieldName, fieldType, fieldName)
- beginControlFlow("if (cache%s != null)", fieldName)
- emitStatement("realmObjectCopy.%s(cache%s)", setter, fieldName)
- nextControlFlow("else")
- emitStatement("realmObjectCopy.%s(%s.copyOrUpdate(realm, (%s) realm.getSchema().getColumnInfo(%s.class), %sObj, update, cache, flags))", setter, Utils.getProxyClassSimpleName(field), columnInfoClassName(field), Utils.getFieldTypeQualifiedName(field), fieldName)
- endControlFlow()
+
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cache%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: cache%s.toString()\")", fieldName)
+ nextControlFlow("else")
+ emitStatement("long objKey = ((RealmObjectProxy) managedCopy).realmGet\$proxyState().getRow\$realm().createEmbeddedObject(%s)", fieldColKey)
+ emitStatement("Row linkedObjectRow = realm.getTable(%s.class).getUncheckedRow(objKey)", linkedQualifiedClassName)
+ emitStatement("%s linkedObject = %s.newProxyInstance(realm, linkedObjectRow)", linkedQualifiedClassName, linkedProxyClass)
+ emitStatement("cache.put(%sObj, (RealmObjectProxy) linkedObject)", fieldName)
+ emitStatement("%s.updateEmbeddedObject(realm, %sObj, linkedObject, cache, flags)", linkedProxyClass, fieldName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (cache%s != null)", fieldName)
+ emitStatement("managedCopy.%s(cache%s)", setter, fieldName)
+ nextControlFlow("else")
+ emitStatement("managedCopy.%s(%s.copyOrUpdate(realm, (%s) realm.getSchema().getColumnInfo(%s.class), %sObj, update, cache, flags))", setter, linkedProxyClass, columnInfoClassName(field), linkedQualifiedClassName, fieldName)
+ endControlFlow()
+ }
+
// No need to throw exception here if the field is not nullable. A exception will be thrown in setter.
endControlFlow()
emitEmptyLine()
}
Utils.isRealmModelList(field) -> {
- val genericType = Utils.getGenericTypeQualifiedName(field)
- emitStatement("RealmList<%s> %sList = realmObjectSource.%s()", genericType, fieldName, getter)
- beginControlFlow("if (%sList != null)", fieldName)
- emitStatement("RealmList<%s> %sRealmList = realmObjectCopy.%s()", genericType, fieldName, getter)
+ // FIXME: How to support Embedded objects defined in another compilation unit?
+ val listElementType: QualifiedClassName = Utils.getRealmListType(field)!!
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(listElementType)
+ val genericType: QualifiedClassName = Utils.getGenericTypeQualifiedName(field)!!
+ val linkedProxyClass: SimpleClassName = Utils.getProxyClassSimpleName(field)
+
+ emitStatement("RealmList<%s> %sUnmanagedList = unmanagedSource.%s()", genericType, fieldName, getter)
+ beginControlFlow("if (%sUnmanagedList != null)", fieldName)
+ emitStatement("RealmList<%s> %sManagedList = managedCopy.%s()", genericType, fieldName, getter)
// Clear is needed. See bug https://github.com/realm/realm-java/issues/4957
- emitStatement("%sRealmList.clear()", fieldName)
- beginControlFlow("for (int i = 0; i < %sList.size(); i++)", fieldName)
- emitStatement("%1\$s %2\$sItem = %2\$sList.get(i)", genericType, fieldName)
- emitStatement("%1\$s cache%2\$s = (%1\$s) cache.get(%2\$sItem)", genericType, fieldName)
- beginControlFlow("if (cache%s != null)", fieldName)
- emitStatement("%1\$sRealmList.add(cache%1\$s)", fieldName)
- nextControlFlow("else")
- emitStatement("%1\$sRealmList.add(%2\$s.copyOrUpdate(realm, (%3\$s) realm.getSchema().getColumnInfo(%4\$s.class), %1\$sItem, update, cache, flags))", fieldName, Utils.getProxyClassSimpleName(field), columnInfoClassName(field), Utils.getGenericTypeQualifiedName(field))
- endControlFlow()
+ emitStatement("%sManagedList.clear()", fieldName)
+ beginControlFlow("for (int i = 0; i < %sUnmanagedList.size(); i++)", fieldName)
+ emitStatement("%1\$s %2\$sUnmanagedItem = %2\$sUnmanagedList.get(i)", genericType, fieldName)
+ emitStatement("%1\$s cache%2\$s = (%1\$s) cache.get(%2\$sUnmanagedItem)", genericType, fieldName)
+
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("if (cache%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: cache%s.toString()\")", fieldName)
+ nextControlFlow("else")
+ emitStatement("long objKey = %sManagedList.getOsList().createAndAddEmbeddedObject()", fieldName)
+ emitStatement("Row linkedObjectRow = realm.getTable(%s.class).getUncheckedRow(objKey)", genericType)
+ emitStatement("%s linkedObject = %s.newProxyInstance(realm, linkedObjectRow)", genericType, linkedProxyClass)
+ emitStatement("cache.put(%sUnmanagedItem, (RealmObjectProxy) linkedObject)", fieldName)
+ emitStatement("%s.updateEmbeddedObject(realm, %sUnmanagedItem, linkedObject, new HashMap(), Collections.EMPTY_SET)", linkedProxyClass, fieldName)
+ endControlFlow()
+
+ } else {
+ beginControlFlow("if (cache%s != null)", fieldName)
+ emitStatement("%1\$sManagedList.add(cache%1\$s)", fieldName)
+ nextControlFlow("else")
+ emitStatement("%1\$sManagedList.add(%2\$s.copyOrUpdate(realm, (%3\$s) realm.getSchema().getColumnInfo(%4\$s.class), %1\$sUnmanagedItem, update, cache, flags))", fieldName, Utils.getProxyClassSimpleName(field), columnInfoClassName(field), Utils.getGenericTypeQualifiedName(field))
+ endControlFlow()
+ }
+
endControlFlow()
endControlFlow()
emitEmptyLine()
@@ -1498,7 +1734,7 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
}
}
}
- emitStatement("return realmObjectCopy")
+ emitStatement("return managedCopy")
endMethod()
emitEmptyLine()
}
@@ -1577,7 +1813,7 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
@Throws(IOException::class)
private fun emitUpdateMethod(writer: JavaWriter) {
- if (!metadata.hasPrimaryKey()) {
+ if (!metadata.hasPrimaryKey() && !metadata.embedded) {
return
}
writer.apply {
@@ -1594,43 +1830,91 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
emitStatement("Table table = realm.getTable(%s.class)", qualifiedJavaClassName)
emitStatement("OsObjectBuilder builder = new OsObjectBuilder(table, flags)")
for (field in metadata.fields) {
- val fieldType = field.asType().toString()
+ val fieldType = QualifiedClassName(field.asType())
val fieldName = field.simpleName.toString()
val getter = metadata.getInternalGetter(fieldName)
val fieldColKey = fieldColKeyVariableReference(field)
when {
Utils.isRealmModel(field) -> {
+ // FIXME: How to support Embedded objects defined in another compilation unit?
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(fieldType)
+
emitEmptyLine()
emitStatement("%s %sObj = realmObjectSource.%s()", fieldType, fieldName, getter)
beginControlFlow("if (%sObj == null)", fieldName)
emitStatement("builder.addNull(%s)", fieldColKeyVariableReference(field))
nextControlFlow("else")
+
+ if (fieldTypeMetaData.embedded) {
+ // Embedded objects are created in-place as we need to know the
+ // parent object + the property containing it.
+ // After this we know that changing values will always be considered
+ // an "update
+ emitSingleLineComment("Embedded objects are created directly instead of using the builder.")
+ emitStatement("%s cache%s = (%s) cache.get(%sObj)", fieldType, fieldName, fieldType, fieldName)
+ beginControlFlow("if (cache%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: cache%s.toString()\")", fieldName)
+ endControlFlow()
+ emitEmptyLine()
+ emitStatement("long objKey = ((RealmObjectProxy) realmObject).realmGet\$proxyState().getRow\$realm().createEmbeddedObject(%s)", fieldColKey)
+ emitStatement("Row row = realm.getTable(%s.class).getUncheckedRow(objKey)", Utils.getFieldTypeQualifiedName(field))
+ emitStatement("%s proxyObject = %s.newProxyInstance(realm, row)", fieldType, Utils.getProxyClassSimpleName(field))
+ emitStatement("cache.put(%sObj, (RealmObjectProxy) proxyObject)", fieldName)
+ emitStatement("%s.updateEmbeddedObject(realm, %sObj, proxyObject, cache, flags)", Utils.getProxyClassSimpleName(field), fieldName)
+ } else {
+ // Non-embedded classes are updating using normal recursive bottom-up approach
emitStatement("%s cache%s = (%s) cache.get(%sObj)", fieldType, fieldName, fieldType, fieldName)
beginControlFlow("if (cache%s != null)", fieldName)
emitStatement("builder.addObject(%s, cache%s)", fieldColKey, fieldName)
nextControlFlow("else")
emitStatement("builder.addObject(%s, %s.copyOrUpdate(realm, (%s) realm.getSchema().getColumnInfo(%s.class), %sObj, true, cache, flags))", fieldColKey, Utils.getProxyClassSimpleName(field), columnInfoClassName(field), Utils.getFieldTypeQualifiedName(field), fieldName)
endControlFlow()
+ }
+
// No need to throw exception here if the field is not nullable. A exception will be thrown in setter.
endControlFlow()
}
Utils.isRealmModelList(field) -> {
- val genericType = Utils.getGenericTypeQualifiedName(field)
+ // FIXME: How to support Embedded objects defined in another compilation unit?
+ val genericType: QualifiedClassName = Utils.getRealmListType(field)!!
+ val fieldTypeMetaData: ClassMetaData = classCollection.getClassFromQualifiedName(genericType)
+ val proxyClass: SimpleClassName = Utils.getProxyClassSimpleName(field)
+
emitEmptyLine()
- emitStatement("RealmList<%s> %sList = realmObjectSource.%s()", genericType, fieldName, getter)
- beginControlFlow("if (%sList != null)", fieldName)
+ emitStatement("RealmList<%s> %sUnmanagedList = realmObjectSource.%s()", genericType, fieldName, getter)
+ beginControlFlow("if (%sUnmanagedList != null)", fieldName)
emitStatement("RealmList<%s> %sManagedCopy = new RealmList<%s>()", genericType, fieldName, genericType)
- beginControlFlow("for (int i = 0; i < %sList.size(); i++)", fieldName)
- emitStatement("%1\$s %2\$sItem = %2\$sList.get(i)", genericType, fieldName)
- emitStatement("%1\$s cache%2\$s = (%1\$s) cache.get(%2\$sItem)", genericType, fieldName)
- beginControlFlow("if (cache%s != null)", fieldName)
- emitStatement("%1\$sManagedCopy.add(cache%1\$s)", fieldName)
- nextControlFlow("else")
- emitStatement("%1\$sManagedCopy.add(%2\$s.copyOrUpdate(realm, (%3\$s) realm.getSchema().getColumnInfo(%4\$s.class), %1\$sItem, true, cache, flags))", fieldName, Utils.getProxyClassSimpleName(field), columnInfoClassName(field), Utils.getGenericTypeQualifiedName(field))
+
+ if (fieldTypeMetaData.embedded) {
+ beginControlFlow("for (int i = 0; i < %sUnmanagedList.size(); i++)", fieldName)
+ emitStatement("%1\$s %2\$sUnmanagedItem = %2\$sUnmanagedList.get(i)", genericType, fieldName)
+ emitStatement("%1\$s cache%2\$s = (%1\$s) cache.get(%2\$sUnmanagedItem)", genericType, fieldName)
+ beginControlFlow("if (cache%s != null)", fieldName)
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: cache%s.toString()\")", fieldName)
+ nextControlFlow("else")
+ emitStatement("long objKey = realmObjectTarget.%s().getOsList().createAndAddEmbeddedObject()", getter)
+ emitStatement("Row row = realm.getTable(%s.class).getUncheckedRow(objKey)", genericType)
+ emitStatement("%s proxyObject = %s.newProxyInstance(realm, row)", genericType, proxyClass)
+ emitStatement("cache.put(%sUnmanagedItem, (RealmObjectProxy) proxyObject)", fieldName)
+ emitStatement("%sManagedCopy.add(proxyObject)", fieldName)
+ emitStatement("%s.updateEmbeddedObject(realm, %sUnmanagedItem, proxyObject, new HashMap(), Collections.EMPTY_SET)", Utils.getProxyClassSimpleName(field), fieldName)
+ endControlFlow()
endControlFlow()
- endControlFlow()
- emitStatement("builder.addObjectList(%s, %sManagedCopy)", fieldColKey, fieldName)
+ emitStatement("builder.addObjectList(%s, %sManagedCopy)", fieldColKey, fieldName)
+ } else {
+ beginControlFlow("for (int i = 0; i < %sUnmanagedList.size(); i++)", fieldName)
+ emitStatement("%1\$s %2\$sItem = %2\$sUnmanagedList.get(i)", genericType, fieldName)
+ emitStatement("%1\$s cache%2\$s = (%1\$s) cache.get(%2\$sItem)", genericType, fieldName)
+ beginControlFlow("if (cache%s != null)", fieldName)
+ emitStatement("%1\$sManagedCopy.add(cache%1\$s)", fieldName)
+ nextControlFlow("else")
+ emitStatement("%1\$sManagedCopy.add(%2\$s.copyOrUpdate(realm, (%3\$s) realm.getSchema().getColumnInfo(%4\$s.class), %1\$sItem, true, cache, flags))", fieldName, proxyClass, columnInfoClassName(field), genericType)
+ endControlFlow()
+ endControlFlow()
+ emitStatement("builder.addObjectList(%s, %sManagedCopy)", fieldColKey, fieldName)
+ }
+
nextControlFlow("else")
emitStatement("builder.addObjectList(%s, new RealmList<%s>())", fieldColKey, genericType)
endControlFlow()
@@ -1641,13 +1925,37 @@ class RealmProxyClassGenerator(private val processingEnvironment: ProcessingEnvi
}
}
emitEmptyLine()
- emitStatement("builder.updateExistingObject()")
+ if (metadata.embedded) {
+ emitStatement("builder.updateExistingEmbeddedObject((RealmObjectProxy) realmObject)")
+ } else {
+ emitStatement("builder.updateExistingTopLevelObject()")
+ }
emitStatement("return realmObject")
endMethod()
emitEmptyLine()
}
}
+ @Throws(IOException::class)
+ private fun emitUpdateEmbeddedObjectMethod(writer: JavaWriter) {
+ if (!metadata.embedded) {
+ return
+ }
+
+ writer.apply {
+ beginMethod("void", "updateEmbeddedObject", EnumSet.of(Modifier.STATIC, Modifier.PUBLIC),
+ "Realm", "realm", // Argument type & argument name
+ qualifiedJavaClassName.toString(), "unmanagedObject",
+ qualifiedJavaClassName.toString(), "managedObject",
+ "Map", "cache",
+ "Set", "flags"
+ )
+ emitStatement("update(realm, (%s) realm.getSchema().getColumnInfo(%s.class), managedObject, unmanagedObject, cache, flags)", Utils.getSimpleColumnInfoClassName(metadata.qualifiedClassName), metadata.qualifiedClassName)
+ endMethod()
+ emitEmptyLine()
+ }
+ }
+
@Throws(IOException::class)
private fun emitToStringMethod(writer: JavaWriter) {
if (metadata.containsToString()) {
diff --git a/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyMediatorGenerator.kt b/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyMediatorGenerator.kt
index 18fd5dea62..c6e3d17793 100644
--- a/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyMediatorGenerator.kt
+++ b/realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyMediatorGenerator.kt
@@ -39,6 +39,7 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
private val qualifiedProxyClasses = ArrayList()
private val simpleModelClassNames = ArrayList()
private val internalClassNames = ArrayList()
+ private val embeddedClass = ArrayList()
init {
for (metadata in classesToValidate) {
@@ -47,6 +48,7 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
qualifiedProxyClasses.add(qualifiedProxyClassName)
simpleModelClassNames.add(metadata.simpleJavaClassName)
internalClassNames.add(metadata.internalClassName)
+ embeddedClass.add(metadata.embedded)
}
}
@@ -101,6 +103,8 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
emitCreteOrUpdateUsingJsonObject(this)
emitCreateUsingJsonStream(this)
emitCreateDetachedCopyMethod(this)
+ emitIsEmbeddedMethod(this)
+ emitUpdateEmbeddedObjectMethod(this)
endType()
close()
}
@@ -147,9 +151,9 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
"Class extends RealmModel>", "clazz", // Argument type & argument name
"OsSchemaInfo", "schemaInfo"
)
- emitMediatorShortCircuitSwitch({ i: Int ->
+ emitMediatorShortCircuitSwitch(writer, emitStatement = { i: Int ->
emitStatement("return %s.createColumnInfo(schemaInfo)", qualifiedProxyClasses[i])
- }, writer)
+ })
endMethod()
emitEmptyLine()
}
@@ -165,9 +169,9 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
EnumSet.of(Modifier.PUBLIC),
"Class extends RealmModel>", "clazz"
)
- emitMediatorShortCircuitSwitch({ i: Int ->
- emitStatement("return \"%s\"", internalClassNames[i])
- }, writer)
+ emitMediatorShortCircuitSwitch(writer, emitStatement = { i: Int ->
+ emitStatement("return \"%s\"", internalClassNames[i])
+ })
endMethod()
emitEmptyLine()
}
@@ -191,9 +195,9 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
emitStatement("final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get()")
beginControlFlow("try")
emitStatement("objectContext.set((BaseRealm) baseRealm, row, columnInfo, acceptDefaultValue, excludeFields)")
- emitMediatorShortCircuitSwitch({ i: Int ->
+ emitMediatorShortCircuitSwitch(writer, emitStatement = { i: Int ->
emitStatement("return clazz.cast(new %s())", qualifiedProxyClasses[i])
- }, writer)
+ })
nextControlFlow("finally")
emitStatement("objectContext.clear()")
endControlFlow()
@@ -231,10 +235,10 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
emitSingleLineComment("generated by RealmProxy or the original type extending directly from RealmObject")
emitStatement("@SuppressWarnings(\"unchecked\") Class clazz = (Class) ((obj instanceof RealmObjectProxy) ? obj.getClass().getSuperclass() : obj.getClass())")
emitEmptyLine()
- emitMediatorShortCircuitSwitch({i: Int ->
+ emitMediatorShortCircuitSwitch(writer, false) { i: Int ->
emitStatement("%1\$s columnInfo = (%1\$s) realm.getSchema().getColumnInfo(%2\$s.class)", Utils.getSimpleColumnInfoClassName(qualifiedModelClasses[i]), qualifiedModelClasses[i])
emitStatement("return clazz.cast(%s.copyOrUpdate(realm, columnInfo, (%s) obj, update, cache, flags))", qualifiedProxyClasses[i], qualifiedModelClasses[i])
- }, writer, false)
+ }
endMethod()
emitEmptyLine()
}
@@ -249,13 +253,23 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
"insert",
EnumSet.of(Modifier.PUBLIC),
"Realm", "realm", "RealmModel", "object", "Map", "cache")
- emitSingleLineComment("This cast is correct because obj is either")
- emitSingleLineComment("generated by RealmProxy or the original type extending directly from RealmObject")
- emitStatement("@SuppressWarnings(\"unchecked\") Class clazz = (Class) ((object instanceof RealmObjectProxy) ? object.getClass().getSuperclass() : object.getClass())")
- emitEmptyLine()
- emitMediatorSwitch({ i: Int ->
- emitStatement("%s.insert(realm, (%s) object, cache)", qualifiedProxyClasses[i], qualifiedModelClasses[i])
- }, writer, false)
+
+ if (embeddedClass.contains(false)) {
+ emitSingleLineComment("This cast is correct because obj is either")
+ emitSingleLineComment("generated by RealmProxy or the original type extending directly from RealmObject")
+ emitStatement("@SuppressWarnings(\"unchecked\") Class clazz = (Class) ((object instanceof RealmObjectProxy) ? object.getClass().getSuperclass() : object.getClass())")
+ emitEmptyLine()
+ emitMediatorSwitch(writer, false, { i: Int ->
+ if (embeddedClass[i]) {
+ emitEmbeddedObjectsCannotBeCopiedException(writer)
+ } else {
+ emitStatement("%s.insert(realm, (%s) object, cache)", qualifiedProxyClasses[i], qualifiedModelClasses[i])
+ }
+ })
+ } else {
+ emitEmbeddedObjectsCannotBeCopiedException(writer)
+ }
+
endMethod()
emitEmptyLine()
}
@@ -270,13 +284,22 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
"insertOrUpdate",
EnumSet.of(Modifier.PUBLIC),
"Realm", "realm", "RealmModel", "obj", "Map", "cache")
+
+ if (embeddedClass.contains(false)) {
emitSingleLineComment("This cast is correct because obj is either")
emitSingleLineComment("generated by RealmProxy or the original type extending directly from RealmObject")
emitStatement("@SuppressWarnings(\"unchecked\") Class clazz = (Class) ((obj instanceof RealmObjectProxy) ? obj.getClass().getSuperclass() : obj.getClass())")
emitEmptyLine()
- emitMediatorSwitch({ i: Int ->
- emitStatement("%s.insertOrUpdate(realm, (%s) obj, cache)", qualifiedProxyClasses[i], qualifiedModelClasses[i])
- }, writer, false)
+ emitMediatorSwitch(writer, false, { i: Int ->
+ if (embeddedClass[i]) {
+ emitEmbeddedObjectsCannotBeCopiedException(writer)
+ } else {
+ emitStatement("%s.insertOrUpdate(realm, (%s) obj, cache)", qualifiedProxyClasses[i], qualifiedModelClasses[i])
+ }
+ })
+ } else {
+ emitEmbeddedObjectsCannotBeCopiedException(writer)
+ }
endMethod()
emitEmptyLine()
}
@@ -292,33 +315,52 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
EnumSet.of(Modifier.PUBLIC),
"Realm", "realm", "Collection extends RealmModel>", "objects")
+ if (embeddedClass.contains(false)) {
emitStatement("Iterator extends RealmModel> iterator = objects.iterator()")
emitStatement("RealmModel object = null")
emitStatement("Map cache = new HashMap(objects.size())")
beginControlFlow("if (iterator.hasNext())")
- emitSingleLineComment(" access the first element to figure out the clazz for the routing below")
- emitStatement("object = iterator.next()")
- emitSingleLineComment("This cast is correct because obj is either")
- emitSingleLineComment("generated by RealmProxy or the original type extending directly from RealmObject")
- emitStatement("@SuppressWarnings(\"unchecked\") Class clazz = (Class) ((object instanceof RealmObjectProxy) ? object.getClass().getSuperclass() : object.getClass())")
- emitEmptyLine()
+ emitSingleLineComment(" access the first element to figure out the clazz for the routing below")
+ emitStatement("object = iterator.next()")
+ emitSingleLineComment("This cast is correct because obj is either")
+ emitSingleLineComment("generated by RealmProxy or the original type extending directly from RealmObject")
+ emitStatement("@SuppressWarnings(\"unchecked\") Class clazz = (Class) ((object instanceof RealmObjectProxy) ? object.getClass().getSuperclass() : object.getClass())")
+ emitEmptyLine()
- emitMediatorSwitch({ i: Int ->
+ emitMediatorSwitch(writer, false) { i: Int ->
+ if (embeddedClass[i]) {
+ emitEmbeddedObjectsCannotBeCopiedException(writer)
+ } else {
emitStatement("%s.insertOrUpdate(realm, (%s) object, cache)", qualifiedProxyClasses[i], qualifiedModelClasses[i])
- }, writer, false)
+ }
+ }
- beginControlFlow("if (iterator.hasNext())")
- emitMediatorSwitch({ i: Int ->
- emitStatement("%s.insertOrUpdate(realm, iterator, cache)", qualifiedProxyClasses[i])
- }, writer, false)
- endControlFlow()
+ beginControlFlow("if (iterator.hasNext())")
+ emitMediatorSwitch(writer, false) { i: Int ->
+ if (embeddedClass[i]) {
+ emitEmbeddedObjectsCannotBeCopiedException(writer)
+ } else {
+ emitStatement("%s.insertOrUpdate(realm, iterator, cache)", qualifiedProxyClasses[i])
+ }
+ }
endControlFlow()
+ endControlFlow()
+ } else {
+ emitEmbeddedObjectsCannotBeCopiedException(writer)
+ }
+
endMethod()
emitEmptyLine()
}
}
+ private fun emitEmbeddedObjectsCannotBeCopiedException(writer: JavaWriter) {
+ writer.apply {
+ emitStatement("throw new IllegalArgumentException(\"Embedded objects cannot be copied into Realm by themselves. They need to be attached to a parent object\")")
+ }
+ }
+
@Throws(IOException::class)
private fun emitInsertListToRealmMethod(writer: JavaWriter) {
writer.apply {
@@ -329,29 +371,41 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
EnumSet.of(Modifier.PUBLIC),
"Realm", "realm", "Collection extends RealmModel>", "objects")
- emitStatement("Iterator extends RealmModel> iterator = objects.iterator()")
- emitStatement("RealmModel object = null")
- emitStatement("Map cache = new HashMap(objects.size())")
+ if (embeddedClass.contains(false)) {
+ emitStatement("Iterator extends RealmModel> iterator = objects.iterator()")
+ emitStatement("RealmModel object = null")
+ emitStatement("Map cache = new HashMap(objects.size())")
- beginControlFlow("if (iterator.hasNext())")
- .emitSingleLineComment(" access the first element to figure out the clazz for the routing below")
- .emitStatement("object = iterator.next()")
- .emitSingleLineComment("This cast is correct because obj is either")
- .emitSingleLineComment("generated by RealmProxy or the original type extending directly from RealmObject")
- .emitStatement("@SuppressWarnings(\"unchecked\") Class clazz = (Class) ((object instanceof RealmObjectProxy) ? object.getClass().getSuperclass() : object.getClass())")
- .emitEmptyLine()
-
- emitMediatorSwitch({ i: Int ->
+ beginControlFlow("if (iterator.hasNext())")
+ .emitSingleLineComment(" access the first element to figure out the clazz for the routing below")
+ .emitStatement("object = iterator.next()")
+ .emitSingleLineComment("This cast is correct because obj is either")
+ .emitSingleLineComment("generated by RealmProxy or the original type extending directly from RealmObject")
+ .emitStatement("@SuppressWarnings(\"unchecked\") Class clazz = (Class) ((object instanceof RealmObjectProxy) ? object.getClass().getSuperclass() : object.getClass())")
+ .emitEmptyLine()
+
+ emitMediatorSwitch(writer, false, { i: Int ->
+ if (embeddedClass[i]) {
+ emitEmbeddedObjectsCannotBeCopiedException(writer)
+ } else {
emitStatement("%s.insert(realm, (%s) object, cache)", qualifiedProxyClasses[i], qualifiedModelClasses[i])
- }, writer, false)
+ }
+ })
beginControlFlow("if (iterator.hasNext())")
- emitMediatorSwitch({ i: Int ->
+ emitMediatorSwitch(writer, false, { i: Int ->
+ if (embeddedClass[i]) {
+ emitEmbeddedObjectsCannotBeCopiedException(writer)
+ } else {
emitStatement("%s.insert(realm, iterator, cache)", qualifiedProxyClasses[i])
- }, writer, false)
+ }
+ })
+ endControlFlow()
endControlFlow()
- endControlFlow()
+ } else {
+ emitEmbeddedObjectsCannotBeCopiedException(writer)
+ }
endMethod()
emitEmptyLine()
}
@@ -368,9 +422,9 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
Arrays.asList("Class", "clazz", "Realm", "realm", "JSONObject", "json", "boolean", "update"),
Arrays.asList("JSONException")
)
- emitMediatorShortCircuitSwitch({ i: Int ->
- emitStatement("return clazz.cast(%s.createOrUpdateUsingJsonObject(realm, json, update))", qualifiedProxyClasses[i])
- }, writer)
+ emitMediatorShortCircuitSwitch(writer, emitStatement = { i: Int ->
+ emitStatement("return clazz.cast(%s.createOrUpdateUsingJsonObject(realm, json, update))", qualifiedProxyClasses[i])
+ })
endMethod()
emitEmptyLine()
}
@@ -387,9 +441,9 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
Arrays.asList("Class", "clazz", "Realm", "realm", "JsonReader", "reader"),
Arrays.asList("java.io.IOException")
)
- emitMediatorShortCircuitSwitch({ i: Int ->
+ emitMediatorShortCircuitSwitch(writer, emitStatement = { i: Int ->
emitStatement("return clazz.cast(%s.createUsingJsonStream(realm, reader))", qualifiedProxyClasses[i])
- }, writer)
+ })
endMethod()
emitEmptyLine()
}
@@ -409,20 +463,70 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
emitSingleLineComment("generated by RealmProxy or the original type extending directly from RealmObject")
emitStatement("@SuppressWarnings(\"unchecked\") Class clazz = (Class) realmObject.getClass().getSuperclass()")
emitEmptyLine()
- emitMediatorShortCircuitSwitch({ i: Int ->
+ emitMediatorShortCircuitSwitch(writer, false, { i: Int ->
emitStatement("return clazz.cast(%s.createDetachedCopy((%s) realmObject, 0, maxDepth, cache))",
qualifiedProxyClasses[i], qualifiedModelClasses[i])
- }, writer, false)
+ })
endMethod()
emitEmptyLine()
}
}
+ @Throws(IOException::class)
+ private fun emitIsEmbeddedMethod(writer: JavaWriter) {
+ writer.apply {
+ emitAnnotation("Override")
+ beginMethod(
+ " boolean",
+ "isEmbedded",
+ EnumSet.of(Modifier.PUBLIC),
+ "Class", "clazz"
+ )
+ emitMediatorShortCircuitSwitch(writer, false, { i: Int ->
+ emitStatement("return %s", if (embeddedClass[i]) "true" else "false")
+ })
+ endMethod()
+ emitEmptyLine()
+ }
+ }
+
+ @Throws(IOException::class)
+ private fun emitUpdateEmbeddedObjectMethod(writer: JavaWriter) {
+ writer.apply {
+ emitAnnotation("Override")
+ beginMethod(
+ " void",
+ "updateEmbeddedObject",
+ EnumSet.of(Modifier.PUBLIC),
+ "Realm", "realm",
+ "E", "unmanagedObject",
+ "E", "managedObject",
+ "Map", "cache",
+ "Set", "flags"
+ )
+
+ emitSingleLineComment("This cast is correct because obj is either")
+ emitSingleLineComment("generated by RealmProxy or the original type extending directly from RealmObject")
+ emitStatement("@SuppressWarnings(\"unchecked\") Class clazz = (Class) managedObject.getClass().getSuperclass()")
+ emitEmptyLine()
+ emitMediatorSwitch(writer, false) { i: Int ->
+ if (embeddedClass[i]) {
+ emitStatement("%1\$s.updateEmbeddedObject(realm, (%2\$s) unmanagedObject, (%2\$s) managedObject, cache, flags)", qualifiedProxyClasses[i], qualifiedModelClasses[i])
+ } else {
+ emitStatement("throw getNotEmbeddedClassException(\"%s\")", qualifiedModelClasses[i])
+ }
+ }
+ endMethod()
+ 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.
@Throws(IOException::class)
- private fun emitMediatorSwitch(emitStatement: (index: Int) -> Unit, writer: JavaWriter, nullPointerCheck: Boolean) {
+ private fun emitMediatorSwitch(writer: JavaWriter, nullPointerCheck: Boolean, emitStatement: (index: Int) -> Unit) {
writer.apply {
if (nullPointerCheck) {
emitStatement("checkClass(clazz)")
@@ -445,7 +549,7 @@ class RealmProxyMediatorGenerator(private val processingEnvironment: ProcessingE
}
@Throws(IOException::class)
- private fun emitMediatorShortCircuitSwitch(emitStatement: (index: Int) -> Unit, writer: JavaWriter, nullPointerCheck: Boolean = true) {
+ private fun emitMediatorShortCircuitSwitch(writer: JavaWriter, nullPointerCheck: Boolean = true, emitStatement: (index: Int) -> Unit) {
writer.apply {
if (nullPointerCheck) {
emitStatement("checkClass(clazz)")
diff --git a/realm/realm-annotations-processor/src/test/java/io/realm/processor/RealmEmbeddedObjectsTest.java b/realm/realm-annotations-processor/src/test/java/io/realm/processor/RealmEmbeddedObjectsTest.java
new file mode 100644
index 0000000000..4aae15f466
--- /dev/null
+++ b/realm/realm-annotations-processor/src/test/java/io/realm/processor/RealmEmbeddedObjectsTest.java
@@ -0,0 +1,115 @@
+package io.realm.processor;
+
+import com.google.testing.compile.JavaFileObjects;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
+import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
+import static org.truth0.Truth.ASSERT;
+
+public class RealmEmbeddedObjectsTest {
+
+ @Test
+ public void compileEmbeddedObjectFile() {
+ ASSERT.about(javaSource())
+ .that(JavaFileObjects.forResource("some/test/EmbeddedClass.java"))
+ .processedWith(new RealmProcessor())
+ .compilesWithoutError();
+ }
+
+ @Test
+ public void compileParentToEmbeddedObjectFile() {
+ ASSERT.about(javaSources())
+ .that(Arrays.asList(
+ JavaFileObjects.forResource("some/test/EmbeddedClassSimpleParent.java"),
+ JavaFileObjects.forResource("some/test/EmbeddedClass.java")
+ ))
+ .processedWith(new RealmProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(JavaFileObjects.forResource("io/realm/some_test_EmbeddedClassSimpleParentRealmProxy.java"));
+ }
+
+ @Test
+ public void compileWithSingleRequiredParent() {
+ ASSERT.about(javaSources())
+ .that(Arrays.asList(
+ JavaFileObjects.forResource("some/test/EmbeddedClassParent.java"),
+ JavaFileObjects.forResource("some/test/EmbeddedClass.java"),
+ JavaFileObjects.forResource("some/test/EmbeddedClassOptionalParents.java"),
+ JavaFileObjects.forResource("some/test/EmbeddedClassRequiredParent.java")
+ ))
+ .processedWith(new RealmProcessor())
+ .compilesWithoutError();
+ }
+
+
+ @Test
+ public void compileWithMultipleOptionalParents() {
+ ASSERT.about(javaSources())
+ .that(Arrays.asList(
+ JavaFileObjects.forResource("some/test/EmbeddedClassParent.java"),
+ JavaFileObjects.forResource("some/test/EmbeddedClass.java"),
+ JavaFileObjects.forResource("some/test/EmbeddedClassRequiredParent.java"),
+ JavaFileObjects.forResource("some/test/EmbeddedClassOptionalParents.java")
+ ))
+ .processedWith(new RealmProcessor())
+ .compilesWithoutError();
+ }
+
+ @Test
+ public void failToCompileIfSingleParentIsMissingFinal() {
+ ASSERT.about(javaSources())
+ .that(Arrays.asList(
+ JavaFileObjects.forResource("some/test/EmbeddedClassParent.java"),
+ JavaFileObjects.forResource("some/test/EmbeddedClassMissingFinalOnLinkingObjects.java")
+ ))
+ .processedWith(new RealmProcessor())
+ .failsToCompile()
+ .withErrorContaining("The @LinkingObjects field \"some.test.EmbeddedClassMissingFinalOnLinkingObjects.parent\" must be final.");
+ }
+
+ // If a single parent type has multiple potential fields that can act as parent. Any
+ // @LinkingObject field in the child must designate the field name in the parent.
+ @Test
+ public void failToCompileIfMissingFieldDescriptor() {
+ ASSERT.about(javaSources())
+ .that(Arrays.asList(
+ JavaFileObjects.forResource("some/test/EmbeddedClassParent.java"),
+ JavaFileObjects.forResource("some/test/EmbeddedClassMissingFieldDescription.java")
+ ))
+ .processedWith(new RealmProcessor())
+ .failsToCompile()
+ .withErrorContaining("The @LinkingObjects annotation for the field \"some.test.EmbeddedClassMissingFieldDescription.parent1\" must have a parameter identifying the link target.");
+ }
+
+
+ // @PrimaryKey is not allowed inside embedded classes
+ @Test
+ public void failToCompileWithPrimaryKey() {
+ ASSERT.about(javaSources())
+ .that(Arrays.asList(
+ JavaFileObjects.forResource("some/test/EmbeddedClassPrimaryKey.java")
+ ))
+ .processedWith(new RealmProcessor())
+ .failsToCompile()
+ .withErrorContaining("A model class marked as embedded cannot contain a @PrimaryKey.");
+ }
+
+ // If a child has multiple potential parents, none of them are allowed to be marked
+ // @Required.
+ @Test
+ public void failToCompileWithMultipleRequiredParents() {
+ ASSERT.about(javaSources())
+ .that(Arrays.asList(
+ JavaFileObjects.forResource("some/test/EmbeddedClassParent.java"),
+ JavaFileObjects.forResource("some/test/EmbeddedClassMultipleRequiredParents.java")
+ ))
+ .processedWith(new RealmProcessor())
+ .failsToCompile()
+ .withErrorContaining("@Required cannot be used on @LinkingObjects field if multiple @LinkingParents are defined");
+ }
+}
diff --git a/realm/realm-annotations-processor/src/test/resources/io/realm/RealmDefaultModuleMediator.java b/realm/realm-annotations-processor/src/test/resources/io/realm/RealmDefaultModuleMediator.java
index 9c5b47c9da..082303afba 100644
--- a/realm/realm-annotations-processor/src/test/resources/io/realm/RealmDefaultModuleMediator.java
+++ b/realm/realm-annotations-processor/src/test/resources/io/realm/RealmDefaultModuleMediator.java
@@ -206,4 +206,25 @@ public E createDetachedCopy(E realmObject, int maxDepth,
throw getMissingProxyClassException(clazz);
}
-}
+ @Override
+ public boolean isEmbedded(Class clazz) {
+ if (clazz.equals(some.test.AllTypes.class)) {
+ return false;
+ }
+ throw getMissingProxyClassException(clazz);
+ }
+
+ @Override
+ public void updateEmbeddedObject(Realm realm, E unmanagedObject, E managedObject, Map cache, Set flags) {
+ // This cast is correct because obj is either
+ // generated by RealmProxy or the original type extending directly from RealmObject
+ @SuppressWarnings("unchecked") Class clazz = (Class) managedObject.getClass().getSuperclass();
+
+ if (clazz.equals(some.test.AllTypes.class)) {
+ throw getNotEmbeddedClassException("some.test.AllTypes");
+ } else {
+ throw getMissingProxyClassException(clazz);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_AllTypesRealmProxy.java b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_AllTypesRealmProxy.java
index 2a61aac1bf..918f1b8bb8 100644
--- a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_AllTypesRealmProxy.java
+++ b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_AllTypesRealmProxy.java
@@ -411,6 +411,7 @@ protected final void copy(ColumnInfo rawSrc, ColumnInfo rawDst) {
@Override
public void realmSet$columnObject(some.test.AllTypes value) {
+ Realm realm = (Realm) proxyState.getRealm$realm();
if (proxyState.isUnderConstruction()) {
if (!proxyState.getAcceptDefaultValue$realm()) {
return;
@@ -419,7 +420,7 @@ protected final void copy(ColumnInfo rawSrc, ColumnInfo rawDst) {
return;
}
if (value != null && !RealmObject.isManaged(value)) {
- value = ((Realm) proxyState.getRealm$realm()).copyToRealm(value);
+ value = realm.copyToRealm(value);
}
final Row row = proxyState.getRow$realm();
if (value == null) {
@@ -982,7 +983,7 @@ protected final void copy(ColumnInfo rawSrc, ColumnInfo rawDst) {
}
private static OsObjectSchemaInfo createExpectedObjectSchemaInfo() {
- OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("AllTypes", 24, 1);
+ OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("AllTypes", false, 24, 1);
builder.addPersistedProperty("columnString", RealmFieldType.STRING, Property.PRIMARY_KEY, !Property.INDEXED, !Property.REQUIRED);
builder.addPersistedProperty("columnLong", RealmFieldType.INTEGER, !Property.PRIMARY_KEY, !Property.INDEXED, Property.REQUIRED);
builder.addPersistedProperty("columnFloat", RealmFieldType.FLOAT, !Property.PRIMARY_KEY, !Property.INDEXED, Property.REQUIRED);
@@ -1368,7 +1369,7 @@ public static some.test.AllTypes createUsingJsonStream(Realm realm, JsonReader r
return realm.copyToRealm(obj);
}
- private static some_test_AllTypesRealmProxy newProxyInstance(BaseRealm realm, Row row) {
+ static some_test_AllTypesRealmProxy newProxyInstance(BaseRealm realm, Row row) {
// Ignore default values to avoid creating unexpected objects from RealmModel/RealmList fields
final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get();
objectContext.set(realm, row, realm.getSchema().getColumnInfo(some.test.AllTypes.class), false, Collections.emptyList());
@@ -1427,70 +1428,70 @@ public static some.test.AllTypes copy(Realm realm, AllTypesColumnInfo columnInfo
return (some.test.AllTypes) cachedRealmObject;
}
- some_test_AllTypesRealmProxyInterface realmObjectSource = (some_test_AllTypesRealmProxyInterface) newObject;
+ some_test_AllTypesRealmProxyInterface unmanagedSource = (some_test_AllTypesRealmProxyInterface) newObject;
Table table = realm.getTable(some.test.AllTypes.class);
OsObjectBuilder builder = new OsObjectBuilder(table, flags);
// Add all non-"object reference" fields
- builder.addString(columnInfo.columnStringColKey, realmObjectSource.realmGet$columnString());
- builder.addInteger(columnInfo.columnLongColKey, realmObjectSource.realmGet$columnLong());
- builder.addFloat(columnInfo.columnFloatColKey, realmObjectSource.realmGet$columnFloat());
- builder.addDouble(columnInfo.columnDoubleColKey, realmObjectSource.realmGet$columnDouble());
- builder.addBoolean(columnInfo.columnBooleanColKey, realmObjectSource.realmGet$columnBoolean());
- builder.addDecimal128(columnInfo.columnDecimal128ColKey, realmObjectSource.realmGet$columnDecimal128());
- builder.addObjectId(columnInfo.columnObjectIdColKey, realmObjectSource.realmGet$columnObjectId());
- builder.addDate(columnInfo.columnDateColKey, realmObjectSource.realmGet$columnDate());
- builder.addByteArray(columnInfo.columnBinaryColKey, realmObjectSource.realmGet$columnBinary());
- builder.addMutableRealmInteger(columnInfo.columnMutableRealmIntegerColKey, realmObjectSource.realmGet$columnMutableRealmInteger());
- builder.addStringList(columnInfo.columnStringListColKey, realmObjectSource.realmGet$columnStringList());
- builder.addByteArrayList(columnInfo.columnBinaryListColKey, realmObjectSource.realmGet$columnBinaryList());
- builder.addBooleanList(columnInfo.columnBooleanListColKey, realmObjectSource.realmGet$columnBooleanList());
- builder.addLongList(columnInfo.columnLongListColKey, realmObjectSource.realmGet$columnLongList());
- builder.addIntegerList(columnInfo.columnIntegerListColKey, realmObjectSource.realmGet$columnIntegerList());
- builder.addShortList(columnInfo.columnShortListColKey, realmObjectSource.realmGet$columnShortList());
- builder.addByteList(columnInfo.columnByteListColKey, realmObjectSource.realmGet$columnByteList());
- builder.addDoubleList(columnInfo.columnDoubleListColKey, realmObjectSource.realmGet$columnDoubleList());
- builder.addFloatList(columnInfo.columnFloatListColKey, realmObjectSource.realmGet$columnFloatList());
- builder.addDateList(columnInfo.columnDateListColKey, realmObjectSource.realmGet$columnDateList());
- builder.addDecimal128List(columnInfo.columnDecimal128ListColKey, realmObjectSource.realmGet$columnDecimal128List());
- builder.addObjectIdList(columnInfo.columnObjectIdListColKey, realmObjectSource.realmGet$columnObjectIdList());
+ builder.addString(columnInfo.columnStringColKey, unmanagedSource.realmGet$columnString());
+ builder.addInteger(columnInfo.columnLongColKey, unmanagedSource.realmGet$columnLong());
+ builder.addFloat(columnInfo.columnFloatColKey, unmanagedSource.realmGet$columnFloat());
+ builder.addDouble(columnInfo.columnDoubleColKey, unmanagedSource.realmGet$columnDouble());
+ builder.addBoolean(columnInfo.columnBooleanColKey, unmanagedSource.realmGet$columnBoolean());
+ builder.addDecimal128(columnInfo.columnDecimal128ColKey, unmanagedSource.realmGet$columnDecimal128());
+ builder.addObjectId(columnInfo.columnObjectIdColKey, unmanagedSource.realmGet$columnObjectId());
+ builder.addDate(columnInfo.columnDateColKey, unmanagedSource.realmGet$columnDate());
+ builder.addByteArray(columnInfo.columnBinaryColKey, unmanagedSource.realmGet$columnBinary());
+ builder.addMutableRealmInteger(columnInfo.columnMutableRealmIntegerColKey, unmanagedSource.realmGet$columnMutableRealmInteger());
+ builder.addStringList(columnInfo.columnStringListColKey, unmanagedSource.realmGet$columnStringList());
+ builder.addByteArrayList(columnInfo.columnBinaryListColKey, unmanagedSource.realmGet$columnBinaryList());
+ builder.addBooleanList(columnInfo.columnBooleanListColKey, unmanagedSource.realmGet$columnBooleanList());
+ builder.addLongList(columnInfo.columnLongListColKey, unmanagedSource.realmGet$columnLongList());
+ builder.addIntegerList(columnInfo.columnIntegerListColKey, unmanagedSource.realmGet$columnIntegerList());
+ builder.addShortList(columnInfo.columnShortListColKey, unmanagedSource.realmGet$columnShortList());
+ builder.addByteList(columnInfo.columnByteListColKey, unmanagedSource.realmGet$columnByteList());
+ builder.addDoubleList(columnInfo.columnDoubleListColKey, unmanagedSource.realmGet$columnDoubleList());
+ builder.addFloatList(columnInfo.columnFloatListColKey, unmanagedSource.realmGet$columnFloatList());
+ builder.addDateList(columnInfo.columnDateListColKey, unmanagedSource.realmGet$columnDateList());
+ builder.addDecimal128List(columnInfo.columnDecimal128ListColKey, unmanagedSource.realmGet$columnDecimal128List());
+ builder.addObjectIdList(columnInfo.columnObjectIdListColKey, unmanagedSource.realmGet$columnObjectIdList());
// Create the underlying object and cache it before setting any object/objectlist references
// This will allow us to break any circular dependencies by using the object cache.
Row row = builder.createNewObject();
- io.realm.some_test_AllTypesRealmProxy realmObjectCopy = newProxyInstance(realm, row);
- cache.put(newObject, realmObjectCopy);
+ io.realm.some_test_AllTypesRealmProxy managedCopy = newProxyInstance(realm, row);
+ cache.put(newObject, managedCopy);
// Finally add all fields that reference other Realm Objects, either directly or through a list
- some.test.AllTypes columnObjectObj = realmObjectSource.realmGet$columnObject();
+ some.test.AllTypes columnObjectObj = unmanagedSource.realmGet$columnObject();
if (columnObjectObj == null) {
- realmObjectCopy.realmSet$columnObject(null);
+ managedCopy.realmSet$columnObject(null);
} else {
some.test.AllTypes cachecolumnObject = (some.test.AllTypes) cache.get(columnObjectObj);
if (cachecolumnObject != null) {
- realmObjectCopy.realmSet$columnObject(cachecolumnObject);
+ managedCopy.realmSet$columnObject(cachecolumnObject);
} else {
- realmObjectCopy.realmSet$columnObject(some_test_AllTypesRealmProxy.copyOrUpdate(realm, (some_test_AllTypesRealmProxy.AllTypesColumnInfo) realm.getSchema().getColumnInfo(some.test.AllTypes.class), columnObjectObj, update, cache, flags));
+ managedCopy.realmSet$columnObject(some_test_AllTypesRealmProxy.copyOrUpdate(realm, (some_test_AllTypesRealmProxy.AllTypesColumnInfo) realm.getSchema().getColumnInfo(some.test.AllTypes.class), columnObjectObj, update, cache, flags));
}
}
- RealmList columnRealmListList = realmObjectSource.realmGet$columnRealmList();
- if (columnRealmListList != null) {
- RealmList columnRealmListRealmList = realmObjectCopy.realmGet$columnRealmList();
- columnRealmListRealmList.clear();
- for (int i = 0; i < columnRealmListList.size(); i++) {
- some.test.AllTypes columnRealmListItem = columnRealmListList.get(i);
- some.test.AllTypes cachecolumnRealmList = (some.test.AllTypes) cache.get(columnRealmListItem);
+ RealmList columnRealmListUnmanagedList = unmanagedSource.realmGet$columnRealmList();
+ if (columnRealmListUnmanagedList != null) {
+ RealmList columnRealmListManagedList = managedCopy.realmGet$columnRealmList();
+ columnRealmListManagedList.clear();
+ for (int i = 0; i < columnRealmListUnmanagedList.size(); i++) {
+ some.test.AllTypes columnRealmListUnmanagedItem = columnRealmListUnmanagedList.get(i);
+ some.test.AllTypes cachecolumnRealmList = (some.test.AllTypes) cache.get(columnRealmListUnmanagedItem);
if (cachecolumnRealmList != null) {
- columnRealmListRealmList.add(cachecolumnRealmList);
+ columnRealmListManagedList.add(cachecolumnRealmList);
} else {
- columnRealmListRealmList.add(some_test_AllTypesRealmProxy.copyOrUpdate(realm, (some_test_AllTypesRealmProxy.AllTypesColumnInfo) realm.getSchema().getColumnInfo(some.test.AllTypes.class), columnRealmListItem, update, cache, flags));
+ columnRealmListManagedList.add(some_test_AllTypesRealmProxy.copyOrUpdate(realm, (some_test_AllTypesRealmProxy.AllTypesColumnInfo) realm.getSchema().getColumnInfo(some.test.AllTypes.class), columnRealmListUnmanagedItem, update, cache, flags));
}
}
}
- return realmObjectCopy;
+ return managedCopy;
}
public static long insert(Realm realm, some.test.AllTypes object, Map cache) {
@@ -2572,11 +2573,11 @@ static some.test.AllTypes update(Realm realm, AllTypesColumnInfo columnInfo, som
}
}
- RealmList columnRealmListList = realmObjectSource.realmGet$columnRealmList();
- if (columnRealmListList != null) {
+ RealmList columnRealmListUnmanagedList = realmObjectSource.realmGet$columnRealmList();
+ if (columnRealmListUnmanagedList != null) {
RealmList columnRealmListManagedCopy = new RealmList();
- for (int i = 0; i < columnRealmListList.size(); i++) {
- some.test.AllTypes columnRealmListItem = columnRealmListList.get(i);
+ for (int i = 0; i < columnRealmListUnmanagedList.size(); i++) {
+ some.test.AllTypes columnRealmListItem = columnRealmListUnmanagedList.get(i);
some.test.AllTypes cachecolumnRealmList = (some.test.AllTypes) cache.get(columnRealmListItem);
if (cachecolumnRealmList != null) {
columnRealmListManagedCopy.add(cachecolumnRealmList);
@@ -2601,7 +2602,7 @@ static some.test.AllTypes update(Realm realm, AllTypesColumnInfo columnInfo, som
builder.addDecimal128List(columnInfo.columnDecimal128ListColKey, realmObjectSource.realmGet$columnDecimal128List());
builder.addObjectIdList(columnInfo.columnObjectIdListColKey, realmObjectSource.realmGet$columnObjectIdList());
- builder.updateExistingObject();
+ builder.updateExistingTopLevelObject();
return realmObject;
}
diff --git a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_BooleansRealmProxy.java b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_BooleansRealmProxy.java
index 6906c18754..719b35e008 100644
--- a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_BooleansRealmProxy.java
+++ b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_BooleansRealmProxy.java
@@ -185,7 +185,7 @@ protected final void copy(ColumnInfo rawSrc, ColumnInfo rawDst) {
}
private static OsObjectSchemaInfo createExpectedObjectSchemaInfo() {
- OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("Booleans", 4, 0);
+ OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("Booleans", false, 4, 0);
builder.addPersistedProperty("done", RealmFieldType.BOOLEAN, !Property.PRIMARY_KEY, !Property.INDEXED, Property.REQUIRED);
builder.addPersistedProperty("isReady", RealmFieldType.BOOLEAN, !Property.PRIMARY_KEY, !Property.INDEXED, Property.REQUIRED);
builder.addPersistedProperty("mCompleted", RealmFieldType.BOOLEAN, !Property.PRIMARY_KEY, !Property.INDEXED, Property.REQUIRED);
@@ -293,7 +293,7 @@ public static some.test.Booleans createUsingJsonStream(Realm realm, JsonReader r
return realm.copyToRealm(obj);
}
- private static some_test_BooleansRealmProxy newProxyInstance(BaseRealm realm, Row row) {
+ static some_test_BooleansRealmProxy newProxyInstance(BaseRealm realm, Row row) {
// Ignore default values to avoid creating unexpected objects from RealmModel/RealmList fields
final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get();
objectContext.set(realm, row, realm.getSchema().getColumnInfo(some.test.Booleans.class), false, Collections.emptyList());
@@ -327,24 +327,24 @@ public static some.test.Booleans copy(Realm realm, BooleansColumnInfo columnInfo
return (some.test.Booleans) cachedRealmObject;
}
- some_test_BooleansRealmProxyInterface realmObjectSource = (some_test_BooleansRealmProxyInterface) newObject;
+ some_test_BooleansRealmProxyInterface unmanagedSource = (some_test_BooleansRealmProxyInterface) newObject;
Table table = realm.getTable(some.test.Booleans.class);
OsObjectBuilder builder = new OsObjectBuilder(table, flags);
// Add all non-"object reference" fields
- builder.addBoolean(columnInfo.doneColKey, realmObjectSource.realmGet$done());
- builder.addBoolean(columnInfo.isReadyColKey, realmObjectSource.realmGet$isReady());
- builder.addBoolean(columnInfo.mCompletedColKey, realmObjectSource.realmGet$mCompleted());
- builder.addBoolean(columnInfo.anotherBooleanColKey, realmObjectSource.realmGet$anotherBoolean());
+ builder.addBoolean(columnInfo.doneColKey, unmanagedSource.realmGet$done());
+ builder.addBoolean(columnInfo.isReadyColKey, unmanagedSource.realmGet$isReady());
+ builder.addBoolean(columnInfo.mCompletedColKey, unmanagedSource.realmGet$mCompleted());
+ builder.addBoolean(columnInfo.anotherBooleanColKey, unmanagedSource.realmGet$anotherBoolean());
// Create the underlying object and cache it before setting any object/objectlist references
// This will allow us to break any circular dependencies by using the object cache.
Row row = builder.createNewObject();
- io.realm.some_test_BooleansRealmProxy realmObjectCopy = newProxyInstance(realm, row);
- cache.put(newObject, realmObjectCopy);
+ io.realm.some_test_BooleansRealmProxy managedCopy = newProxyInstance(realm, row);
+ cache.put(newObject, managedCopy);
- return realmObjectCopy;
+ return managedCopy;
}
public static long insert(Realm realm, some.test.Booleans object, Map cache) {
diff --git a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_EmbeddedClassSimpleParentRealmProxy.java b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_EmbeddedClassSimpleParentRealmProxy.java
new file mode 100644
index 0000000000..44e2dc1f99
--- /dev/null
+++ b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_EmbeddedClassSimpleParentRealmProxy.java
@@ -0,0 +1,867 @@
+package io.realm;
+
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.util.JsonReader;
+import android.util.JsonToken;
+import io.realm.ImportFlag;
+import io.realm.ProxyUtils;
+import io.realm.exceptions.RealmMigrationNeededException;
+import io.realm.internal.ColumnInfo;
+import io.realm.internal.OsList;
+import io.realm.internal.OsObject;
+import io.realm.internal.OsObjectSchemaInfo;
+import io.realm.internal.OsSchemaInfo;
+import io.realm.internal.Property;
+import io.realm.internal.RealmObjectProxy;
+import io.realm.internal.Row;
+import io.realm.internal.Table;
+import io.realm.internal.android.JsonUtils;
+import io.realm.internal.objectstore.OsObjectBuilder;
+import io.realm.log.RealmLog;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+@SuppressWarnings("all")
+public class some_test_EmbeddedClassSimpleParentRealmProxy extends some.test.EmbeddedClassSimpleParent
+ implements RealmObjectProxy, some_test_EmbeddedClassSimpleParentRealmProxyInterface {
+
+ static final class EmbeddedClassSimpleParentColumnInfo extends ColumnInfo {
+ long idColKey;
+ long childColKey;
+ long childrenColKey;
+
+ EmbeddedClassSimpleParentColumnInfo(OsSchemaInfo schemaInfo) {
+ super(3);
+ OsObjectSchemaInfo objectSchemaInfo = schemaInfo.getObjectSchemaInfo("EmbeddedClassSimpleParent");
+ this.idColKey = addColumnDetails("id", "id", objectSchemaInfo);
+ this.childColKey = addColumnDetails("child", "child", objectSchemaInfo);
+ this.childrenColKey = addColumnDetails("children", "children", objectSchemaInfo);
+ }
+
+ EmbeddedClassSimpleParentColumnInfo(ColumnInfo src, boolean mutable) {
+ super(src, mutable);
+ copy(src, this);
+ }
+
+ @Override
+ protected final ColumnInfo copy(boolean mutable) {
+ return new EmbeddedClassSimpleParentColumnInfo(this, mutable);
+ }
+
+ @Override
+ protected final void copy(ColumnInfo rawSrc, ColumnInfo rawDst) {
+ final EmbeddedClassSimpleParentColumnInfo src = (EmbeddedClassSimpleParentColumnInfo) rawSrc;
+ final EmbeddedClassSimpleParentColumnInfo dst = (EmbeddedClassSimpleParentColumnInfo) rawDst;
+ dst.idColKey = src.idColKey;
+ dst.childColKey = src.childColKey;
+ dst.childrenColKey = src.childrenColKey;
+ }
+ }
+
+ private static final OsObjectSchemaInfo expectedObjectSchemaInfo = createExpectedObjectSchemaInfo();
+
+ private EmbeddedClassSimpleParentColumnInfo columnInfo;
+ private ProxyState proxyState;
+ private RealmList childrenRealmList;
+
+ some_test_EmbeddedClassSimpleParentRealmProxy() {
+ proxyState.setConstructionFinished();
+ }
+
+ @Override
+ public void realm$injectObjectContext() {
+ if (this.proxyState != null) {
+ return;
+ }
+ final BaseRealm.RealmObjectContext context = BaseRealm.objectContext.get();
+ this.columnInfo = (EmbeddedClassSimpleParentColumnInfo) context.getColumnInfo();
+ this.proxyState = new ProxyState(this);
+ proxyState.setRealm$realm(context.getRealm());
+ proxyState.setRow$realm(context.getRow());
+ proxyState.setAcceptDefaultValue$realm(context.getAcceptDefaultValue());
+ proxyState.setExcludeFields$realm(context.getExcludeFields());
+ }
+
+ @Override
+ @SuppressWarnings("cast")
+ public String realmGet$id() {
+ proxyState.getRealm$realm().checkIfValid();
+ return (java.lang.String) proxyState.getRow$realm().getString(columnInfo.idColKey);
+ }
+
+ @Override
+ public void realmSet$id(String value) {
+ if (proxyState.isUnderConstruction()) {
+ // default value of the primary key is always ignored.
+ return;
+ }
+
+ proxyState.getRealm$realm().checkIfValid();
+ throw new io.realm.exceptions.RealmException("Primary key field 'id' cannot be changed after object was created.");
+ }
+
+ @Override
+ public some.test.EmbeddedClass realmGet$child() {
+ proxyState.getRealm$realm().checkIfValid();
+ if (proxyState.getRow$realm().isNullLink(columnInfo.childColKey)) {
+ return null;
+ }
+ return proxyState.getRealm$realm().get(some.test.EmbeddedClass.class, proxyState.getRow$realm().getLink(columnInfo.childColKey), false, Collections.emptyList());
+ }
+
+ @Override
+ public void realmSet$child(some.test.EmbeddedClass value) {
+ Realm realm = (Realm) proxyState.getRealm$realm();
+ if (proxyState.isUnderConstruction()) {
+ if (!proxyState.getAcceptDefaultValue$realm()) {
+ return;
+ }
+ if (proxyState.getExcludeFields$realm().contains("child")) {
+ return;
+ }
+ if (value != null && !RealmObject.isManaged(value)) {
+ some.test.EmbeddedClass proxyObject = realm.createEmbeddedObject(some.test.EmbeddedClass.class, this, "child");
+ some_test_EmbeddedClassRealmProxy.updateEmbeddedObject(realm, value, proxyObject, new HashMap(), Collections.EMPTY_SET);
+ value = proxyObject;
+ }
+ final Row row = proxyState.getRow$realm();
+ if (value == null) {
+ // Table#nullifyLink() does not support default value. Just using Row.
+ row.nullifyLink(columnInfo.childColKey);
+ return;
+ }
+ proxyState.checkValidObject(value);
+ row.getTable().setLink(columnInfo.childColKey, row.getObjectKey(), ((RealmObjectProxy) value).realmGet$proxyState().getRow$realm().getObjectKey(), true);
+ return;
+ }
+
+ proxyState.getRealm$realm().checkIfValid();
+ if (value == null) {
+ proxyState.getRow$realm().nullifyLink(columnInfo.childColKey);
+ return;
+ }
+ if (RealmObject.isManaged(value)) {
+ proxyState.checkValidObject(value);
+ }
+ some.test.EmbeddedClass proxyObject = realm.createEmbeddedObject(some.test.EmbeddedClass.class, this, "child");
+ some_test_EmbeddedClassRealmProxy.updateEmbeddedObject(realm, value, proxyObject, new HashMap(), Collections.EMPTY_SET);
+ }
+
+ @Override
+ public RealmList realmGet$children() {
+ proxyState.getRealm$realm().checkIfValid();
+ // use the cached value if available
+ if (childrenRealmList != null) {
+ return childrenRealmList;
+ } else {
+ OsList osList = proxyState.getRow$realm().getModelList(columnInfo.childrenColKey);
+ childrenRealmList = new RealmList(some.test.EmbeddedClass.class, osList, proxyState.getRealm$realm());
+ return childrenRealmList;
+ }
+ }
+
+ @Override
+ public void realmSet$children(RealmList value) {
+ if (proxyState.isUnderConstruction()) {
+ if (!proxyState.getAcceptDefaultValue$realm()) {
+ return;
+ }
+ if (proxyState.getExcludeFields$realm().contains("children")) {
+ return;
+ }
+ // if the list contains unmanaged RealmObjects, convert them to managed.
+ if (value != null && !value.isManaged()) {
+ final Realm realm = (Realm) proxyState.getRealm$realm();
+ final RealmList original = value;
+ value = new RealmList();
+ for (some.test.EmbeddedClass item : original) {
+ if (item == null || RealmObject.isManaged(item)) {
+ value.add(item);
+ } else {
+ value.add(realm.copyToRealm(item));
+ }
+ }
+ }
+ }
+
+ proxyState.getRealm$realm().checkIfValid();
+ OsList osList = proxyState.getRow$realm().getModelList(columnInfo.childrenColKey);
+ // For lists of equal lengths, we need to set each element directly as clearing the receiver list can be wrong if the input and target list are the same.
+ if (value != null && value.size() == osList.size()) {
+ int objects = value.size();
+ for (int i = 0; i < objects; i++) {
+ some.test.EmbeddedClass linkedObject = value.get(i);
+ proxyState.checkValidObject(linkedObject);
+ osList.setRow(i, ((RealmObjectProxy) linkedObject).realmGet$proxyState().getRow$realm().getObjectKey());
+ }
+ } else {
+ osList.removeAll();
+ if (value == null) {
+ return;
+ }
+ int objects = value.size();
+ for (int i = 0; i < objects; i++) {
+ some.test.EmbeddedClass linkedObject = value.get(i);
+ proxyState.checkValidObject(linkedObject);
+ osList.addRow(((RealmObjectProxy) linkedObject).realmGet$proxyState().getRow$realm().getObjectKey());
+ }
+ }
+ }
+
+ private static OsObjectSchemaInfo createExpectedObjectSchemaInfo() {
+ OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("EmbeddedClassSimpleParent", false, 3, 0);
+ builder.addPersistedProperty("id", RealmFieldType.STRING, Property.PRIMARY_KEY, !Property.INDEXED, !Property.REQUIRED);
+ builder.addPersistedLinkProperty("child", RealmFieldType.OBJECT, "EmbeddedClass");
+ builder.addPersistedLinkProperty("children", RealmFieldType.LIST, "EmbeddedClass");
+ return builder.build();
+ }
+
+ public static OsObjectSchemaInfo getExpectedObjectSchemaInfo() {
+ return expectedObjectSchemaInfo;
+ }
+
+ public static EmbeddedClassSimpleParentColumnInfo createColumnInfo(OsSchemaInfo schemaInfo) {
+ return new EmbeddedClassSimpleParentColumnInfo(schemaInfo);
+ }
+
+ public static String getSimpleClassName() {
+ return "EmbeddedClassSimpleParent";
+ }
+
+ public static final class ClassNameHelper {
+ public static final String INTERNAL_CLASS_NAME = "EmbeddedClassSimpleParent";
+ }
+
+ @SuppressWarnings("cast")
+ public static some.test.EmbeddedClassSimpleParent createOrUpdateUsingJsonObject(Realm realm, JSONObject json, boolean update)
+ throws JSONException {
+ final List excludeFields = new ArrayList(2);
+ some.test.EmbeddedClassSimpleParent obj = null;
+ if (update) {
+ Table table = realm.getTable(some.test.EmbeddedClassSimpleParent.class);
+ EmbeddedClassSimpleParentColumnInfo columnInfo = (EmbeddedClassSimpleParentColumnInfo) realm.getSchema().getColumnInfo(some.test.EmbeddedClassSimpleParent.class);
+ long pkColumnKey = columnInfo.idColKey;
+ long objKey = Table.NO_MATCH;
+ if (json.isNull("id")) {
+ objKey = table.findFirstNull(pkColumnKey);
+ } else {
+ objKey = table.findFirstString(pkColumnKey, json.getString("id"));
+ }
+ if (objKey != Table.NO_MATCH) {
+ final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get();
+ try {
+ objectContext.set(realm, table.getUncheckedRow(objKey), realm.getSchema().getColumnInfo(some.test.EmbeddedClassSimpleParent.class), false, Collections. emptyList());
+ obj = new io.realm.some_test_EmbeddedClassSimpleParentRealmProxy();
+ } finally {
+ objectContext.clear();
+ }
+ }
+ }
+ if (obj == null) {
+ if (json.has("child")) {
+ excludeFields.add("child");
+ }
+ if (json.has("children")) {
+ excludeFields.add("children");
+ }
+ if (json.has("id")) {
+ if (json.isNull("id")) {
+ obj = (io.realm.some_test_EmbeddedClassSimpleParentRealmProxy) realm.createObjectInternal(some.test.EmbeddedClassSimpleParent.class, null, true, excludeFields);
+ } else {
+ obj = (io.realm.some_test_EmbeddedClassSimpleParentRealmProxy) realm.createObjectInternal(some.test.EmbeddedClassSimpleParent.class, json.getString("id"), true, excludeFields);
+ }
+ } else {
+ throw new IllegalArgumentException("JSON object doesn't have the primary key field 'id'.");
+ }
+ }
+
+ final some_test_EmbeddedClassSimpleParentRealmProxyInterface objProxy = (some_test_EmbeddedClassSimpleParentRealmProxyInterface) obj;
+ if (json.has("child")) {
+ if (json.isNull("child")) {
+ objProxy.realmSet$child(null);
+ } else {
+ some.test.EmbeddedClass childObj = some_test_EmbeddedClassRealmProxy.createOrUpdateUsingJsonObject(realm, json.getJSONObject("child"), update);
+ objProxy.realmSet$child(childObj);
+ }
+ }
+ if (json.has("children")) {
+ if (json.isNull("children")) {
+ objProxy.realmSet$children(null);
+ } else {
+ objProxy.realmGet$children().clear();
+ JSONArray array = json.getJSONArray("children");
+ for (int i = 0; i < array.length(); i++) {
+ some.test.EmbeddedClass item = some_test_EmbeddedClassRealmProxy.createOrUpdateUsingJsonObject(realm, array.getJSONObject(i), update);
+ objProxy.realmGet$children().add(item);
+ }
+ }
+ }
+ return obj;
+ }
+
+ @SuppressWarnings("cast")
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public static some.test.EmbeddedClassSimpleParent createUsingJsonStream(Realm realm, JsonReader reader)
+ throws IOException {
+ boolean jsonHasPrimaryKey = false;
+ final some.test.EmbeddedClassSimpleParent obj = new some.test.EmbeddedClassSimpleParent();
+ final some_test_EmbeddedClassSimpleParentRealmProxyInterface objProxy = (some_test_EmbeddedClassSimpleParentRealmProxyInterface) obj;
+ reader.beginObject();
+ while (reader.hasNext()) {
+ String name = reader.nextName();
+ if (false) {
+ } else if (name.equals("id")) {
+ if (reader.peek() != JsonToken.NULL) {
+ objProxy.realmSet$id((String) reader.nextString());
+ } else {
+ reader.skipValue();
+ objProxy.realmSet$id(null);
+ }
+ jsonHasPrimaryKey = true;
+ } else if (name.equals("child")) {
+ if (reader.peek() == JsonToken.NULL) {
+ reader.skipValue();
+ objProxy.realmSet$child(null);
+ } else {
+ some.test.EmbeddedClass childObj = some_test_EmbeddedClassRealmProxy.createUsingJsonStream(realm, reader);
+ objProxy.realmSet$child(childObj);
+ }
+ } else if (name.equals("children")) {
+ if (reader.peek() == JsonToken.NULL) {
+ reader.skipValue();
+ objProxy.realmSet$children(null);
+ } else {
+ objProxy.realmSet$children(new RealmList());
+ reader.beginArray();
+ while (reader.hasNext()) {
+ some.test.EmbeddedClass item = some_test_EmbeddedClassRealmProxy.createUsingJsonStream(realm, reader);
+ objProxy.realmGet$children().add(item);
+ }
+ reader.endArray();
+ }
+ } else {
+ reader.skipValue();
+ }
+ }
+ reader.endObject();
+ if (!jsonHasPrimaryKey) {
+ throw new IllegalArgumentException("JSON object doesn't have the primary key field 'id'.");
+ }
+ return realm.copyToRealm(obj);
+ }
+
+ static some_test_EmbeddedClassSimpleParentRealmProxy newProxyInstance(BaseRealm realm, Row row) {
+ // Ignore default values to avoid creating unexpected objects from RealmModel/RealmList fields
+ final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get();
+ objectContext.set(realm, row, realm.getSchema().getColumnInfo(some.test.EmbeddedClassSimpleParent.class), false, Collections.emptyList());
+ io.realm.some_test_EmbeddedClassSimpleParentRealmProxy obj = new io.realm.some_test_EmbeddedClassSimpleParentRealmProxy();
+ objectContext.clear();
+ return obj;
+ }
+
+ public static some.test.EmbeddedClassSimpleParent copyOrUpdate(Realm realm, EmbeddedClassSimpleParentColumnInfo columnInfo, some.test.EmbeddedClassSimpleParent object, boolean update, Map cache, Set flags) {
+ if (object instanceof RealmObjectProxy && !RealmObject.isFrozen(object) && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm() != null) {
+ final BaseRealm otherRealm = ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm();
+ if (otherRealm.threadId != realm.threadId) {
+ throw new IllegalArgumentException("Objects which belong to Realm instances in other threads cannot be copied into this Realm instance.");
+ }
+ if (otherRealm.getPath().equals(realm.getPath())) {
+ return object;
+ }
+ }
+ final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get();
+ RealmObjectProxy cachedRealmObject = cache.get(object);
+ if (cachedRealmObject != null) {
+ return (some.test.EmbeddedClassSimpleParent) cachedRealmObject;
+ }
+
+ some.test.EmbeddedClassSimpleParent realmObject = null;
+ boolean canUpdate = update;
+ if (canUpdate) {
+ Table table = realm.getTable(some.test.EmbeddedClassSimpleParent.class);
+ long pkColumnKey = columnInfo.idColKey;
+ String value = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$id();
+ long objKey = Table.NO_MATCH;
+ if (value == null) {
+ objKey = table.findFirstNull(pkColumnKey);
+ } else {
+ objKey = table.findFirstString(pkColumnKey, value);
+ }
+ if (objKey == Table.NO_MATCH) {
+ canUpdate = false;
+ } else {
+ try {
+ objectContext.set(realm, table.getUncheckedRow(objKey), columnInfo, false, Collections. emptyList());
+ realmObject = new io.realm.some_test_EmbeddedClassSimpleParentRealmProxy();
+ cache.put(object, (RealmObjectProxy) realmObject);
+ } finally {
+ objectContext.clear();
+ }
+ }
+ }
+
+ return (canUpdate) ? update(realm, columnInfo, realmObject, object, cache, flags) : copy(realm, columnInfo, object, update, cache, flags);
+ }
+
+ public static some.test.EmbeddedClassSimpleParent copy(Realm realm, EmbeddedClassSimpleParentColumnInfo columnInfo, some.test.EmbeddedClassSimpleParent newObject, boolean update, Map cache, Set flags) {
+ RealmObjectProxy cachedRealmObject = cache.get(newObject);
+ if (cachedRealmObject != null) {
+ return (some.test.EmbeddedClassSimpleParent) cachedRealmObject;
+ }
+
+ some_test_EmbeddedClassSimpleParentRealmProxyInterface unmanagedSource = (some_test_EmbeddedClassSimpleParentRealmProxyInterface) newObject;
+
+ Table table = realm.getTable(some.test.EmbeddedClassSimpleParent.class);
+ OsObjectBuilder builder = new OsObjectBuilder(table, flags);
+
+ // Add all non-"object reference" fields
+ builder.addString(columnInfo.idColKey, unmanagedSource.realmGet$id());
+
+ // Create the underlying object and cache it before setting any object/objectlist references
+ // This will allow us to break any circular dependencies by using the object cache.
+ Row row = builder.createNewObject();
+ io.realm.some_test_EmbeddedClassSimpleParentRealmProxy managedCopy = newProxyInstance(realm, row);
+ cache.put(newObject, managedCopy);
+
+ // Finally add all fields that reference other Realm Objects, either directly or through a list
+ some.test.EmbeddedClass childObj = unmanagedSource.realmGet$child();
+ if (childObj == null) {
+ managedCopy.realmSet$child(null);
+ } else {
+ some.test.EmbeddedClass cachechild = (some.test.EmbeddedClass) cache.get(childObj);
+ if (cachechild != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: cachechild.toString()");
+ } else {
+ long objKey = ((RealmObjectProxy) managedCopy).realmGet$proxyState().getRow$realm().createEmbeddedObject(columnInfo.childColKey);
+ Row linkedObjectRow = realm.getTable(some.test.EmbeddedClass.class).getUncheckedRow(objKey);
+ some.test.EmbeddedClass linkedObject = some_test_EmbeddedClassRealmProxy.newProxyInstance(realm, linkedObjectRow);
+ cache.put(childObj, (RealmObjectProxy) linkedObject);
+ some_test_EmbeddedClassRealmProxy.updateEmbeddedObject(realm, childObj, linkedObject, cache, flags);
+ }
+ }
+
+ RealmList childrenUnmanagedList = unmanagedSource.realmGet$children();
+ if (childrenUnmanagedList != null) {
+ RealmList childrenManagedList = managedCopy.realmGet$children();
+ childrenManagedList.clear();
+ for (int i = 0; i < childrenUnmanagedList.size(); i++) {
+ some.test.EmbeddedClass childrenUnmanagedItem = childrenUnmanagedList.get(i);
+ some.test.EmbeddedClass cachechildren = (some.test.EmbeddedClass) cache.get(childrenUnmanagedItem);
+ if (cachechildren != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: cachechildren.toString()");
+ } else {
+ long objKey = childrenManagedList.getOsList().createAndAddEmbeddedObject();
+ Row linkedObjectRow = realm.getTable(some.test.EmbeddedClass.class).getUncheckedRow(objKey);
+ some.test.EmbeddedClass linkedObject = some_test_EmbeddedClassRealmProxy.newProxyInstance(realm, linkedObjectRow);
+ cache.put(childrenUnmanagedItem, (RealmObjectProxy) linkedObject);
+ some_test_EmbeddedClassRealmProxy.updateEmbeddedObject(realm, childrenUnmanagedItem, linkedObject, new HashMap(), Collections.EMPTY_SET);
+ }
+ }
+ }
+
+ return managedCopy;
+ }
+
+ public static long insert(Realm realm, some.test.EmbeddedClassSimpleParent object, Map cache) {
+ if (object instanceof RealmObjectProxy && !RealmObject.isFrozen(object) && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm() != null && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm().getPath().equals(realm.getPath())) {
+ return ((RealmObjectProxy) object).realmGet$proxyState().getRow$realm().getObjectKey();
+ }
+ Table table = realm.getTable(some.test.EmbeddedClassSimpleParent.class);
+ long tableNativePtr = table.getNativePtr();
+ EmbeddedClassSimpleParentColumnInfo columnInfo = (EmbeddedClassSimpleParentColumnInfo) realm.getSchema().getColumnInfo(some.test.EmbeddedClassSimpleParent.class);
+ long pkColumnKey = columnInfo.idColKey;
+ String primaryKeyValue = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$id();
+ long objKey = Table.NO_MATCH;
+ if (primaryKeyValue == null) {
+ objKey = Table.nativeFindFirstNull(tableNativePtr, pkColumnKey);
+ } else {
+ objKey = Table.nativeFindFirstString(tableNativePtr, pkColumnKey, primaryKeyValue);
+ }
+ if (objKey == Table.NO_MATCH) {
+ objKey = OsObject.createRowWithPrimaryKey(table, pkColumnKey, primaryKeyValue);
+ } else {
+ Table.throwDuplicatePrimaryKeyException(primaryKeyValue);
+ }
+ cache.put(object, objKey);
+
+ some.test.EmbeddedClass childObj = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$child();
+ if (childObj != null) {
+ Long cachechild = cache.get(childObj);
+ if (cachechild != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: " + cachechild.toString());
+ } else {
+ cachechild = some_test_EmbeddedClassRealmProxy.insert(realm, table, columnInfo.childColKey, objKey, childObj, cache);
+ }
+ }
+
+ RealmList childrenList = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$children();
+ if (childrenList != null) {
+ OsList childrenOsList = new OsList(table.getUncheckedRow(objKey), columnInfo.childrenColKey);
+ for (some.test.EmbeddedClass childrenItem : childrenList) {
+ Long cacheItemIndexchildren = cache.get(childrenItem);
+ if (cacheItemIndexchildren != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: " + cacheItemIndexchildren.toString());
+ } else {
+ cacheItemIndexchildren = some_test_EmbeddedClassRealmProxy.insert(realm, table, columnInfo.childrenColKey, objKey, childrenItem, cache);
+ }
+ }
+ }
+ return objKey;
+ }
+
+ public static void insert(Realm realm, Iterator extends RealmModel> objects, Map cache) {
+ Table table = realm.getTable(some.test.EmbeddedClassSimpleParent.class);
+ long tableNativePtr = table.getNativePtr();
+ EmbeddedClassSimpleParentColumnInfo columnInfo = (EmbeddedClassSimpleParentColumnInfo) realm.getSchema().getColumnInfo(some.test.EmbeddedClassSimpleParent.class);
+ long pkColumnKey = columnInfo.idColKey;
+ some.test.EmbeddedClassSimpleParent object = null;
+ while (objects.hasNext()) {
+ object = (some.test.EmbeddedClassSimpleParent) objects.next();
+ if (cache.containsKey(object)) {
+ continue;
+ }
+ if (object instanceof RealmObjectProxy && !RealmObject.isFrozen(object) && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm() != null && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm().getPath().equals(realm.getPath())) {
+ cache.put(object, ((RealmObjectProxy) object).realmGet$proxyState().getRow$realm().getObjectKey());
+ continue;
+ }
+ String primaryKeyValue = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$id();
+ long objKey = Table.NO_MATCH;
+ if (primaryKeyValue == null) {
+ objKey = Table.nativeFindFirstNull(tableNativePtr, pkColumnKey);
+ } else {
+ objKey = Table.nativeFindFirstString(tableNativePtr, pkColumnKey, primaryKeyValue);
+ }
+ if (objKey == Table.NO_MATCH) {
+ objKey = OsObject.createRowWithPrimaryKey(table, pkColumnKey, primaryKeyValue);
+ } else {
+ Table.throwDuplicatePrimaryKeyException(primaryKeyValue);
+ }
+ cache.put(object, objKey);
+
+ some.test.EmbeddedClass childObj = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$child();
+ if (childObj != null) {
+ Long cachechild = cache.get(childObj);
+ if (cachechild != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: " + cachechild.toString());
+ } else {
+ cachechild = some_test_EmbeddedClassRealmProxy.insert(realm, table, columnInfo.childColKey, objKey, childObj, cache);
+ }
+ }
+
+ RealmList childrenList = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$children();
+ if (childrenList != null) {
+ OsList childrenOsList = new OsList(table.getUncheckedRow(objKey), columnInfo.childrenColKey);
+ for (some.test.EmbeddedClass childrenItem : childrenList) {
+ Long cacheItemIndexchildren = cache.get(childrenItem);
+ if (cacheItemIndexchildren != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: " + cacheItemIndexchildren.toString());
+ } else {
+ cacheItemIndexchildren = some_test_EmbeddedClassRealmProxy.insert(realm, table, columnInfo.childrenColKey, objKey, childrenItem, cache);
+ }
+ }
+ }
+ }
+ }
+
+ public static long insertOrUpdate(Realm realm, some.test.EmbeddedClassSimpleParent object, Map cache) {
+ if (object instanceof RealmObjectProxy && !RealmObject.isFrozen(object) && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm() != null && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm().getPath().equals(realm.getPath())) {
+ return ((RealmObjectProxy) object).realmGet$proxyState().getRow$realm().getObjectKey();
+ }
+ Table table = realm.getTable(some.test.EmbeddedClassSimpleParent.class);
+ long tableNativePtr = table.getNativePtr();
+ EmbeddedClassSimpleParentColumnInfo columnInfo = (EmbeddedClassSimpleParentColumnInfo) realm.getSchema().getColumnInfo(some.test.EmbeddedClassSimpleParent.class);
+ long pkColumnKey = columnInfo.idColKey;
+ String primaryKeyValue = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$id();
+ long objKey = Table.NO_MATCH;
+ if (primaryKeyValue == null) {
+ objKey = Table.nativeFindFirstNull(tableNativePtr, pkColumnKey);
+ } else {
+ objKey = Table.nativeFindFirstString(tableNativePtr, pkColumnKey, primaryKeyValue);
+ }
+ if (objKey == Table.NO_MATCH) {
+ objKey = OsObject.createRowWithPrimaryKey(table, pkColumnKey, primaryKeyValue);
+ }
+ cache.put(object, objKey);
+
+ some.test.EmbeddedClass childObj = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$child();
+ if (childObj != null) {
+ Long cachechild = cache.get(childObj);
+ if (cachechild != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: " + cachechild.toString());
+ } else {
+ cachechild = some_test_EmbeddedClassRealmProxy.insertOrUpdate(realm, table, columnInfo.childColKey, objKey, childObj, cache);
+ }
+ } else {
+ Table.nativeNullifyLink(tableNativePtr, columnInfo.childColKey, objKey);
+ }
+
+ OsList childrenOsList = new OsList(table.getUncheckedRow(objKey), columnInfo.childrenColKey);
+ RealmList childrenList = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$children();
+ if (childrenList != null && childrenList.size() == childrenOsList.size()) {
+ // For lists of equal lengths, we need to set each element directly as clearing the receiver list can be wrong if the input and target list are the same.
+ int objects = childrenList.size();
+ for (int i = 0; i < objects; i++) {
+ some.test.EmbeddedClass childrenItem = childrenList.get(i);
+ Long cacheItemIndexchildren = cache.get(childrenItem);
+ if (cacheItemIndexchildren != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: " + cacheItemIndexchildren.toString());
+ } else {
+ cacheItemIndexchildren = some_test_EmbeddedClassRealmProxy.insertOrUpdate(realm, table, columnInfo.childrenColKey, objKey, childrenItem, cache);
+ }
+ }
+ } else {
+ childrenOsList.removeAll();
+ if (childrenList != null) {
+ for (some.test.EmbeddedClass childrenItem : childrenList) {
+ Long cacheItemIndexchildren = cache.get(childrenItem);
+ if (cacheItemIndexchildren != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: " + cacheItemIndexchildren.toString());
+ } else {
+ cacheItemIndexchildren = some_test_EmbeddedClassRealmProxy.insertOrUpdate(realm, table, columnInfo.childrenColKey, objKey, childrenItem, cache);
+ }
+ }
+ }
+ }
+
+ return objKey;
+ }
+
+ public static void insertOrUpdate(Realm realm, Iterator extends RealmModel> objects, Map cache) {
+ Table table = realm.getTable(some.test.EmbeddedClassSimpleParent.class);
+ long tableNativePtr = table.getNativePtr();
+ EmbeddedClassSimpleParentColumnInfo columnInfo = (EmbeddedClassSimpleParentColumnInfo) realm.getSchema().getColumnInfo(some.test.EmbeddedClassSimpleParent.class);
+ long pkColumnKey = columnInfo.idColKey;
+ some.test.EmbeddedClassSimpleParent object = null;
+ while (objects.hasNext()) {
+ object = (some.test.EmbeddedClassSimpleParent) objects.next();
+ if (cache.containsKey(object)) {
+ continue;
+ }
+ if (object instanceof RealmObjectProxy && !RealmObject.isFrozen(object) && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm() != null && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm().getPath().equals(realm.getPath())) {
+ cache.put(object, ((RealmObjectProxy) object).realmGet$proxyState().getRow$realm().getObjectKey());
+ continue;
+ }
+ String primaryKeyValue = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$id();
+ long objKey = Table.NO_MATCH;
+ if (primaryKeyValue == null) {
+ objKey = Table.nativeFindFirstNull(tableNativePtr, pkColumnKey);
+ } else {
+ objKey = Table.nativeFindFirstString(tableNativePtr, pkColumnKey, primaryKeyValue);
+ }
+ if (objKey == Table.NO_MATCH) {
+ objKey = OsObject.createRowWithPrimaryKey(table, pkColumnKey, primaryKeyValue);
+ }
+ cache.put(object, objKey);
+
+ some.test.EmbeddedClass childObj = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$child();
+ if (childObj != null) {
+ Long cachechild = cache.get(childObj);
+ if (cachechild != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: " + cachechild.toString());
+ } else {
+ cachechild = some_test_EmbeddedClassRealmProxy.insertOrUpdate(realm, table, columnInfo.childColKey, objKey, childObj, cache);
+ }
+ } else {
+ Table.nativeNullifyLink(tableNativePtr, columnInfo.childColKey, objKey);
+ }
+
+ OsList childrenOsList = new OsList(table.getUncheckedRow(objKey), columnInfo.childrenColKey);
+ RealmList childrenList = ((some_test_EmbeddedClassSimpleParentRealmProxyInterface) object).realmGet$children();
+ if (childrenList != null && childrenList.size() == childrenOsList.size()) {
+ // For lists of equal lengths, we need to set each element directly as clearing the receiver list can be wrong if the input and target list are the same.
+ int objectCount = childrenList.size();
+ for (int i = 0; i < objectCount; i++) {
+ some.test.EmbeddedClass childrenItem = childrenList.get(i);
+ Long cacheItemIndexchildren = cache.get(childrenItem);
+ if (cacheItemIndexchildren != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: " + cacheItemIndexchildren.toString());
+ } else {
+ cacheItemIndexchildren = some_test_EmbeddedClassRealmProxy.insertOrUpdate(realm, table, columnInfo.childrenColKey, objKey, childrenItem, cache);
+ }
+ }
+ } else {
+ childrenOsList.removeAll();
+ if (childrenList != null) {
+ for (some.test.EmbeddedClass childrenItem : childrenList) {
+ Long cacheItemIndexchildren = cache.get(childrenItem);
+ if (cacheItemIndexchildren != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: " + cacheItemIndexchildren.toString());
+ } else {
+ cacheItemIndexchildren = some_test_EmbeddedClassRealmProxy.insertOrUpdate(realm, table, columnInfo.childrenColKey, objKey, childrenItem, cache);
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ public static some.test.EmbeddedClassSimpleParent createDetachedCopy(some.test.EmbeddedClassSimpleParent realmObject, int currentDepth, int maxDepth, Map> cache) {
+ if (currentDepth > maxDepth || realmObject == null) {
+ return null;
+ }
+ CacheData cachedObject = cache.get(realmObject);
+ some.test.EmbeddedClassSimpleParent unmanagedObject;
+ if (cachedObject == null) {
+ unmanagedObject = new some.test.EmbeddedClassSimpleParent();
+ cache.put(realmObject, new RealmObjectProxy.CacheData(currentDepth, unmanagedObject));
+ } else {
+ // Reuse cached object or recreate it because it was encountered at a lower depth.
+ if (currentDepth >= cachedObject.minDepth) {
+ return (some.test.EmbeddedClassSimpleParent) cachedObject.object;
+ }
+ unmanagedObject = (some.test.EmbeddedClassSimpleParent) cachedObject.object;
+ cachedObject.minDepth = currentDepth;
+ }
+ some_test_EmbeddedClassSimpleParentRealmProxyInterface unmanagedCopy = (some_test_EmbeddedClassSimpleParentRealmProxyInterface) unmanagedObject;
+ some_test_EmbeddedClassSimpleParentRealmProxyInterface realmSource = (some_test_EmbeddedClassSimpleParentRealmProxyInterface) realmObject;
+ unmanagedCopy.realmSet$id(realmSource.realmGet$id());
+
+ // Deep copy of child
+ unmanagedCopy.realmSet$child(some_test_EmbeddedClassRealmProxy.createDetachedCopy(realmSource.realmGet$child(), currentDepth + 1, maxDepth, cache));
+
+ // Deep copy of children
+ if (currentDepth == maxDepth) {
+ unmanagedCopy.realmSet$children(null);
+ } else {
+ RealmList managedchildrenList = realmSource.realmGet$children();
+ RealmList unmanagedchildrenList = new RealmList();
+ unmanagedCopy.realmSet$children(unmanagedchildrenList);
+ int nextDepth = currentDepth + 1;
+ int size = managedchildrenList.size();
+ for (int i = 0; i < size; i++) {
+ some.test.EmbeddedClass item = some_test_EmbeddedClassRealmProxy.createDetachedCopy(managedchildrenList.get(i), nextDepth, maxDepth, cache);
+ unmanagedchildrenList.add(item);
+ }
+ }
+
+ return unmanagedObject;
+ }
+
+ static some.test.EmbeddedClassSimpleParent update(Realm realm, EmbeddedClassSimpleParentColumnInfo columnInfo, some.test.EmbeddedClassSimpleParent realmObject, some.test.EmbeddedClassSimpleParent newObject, Map cache, Set flags) {
+ some_test_EmbeddedClassSimpleParentRealmProxyInterface realmObjectTarget = (some_test_EmbeddedClassSimpleParentRealmProxyInterface) realmObject;
+ some_test_EmbeddedClassSimpleParentRealmProxyInterface realmObjectSource = (some_test_EmbeddedClassSimpleParentRealmProxyInterface) newObject;
+ Table table = realm.getTable(some.test.EmbeddedClassSimpleParent.class);
+ OsObjectBuilder builder = new OsObjectBuilder(table, flags);
+ builder.addString(columnInfo.idColKey, realmObjectSource.realmGet$id());
+
+ some.test.EmbeddedClass childObj = realmObjectSource.realmGet$child();
+ if (childObj == null) {
+ builder.addNull(columnInfo.childColKey);
+ } else {
+ // Embedded objects are created directly instead of using the builder.
+ some.test.EmbeddedClass cachechild = (some.test.EmbeddedClass) cache.get(childObj);
+ if (cachechild != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: cachechild.toString()");
+ }
+
+ long objKey = ((RealmObjectProxy) realmObject).realmGet$proxyState().getRow$realm().createEmbeddedObject(columnInfo.childColKey);
+ Row row = realm.getTable(some.test.EmbeddedClass.class).getUncheckedRow(objKey);
+ some.test.EmbeddedClass proxyObject = some_test_EmbeddedClassRealmProxy.newProxyInstance(realm, row);
+ cache.put(childObj, (RealmObjectProxy) proxyObject);
+ some_test_EmbeddedClassRealmProxy.updateEmbeddedObject(realm, childObj, proxyObject, cache, flags);
+ }
+
+ RealmList childrenUnmanagedList = realmObjectSource.realmGet$children();
+ if (childrenUnmanagedList != null) {
+ RealmList childrenManagedCopy = new RealmList();
+ for (int i = 0; i < childrenUnmanagedList.size(); i++) {
+ some.test.EmbeddedClass childrenUnmanagedItem = childrenUnmanagedList.get(i);
+ some.test.EmbeddedClass cachechildren = (some.test.EmbeddedClass) cache.get(childrenUnmanagedItem);
+ if (cachechildren != null) {
+ throw new IllegalArgumentException("Embedded objects can only have one parent pointing to them. This object was already copied, so another object is pointing to it: cachechildren.toString()");
+ } else {
+ long objKey = realmObjectTarget.realmGet$children().getOsList().createAndAddEmbeddedObject();
+ Row row = realm.getTable(some.test.EmbeddedClass.class).getUncheckedRow(objKey);
+ some.test.EmbeddedClass proxyObject = some_test_EmbeddedClassRealmProxy.newProxyInstance(realm, row);
+ cache.put(childrenUnmanagedItem, (RealmObjectProxy) proxyObject);
+ childrenManagedCopy.add(proxyObject);
+ some_test_EmbeddedClassRealmProxy.updateEmbeddedObject(realm, childrenUnmanagedItem, proxyObject, new HashMap(), Collections.EMPTY_SET);
+ }
+ }
+ builder.addObjectList(columnInfo.childrenColKey, childrenManagedCopy);
+ } else {
+ builder.addObjectList(columnInfo.childrenColKey, new RealmList());
+ }
+
+ builder.updateExistingTopLevelObject();
+ return realmObject;
+ }
+
+ @Override
+ @SuppressWarnings("ArrayToString")
+ public String toString() {
+ if (!RealmObject.isValid(this)) {
+ return "Invalid object";
+ }
+ StringBuilder stringBuilder = new StringBuilder("EmbeddedClassSimpleParent = proxy[");
+ stringBuilder.append("{id:");
+ stringBuilder.append(realmGet$id() != null ? realmGet$id() : "null");
+ stringBuilder.append("}");
+ stringBuilder.append(",");
+ stringBuilder.append("{child:");
+ stringBuilder.append(realmGet$child() != null ? "EmbeddedClass" : "null");
+ stringBuilder.append("}");
+ stringBuilder.append(",");
+ stringBuilder.append("{children:");
+ stringBuilder.append("RealmList[").append(realmGet$children().size()).append("]");
+ stringBuilder.append("}");
+ stringBuilder.append("]");
+ return stringBuilder.toString();
+ }
+
+ @Override
+ public ProxyState> realmGet$proxyState() {
+ return proxyState;
+ }
+
+ @Override
+ public int hashCode() {
+ String realmName = proxyState.getRealm$realm().getPath();
+ String tableName = proxyState.getRow$realm().getTable().getName();
+ long objKey = proxyState.getRow$realm().getObjectKey();
+
+ int result = 17;
+ result = 31 * result + ((realmName != null) ? realmName.hashCode() : 0);
+ result = 31 * result + ((tableName != null) ? tableName.hashCode() : 0);
+ result = 31 * result + (int) (objKey ^ (objKey >>> 32));
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ some_test_EmbeddedClassSimpleParentRealmProxy aEmbeddedClassSimpleParent = (some_test_EmbeddedClassSimpleParentRealmProxy)o;
+
+ BaseRealm realm = proxyState.getRealm$realm();
+ BaseRealm otherRealm = aEmbeddedClassSimpleParent.proxyState.getRealm$realm();
+ String path = realm.getPath();
+ String otherPath = otherRealm.getPath();
+ if (path != null ? !path.equals(otherPath) : otherPath != null) return false;
+ if (realm.isFrozen() != otherRealm.isFrozen()) return false;
+ if (!realm.sharedRealm.getVersionID().equals(otherRealm.sharedRealm.getVersionID())) {
+ return false;
+ }
+
+ String tableName = proxyState.getRow$realm().getTable().getName();
+ String otherTableName = aEmbeddedClassSimpleParent.proxyState.getRow$realm().getTable().getName();
+ if (tableName != null ? !tableName.equals(otherTableName) : otherTableName != null) return false;
+
+ if (proxyState.getRow$realm().getObjectKey() != aEmbeddedClassSimpleParent.proxyState.getRow$realm().getObjectKey()) return false;
+
+ return true;
+ }
+}
diff --git a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NamePolicyMixedClassSettingsRealmProxy.java b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NamePolicyMixedClassSettingsRealmProxy.java
index b879ae03d8..f7cbbd3b2e 100644
--- a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NamePolicyMixedClassSettingsRealmProxy.java
+++ b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NamePolicyMixedClassSettingsRealmProxy.java
@@ -151,7 +151,7 @@ protected final void copy(ColumnInfo rawSrc, ColumnInfo rawDst) {
}
private static OsObjectSchemaInfo createExpectedObjectSchemaInfo() {
- OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("customName", 2, 0);
+ OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("customName", false, 2, 0);
builder.addPersistedProperty("first_name", RealmFieldType.STRING, !Property.PRIMARY_KEY, !Property.INDEXED, !Property.REQUIRED);
builder.addPersistedProperty("LastName", RealmFieldType.STRING, !Property.PRIMARY_KEY, !Property.INDEXED, !Property.REQUIRED);
return builder.build();
@@ -229,7 +229,7 @@ public static some.test.NamePolicyMixedClassSettings createUsingJsonStream(Realm
return realm.copyToRealm(obj);
}
- private static some_test_NamePolicyMixedClassSettingsRealmProxy newProxyInstance(BaseRealm realm, Row row) {
+ static some_test_NamePolicyMixedClassSettingsRealmProxy newProxyInstance(BaseRealm realm, Row row) {
// Ignore default values to avoid creating unexpected objects from RealmModel/RealmList fields
final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get();
objectContext.set(realm, row, realm.getSchema().getColumnInfo(some.test.NamePolicyMixedClassSettings.class), false, Collections.emptyList());
@@ -263,22 +263,22 @@ public static some.test.NamePolicyMixedClassSettings copy(Realm realm, NamePolic
return (some.test.NamePolicyMixedClassSettings) cachedRealmObject;
}
- some_test_NamePolicyMixedClassSettingsRealmProxyInterface realmObjectSource = (some_test_NamePolicyMixedClassSettingsRealmProxyInterface) newObject;
+ some_test_NamePolicyMixedClassSettingsRealmProxyInterface unmanagedSource = (some_test_NamePolicyMixedClassSettingsRealmProxyInterface) newObject;
Table table = realm.getTable(some.test.NamePolicyMixedClassSettings.class);
OsObjectBuilder builder = new OsObjectBuilder(table, flags);
// Add all non-"object reference" fields
- builder.addString(columnInfo.firstNameColKey, realmObjectSource.realmGet$firstName());
- builder.addString(columnInfo.lastNameColKey, realmObjectSource.realmGet$lastName());
+ builder.addString(columnInfo.firstNameColKey, unmanagedSource.realmGet$firstName());
+ builder.addString(columnInfo.lastNameColKey, unmanagedSource.realmGet$lastName());
// Create the underlying object and cache it before setting any object/objectlist references
// This will allow us to break any circular dependencies by using the object cache.
Row row = builder.createNewObject();
- io.realm.some_test_NamePolicyMixedClassSettingsRealmProxy realmObjectCopy = newProxyInstance(realm, row);
- cache.put(newObject, realmObjectCopy);
+ io.realm.some_test_NamePolicyMixedClassSettingsRealmProxy managedCopy = newProxyInstance(realm, row);
+ cache.put(newObject, managedCopy);
- return realmObjectCopy;
+ return managedCopy;
}
public static long insert(Realm realm, some.test.NamePolicyMixedClassSettings object, Map cache) {
diff --git a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NamePolicyModuleDefaultsRealmProxy.java b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NamePolicyModuleDefaultsRealmProxy.java
index 83bc876281..aef2c2fd5a 100644
--- a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NamePolicyModuleDefaultsRealmProxy.java
+++ b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NamePolicyModuleDefaultsRealmProxy.java
@@ -151,7 +151,7 @@ protected final void copy(ColumnInfo rawSrc, ColumnInfo rawDst) {
}
private static OsObjectSchemaInfo createExpectedObjectSchemaInfo() {
- OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("NamePolicyModuleDefaults", 2, 0);
+ OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("NamePolicyModuleDefaults", false, 2, 0);
builder.addPersistedProperty("FirstName", RealmFieldType.STRING, !Property.PRIMARY_KEY, !Property.INDEXED, !Property.REQUIRED);
builder.addPersistedProperty("LastName", RealmFieldType.STRING, !Property.PRIMARY_KEY, !Property.INDEXED, !Property.REQUIRED);
return builder.build();
@@ -229,7 +229,7 @@ public static some.test.NamePolicyModuleDefaults createUsingJsonStream(Realm rea
return realm.copyToRealm(obj);
}
- private static some_test_NamePolicyModuleDefaultsRealmProxy newProxyInstance(BaseRealm realm, Row row) {
+ static some_test_NamePolicyModuleDefaultsRealmProxy newProxyInstance(BaseRealm realm, Row row) {
// Ignore default values to avoid creating unexpected objects from RealmModel/RealmList fields
final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get();
objectContext.set(realm, row, realm.getSchema().getColumnInfo(some.test.NamePolicyModuleDefaults.class), false, Collections.emptyList());
@@ -263,22 +263,22 @@ public static some.test.NamePolicyModuleDefaults copy(Realm realm, NamePolicyMod
return (some.test.NamePolicyModuleDefaults) cachedRealmObject;
}
- some_test_NamePolicyModuleDefaultsRealmProxyInterface realmObjectSource = (some_test_NamePolicyModuleDefaultsRealmProxyInterface) newObject;
+ some_test_NamePolicyModuleDefaultsRealmProxyInterface unmanagedSource = (some_test_NamePolicyModuleDefaultsRealmProxyInterface) newObject;
Table table = realm.getTable(some.test.NamePolicyModuleDefaults.class);
OsObjectBuilder builder = new OsObjectBuilder(table, flags);
// Add all non-"object reference" fields
- builder.addString(columnInfo.firstNameColKey, realmObjectSource.realmGet$firstName());
- builder.addString(columnInfo.lastNameColKey, realmObjectSource.realmGet$lastName());
+ builder.addString(columnInfo.firstNameColKey, unmanagedSource.realmGet$firstName());
+ builder.addString(columnInfo.lastNameColKey, unmanagedSource.realmGet$lastName());
// Create the underlying object and cache it before setting any object/objectlist references
// This will allow us to break any circular dependencies by using the object cache.
Row row = builder.createNewObject();
- io.realm.some_test_NamePolicyModuleDefaultsRealmProxy realmObjectCopy = newProxyInstance(realm, row);
- cache.put(newObject, realmObjectCopy);
+ io.realm.some_test_NamePolicyModuleDefaultsRealmProxy managedCopy = newProxyInstance(realm, row);
+ cache.put(newObject, managedCopy);
- return realmObjectCopy;
+ return managedCopy;
}
public static long insert(Realm realm, some.test.NamePolicyModuleDefaults object, Map cache) {
diff --git a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NullTypesRealmProxy.java b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NullTypesRealmProxy.java
index 20327c69ca..396c74efbe 100644
--- a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NullTypesRealmProxy.java
+++ b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_NullTypesRealmProxy.java
@@ -992,6 +992,7 @@ protected final void copy(ColumnInfo rawSrc, ColumnInfo rawDst) {
@Override
public void realmSet$fieldObjectNull(some.test.NullTypes value) {
+ Realm realm = (Realm) proxyState.getRealm$realm();
if (proxyState.isUnderConstruction()) {
if (!proxyState.getAcceptDefaultValue$realm()) {
return;
@@ -1000,7 +1001,7 @@ protected final void copy(ColumnInfo rawSrc, ColumnInfo rawDst) {
return;
}
if (value != null && !RealmObject.isManaged(value)) {
- value = ((Realm) proxyState.getRealm$realm()).copyToRealm(value);
+ value = realm.copyToRealm(value);
}
final Row row = proxyState.getRow$realm();
if (value == null) {
@@ -1959,7 +1960,7 @@ protected final void copy(ColumnInfo rawSrc, ColumnInfo rawDst) {
}
private static OsObjectSchemaInfo createExpectedObjectSchemaInfo() {
- OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("NullTypes", 49, 0);
+ OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("NullTypes", false, 49, 0);
builder.addPersistedProperty("fieldStringNotNull", RealmFieldType.STRING, !Property.PRIMARY_KEY, !Property.INDEXED, Property.REQUIRED);
builder.addPersistedProperty("fieldStringNull", RealmFieldType.STRING, !Property.PRIMARY_KEY, !Property.INDEXED, !Property.REQUIRED);
builder.addPersistedProperty("fieldBooleanNotNull", RealmFieldType.BOOLEAN, !Property.PRIMARY_KEY, !Property.INDEXED, Property.REQUIRED);
@@ -2611,7 +2612,7 @@ public static some.test.NullTypes createUsingJsonStream(Realm realm, JsonReader
return realm.copyToRealm(obj);
}
- private static some_test_NullTypesRealmProxy newProxyInstance(BaseRealm realm, Row row) {
+ static some_test_NullTypesRealmProxy newProxyInstance(BaseRealm realm, Row row) {
// Ignore default values to avoid creating unexpected objects from RealmModel/RealmList fields
final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get();
objectContext.set(realm, row, realm.getSchema().getColumnInfo(some.test.NullTypes.class), false, Collections.emptyList());
@@ -2645,81 +2646,81 @@ public static some.test.NullTypes copy(Realm realm, NullTypesColumnInfo columnIn
return (some.test.NullTypes) cachedRealmObject;
}
- some_test_NullTypesRealmProxyInterface realmObjectSource = (some_test_NullTypesRealmProxyInterface) newObject;
+ some_test_NullTypesRealmProxyInterface unmanagedSource = (some_test_NullTypesRealmProxyInterface) newObject;
Table table = realm.getTable(some.test.NullTypes.class);
OsObjectBuilder builder = new OsObjectBuilder(table, flags);
// Add all non-"object reference" fields
- builder.addString(columnInfo.fieldStringNotNullColKey, realmObjectSource.realmGet$fieldStringNotNull());
- builder.addString(columnInfo.fieldStringNullColKey, realmObjectSource.realmGet$fieldStringNull());
- builder.addBoolean(columnInfo.fieldBooleanNotNullColKey, realmObjectSource.realmGet$fieldBooleanNotNull());
- builder.addBoolean(columnInfo.fieldBooleanNullColKey, realmObjectSource.realmGet$fieldBooleanNull());
- builder.addByteArray(columnInfo.fieldBytesNotNullColKey, realmObjectSource.realmGet$fieldBytesNotNull());
- builder.addByteArray(columnInfo.fieldBytesNullColKey, realmObjectSource.realmGet$fieldBytesNull());
- builder.addInteger(columnInfo.fieldByteNotNullColKey, realmObjectSource.realmGet$fieldByteNotNull());
- builder.addInteger(columnInfo.fieldByteNullColKey, realmObjectSource.realmGet$fieldByteNull());
- builder.addInteger(columnInfo.fieldShortNotNullColKey, realmObjectSource.realmGet$fieldShortNotNull());
- builder.addInteger(columnInfo.fieldShortNullColKey, realmObjectSource.realmGet$fieldShortNull());
- builder.addInteger(columnInfo.fieldIntegerNotNullColKey, realmObjectSource.realmGet$fieldIntegerNotNull());
- builder.addInteger(columnInfo.fieldIntegerNullColKey, realmObjectSource.realmGet$fieldIntegerNull());
- builder.addInteger(columnInfo.fieldLongNotNullColKey, realmObjectSource.realmGet$fieldLongNotNull());
- builder.addInteger(columnInfo.fieldLongNullColKey, realmObjectSource.realmGet$fieldLongNull());
- builder.addFloat(columnInfo.fieldFloatNotNullColKey, realmObjectSource.realmGet$fieldFloatNotNull());
- builder.addFloat(columnInfo.fieldFloatNullColKey, realmObjectSource.realmGet$fieldFloatNull());
- builder.addDouble(columnInfo.fieldDoubleNotNullColKey, realmObjectSource.realmGet$fieldDoubleNotNull());
- builder.addDouble(columnInfo.fieldDoubleNullColKey, realmObjectSource.realmGet$fieldDoubleNull());
- builder.addDate(columnInfo.fieldDateNotNullColKey, realmObjectSource.realmGet$fieldDateNotNull());
- builder.addDate(columnInfo.fieldDateNullColKey, realmObjectSource.realmGet$fieldDateNull());
- builder.addDecimal128(columnInfo.fieldDecimal128NotNullColKey, realmObjectSource.realmGet$fieldDecimal128NotNull());
- builder.addDecimal128(columnInfo.fieldDecimal128NullColKey, realmObjectSource.realmGet$fieldDecimal128Null());
- builder.addObjectId(columnInfo.fieldObjectIdNotNullColKey, realmObjectSource.realmGet$fieldObjectIdNotNull());
- builder.addObjectId(columnInfo.fieldObjectIdNullColKey, realmObjectSource.realmGet$fieldObjectIdNull());
- builder.addStringList(columnInfo.fieldStringListNotNullColKey, realmObjectSource.realmGet$fieldStringListNotNull());
- builder.addStringList(columnInfo.fieldStringListNullColKey, realmObjectSource.realmGet$fieldStringListNull());
- builder.addByteArrayList(columnInfo.fieldBinaryListNotNullColKey, realmObjectSource.realmGet$fieldBinaryListNotNull());
- builder.addByteArrayList(columnInfo.fieldBinaryListNullColKey, realmObjectSource.realmGet$fieldBinaryListNull());
- builder.addBooleanList(columnInfo.fieldBooleanListNotNullColKey, realmObjectSource.realmGet$fieldBooleanListNotNull());
- builder.addBooleanList(columnInfo.fieldBooleanListNullColKey, realmObjectSource.realmGet$fieldBooleanListNull());
- builder.addLongList(columnInfo.fieldLongListNotNullColKey, realmObjectSource.realmGet$fieldLongListNotNull());
- builder.addLongList(columnInfo.fieldLongListNullColKey, realmObjectSource.realmGet$fieldLongListNull());
- builder.addIntegerList(columnInfo.fieldIntegerListNotNullColKey, realmObjectSource.realmGet$fieldIntegerListNotNull());
- builder.addIntegerList(columnInfo.fieldIntegerListNullColKey, realmObjectSource.realmGet$fieldIntegerListNull());
- builder.addShortList(columnInfo.fieldShortListNotNullColKey, realmObjectSource.realmGet$fieldShortListNotNull());
- builder.addShortList(columnInfo.fieldShortListNullColKey, realmObjectSource.realmGet$fieldShortListNull());
- builder.addByteList(columnInfo.fieldByteListNotNullColKey, realmObjectSource.realmGet$fieldByteListNotNull());
- builder.addByteList(columnInfo.fieldByteListNullColKey, realmObjectSource.realmGet$fieldByteListNull());
- builder.addDoubleList(columnInfo.fieldDoubleListNotNullColKey, realmObjectSource.realmGet$fieldDoubleListNotNull());
- builder.addDoubleList(columnInfo.fieldDoubleListNullColKey, realmObjectSource.realmGet$fieldDoubleListNull());
- builder.addFloatList(columnInfo.fieldFloatListNotNullColKey, realmObjectSource.realmGet$fieldFloatListNotNull());
- builder.addFloatList(columnInfo.fieldFloatListNullColKey, realmObjectSource.realmGet$fieldFloatListNull());
- builder.addDateList(columnInfo.fieldDateListNotNullColKey, realmObjectSource.realmGet$fieldDateListNotNull());
- builder.addDateList(columnInfo.fieldDateListNullColKey, realmObjectSource.realmGet$fieldDateListNull());
- builder.addDecimal128List(columnInfo.fieldDecimal128ListNotNullColKey, realmObjectSource.realmGet$fieldDecimal128ListNotNull());
- builder.addDecimal128List(columnInfo.fieldDecimal128ListNullColKey, realmObjectSource.realmGet$fieldDecimal128ListNull());
- builder.addObjectIdList(columnInfo.fieldObjectIdListNotNullColKey, realmObjectSource.realmGet$fieldObjectIdListNotNull());
- builder.addObjectIdList(columnInfo.fieldObjectIdListNullColKey, realmObjectSource.realmGet$fieldObjectIdListNull());
+ builder.addString(columnInfo.fieldStringNotNullColKey, unmanagedSource.realmGet$fieldStringNotNull());
+ builder.addString(columnInfo.fieldStringNullColKey, unmanagedSource.realmGet$fieldStringNull());
+ builder.addBoolean(columnInfo.fieldBooleanNotNullColKey, unmanagedSource.realmGet$fieldBooleanNotNull());
+ builder.addBoolean(columnInfo.fieldBooleanNullColKey, unmanagedSource.realmGet$fieldBooleanNull());
+ builder.addByteArray(columnInfo.fieldBytesNotNullColKey, unmanagedSource.realmGet$fieldBytesNotNull());
+ builder.addByteArray(columnInfo.fieldBytesNullColKey, unmanagedSource.realmGet$fieldBytesNull());
+ builder.addInteger(columnInfo.fieldByteNotNullColKey, unmanagedSource.realmGet$fieldByteNotNull());
+ builder.addInteger(columnInfo.fieldByteNullColKey, unmanagedSource.realmGet$fieldByteNull());
+ builder.addInteger(columnInfo.fieldShortNotNullColKey, unmanagedSource.realmGet$fieldShortNotNull());
+ builder.addInteger(columnInfo.fieldShortNullColKey, unmanagedSource.realmGet$fieldShortNull());
+ builder.addInteger(columnInfo.fieldIntegerNotNullColKey, unmanagedSource.realmGet$fieldIntegerNotNull());
+ builder.addInteger(columnInfo.fieldIntegerNullColKey, unmanagedSource.realmGet$fieldIntegerNull());
+ builder.addInteger(columnInfo.fieldLongNotNullColKey, unmanagedSource.realmGet$fieldLongNotNull());
+ builder.addInteger(columnInfo.fieldLongNullColKey, unmanagedSource.realmGet$fieldLongNull());
+ builder.addFloat(columnInfo.fieldFloatNotNullColKey, unmanagedSource.realmGet$fieldFloatNotNull());
+ builder.addFloat(columnInfo.fieldFloatNullColKey, unmanagedSource.realmGet$fieldFloatNull());
+ builder.addDouble(columnInfo.fieldDoubleNotNullColKey, unmanagedSource.realmGet$fieldDoubleNotNull());
+ builder.addDouble(columnInfo.fieldDoubleNullColKey, unmanagedSource.realmGet$fieldDoubleNull());
+ builder.addDate(columnInfo.fieldDateNotNullColKey, unmanagedSource.realmGet$fieldDateNotNull());
+ builder.addDate(columnInfo.fieldDateNullColKey, unmanagedSource.realmGet$fieldDateNull());
+ builder.addDecimal128(columnInfo.fieldDecimal128NotNullColKey, unmanagedSource.realmGet$fieldDecimal128NotNull());
+ builder.addDecimal128(columnInfo.fieldDecimal128NullColKey, unmanagedSource.realmGet$fieldDecimal128Null());
+ builder.addObjectId(columnInfo.fieldObjectIdNotNullColKey, unmanagedSource.realmGet$fieldObjectIdNotNull());
+ builder.addObjectId(columnInfo.fieldObjectIdNullColKey, unmanagedSource.realmGet$fieldObjectIdNull());
+ builder.addStringList(columnInfo.fieldStringListNotNullColKey, unmanagedSource.realmGet$fieldStringListNotNull());
+ builder.addStringList(columnInfo.fieldStringListNullColKey, unmanagedSource.realmGet$fieldStringListNull());
+ builder.addByteArrayList(columnInfo.fieldBinaryListNotNullColKey, unmanagedSource.realmGet$fieldBinaryListNotNull());
+ builder.addByteArrayList(columnInfo.fieldBinaryListNullColKey, unmanagedSource.realmGet$fieldBinaryListNull());
+ builder.addBooleanList(columnInfo.fieldBooleanListNotNullColKey, unmanagedSource.realmGet$fieldBooleanListNotNull());
+ builder.addBooleanList(columnInfo.fieldBooleanListNullColKey, unmanagedSource.realmGet$fieldBooleanListNull());
+ builder.addLongList(columnInfo.fieldLongListNotNullColKey, unmanagedSource.realmGet$fieldLongListNotNull());
+ builder.addLongList(columnInfo.fieldLongListNullColKey, unmanagedSource.realmGet$fieldLongListNull());
+ builder.addIntegerList(columnInfo.fieldIntegerListNotNullColKey, unmanagedSource.realmGet$fieldIntegerListNotNull());
+ builder.addIntegerList(columnInfo.fieldIntegerListNullColKey, unmanagedSource.realmGet$fieldIntegerListNull());
+ builder.addShortList(columnInfo.fieldShortListNotNullColKey, unmanagedSource.realmGet$fieldShortListNotNull());
+ builder.addShortList(columnInfo.fieldShortListNullColKey, unmanagedSource.realmGet$fieldShortListNull());
+ builder.addByteList(columnInfo.fieldByteListNotNullColKey, unmanagedSource.realmGet$fieldByteListNotNull());
+ builder.addByteList(columnInfo.fieldByteListNullColKey, unmanagedSource.realmGet$fieldByteListNull());
+ builder.addDoubleList(columnInfo.fieldDoubleListNotNullColKey, unmanagedSource.realmGet$fieldDoubleListNotNull());
+ builder.addDoubleList(columnInfo.fieldDoubleListNullColKey, unmanagedSource.realmGet$fieldDoubleListNull());
+ builder.addFloatList(columnInfo.fieldFloatListNotNullColKey, unmanagedSource.realmGet$fieldFloatListNotNull());
+ builder.addFloatList(columnInfo.fieldFloatListNullColKey, unmanagedSource.realmGet$fieldFloatListNull());
+ builder.addDateList(columnInfo.fieldDateListNotNullColKey, unmanagedSource.realmGet$fieldDateListNotNull());
+ builder.addDateList(columnInfo.fieldDateListNullColKey, unmanagedSource.realmGet$fieldDateListNull());
+ builder.addDecimal128List(columnInfo.fieldDecimal128ListNotNullColKey, unmanagedSource.realmGet$fieldDecimal128ListNotNull());
+ builder.addDecimal128List(columnInfo.fieldDecimal128ListNullColKey, unmanagedSource.realmGet$fieldDecimal128ListNull());
+ builder.addObjectIdList(columnInfo.fieldObjectIdListNotNullColKey, unmanagedSource.realmGet$fieldObjectIdListNotNull());
+ builder.addObjectIdList(columnInfo.fieldObjectIdListNullColKey, unmanagedSource.realmGet$fieldObjectIdListNull());
// Create the underlying object and cache it before setting any object/objectlist references
// This will allow us to break any circular dependencies by using the object cache.
Row row = builder.createNewObject();
- io.realm.some_test_NullTypesRealmProxy realmObjectCopy = newProxyInstance(realm, row);
- cache.put(newObject, realmObjectCopy);
+ io.realm.some_test_NullTypesRealmProxy managedCopy = newProxyInstance(realm, row);
+ cache.put(newObject, managedCopy);
// Finally add all fields that reference other Realm Objects, either directly or through a list
- some.test.NullTypes fieldObjectNullObj = realmObjectSource.realmGet$fieldObjectNull();
+ some.test.NullTypes fieldObjectNullObj = unmanagedSource.realmGet$fieldObjectNull();
if (fieldObjectNullObj == null) {
- realmObjectCopy.realmSet$fieldObjectNull(null);
+ managedCopy.realmSet$fieldObjectNull(null);
} else {
some.test.NullTypes cachefieldObjectNull = (some.test.NullTypes) cache.get(fieldObjectNullObj);
if (cachefieldObjectNull != null) {
- realmObjectCopy.realmSet$fieldObjectNull(cachefieldObjectNull);
+ managedCopy.realmSet$fieldObjectNull(cachefieldObjectNull);
} else {
- realmObjectCopy.realmSet$fieldObjectNull(some_test_NullTypesRealmProxy.copyOrUpdate(realm, (some_test_NullTypesRealmProxy.NullTypesColumnInfo) realm.getSchema().getColumnInfo(some.test.NullTypes.class), fieldObjectNullObj, update, cache, flags));
+ managedCopy.realmSet$fieldObjectNull(some_test_NullTypesRealmProxy.copyOrUpdate(realm, (some_test_NullTypesRealmProxy.NullTypesColumnInfo) realm.getSchema().getColumnInfo(some.test.NullTypes.class), fieldObjectNullObj, update, cache, flags));
}
}
- return realmObjectCopy;
+ return managedCopy;
}
public static long insert(Realm realm, some.test.NullTypes object, Map cache) {
diff --git a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_SimpleRealmProxy.java b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_SimpleRealmProxy.java
index dcec635152..8c5590e1e2 100644
--- a/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_SimpleRealmProxy.java
+++ b/realm/realm-annotations-processor/src/test/resources/io/realm/some_test_SimpleRealmProxy.java
@@ -143,7 +143,7 @@ protected final void copy(ColumnInfo rawSrc, ColumnInfo rawDst) {
}
private static OsObjectSchemaInfo createExpectedObjectSchemaInfo() {
- OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("Simple", 2, 0);
+ OsObjectSchemaInfo.Builder builder = new OsObjectSchemaInfo.Builder("Simple", false, 2, 0);
builder.addPersistedProperty("name", RealmFieldType.STRING, !Property.PRIMARY_KEY, !Property.INDEXED, !Property.REQUIRED);
builder.addPersistedProperty("age", RealmFieldType.INTEGER, !Property.PRIMARY_KEY, !Property.INDEXED, Property.REQUIRED);
return builder.build();
@@ -221,7 +221,7 @@ public static some.test.Simple createUsingJsonStream(Realm realm, JsonReader rea
return realm.copyToRealm(obj);
}
- private static some_test_SimpleRealmProxy newProxyInstance(BaseRealm realm, Row row) {
+ static some_test_SimpleRealmProxy newProxyInstance(BaseRealm realm, Row row) {
// Ignore default values to avoid creating unexpected objects from RealmModel/RealmList fields
final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get();
objectContext.set(realm, row, realm.getSchema().getColumnInfo(some.test.Simple.class), false, Collections.emptyList());
@@ -255,22 +255,22 @@ public static some.test.Simple copy(Realm realm, SimpleColumnInfo columnInfo, so
return (some.test.Simple) cachedRealmObject;
}
- some_test_SimpleRealmProxyInterface realmObjectSource = (some_test_SimpleRealmProxyInterface) newObject;
+ some_test_SimpleRealmProxyInterface unmanagedSource = (some_test_SimpleRealmProxyInterface) newObject;
Table table = realm.getTable(some.test.Simple.class);
OsObjectBuilder builder = new OsObjectBuilder(table, flags);
// Add all non-"object reference" fields
- builder.addString(columnInfo.nameColKey, realmObjectSource.realmGet$name());
- builder.addInteger(columnInfo.ageColKey, realmObjectSource.realmGet$age());
+ builder.addString(columnInfo.nameColKey, unmanagedSource.realmGet$name());
+ builder.addInteger(columnInfo.ageColKey, unmanagedSource.realmGet$age());
// Create the underlying object and cache it before setting any object/objectlist references
// This will allow us to break any circular dependencies by using the object cache.
Row row = builder.createNewObject();
- io.realm.some_test_SimpleRealmProxy realmObjectCopy = newProxyInstance(realm, row);
- cache.put(newObject, realmObjectCopy);
+ io.realm.some_test_SimpleRealmProxy managedCopy = newProxyInstance(realm, row);
+ cache.put(newObject, managedCopy);
- return realmObjectCopy;
+ return managedCopy;
}
public static long insert(Realm realm, some.test.Simple object, Map cache) {
diff --git a/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClass.java b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClass.java
new file mode 100644
index 0000000000..d3bd25f2dd
--- /dev/null
+++ b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClass.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2020 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 some.test;
+
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.RealmClass;
+
+@RealmClass(embedded = true)
+public class EmbeddedClass extends RealmObject {
+ public String name;
+ public int age;
+}
diff --git a/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassMissingFieldDescription.java b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassMissingFieldDescription.java
new file mode 100644
index 0000000000..2e4895ef6f
--- /dev/null
+++ b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassMissingFieldDescription.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2020 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 some.test;
+
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+@RealmClass(embedded = true)
+public class EmbeddedClassMissingFieldDescription extends RealmObject {
+ public String name;
+ public int age;
+
+ @LinkingObjects
+ public final EmbeddedClassParent parent1 = new EmbeddedClassParent();
+}
diff --git a/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassMissingFinalOnLinkingObjects.java b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassMissingFinalOnLinkingObjects.java
new file mode 100644
index 0000000000..f870670852
--- /dev/null
+++ b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassMissingFinalOnLinkingObjects.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2020 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 some.test;
+
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+@RealmClass(embedded = true)
+public class EmbeddedClassMissingFinalOnLinkingObjects extends RealmObject {
+ public String name;
+ public int age;
+
+ @LinkingObjects("child5")
+ public EmbeddedClassParent parent;
+}
diff --git a/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassMultipleRequiredParents.java b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassMultipleRequiredParents.java
new file mode 100644
index 0000000000..e72094ef7f
--- /dev/null
+++ b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassMultipleRequiredParents.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 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 some.test;
+
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+@RealmClass(embedded = true)
+public class EmbeddedClassMultipleRequiredParents extends RealmObject {
+ public String name;
+ public int age;
+
+ // If multiple @LinkingObjects are defined
+ // the @Required annotation is not allowed.
+ @Required
+ @LinkingObjects("child6")
+ public final EmbeddedClassParent parent1 = new EmbeddedClassParent();
+
+ @Required
+ @LinkingObjects("child7")
+ public final EmbeddedClassParent parent2 = new EmbeddedClassParent();
+}
diff --git a/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassOptionalParents.java b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassOptionalParents.java
new file mode 100644
index 0000000000..75eb54c600
--- /dev/null
+++ b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassOptionalParents.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 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 some.test;
+
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+@RealmClass(embedded = true)
+public class EmbeddedClassOptionalParents extends RealmObject {
+ public String name;
+ public int age;
+
+ // If multiple @LinkingObjects are defined
+ // They are not treated as @Required.
+ // This mostly impact Kotlin model classes
+ @LinkingObjects("child3")
+ public final EmbeddedClassParent parent1 = new EmbeddedClassParent(); // Field must be final, because parent cannot change once set
+
+ @LinkingObjects("child4")
+ public final EmbeddedClassParent parent2 = new EmbeddedClassParent(); // Field must be final, because parent cannot change once set
+}
diff --git a/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassParent.java b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassParent.java
new file mode 100644
index 0000000000..02b1ae2cc7
--- /dev/null
+++ b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassParent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2020 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 some.test;
+
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+import io.realm.annotations.RealmClass;
+
+// This class is only for creating the correct type hiearchy when testing Embedded Objects
+// This class can work as a parent for all legal embedded object classes
+public class EmbeddedClassParent extends RealmObject {
+ public String name;
+ public int age;
+
+ // Valid single children references
+ public EmbeddedClass child1;
+ public EmbeddedClassRequiredParent child2;
+ public EmbeddedClassOptionalParents child3;
+ public EmbeddedClassOptionalParents child4;
+}
diff --git a/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassPrimaryKey.java b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassPrimaryKey.java
new file mode 100644
index 0000000000..174c2e8c21
--- /dev/null
+++ b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassPrimaryKey.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 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 some.test;
+
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+
+@RealmClass(embedded = true)
+public class EmbeddedClassPrimaryKey extends RealmObject {
+ @PrimaryKey // This is not allowed in embedded classes
+ public String name;
+ public int age;
+}
diff --git a/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassRequiredParent.java b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassRequiredParent.java
new file mode 100644
index 0000000000..db7804f876
--- /dev/null
+++ b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassRequiredParent.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 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 some.test;
+
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+import io.realm.annotations.RealmClass;
+import io.realm.annotations.Required;
+
+@RealmClass(embedded = true)
+public class EmbeddedClassRequiredParent extends RealmObject {
+ public String name;
+ public int age;
+
+ @Required // Optional, is implied if only a single @LinkingObjects parent is defined
+ @LinkingObjects("child2")
+ public final EmbeddedClassParent parent = new EmbeddedClassParent(); // Field must be final, because parent cannot change once set
+}
diff --git a/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassSimpleParent.java b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassSimpleParent.java
new file mode 100644
index 0000000000..1cc405105c
--- /dev/null
+++ b/realm/realm-annotations-processor/src/test/resources/some/test/EmbeddedClassSimpleParent.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 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 some.test;
+
+import io.realm.RealmList;
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+import io.realm.annotations.LinkingObjects;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+
+// Simple parent of embedded objects. Used to verify the output of the annotation processor.
+public class EmbeddedClassSimpleParent extends RealmObject {
+ @PrimaryKey
+ public String id;
+ public EmbeddedClass child;
+ public RealmList children;
+
+}
diff --git a/realm/realm-library/src/androidTest/java/io/realm/LinkingObjectsManagedTests.java b/realm/realm-library/src/androidTest/java/io/realm/LinkingObjectsManagedTests.java
index 13ed88e978..4174c14099 100644
--- a/realm/realm-library/src/androidTest/java/io/realm/LinkingObjectsManagedTests.java
+++ b/realm/realm-library/src/androidTest/java/io/realm/LinkingObjectsManagedTests.java
@@ -604,11 +604,11 @@ public void migration_backlinkedSourceFieldDoesntExist() throws ClassNotFoundExc
// Mock the schema info so the only difference compared with the original schema is that the LinkingObject field
// points to BacklinksSource.childNotExist.
- OsObjectSchemaInfo targetSchemaInfo = new OsObjectSchemaInfo.Builder("BacklinksTarget", 1, 1)
+ OsObjectSchemaInfo targetSchemaInfo = new OsObjectSchemaInfo.Builder("BacklinksTarget", false, 1, 1)
.addPersistedProperty("id", RealmFieldType.INTEGER, !Property.PRIMARY_KEY, !Property.INDEXED, Property.REQUIRED)
.addComputedLinkProperty("parents", "BacklinksSource", "childNotExist" /*"child" is the original value*/)
.build();
- OsObjectSchemaInfo sourceSchemaInfo = new OsObjectSchemaInfo.Builder("BacklinksSource", 2, 0)
+ OsObjectSchemaInfo sourceSchemaInfo = new OsObjectSchemaInfo.Builder("BacklinksSource", false, 2, 0)
.addPersistedProperty("name", RealmFieldType.STRING, !Property.PRIMARY_KEY, !Property.INDEXED, !Property.REQUIRED)
.addPersistedLinkProperty("child", RealmFieldType.OBJECT, "BacklinksTarget")
.build();
@@ -647,11 +647,11 @@ public void migration_backlinkedSourceFieldWrongType() {
// Mock the schema info so the only difference compared with the original schema is that BacklinksSource.child
// type is changed to BacklinksSource from BacklinksTarget.
- OsObjectSchemaInfo targetSchemaInfo = new OsObjectSchemaInfo.Builder("BacklinksTarget", 1, 1)
+ OsObjectSchemaInfo targetSchemaInfo = new OsObjectSchemaInfo.Builder("BacklinksTarget", false, 1, 1)
.addPersistedProperty("id", RealmFieldType.INTEGER, !Property.PRIMARY_KEY, !Property.INDEXED, Property.REQUIRED)
.addComputedLinkProperty("parents", "BacklinksSource", "child")
.build();
- OsObjectSchemaInfo sourceSchemaInfo = new OsObjectSchemaInfo.Builder("BacklinksSource", 2, 0)
+ OsObjectSchemaInfo sourceSchemaInfo = new OsObjectSchemaInfo.Builder("BacklinksSource", false, 2, 0)
.addPersistedProperty("name", RealmFieldType.STRING, !Property.PRIMARY_KEY, !Property.INDEXED, !Property.REQUIRED)
.addPersistedLinkProperty("child", RealmFieldType.OBJECT,
"BacklinksSource"/*"BacklinksTarget" is the original value*/)
diff --git a/realm/realm-library/src/androidTest/java/io/realm/internal/OsListTests.java b/realm/realm-library/src/androidTest/java/io/realm/internal/OsListTests.java
index 2b79b9d1b2..5f28f7e53a 100644
--- a/realm/realm-library/src/androidTest/java/io/realm/internal/OsListTests.java
+++ b/realm/realm-library/src/androidTest/java/io/realm/internal/OsListTests.java
@@ -51,7 +51,7 @@ public class OsListTests {
@Before
public void setUp() {
- OsObjectSchemaInfo objectSchemaInfo = new OsObjectSchemaInfo.Builder("TestModel",14, 0)
+ OsObjectSchemaInfo objectSchemaInfo = new OsObjectSchemaInfo.Builder("TestModel", false,14, 0)
.addPersistedValueListProperty("longList", RealmFieldType.INTEGER_LIST, !Property.REQUIRED)
.addPersistedValueListProperty("doubleList", RealmFieldType.DOUBLE_LIST, !Property.REQUIRED)
.addPersistedValueListProperty("floatList", RealmFieldType.FLOAT_LIST, !Property.REQUIRED)
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/Decimal128Tests.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/Decimal128Tests.kt
index c6b3094613..b47a5d8d6b 100644
--- a/realm/realm-library/src/androidTest/kotlin/io/realm/Decimal128Tests.kt
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/Decimal128Tests.kt
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2020 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 androidx.test.ext.junit.runners.AndroidJUnit4
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/EmbeddedObjectsTest.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/EmbeddedObjectsTest.kt
new file mode 100644
index 0000000000..a6be56f3c9
--- /dev/null
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/EmbeddedObjectsTest.kt
@@ -0,0 +1,592 @@
+/*
+ * Copyright 2020 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 androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import io.realm.entities.*
+import io.realm.entities.embedded.*
+import io.realm.kotlin.addChangeListener
+import io.realm.kotlin.createEmbeddedObject
+import io.realm.kotlin.createObject
+import io.realm.kotlin.where
+import io.realm.rule.BlockingLooperThread
+import io.realm.rule.TestRealmConfigurationFactory
+import org.junit.*
+import org.junit.Assert.*
+import org.junit.runner.RunWith
+import java.util.*
+import kotlin.test.assertFailsWith
+
+/**
+ * Class testing the Embedded Objects feature.
+ */
+// FIXME: Move all of these tests out from here. We try to tests by Class, not Feature.
+@RunWith(AndroidJUnit4::class)
+class EmbeddedObjectsTest {
+
+ @get:Rule
+ val configFactory = TestRealmConfigurationFactory()
+
+ private val looperThread = BlockingLooperThread()
+
+ private lateinit var realmConfig: RealmConfiguration
+ private lateinit var realm: Realm
+
+ @Before
+ fun setUp() {
+ Realm.init(InstrumentationRegistry.getInstrumentation().targetContext)
+ realmConfig = configFactory.createConfiguration()
+ realm = Realm.getInstance(realmConfig)
+ }
+
+ @After
+ fun tearDown() {
+ if (this::realm.isInitialized) {
+ realm.close()
+ }
+ }
+
+ @Test
+ fun createObject_throwsForEmbeddedClasses() = realm.executeTransaction { realm ->
+ assertFailsWith { realm.createObject() }
+ }
+
+ @Test
+ fun createObjectWithPrimaryKey_throwsForEmbeddedClasses() = realm.executeTransaction { realm ->
+ assertFailsWith { realm.createObject("foo") }
+ }
+
+ @Test
+ fun createEmbeddedObject_nullArgsThrows() = realm.executeTransaction { realm ->
+ assertFailsWith { realm.createEmbeddedObject(EmbeddedSimpleChild::class.java, TestHelper.getNull(), "foo") }
+ val parent = realm.createObject("parent")
+ assertFailsWith { realm.createEmbeddedObject(EmbeddedSimpleChild::class.java, parent, TestHelper.getNull()) }
+ }
+
+ @Test
+ fun createEmbeddedObject_nonExistingParentPropertyNameThrows() = realm.executeTransaction { realm ->
+ val parent = realm.createObject("parent")
+ assertFailsWith { realm.createEmbeddedObject(parent, "foo") }
+ }
+
+ @Test
+ fun createEmbeddedObject_wrongParentPropertyTypeThrows() = realm.executeTransaction { realm ->
+ val parent = realm.createObject("parent")
+
+ // TODO: Smoke-test for wrong type. Figure out how to test all unsupported types.
+ assertFailsWith { realm.createEmbeddedObject(parent, "id") }
+ }
+
+ @Test
+ @Ignore("FIXME")
+ fun createEmbeddedObject_wrongParentPropertyObjectTypeThrows() = realm.executeTransaction { realm ->
+ val parent = realm.createObject("parent")
+
+ assertFailsWith {
+ // Embedded object is not of the type the parent object links to.
+ realm.createEmbeddedObject(parent, "child")
+ }
+ }
+
+ @Test
+ @Ignore("FIXME")
+ fun createEmbeddedObject_wrongParentPropertyListTypeThrows() = realm.executeTransaction { realm ->
+ val parent = realm.createObject("parent")
+
+ assertFailsWith {
+ // Embedded object is not of the type the parent object links to.
+ realm.createEmbeddedObject(parent, "children")
+ }
+ }
+
+ @Test
+ fun createEmbeddedObject_simpleSingleChild() = realm.executeTransaction { realm ->
+ val parent = realm.createObject("parent")
+ val child = realm.createEmbeddedObject(parent, "child");
+ assertEquals(child.parent, parent)
+ }
+
+ @Test
+ fun createEmbeddedObject_simpleChildList() = realm.executeTransaction { realm ->
+ // Using createEmbeddedObject() with a parent list, will append the object to the end
+ // of the list
+ val parent = realm.createObject(UUID.randomUUID().toString())
+ val child1 = realm.createEmbeddedObject(parent, "children")
+ val child2 = realm.createEmbeddedObject(parent, "children")
+ assertEquals(2, parent.children.size.toLong())
+ assertEquals(child1, parent.children.first()!!)
+ assertEquals(child2, parent.children.last()!!)
+ }
+
+ @Test
+ @Ignore("Placeholder for all tests for DynamicRealm.createEmbeddedObject()")
+ fun dynamicRealm_createEmbeddedObject() {
+ TODO()
+ }
+
+ @Test
+ fun settingParentFieldDeletesChild() = realm.executeTransaction { realm ->
+ val parent = EmbeddedSimpleParent("parent")
+ parent.child = EmbeddedSimpleChild("child")
+
+ val managedParent: EmbeddedSimpleParent = realm.copyToRealm(parent)
+ val managedChild: EmbeddedSimpleChild = managedParent.child!!
+ managedParent.child = null // Will delete the embedded object
+ assertFalse(managedChild.isValid)
+ assertEquals(0, realm.where().count())
+ }
+
+ @Test
+ fun objectAccessor_willAutomaticallyCopyUnmanaged() = realm.executeTransaction { realm ->
+ // Checks that adding an unmanaged embedded object to a property will automatically copy it.
+ val parent = EmbeddedSimpleParent("parent")
+ val managedParent: EmbeddedSimpleParent = realm.copyToRealm(parent)
+
+ assertEquals(0, realm.where().count())
+ managedParent.child = EmbeddedSimpleChild("child") // Will copy the object to Realm
+ assertEquals(1, realm.where().count())
+ assertTrue(managedParent.child!!.isValid)
+ }
+
+ @Test
+ fun objectAccessor_willAutomaticallyCopyManaged() = realm.executeTransaction { realm ->
+ // Checks that setting a link to a managed embedded object will automatically copy it unlike
+ // normal objects that allow multiple parents. Note: This behavior is a bit controversial
+ // and was subject to a lot of discussion during API design. The problem is that making
+ // the behavior explicit will result in an extremely annoying API. We need to carefully
+ // monitor if people understand how this behaves.
+ val managedParent1: EmbeddedSimpleParent = realm.copyToRealm(EmbeddedSimpleParent("parent1"))
+ val managedParent2: EmbeddedSimpleParent = realm.copyToRealm(EmbeddedSimpleParent("parent2"))
+
+ assertEquals(0, realm.where().count())
+ managedParent1.child = EmbeddedSimpleChild("child")
+ assertEquals(1, realm.where().count())
+ managedParent2.child = managedParent1.child // Will copy the embedded object
+ assertEquals(2, realm.where().count())
+ assertNotEquals(managedParent1.child, managedParent2.child)
+ }
+
+ @Test
+ fun objectAccessor_willCopyUnderConstruction() = realm.executeTransaction { realm ->
+ val unmanagedObj = EmbeddedWithConstructorArgs()
+ val managedObj = realm.copyToRealm(unmanagedObj)
+ assertEquals(EmbeddedWithConstructorArgs.INNER_CHILD_ID, managedObj.child!!.id)
+ }
+
+ @Test
+ fun realmList_add_willAutomaticallyCopy() = realm.executeTransaction { realm ->
+ val parent = realm.copyToRealm(EmbeddedSimpleListParent("parent"))
+ assertTrue(parent.children.add(EmbeddedSimpleChild("child")))
+ val child = parent.children.first()!!
+ assertTrue(child.isValid)
+ assertEquals("child", child.id)
+
+ // FIXME: How to handle DynamicRealmObject :(
+ }
+
+ @Test
+ fun realmList_addIndex_willAutomaticallyCopy() = realm.executeTransaction { realm ->
+ val parent = realm.copyToRealm(EmbeddedSimpleListParent("parent"))
+ parent.children.add(EmbeddedSimpleChild("secondChild"))
+ parent.children.add(0, EmbeddedSimpleChild("firstChild"))
+ val child = parent.children.first()!!
+ assertTrue(child.isValid)
+ assertEquals("firstChild", child.id)
+
+ // FIXME: How to handle DynamicRealmObject :(
+ }
+
+ @Test
+ fun realmList_set_willAutomaticallyCopy() = realm.executeTransaction { realm ->
+ // Checks that adding an unmanaged embedded object to a list will automatically make
+ // it managed
+ val parent = realm.copyToRealm(EmbeddedSimpleListParent("parent"))
+ assertTrue(parent.children.add(EmbeddedSimpleChild("child")))
+ assertEquals(1, realm.where().count())
+ parent.children[0] = EmbeddedSimpleChild("OtherChild")
+ assertEquals("OtherChild", parent.children.first()!!.id)
+ assertEquals(1, realm.where().count())
+
+ // FIXME: How to handle DynamicRealmObject :(
+ }
+
+ @Test
+ fun copyToRealm_noParentThrows() = realm.executeTransaction {
+ assertFailsWith {
+ realm.copyToRealm(EmbeddedSimpleChild("child"))
+ }
+ }
+
+ @Test
+ fun copyToRealmOrUpdate_NoParentThrows() = realm.executeTransaction {
+ assertFailsWith {
+ realm.copyToRealmOrUpdate(EmbeddedSimpleChild("child"))
+ }
+ }
+
+ @Test
+ fun copyToRealm_simpleSingleChild() {
+ realm.executeTransaction {
+ val parent = EmbeddedSimpleParent("parent1")
+ parent.child = EmbeddedSimpleChild("child1")
+ it.copyToRealm(parent)
+ }
+
+ assertEquals(1, realm.where().count())
+ assertEquals(1, realm.where().count())
+ }
+
+ @Test
+ fun copyToRealm_simpleChildList() {
+ realm.executeTransaction {
+ val parent = EmbeddedSimpleListParent("parent1")
+ parent.children = RealmList(EmbeddedSimpleChild("child1"))
+ it.copyToRealm(parent)
+ }
+
+ assertEquals(1, realm.where().count())
+ assertEquals(1, realm.where().count())
+ }
+
+ @Test
+ fun copyToRealm_treeSchema() {
+ realm.executeTransaction {
+ val parent = EmbeddedTreeParent("parent1")
+
+ val node1 = EmbeddedTreeNode("node1")
+ node1.leafNode = EmbeddedTreeLeaf("leaf1")
+ parent.middleNode = node1
+ val node2 = EmbeddedTreeNode("node2")
+ node2.leafNodeList.add(EmbeddedTreeLeaf("leaf2"))
+ node2.leafNodeList.add(EmbeddedTreeLeaf("leaf3"))
+ parent.middleNodeList.add(node2)
+
+ it.copyToRealm(parent)
+ }
+
+ assertEquals(1, realm.where().count())
+ assertEquals(2, realm.where().count())
+ assertEquals(3, realm.where().count())
+ }
+
+ @Test
+ fun copyToRealm_circularSchema() {
+ realm.executeTransaction {
+ val parent = EmbeddedCircularParent("parent")
+ val child1 = EmbeddedCircularChild("child1")
+ val child2 = EmbeddedCircularChild("child2")
+ child1.singleChild = child2
+ parent.singleChild = child1
+ it.copyToRealm(parent)
+ }
+
+ assertEquals(1, realm.where().count())
+ assertEquals(2, realm.where().count())
+ }
+
+ @Test
+ fun copyToRealm_throwsIfMultipleRefsToSingleObjectsExists() {
+ realm.executeTransaction { r ->
+ val parent = EmbeddedCircularParent("parent")
+ val child = EmbeddedCircularChild("child")
+ child.singleChild = child // Create circle between children
+ parent.singleChild = child
+ assertFailsWith { r.copyToRealm(parent) }
+ }
+ }
+
+ @Test
+ fun copyToRealm_throwsIfMultipleRefsToListObjectsExists() {
+ realm.executeTransaction { r ->
+ val parent = EmbeddedSimpleListParent("parent")
+ val child = EmbeddedSimpleChild("child")
+ parent.children = RealmList(child, child)
+ assertFailsWith { r.copyToRealm(parent) }
+ }
+ }
+
+ @Test
+ @Ignore("FIXME")
+ fun copyToRealmOrUpdate_deleteReplacedObjects() {
+ TODO()
+
+ }
+
+ @Test
+ @Ignore("Add in another PR")
+ fun insert_noParentThrows() {
+ TODO()
+ }
+
+ @Test
+ @Ignore("Add in another PR")
+ fun insertOrUpdate_throws() {
+ TODO()
+ }
+
+ @Test
+ fun insert_simpleSingleChild() {
+ realm.executeTransaction {
+ val parent = EmbeddedSimpleParent("parent1")
+ parent.child = EmbeddedSimpleChild("child1")
+ it.insert(parent)
+ }
+
+ assertEquals(1, realm.where().count())
+ assertEquals(1, realm.where().count())
+ }
+
+ @Test
+ fun insert_simpleChildList() {
+ realm.executeTransaction {
+ val parent = EmbeddedSimpleListParent("parent1")
+ parent.children = RealmList(EmbeddedSimpleChild("child1"))
+ it.insert(parent)
+ }
+
+ assertEquals(1, realm.where().count())
+ assertEquals(1, realm.where().count())
+ }
+
+ @Test
+ fun insert_treeSchema() {
+ realm.executeTransaction {
+ val parent = EmbeddedTreeParent("parent1")
+
+ val node1 = EmbeddedTreeNode("node1")
+ node1.leafNode = EmbeddedTreeLeaf("leaf1")
+ parent.middleNode = node1
+ val node2 = EmbeddedTreeNode("node2")
+ node2.leafNodeList.add(EmbeddedTreeLeaf("leaf2"))
+ node2.leafNodeList.add(EmbeddedTreeLeaf("leaf3"))
+ parent.middleNodeList.add(node2)
+
+ it.insert(parent)
+ }
+
+ assertEquals(1, realm.where().count())
+ assertEquals(2, realm.where().count())
+ assertEquals(3, realm.where().count())
+ }
+
+ @Test
+ fun insert_circularSchema() {
+ realm.executeTransaction {
+ val parent = EmbeddedCircularParent("parent")
+ val child1 = EmbeddedCircularChild("child1")
+ val child2 = EmbeddedCircularChild("child2")
+ child1.singleChild = child2
+ parent.singleChild = child1
+ it.insert(parent)
+ }
+
+ assertEquals(1, realm.where().count())
+ assertEquals(2, realm.where().count())
+ }
+
+ @Test
+ @Ignore("Add in another PR")
+ fun insertOrUpdate_deletesOldEmbeddedObject() {
+ TODO()
+ }
+
+ @Test
+ @Ignore("Add in another PR")
+ fun insert_listWithEmbeddedObjects() {
+ TODO()
+ }
+
+ @Test
+ @Ignore("Add in another PR")
+ fun insertOrUpdate_listWithEmbeddedObjects() {
+ TODO()
+ }
+
+ @Test
+ @Ignore("Add in another PR")
+ fun createObjectFromJson() {
+ TODO("Placeholder for all tests regarding importing from JSON")
+ }
+
+ @Test
+ @Ignore("Add in another PR")
+ fun dynamicRealmObject_createEmbeddedObject() {
+ TODO("Consider which kind of support there should be for embedded objets in DynamicRealm")
+ }
+
+
+ @Test
+ fun realmObjectSchema_setEmbedded() {
+ DynamicRealm.getInstance(realm.configuration).use { realm ->
+ realm.executeTransaction {
+ val objSchema: RealmObjectSchema = realm.schema[EmbeddedSimpleChild.NAME]!!
+ assertTrue(objSchema.isEmbedded)
+ objSchema.isEmbedded = false
+ assertFalse(objSchema.isEmbedded)
+ objSchema.isEmbedded = true
+ assertTrue(objSchema.isEmbedded)
+ }
+ }
+ }
+
+ @Test
+ fun realmObjectSchema_setEmbedded_throwsWithPrimaryKey() {
+ DynamicRealm.getInstance(realm.configuration).use { realm ->
+ realm.executeTransaction {
+ val objSchema: RealmObjectSchema = realm.schema[AllJavaTypes.CLASS_NAME]!!
+ assertFailsWith { objSchema.isEmbedded = true }
+ }
+ }
+ }
+
+ @Test
+ fun realmObjectSchema_setEmbedded_throwsIfBreaksParentInvariants() {
+ // Classes can only be converted to be embedded if all objects have exactly one other
+ // object pointing to it.
+ DynamicRealm.getInstance(realm.configuration).use { realm ->
+ realm.executeTransaction {
+
+ // Create object with no parents
+ realm.createObject(Dog.CLASS_NAME)
+ val dogSchema = realm.schema[Dog.CLASS_NAME]!!
+ // Succeed by mistake right now.
+ // See https://github.com/realm/realm-core/issues/3729
+ // The correct check is just below
+ dogSchema.isEmbedded = true
+ // assertFailsWith {
+ // dogSchema.isEmbedded = true
+ // }
+
+ // Create object with two parents
+ val cat: DynamicRealmObject = realm.createObject(Cat.CLASS_NAME)
+ val owner1: DynamicRealmObject = realm.createObject(Owner.CLASS_NAME)
+ owner1.setObject(Owner.FIELD_CAT, cat)
+ val owner2: DynamicRealmObject = realm.createObject(Owner.CLASS_NAME)
+ owner2.setObject(Owner.FIELD_CAT, cat)
+ val catSchema = realm.schema[Cat.CLASS_NAME]!!
+ assertFailsWith {
+ catSchema.isEmbedded = true
+ }
+ }
+ }
+ }
+
+ @Test
+ fun realmObjectSchema_isEmbedded() {
+ assertTrue(realm.schema[EmbeddedSimpleChild.NAME]!!.isEmbedded)
+ assertFalse(realm.schema[AllTypes.CLASS_NAME]!!.isEmbedded)
+ }
+
+ // Check that deleting a non-embedded parent deletes all embedded children
+ @Test
+ fun deleteParentObject_deletesEmbeddedChildren() = realm.executeTransaction {
+ val parent = EmbeddedSimpleParent("parent")
+ parent.child = EmbeddedSimpleChild("child")
+
+ val managedParent: EmbeddedSimpleParent = it.copyToRealm(parent)
+ assertEquals(1, realm.where().count())
+ val managedChild: EmbeddedSimpleChild = managedParent.child!!
+
+ managedParent.deleteFromRealm()
+ assertFalse(managedChild.isValid)
+ assertEquals(0, realm.where().count())
+ assertEquals(0, realm.where().count())
+ }
+
+ // Check that deleting a embedded parent deletes all embedded children
+ @Test
+ fun deleteParentEmbeddedObject_deletesEmbeddedChildren() = realm.executeTransaction {
+ val parent = EmbeddedTreeParent("parent1")
+ val middleNode = EmbeddedTreeNode("node1")
+ middleNode.leafNode = EmbeddedTreeLeaf("leaf1")
+ middleNode.leafNodeList.add(EmbeddedTreeLeaf("leaf2"))
+ middleNode.leafNodeList.add(EmbeddedTreeLeaf("leaf3"))
+ parent.middleNode = middleNode
+
+ val managedParent: EmbeddedTreeParent = it.copyToRealm(parent)
+ assertEquals(1, realm.where().count())
+ assertEquals(3, realm.where().count())
+ managedParent.deleteFromRealm()
+ assertEquals(0, realm.where().count())
+ assertEquals(0, realm.where().count())
+ }
+
+ // Cascade deleting an embedded object will trigger its object listener.
+ @Test
+ fun deleteParent_triggerChildObjectNotifications() = looperThread.runBlocking {
+ val realm = Realm.getInstance(realm.configuration)
+ looperThread.closeAfterTest(realm)
+
+ realm.executeTransaction {
+ val parent = EmbeddedSimpleParent("parent")
+ val child = EmbeddedSimpleChild("child")
+ parent.child = child
+ it.copyToRealm(parent)
+ }
+
+ val child = realm.where().findFirst()!!.child!!
+ child.addChangeListener(RealmChangeListener {
+ if (!it.isValid) {
+ looperThread.testComplete()
+ }
+ })
+
+ realm.executeTransaction {
+ child.parent!!.deleteFromRealm()
+ }
+ }
+
+ // Cascade deleting a parent will trigger the listener on any lists in child embedded
+ // objects
+ @Test
+ fun deleteParent_triggerChildListObjectNotifications() = looperThread.runBlocking {
+ val realm = Realm.getInstance(realm.configuration)
+ looperThread.closeAfterTest(realm)
+
+ realm.executeTransaction {
+ val parent = EmbeddedSimpleListParent("parent")
+ val child1 = EmbeddedSimpleChild("child1")
+ val child2 = EmbeddedSimpleChild("child2")
+ parent.children.add(child1)
+ parent.children.add(child2)
+ it.copyToRealm(parent)
+ }
+
+ val children: RealmList = realm.where()
+ .findFirst()!!
+ .children
+
+ children.addChangeListener { list ->
+ if (!list.isValid) {
+ looperThread.testComplete()
+ }
+ }
+
+ realm.executeTransaction {
+ realm.where().findFirst()!!.deleteFromRealm()
+ }
+ }
+
+
+ @Test
+ @Ignore("Add in another PR")
+ fun results_bulkUpdate() {
+ // What happens if you bulk update a RealmResults. Should it be allowed to use embeded
+ // objects here?
+ TODO()
+ }
+}
\ No newline at end of file
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/ObjectIdTests.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/ObjectIdTests.kt
index f1e755a5e1..7162bbb1f2 100644
--- a/realm/realm-library/src/androidTest/kotlin/io/realm/ObjectIdTests.kt
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/ObjectIdTests.kt
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2020 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 androidx.test.ext.junit.runners.AndroidJUnit4
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedCircularChild.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedCircularChild.kt
new file mode 100644
index 0000000000..f706aeaab6
--- /dev/null
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedCircularChild.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 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.entities.embedded
+
+import io.realm.RealmObject
+import io.realm.annotations.RealmClass
+import java.util.*
+
+/**
+ * Embedded object that point to itself. Note, this is only allowed in the schema. The actual
+ * objects are not allowed to have circular references.
+ */
+@RealmClass(embedded = true)
+open class EmbeddedCircularChild(var id: String = UUID.randomUUID().toString()) : RealmObject() {
+ var singleChild: EmbeddedCircularChild? = null
+}
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedCircularParent.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedCircularParent.kt
new file mode 100644
index 0000000000..8209ca9436
--- /dev/null
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedCircularParent.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2020 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.entities.embedded
+
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import java.util.*
+
+// Parent pointing to an embedded object that has a circular schema, i.e. objects can point
+// to themselves. Note, this isn't actually allowed at runtime. Only at schema validation time.
+open class EmbeddedCircularParent(@PrimaryKey var id: String = UUID.randomUUID().toString()) : RealmObject() {
+ var singleChild: EmbeddedCircularChild? = null
+}
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedSimpleChild.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedSimpleChild.kt
new file mode 100644
index 0000000000..345cbbf908
--- /dev/null
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedSimpleChild.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 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.entities.embedded
+
+import io.realm.RealmObject
+import io.realm.annotations.LinkingObjects
+import io.realm.annotations.RealmClass
+import java.util.*
+
+/**
+ * The embedded object part of a simple object graph. This object can have two parents
+ * [EmbeddedSimpleParent] and [EmbeddedSimpleListParent].
+ */
+@RealmClass(embedded = true)
+open class EmbeddedSimpleChild(var id: String = UUID.randomUUID().toString()) : RealmObject() {
+
+ @LinkingObjects("child")
+ val parent = EmbeddedSimpleParent()
+
+ companion object {
+ const val NAME = "EmbeddedSimpleChild"
+ }
+
+}
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedSimpleListParent.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedSimpleListParent.kt
new file mode 100644
index 0000000000..507e7f1f22
--- /dev/null
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedSimpleListParent.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 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.entities.embedded
+
+import io.realm.RealmList
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import java.util.*
+
+// Top-level object describing a simple embedded objects structure consisting of only a
+// list of embedded objects.
+open class EmbeddedSimpleListParent(@PrimaryKey var id: String = UUID.randomUUID().toString()) : RealmObject() {
+ var children: RealmList = RealmList()
+}
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedSimpleParent.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedSimpleParent.kt
new file mode 100644
index 0000000000..1f7f07c15d
--- /dev/null
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedSimpleParent.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2020 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.entities.embedded
+
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import java.util.*
+
+// Top-level object describing a simple embedded objects structure consisting of only an object reference.
+open class EmbeddedSimpleParent(@PrimaryKey var id: String = UUID.randomUUID().toString()) : RealmObject() {
+ var child: EmbeddedSimpleChild? = null
+}
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedTreeLeaf.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedTreeLeaf.kt
new file mode 100644
index 0000000000..6efb4022b3
--- /dev/null
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedTreeLeaf.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 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.entities.embedded
+
+import io.realm.RealmObject
+import io.realm.annotations.LinkingObjects
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.RealmClass
+import java.util.*
+
+// Middle-level node in a object-graph that is three-shaped, i.e. no circular references.
+// The tree depth can be described as:
+// - 1 TreeParent
+// - 1 or more TreeNode's. I.e. a TreeNode can be the child of another TreeNode.
+// - 1 or more TreeLeaf objects. TreeLeaf objects are always at the bottom of tree.
+@RealmClass(embedded = true)
+open class EmbeddedTreeLeaf(var id: String = UUID.randomUUID().toString()) : RealmObject() {
+
+ @LinkingObjects("leafNode")
+ val parentRef: EmbeddedTreeNode? = null
+
+ @LinkingObjects("leafNodeList")
+ val parentListRef: EmbeddedTreeNode? = null
+}
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedTreeNode.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedTreeNode.kt
new file mode 100644
index 0000000000..bd70f05c7e
--- /dev/null
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedTreeNode.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 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.entities.embedded
+
+import io.realm.RealmList
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import io.realm.annotations.RealmClass
+import java.util.*
+
+// Middle-level node in a object-graph that is three-shaped, i.e. no circular references.
+// The tree depth can be described as:
+// - 1 TreeParent
+// - 1 or more TreeNode's. I.e. a TreeNode can be the child of another TreeNode.
+// - 1 or more TreeLeaf objects. TreeLeaf objects are always at the bottom of tree.
+@RealmClass(embedded = true)
+open class EmbeddedTreeNode(var id: String = UUID.randomUUID().toString()) : RealmObject() {
+ var middleNode: EmbeddedTreeNode? = null
+ var leafNode: EmbeddedTreeLeaf? = null
+ var middleNodeList: RealmList = RealmList()
+ var leafNodeList: RealmList = RealmList()
+}
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedTreeParent.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedTreeParent.kt
new file mode 100644
index 0000000000..4f2b035dbf
--- /dev/null
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedTreeParent.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2020 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.entities.embedded
+
+import io.realm.RealmList
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import java.util.*
+
+// Top-level node in a object-graph that is three-shaped, i.e. no circular references.
+// The tree depth can be described as:
+// - 1 TreeParent
+// - 1 or more TreeNode's. I.e. a TreeNode can be the child of another TreeNode.
+// - 1 or more TreeLeaf objects. TreeLeaf objects are always at the bottom of tree.
+open class EmbeddedTreeParent(@PrimaryKey var id: String = UUID.randomUUID().toString()) : RealmObject() {
+ var middleNode: EmbeddedTreeNode? = null
+ var middleNodeList: RealmList = RealmList()
+}
diff --git a/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedWithConstructorArgs.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedWithConstructorArgs.kt
new file mode 100644
index 0000000000..7ce1624ba7
--- /dev/null
+++ b/realm/realm-library/src/androidTest/kotlin/io/realm/entities/embedded/EmbeddedWithConstructorArgs.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2020 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.entities.embedded
+
+import io.realm.RealmObject
+import io.realm.annotations.Ignore
+
+open class EmbeddedWithConstructorArgs : RealmObject() {
+ var child: EmbeddedSimpleChild? = null
+ init {
+ child = EmbeddedSimpleChild(INNER_CHILD_ID)
+ }
+
+ companion object {
+ const val INNER_CHILD_ID = "innerChild"
+ }
+}
diff --git a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/rule/BlockingLooperThread.kt b/realm/realm-library/src/androidTest/kotlin/io/realm/rule/BlockingLooperThread.kt
similarity index 100%
rename from realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/rule/BlockingLooperThread.kt
rename to realm/realm-library/src/androidTest/kotlin/io/realm/rule/BlockingLooperThread.kt
diff --git a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SyncedRealmMigrationTests.kt b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SyncedRealmMigrationTests.kt
index 4d1b63393a..937959fe2f 100644
--- a/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SyncedRealmMigrationTests.kt
+++ b/realm/realm-library/src/androidTestObjectServer/kotlin/io/realm/SyncedRealmMigrationTests.kt
@@ -124,7 +124,7 @@ class SyncedRealmMigrationTests {
.build()
// Setup initial Realm schema (with a different primary key)
- val expectedObjectSchema = OsObjectSchemaInfo.Builder(PrimaryKeyAsString.CLASS_NAME, 2, 0)
+ val expectedObjectSchema = OsObjectSchemaInfo.Builder(PrimaryKeyAsString.CLASS_NAME, false,2, 0)
.addPersistedProperty(PrimaryKeyAsString.FIELD_PRIMARY_KEY, RealmFieldType.STRING, false, true, false)
.addPersistedProperty(PrimaryKeyAsString.FIELD_ID, RealmFieldType.INTEGER, true, true, true)
.build()
diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_OsList.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_OsList.cpp
index c5f6544108..f2023f6563 100644
--- a/realm/realm-library/src/main/cpp/io_realm_internal_OsList.cpp
+++ b/realm/realm-library/src/main/cpp/io_realm_internal_OsList.cpp
@@ -22,6 +22,7 @@
#include "observable_collection_wrapper.hpp"
#include "java_accessor.hpp"
+#include "java_object_accessor.hpp"
#include "java_exception_def.hpp"
#include "jni_util/java_exception_thrower.hpp"
#include "util.hpp"
@@ -550,6 +551,43 @@ JNIEXPORT jobject JNICALL Java_io_realm_internal_OsList_nativeGetValue(JNIEnv* e
return nullptr;
}
+JNIEXPORT jlong JNICALL Java_io_realm_internal_OsList_nativeCreateAndAddEmbeddedObject(JNIEnv* env, jclass, jlong native_list_ptr, jlong j_index)
+{
+ try {
+ List& list = reinterpret_cast(native_list_ptr)->collection();
+ auto& realm = list.get_realm();
+ auto& object_schema = list.get_object_schema();
+ JavaContext ctx(env, realm, object_schema);
+ // Create dummy object. Properties must be added later.
+ // TODO CreatePolicy::Skip is a hack right after the object is inserted and before Schemas
+ // are validated. Figure out a better approach.
+ auto array_index = static_cast(j_index);
+ list.insert(ctx, array_index, JavaValue(std::map()), CreatePolicy::Skip);
+ return reinterpret_cast(list.get(array_index).get_key().value);
+ }
+ CATCH_STD()
+ return reinterpret_cast(nullptr);
+}
+
+
+JNIEXPORT jlong JNICALL Java_io_realm_internal_OsList_nativeCreateAndSetEmbeddedObject(JNIEnv* env, jclass, jlong native_list_ptr, jlong j_index)
+{
+ try {
+ List& list = reinterpret_cast(native_list_ptr)->collection();
+ auto& realm = list.get_realm();
+ auto& object_schema = list.get_object_schema();
+ JavaContext ctx(env, realm, object_schema);
+ size_t array_index = static_cast(j_index);
+ // Create dummy object. Properties must be added later.
+ // TODO CreatePolicy::Skip is a hack right after the object is inserted and before Schemas
+ // are validated. Figure out a better approach.
+ list.set(ctx, array_index, JavaValue(std::map()), CreatePolicy::Skip);
+ return reinterpret_cast(list.get(list.size() - 1).get_key().value);
+ }
+ CATCH_STD()
+ return reinterpret_cast(nullptr);
+}
+
JNIEXPORT jlong JNICALL Java_io_realm_internal_OsList_nativeFreeze(JNIEnv* env, jclass, jlong native_list_ptr, jlong frozen_realm_native_ptr)
{
try {
diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_OsObject.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_OsObject.cpp
index 5bfcdf1439..7e3305fcbb 100644
--- a/realm/realm-library/src/main/cpp/io_realm_internal_OsObject.cpp
+++ b/realm/realm-library/src/main/cpp/io_realm_internal_OsObject.cpp
@@ -389,3 +389,24 @@ JNIEXPORT jlong JNICALL Java_io_realm_internal_OsObject_nativeCreateNewObjectWit
CATCH_STD()
return 0;
}
+
+JNIEXPORT jlong JNICALL Java_io_realm_internal_OsObject_nativeCreateEmbeddedObject(
+ JNIEnv* env, jclass, jlong j_parent_table_ptr, jlong j_parent_object_key, jlong j_parent_column_key)
+{
+ try {
+ TableRef table = TBL_REF(j_parent_table_ptr);
+ ObjKey obj_key(static_cast(j_parent_object_key));
+ Obj parent_obj = table->get_object(obj_key);
+ ColKey col_key(static_cast(j_parent_column_key));
+ Obj child_obj;
+ if (table->get_column_type(col_key) == type_Link) {
+ child_obj = parent_obj.create_and_set_linked_object(col_key);
+ } else {
+ LnkLstPtr list = parent_obj.get_linklist_ptr(col_key);
+ child_obj = list->create_and_insert_linked_object(list->size());
+ }
+ return to_jlong_or_not_found(child_obj.get_key());
+ }
+ CATCH_STD()
+ return 0;
+}
diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_OsObjectSchemaInfo.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_OsObjectSchemaInfo.cpp
index 8e7841e674..5835ced274 100644
--- a/realm/realm-library/src/main/cpp/io_realm_internal_OsObjectSchemaInfo.cpp
+++ b/realm/realm-library/src/main/cpp/io_realm_internal_OsObjectSchemaInfo.cpp
@@ -36,12 +36,14 @@ static void finalize_object_schema(jlong ptr)
}
JNIEXPORT jlong JNICALL Java_io_realm_internal_OsObjectSchemaInfo_nativeCreateRealmObjectSchema(JNIEnv* env, jclass,
- jstring j_name_str)
+ jstring j_name_str,
+ jboolean j_embedded)
{
try {
JStringAccessor name(env, j_name_str);
ObjectSchema* object_schema = new ObjectSchema();
object_schema->name = name;
+ object_schema->is_embedded = to_bool(j_embedded);
return reinterpret_cast(object_schema);
}
CATCH_STD()
@@ -129,3 +131,13 @@ JNIEXPORT jlong JNICALL Java_io_realm_internal_OsObjectSchemaInfo_nativeGetPrima
CATCH_STD()
return reinterpret_cast(nullptr);
}
+
+JNIEXPORT jboolean JNICALL Java_io_realm_internal_OsObjectSchemaInfo_nativeIsEmbedded(JNIEnv* env, jclass, jlong native_ptr)
+{
+ try {
+ auto& object_schema = *reinterpret_cast(native_ptr);
+ return to_jbool(object_schema.is_embedded);
+ }
+ CATCH_STD()
+ return to_jbool(false);
+}
diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_Table.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_Table.cpp
index 60f3231742..ad93293322 100644
--- a/realm/realm-library/src/main/cpp/io_realm_internal_Table.cpp
+++ b/realm/realm-library/src/main/cpp/io_realm_internal_Table.cpp
@@ -939,3 +939,23 @@ JNIEXPORT jlong JNICALL Java_io_realm_internal_Table_nativeFreeze(JNIEnv*, jclas
TableRef* frozen_table = new TableRef(shared_realm->import_copy_of(table));
return reinterpret_cast(frozen_table);
}
+
+JNIEXPORT jboolean JNICALL Java_io_realm_internal_Table_nativeIsEmbedded(JNIEnv* env, jclass, jlong j_table_ptr)
+{
+ try {
+ TableRef table = TableRef(TBL_REF(j_table_ptr));
+ return to_jbool(table->is_embedded());
+ }
+ CATCH_STD()
+ return false;
+}
+
+JNIEXPORT jboolean JNICALL Java_io_realm_internal_Table_nativeSetEmbedded(JNIEnv* env, jclass, jlong j_table_ptr, jboolean j_embedded)
+{
+ try {
+ TableRef table = TableRef(TBL_REF(j_table_ptr));
+ return to_jbool(table->set_embedded(to_bool(j_embedded)));
+ }
+ CATCH_STD()
+ return false;
+}
diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_UncheckedRow.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_UncheckedRow.cpp
index fb00218b93..6506ca5216 100644
--- a/realm/realm-library/src/main/cpp/io_realm_internal_UncheckedRow.cpp
+++ b/realm/realm-library/src/main/cpp/io_realm_internal_UncheckedRow.cpp
@@ -476,3 +476,18 @@ JNIEXPORT void JNICALL Java_io_realm_internal_UncheckedRow_nativeSetObjectId(JNI
}
CATCH_STD()
}
+
+JNIEXPORT jlong JNICALL Java_io_realm_internal_UncheckedRow_nativeCreateEmbeddedObject(JNIEnv* env, jobject,
+ jlong j_obj_ptr,
+ jlong j_column_key)
+{
+ if (!ROW_VALID(env, OBJ(j_obj_ptr))) {
+ return -1;
+ }
+ try {
+ Obj embedded_object = OBJ(j_obj_ptr)->create_and_set_linked_object(ColKey(j_column_key));
+ return reinterpret_cast(embedded_object.get_key().value);
+ }
+ CATCH_STD()
+ return -1;
+}
diff --git a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsObjectBuilder.cpp b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsObjectBuilder.cpp
index 2427b6c3f3..57bd430a55 100644
--- a/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsObjectBuilder.cpp
+++ b/realm/realm-library/src/main/cpp/io_realm_internal_objectstore_OsObjectBuilder.cpp
@@ -176,8 +176,13 @@ static inline const ObjectSchema& get_schema(const Schema& schema, TableRef tabl
return *it;
}
-JNIEXPORT jlong JNICALL Java_io_realm_internal_objectstore_OsObjectBuilder_nativeCreateOrUpdate
- (JNIEnv* env, jclass, jlong shared_realm_ptr, jlong table_ref_ptr, jlong builder_ptr, jboolean update_existing, jboolean ignore_same_values)
+JNIEXPORT jlong JNICALL Java_io_realm_internal_objectstore_OsObjectBuilder_nativeCreateOrUpdateTopLevelObject(JNIEnv* env,
+ jclass,
+ jlong shared_realm_ptr,
+ jlong table_ref_ptr,
+ jlong builder_ptr,
+ jboolean update_existing,
+ jboolean ignore_same_values)
{
try {
SharedRealm shared_realm = *(reinterpret_cast(shared_realm_ptr));
@@ -202,6 +207,31 @@ JNIEXPORT jlong JNICALL Java_io_realm_internal_objectstore_OsObjectBuilder_nativ
return realm::npos;
}
+JNIEXPORT jlong JNICALL Java_io_realm_internal_objectstore_OsObjectBuilder_nativeUpdateEmbeddedObject(JNIEnv* env,
+ jclass,
+ jlong shared_realm_ptr,
+ jlong table_ref_ptr,
+ jlong builder_ptr,
+ jlong j_obj_key,
+ jboolean ignore_same_values)
+{
+ try {
+ SharedRealm shared_realm = *(reinterpret_cast(shared_realm_ptr));
+ CreatePolicy policy = (ignore_same_values) ? CreatePolicy::UpdateModified : CreatePolicy::UpdateAll;
+ TableRef table = TBL_REF(table_ref_ptr);
+ ObjKey embedded_object_key(j_obj_key);
+ const auto& schema = shared_realm->schema();
+ const ObjectSchema& object_schema = get_schema(schema, table);
+ JavaContext ctx(env, shared_realm, object_schema);
+ auto list = *reinterpret_cast(builder_ptr);
+ JavaValue values = JavaValue(list);
+ Object obj = Object::create(ctx, shared_realm, object_schema, values, policy, embedded_object_key);
+ return reinterpret_cast(new Obj(obj.obj()));
+ }
+ CATCH_STD()
+ return realm::npos;
+}
+
JNIEXPORT jlong JNICALL Java_io_realm_internal_objectstore_OsObjectBuilder_nativeStartList
(JNIEnv* env, jclass, jlong list_size)
{
diff --git a/realm/realm-library/src/main/java/io/realm/FrozenPendingRow.java b/realm/realm-library/src/main/java/io/realm/FrozenPendingRow.java
index 693fe41909..58fc0fb63f 100644
--- a/realm/realm-library/src/main/java/io/realm/FrozenPendingRow.java
+++ b/realm/realm-library/src/main/java/io/realm/FrozenPendingRow.java
@@ -197,6 +197,11 @@ public void setObjectId(long columnKey, ObjectId value) {
throw new IllegalStateException(QUERY_NOT_RETURNED_MESSAGE);
}
+ @Override
+ public long createEmbeddedObject(long columnKey) {
+ throw new IllegalStateException(QUERY_NOT_RETURNED_MESSAGE);
+ }
+
@Override
public boolean isValid() {
return false;
diff --git a/realm/realm-library/src/main/java/io/realm/Realm.java b/realm/realm-library/src/main/java/io/realm/Realm.java
index 364f8d6d2b..716080fa74 100644
--- a/realm/realm-library/src/main/java/io/realm/Realm.java
+++ b/realm/realm-library/src/main/java/io/realm/Realm.java
@@ -47,6 +47,7 @@
import javax.annotation.Nullable;
import io.reactivex.Flowable;
+import io.realm.annotations.RealmClass;
import io.realm.exceptions.RealmException;
import io.realm.exceptions.RealmFileException;
import io.realm.exceptions.RealmMigrationNeededException;
@@ -62,6 +63,7 @@
import io.realm.internal.RealmNotifier;
import io.realm.internal.RealmObjectProxy;
import io.realm.internal.RealmProxyMediator;
+import io.realm.internal.Row;
import io.realm.internal.Table;
import io.realm.internal.Util;
import io.realm.internal.annotations.ObjectServer;
@@ -158,9 +160,9 @@ private Realm(RealmCache cache, OsSharedRealm.VersionID version) {
schema = new ImmutableRealmSchema(this,
new ColumnIndices(configuration.getSchemaMediator(), sharedRealm.getSchemaInfo()));
// FIXME: This is to work around the different behaviour between the read only Realms in the Object Store and
- // in current java implementation. Opening a read only Realm with some missing schemas is allowed by Object
- // Store and realm-cocoa. In that case, any query based on the missing schema should just return an empty
- // results. Fix this together with https://github.com/realm/realm-java/issues/2953
+ // in current java implementation. Opening a read only Realm with some missing schemas is allowed by Object
+ // Store and realm-cocoa. In that case, any query based on the missing schema should just return an empty
+ // results. Fix this together with https://github.com/realm/realm-java/issues/2953
if (configuration.isReadOnly()) {
RealmProxyMediator mediator = configuration.getSchemaMediator();
Set> classes = mediator.getModelClasses();
@@ -964,6 +966,10 @@ private Scanner getFullStringScanner(InputStream in) {
*/
public E createObject(Class clazz) {
checkIfValid();
+ RealmProxyMediator mediator = configuration.getSchemaMediator();
+ if (mediator.isEmbedded(clazz)) {
+ throw new IllegalArgumentException("This class is marked embedded. Use `createEmbeddedObject(class, parent, property)` instead: " + mediator.getSimpleClassName(clazz));
+ }
return createObjectInternal(clazz, true, Collections.emptyList());
}
@@ -1011,9 +1017,63 @@ E createObjectInternal(
*/
public E createObject(Class clazz, @Nullable Object primaryKeyValue) {
checkIfValid();
+ RealmProxyMediator mediator = configuration.getSchemaMediator();
+ if (mediator.isEmbedded(clazz)) {
+ throw new IllegalArgumentException("This class is marked embedded. Use `createEmbeddedObject(class, parent, property)` instead: " + mediator.getSimpleClassName(clazz));
+ }
return createObjectInternal(clazz, primaryKeyValue, true, Collections.emptyList());
}
+ /**
+ * Instantiates and adds a new embedded object to the Realm.
+ *
+ * This method should only be used to created objects of types marked as embedded.
+ *
+ * @param clazz the Class of the object to create. It must be marked with {@code \@RealmClass(embedded = true)}.
+ * @param parent The parent object which should a reference to the embedded object. If the parent property is a list
+ * the embedded object will be added to the end of that list.
+ * @param parentProperty the property in the parent class which holds the reference.
+ * @return the newly created embedded object.
+ * @throws IllegalArgumentException if {@code clazz} is not an embedded class or if the property
+ * in the parent class cannot hold objects of the appropriate type.
+ * @see RealmClass#embedded()
+ */
+ public E createEmbeddedObject(Class clazz, RealmModel parentObject, String parentProperty) {
+ checkIfValid();
+ Util.checkNull(parentObject, "parentObject");
+ Util.checkEmpty(parentProperty, "parentProperty");
+ if (!RealmObject.isManaged(parentObject) || !RealmObject.isValid(parentObject)) {
+ throw new IllegalArgumentException("Only valid, managed objects can be a parent to an embedded object.");
+ }
+ RealmObjectProxy proxy = (RealmObjectProxy) parentObject;
+ long parentPropertyColKey = schema.getSchemaForClass(parentObject.getClass()).getColumnKey(parentProperty);
+ RealmFieldType parentPropertyType = schema.getSchemaForClass(parentObject.getClass()).getFieldType(parentProperty);
+ Row embeddedObject;
+ switch(parentPropertyType) {
+ case OBJECT: {
+ // FIXME: Check type of link
+ long objKey = proxy.realmGet$proxyState().getRow$realm().createEmbeddedObject(parentPropertyColKey);
+ embeddedObject = getTable(clazz).getUncheckedRow(objKey);
+ break;
+ }
+ case LIST: {
+ // FIXME: Check type of link
+ long objKey = proxy.realmGet$proxyState().getRow$realm().getModelList(parentPropertyColKey).createAndAddEmbeddedObject();
+ embeddedObject = getTable(clazz).getUncheckedRow(objKey);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Parent property is not a reference to embedded objects of the appropriate type: " + parentPropertyType);
+ }
+
+ //noinspection unchecked
+ return (E) configuration.getSchemaMediator().newInstance(clazz,
+ this,
+ embeddedObject,
+ schema.getColumnInfo(clazz),
+ true, Collections.EMPTY_LIST);
+ }
+
/**
* Same as {@link #createObject(Class, Object)} but this does not check the thread.
*
@@ -1057,6 +1117,7 @@ E createObjectInternal(
*/
public E copyToRealm(E object, ImportFlag... flags) {
checkNotNullObject(object);
+
return copyOrUpdate(object, false, new HashMap<>(), Util.toSet(flags));
}
@@ -1685,6 +1746,10 @@ private E copyOrUpdate(E object, boolean update, Map E copyToRealmIfNeeded(E object) {
+ private boolean checkCanObjectBeCopied(BaseRealm realm, RealmModel object) {
if (object instanceof RealmObjectProxy) {
RealmObjectProxy proxy = (RealmObjectProxy) object;
@@ -1559,7 +1604,7 @@ private E copyToRealmIfNeeded(E object) {
String objectClassName = ((DynamicRealmObject) object).getType();
if (listClassName.equals(objectClassName)) {
// Same Realm instance and same target table
- return object;
+ return false;
} else {
// Different target table
throw new IllegalArgumentException(String.format(Locale.US,
@@ -1580,11 +1625,15 @@ private E copyToRealmIfNeeded(E object) {
if (realm != proxy.realmGet$proxyState().getRealm$realm()) {
throw new IllegalArgumentException("Cannot copy an object from another Realm instance.");
}
- return object;
+ return false;
}
}
}
+ return true;
+ }
+ // Transparently copies an unmanaged object or managed object from another Realm to the Realm backing this RealmList.
+ private E copyToRealm(E object) {
// At this point the object can only be a typed object, so the backing Realm cannot be a DynamicRealm.
Realm realm = (Realm) this.realm;
if (OsObjectStore.getPrimaryKeyForObject(realm.getSharedRealm(),
@@ -1594,6 +1643,15 @@ private