Skip to content

Array of Primitives: RealmObjectSchema support#5329

Merged
cmelchior merged 273 commits intomasterfrom
cm/cm/primitivelist-schema-support
Oct 3, 2017
Merged

Array of Primitives: RealmObjectSchema support#5329
cmelchior merged 273 commits intomasterfrom
cm/cm/primitivelist-schema-support

Conversation

@cmelchior
Copy link
Copy Markdown
Contributor

@cmelchior cmelchior commented Sep 26, 2017

This adds support for primitive lists to `RealmObjectSchema

  • Going around Object Store and directly to Core to dynamically add primitive lists during migrations. Not ideal, but using Object Store constants where possible.
  • Refactored Table.cpp#nativeConvertColumnToNotNullable and Table.cpp#nativeConvertColumnToNullable` so they work on both normal tables and sub tables. This is the biggest change, so please focus review there.
  • Added bunch of tests.

Open questions:
Is RealmObjectSchema.addRealmListField(String name, Class<?> class) the best name? We already have RealmObjectSchema.addRealmListField(String name, RealmObjectSchema class), but is the overload weird? Another alternative could be addPrimitiveRealmListField(name, class) or addPrimitiveListField(name, class)? For the dynamic API, we have getList(string fieldName). This will either need an overload with getList(String fieldName, Class<?> or getPrimitiveList(String fieldName, Class<?> clazz).

Since these lists behavior is different from lists with Realm models, I would be tempted to go with:

  • `RealmObjectSchema.addPrimitiveListField(String fieldName, Class<?> elementType);
  • `DynamicRealmObject.getPrimitiveList(String fieldName, Class<?> elementType);

or

  • `RealmObjectSchema.addValueListField(String fieldName, Class<?> elementType);
  • `DynamicRealmObject.getValueList(String fieldName, Class<?> elementType);

ValueList is probably more technically correct as we also support more types than just the primitives, even though Date technically isn't a value type. On the other hand, I suspect that primitive type is more familiar to people than value type,

So 3 proposals:

  1. Overload methods:
  • RealmObjectSchema.addRealmListField(String fieldName, Class<?> elementType);
  • RealmObjectSchema.addRealmListField(String fieldName, RealmObjectSchema objectSchema);
  • RealmList<E> list = DynamicRealmObject.getList(String fieldName, Class<?> elementType);
  • RealmList<DynamicRealmObject> list = DynamicRealmObject.getList(String fieldName);
  1. Use primitive:
  • RealmObjectSchema.addPrimitiveListField(String fieldName, Class<?> elementType);
  • RealmObjectSchema.addRealmListField(String fieldName, RealmObjectSchema objectSchema);
  • RealmList<E> list = DynamicRealmObject.getPrimitiveList(String fieldName, Class<E> elementType);
  • RealmList<DynamicRealmObject> list = DynamicRealmObject.getList(String fieldName);
  1. Use value:
  • RealmObjectSchema.addValueListField(String fieldName, Class<?> elementType);
  • RealmObjectSchema.addRealmListField(String fieldName, RealmObjectSchema objectSchema);
  • RealmList<E> list = DynamicRealmObject.getValueList(String fieldName, Class<E> elementType);
  • RealmList<DynamicRealmObject> list = DynamicRealmObject.getList(String fieldName);

Thoughts?

TODO

  • Fix currently broken unit tests (changing nullability doesn't seem to take effect).
  • Update IndexFieldType with primitive list types
  • Update InvalidIndexFieldType with primitive list types
  • Update InvalidPrimaryKeyFieldType with primitive list types

zaki50 and others added 30 commits September 6, 2017 22:09
- JavaAccessorContext.
- Accessors for OsList. String and binary don't work yet.
- Tests for OsList.
@cmelchior cmelchior changed the base branch from feature/primitive_list to master October 1, 2017 10:18
* @throws IllegalArgumentException if the field name is illegal, a field with that name already exists or
* the element type isn't supported.
* @throws UnsupportedOperationException if this {@link RealmObjectSchema} is immutable.
*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think naming elementType to be primitiveType would make this method easier to understand, that way you don't need to read the javadoc to know that this is for array of primitives only.

FieldMetaData metadata = SUPPORTED_SIMPLE_FIELDS.get(primitiveType);
if (metadata == null) {
if (elementType.equals(RealmObjectSchema.class)) {
if (primitiveType.equals(RealmObjectSchema.class)) {
Copy link
Copy Markdown
Contributor

@Zhuinden Zhuinden Oct 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should check against if (primitiveType.equals(RealmObjectSchema.class) || RealmModel.class.isAssignableFrom(primitiveType)) { to ensure that they get this error message if they provide the Class<?> that belongs to a RealmModel.

Currently I think they'd get RealmList does not support lists with this type: "SomeRealmObject" which could be confusing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cmelchior please verify this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this would be nice. Fixing

// All simple list types
schema.addRealmListField(fieldName, fieldType.getType());
if (fieldType.isNullable()) {
schema.setRequired(fieldName, true);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you changing the field from non-required to required?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, changing name of method to increase clarity

} catch (IllegalArgumentException ignored) {
}
break;
default:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this include all PRIMITIVE_* type as well? i.e setting primitive type to null? Shouldn't this throw?
arent' we missing if (!type.isNullable()) { continue; } ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified the enum, so it only contains non_list types. The lists are check in the next block

break;
}
}
for (FieldListType fieldType : FieldListType.values()) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no logic tested here? I wonder why you're keeping this block?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A test was being added here in #5351 . I would prefer to keep this for now to decrease merge conflicts.

return table->get_subdescriptor(col)->add_column(data_type, ObjectStore::ArrayColumnName, nullptr, is_column_nullable);
}
CATCH_STD()
return 0;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reinterpret_cast<jlong>(nullptr)

* }
* </pre>
* If the list contains references to other Realm classes, then use
* If the list contains references to other Realm classes use
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comma was needed here 😛

@Zhuinden
Copy link
Copy Markdown
Contributor

Zhuinden commented Oct 2, 2017

#5329 (comment)

public <T> RealmList<T> getValueList(String fieldName, Class<T> valueClass) {
// TODO implement this
return null;
public <E> RealmList<E> getList(String fieldName, Class<E> elementType) {
Copy link
Copy Markdown
Contributor

@Zhuinden Zhuinden Oct 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sooo, elementType vs primitiveType vs valueType?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

primitiveType.
I'm renaming variables right now 😛

switch (columnType) {
case LIST:
//noinspection unchecked
setModelList(fieldName, (RealmList<DynamicRealmObject>) list);
Copy link
Copy Markdown
Contributor

@Zhuinden Zhuinden Oct 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there should be some kind of custom error message for providing a RealmList<? extends RealmModel> that is not RealmList<DynamicRealmObject>.

Currently it'll just throw a ClassCastException

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

* @throws IllegalArgumentException if field name doesn't exist, it is not a list field, the type
* of the object represented by the DynamicRealmObject doesn't match or any element in the list belongs to a
* different Realm.
* @param list list of objects. Must either be primitive types or Realm objects.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a bit more specific that it requires {@link DynamicRealmObject}.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

schema.addRealmListField("field", AllJavaTypes.class);
fail();
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains("Use 'addRealmListField(String name, RealmObjectSchema schema)' instead"));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

FieldMetaData metadata = SUPPORTED_SIMPLE_FIELDS.get(primitiveType);
if (metadata == null) {
if (primitiveType.equals(RealmObjectSchema.class)) {
if (primitiveType.equals(RealmObjectSchema.class) || RealmModel.class.isAssignableFrom(primitiveType)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

// to throw a better error message.
// Primitive types are checked inside `setModelList`
if (!list.isEmpty()) {
E element = list.first();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just catching the possible ClassCastException and re-throwing it with the custom message would be more reliable and less hacky.

I like the message though!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The classcast exception would not catch the generic parameter, only if RealmList was of the wrong type. Type erasure is a pain :(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I guess it'll do. Thank you! 🙂

@cmelchior cmelchior merged commit b922b09 into master Oct 3, 2017
@cmelchior cmelchior deleted the cm/cm/primitivelist-schema-support branch October 3, 2017 10:43
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Mar 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants