- 
                Notifications
    
You must be signed in to change notification settings  - Fork 1.8k
 
Support for overriding internal names using annotations #5280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…me of classes and fields.
Conflicts: realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java realm/realm-library/src/main/java/io/realm/internal/ColumnIndices.java
          
 I don't really get the "policy" thing (I know it's like from GSON, but...) I'd just expect something like a  Maybe that's just me though. I just think the policy is not as essential as the ability of setting a schema field name.  | 
    
| 
           @Zhuinden The problem with  Originally I was playing around with overloading  Modules are a bit of a special case though and allowing   | 
    
          
 I think it is the distinction between   | 
    
| 
           I guess you are right. So there is precedence for that 👍 I kinda like that. It also means we can remove the   | 
    
| 
           @cmelchior The only tricky thing though is that you already have a   | 
    
| 
           Yes, but that last one would take priority I would assume, but I haven't tested how the annotation processor actually behaves in that scenario  | 
    
| 
           Just for reference. This is how LoganSquare does it: bluelinelabs/LoganSquare@41572aa  | 
    
# Conflicts: # realm/realm-annotations-processor/src/main/java/io/realm/processor/ClassMetaData.java # realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProcessor.java # realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java
…mModule, RealmClass and a new RealmField annotation.
# Conflicts: # realm-annotations/src/main/java/io/realm/annotations/RealmClass.java # realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java
# Conflicts: # realm/realm-annotations-processor/build.gradle # realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProcessor.java
| import some.test.AllTypes; | ||
| 
               | 
          ||
| public class NamingPolicyConflictingModules { | ||
| public class NamePoliceConflictingModuleDefinitionsForAllClasses { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Police!
# Conflicts: # CHANGELOG.md # realm/realm-annotations-processor/src/main/java/io/realm/processor/RealmProxyClassGenerator.java
| 
           Ready for review @nhachicha  | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewing doc; disclaimer: not a native English speaker.
| 
               | 
          ||
| /** | ||
| * The naming policy applied to all classes part of this module. The default policy is {@link RealmNamingPolicy#NO_POLICY}. | ||
| * To define a naming policy for all fields in those classes, use {@link #fieldNamingPolicy()}. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"those" -> "the"
| * This enum defines the possible ways class and field names can be mapped from what is used in Java | ||
| * to the name used internally in the Realm file. | ||
| * <p> | ||
| * Examples where this can be useful is e.g: | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove "e.g"
"is" -> "are"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can't be right!! 😄
| * </li> | ||
| * </ol> | ||
| * <p> | ||
| * Note, that changing the internal name does <i>NOT</i> effect importing data from JSON. The JSON | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No comma
| * if set in {@link RealmClass#fieldNamingPolicy}, the module policy will still apply to field | ||
| * names. | ||
| * <p> | ||
| * If two modules disagree on the policy and one of them is {@code NO_POLICY}, the other one | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"other one" -> "other"
| * Example is "_FooBar" or "_Foo$Bar" which both becomes "Foo" and "Bar". | ||
| * </li> | ||
| * <li> | ||
| * Anytime your switch from a lower case character to a upper case character as | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"your" -> "you"
| * </li> | ||
| * <li> | ||
| * Anytime your switch from a lower case character to a upper case character as | ||
| * identified by a Character.isUpperCase(codepoint)` and `Character.isLowerCase(codepoint)`. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing backquote (after "by a")
| * Examples are "my😁" and "MY😁" which are both treated as one word. | ||
| * </li> | ||
| * <li> | ||
| * Hungarian notation, i.e. Strings starting with lowercase "m" followed by uppercase letter | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Strings" -> "strings"
| * to the name used internally in the Realm file. | ||
| * <p> | ||
| * Examples where this can be useful is e.g: | ||
| * Examples where this are useful: | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this are? That can't be right, the previous was better 😛
It's probably is.
| * </ol> | ||
| * <p> | ||
| * Note, that changing the internal name does <i>NOT</i> effect importing data from JSON. The JSON | ||
| * Note that changing the internal name does <i>NOT</i> effect importing data from JSON. The JSON | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now I wonder if this is affect or effect.
I never know that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://www.vocabulary.com/articles/chooseyourwords/affect-effect/
It should be affect I think :)
| * <p> | ||
| * If two modules disagree on the policy and one of them is {@code NO_POLICY}, the other one | ||
| * will be chosen without an error being thrown. | ||
| * If two modules disagree on the policy and one of them is {@code NO_POLICY}, the other will | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe the other module's policy
But what happens if there are 3 @RealmModules and they all have conflicting policy? Which one will override NO_POLICY?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For each class you find all modules it is part of, then you compare the policy between all these 3, so it will throw. The implementation is slightly different, but the effect is the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice PR, some minor comments, also I didn't see tests with models defined using RealmModel interface
| @@ -0,0 +1,124 @@ | |||
| /* | |||
| * Copyright 2017 Realm Inc. | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2018
| * <ol> | ||
| * <li> | ||
| * Pre-processing. Done by calling {@link #preProcess(Set)}, which will do an initial parse | ||
| * of the modules and build up all information it can before before processing any model | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove before
| * Validates that the class/field naming policy for this module is correct. | ||
| * | ||
| * @param globalModuleInfo list of all modules with `allClasses` set | ||
| * @param classSpecificModuleInfo | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing Javadoc
| } | ||
| 
               | 
          ||
| // Check for conflicts with specifically named classes. This can happen if another | ||
| // module are listing specific classes with another policy. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is
| // with NO_POLICY, meaning it will not trigger any errors. | ||
| if (moduleAnnotation.allClasses()) { | ||
| // Check for conflicts with other modules with `allClasses` set. Only need to | ||
| // check the first since other conflicts would have been detected in an earlier | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't this depend on the order on which they were added?
ex: we added the following modules :
- module 1
allClass - module 2
allClass - module 3 
allClass
now we add module4 which is incompatible with module 2, how can you detect this if you're checking only the first one? 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm thinking about this, you might be right. E.g. the following sequence of illegal modules would not be detected.
module1: NO_POLICY
module2: PASCAL_CASE
module3: NO_POLICY
module4: CAMEL_CASE
I'll compare against all previous modules.
| import io.realm.annotations.RealmNamingPolicy; | ||
| 
               | 
          ||
| /** | ||
| * Class with only a custom name | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where's the custom name definition? or do you mean the module that uses this class use a custom name?
| public void dynamicQueryWithInternalNames() { | ||
| // Backlink queries not supported on dynamic queries | ||
| RealmResults<DynamicRealmObject> results = dynamicRealm.where(ClassWithPolicy.CLASS_NAME) | ||
| .equalTo(ClassWithPolicy.FIELD_CAMEL_CASE, "foo") // Java name in model class | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Java name? shouldn't be internal name for Dynamic Realm. same for below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the class no longer exists, then you can't obtain any field-specific / class-specific policy configuration for it.
So I wonder if DynamicRealm should be able to see Java name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DynamicRealms has to work with the assumption that no java model classes exists. That this test case actually references ClassWithPolicy is just so it is easier to maintain the test case.
But you are correct, DynamicRealms do not understand the concept of name policies as that concept isn't relevant to them.
| @@ -0,0 +1,16 @@ | |||
| package io.realm.entities.realmname; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
licence
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| @@ -0,0 +1,7 @@ | |||
| package io.realm.entities.realmname; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
licence
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| buf.append(", InternalFieldNames=["); | ||
| boolean commaNeeded = false; | ||
| for (Map.Entry<String, ColumnDetails> entry : indicesFromColumnNames.entrySet()) { | ||
| if (commaNeeded) { buf.append(","); } | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
commaNeeded = true; should be inside the if?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, in that case, it would never be set to the value true.
| * | ||
| * @param table the start table. | ||
| * @param fieldDescription the field description. | ||
| * @param fieldDescription the field description using internal column columns. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
column columns?
| @Override | ||
| public void emitStatement(int i, JavaWriter writer) throws IOException { | ||
| writer.emitStatement("return %s.getSimpleClassName()", qualifiedProxyClasses.get(i)); | ||
| writer.emitStatement("return \"%s\"", internalClassNames.get(i)); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That does seem more Proguard-friendly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not to mention correct 😉 The previous implementation did not work if you renamed the class name using @RealmClass(name = "othername").
| 
           All comments addressed @nhachicha  | 
    
| for (Class<? extends RealmModel> realmClass : mediator.getModelClasses()) { | ||
| // Verify that the module doesn't contain conflicting definitions for the same | ||
| // underlying internal name. Can only happen if we add a module from a library and | ||
| // and a module from the app at the same time. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove and
Fixes #2476
Fixes #1616
This PR adds support for creating a mapping between internal names used by Realm and those used in the model classes which e.g. makes it easier to share schemas between languages without compromising the standard naming conventions in Java
it is also possible to apply a
RealmNamePolicyfor a class or module instead of being forced to manually override each and every field.The basic implementation is a simple mapping scheme in Java before any relevant field or class name is sent to Object Store. As long as Object Store does not support a true dynamic mode, we cannot have this functionality living there.
Especially, the following semantics are in place:
RealmObjectSchemauses the internal names. This might seem really annoying, but the primary use case is sync, where migrations are automatic.useInternalNameForJsonto these annotations though Schema errors will use the internal names instead of the external ones.toString()still uses Java namesReview Guide:
ModuleMetaData. This covers detecting proper use of the introduced naming policiesColumnInfowas refactored to store both external and internal names. Most refactors here are adding Javadoc + renaming variables to express intent better.RealmNameTestsfor annotation processor tests that test converter logic + proper use of annotationsCustomRealmNameTestsfor library unit tests that mostly test the functionality of queries and schema API's.TODO: