@@ -588,8 +588,8 @@ private static class BeanMapper<T> {
588
588
private final HashSet <String > serverTimestamps ;
589
589
590
590
// A set of property names that were annotated with @DocumentId. These properties will be
591
- // populated with
592
- // document ID values during deserialization, and be skipped during serialization.
591
+ // populated with document ID values during deserialization, and be skipped during
592
+ // serialization.
593
593
private final HashSet <String > documentIdPropertyNames ;
594
594
595
595
BeanMapper (Class <T > clazz ) {
@@ -648,49 +648,47 @@ private static class BeanMapper<T> {
648
648
do {
649
649
// Add any setters
650
650
for (Method method : currentClass .getDeclaredMethods ()) {
651
- if (!shouldIncludeSetter (method )) {
652
- continue ;
653
- }
654
- String propertyName = propertyName (method );
655
- String existingPropertyName = properties .get (propertyName .toLowerCase (Locale .US ));
656
- if (existingPropertyName != null && !existingPropertyName .equals (propertyName )) {
657
- throw new RuntimeException (
658
- "Found setter on "
659
- + currentClass .getName ()
660
- + " with invalid case-sensitive name: "
661
- + method .getName ());
662
- }
663
-
664
- Method existingSetter = setters .get (propertyName );
665
-
666
- // Raise exception if a conflict between setters is found.
667
- if (existingSetter != null && !isSetterOverride (method , existingSetter )) {
668
- if (currentClass == clazz ) {
669
- // TODO: Should we support overloads?
670
- throw new RuntimeException (
671
- "Class "
672
- + clazz .getName ()
673
- + " has multiple setter overloads with name "
674
- + method .getName ());
675
- } else {
676
- throw new RuntimeException (
677
- "Found conflicting setters "
678
- + "with name: "
679
- + method .getName ()
680
- + " (conflicts with "
681
- + existingSetter .getName ()
682
- + " defined on "
683
- + existingSetter .getDeclaringClass ().getName ()
684
- + ")" );
651
+ if (shouldIncludeSetter (method )) {
652
+ String propertyName = propertyName (method );
653
+ String existingPropertyName = properties .get (propertyName .toLowerCase (Locale .US ));
654
+ if (existingPropertyName != null ) {
655
+ if (!existingPropertyName .equals (propertyName )) {
656
+ throw new RuntimeException (
657
+ "Found setter on "
658
+ + currentClass .getName ()
659
+ + " with invalid case-sensitive name: "
660
+ + method .getName ());
661
+ } else {
662
+ Method existingSetter = setters .get (propertyName );
663
+ if (existingSetter == null ) {
664
+ method .setAccessible (true );
665
+ setters .put (propertyName , method );
666
+ applySetterAnnotations (method );
667
+ } else if (!isSetterOverride (method , existingSetter )) {
668
+ // We require that setters with conflicting property names are
669
+ // overrides from a base class
670
+ if (currentClass == clazz ) {
671
+ // TODO: Should we support overloads?
672
+ throw new RuntimeException (
673
+ "Class "
674
+ + clazz .getName ()
675
+ + " has multiple setter overloads with name "
676
+ + method .getName ());
677
+ } else {
678
+ throw new RuntimeException (
679
+ "Found conflicting setters "
680
+ + "with name: "
681
+ + method .getName ()
682
+ + " (conflicts with "
683
+ + existingSetter .getName ()
684
+ + " defined on "
685
+ + existingSetter .getDeclaringClass ().getName ()
686
+ + ")" );
687
+ }
688
+ }
689
+ }
685
690
}
686
691
}
687
-
688
- // Make it accessible and process annotations if not yet.
689
- if (existingSetter == null ) {
690
- method .setAccessible (true );
691
- setters .put (propertyName , method );
692
- applySetterAnnotations (method );
693
- }
694
692
}
695
693
696
694
for (Field field : currentClass .getDeclaredFields ()) {
@@ -717,15 +715,14 @@ private static class BeanMapper<T> {
717
715
718
716
// Make sure we can write to @DocumentId annotated properties before proceeding.
719
717
for (String docIdProperty : documentIdPropertyNames ) {
720
- if (setters .containsKey (docIdProperty ) || fields .containsKey (docIdProperty )) {
721
- continue ;
718
+ if (!setters .containsKey (docIdProperty ) && !fields .containsKey (docIdProperty )) {
719
+ throw new RuntimeException (
720
+ "@DocumentId is annotated on property "
721
+ + docIdProperty
722
+ + " of class "
723
+ + clazz .getName ()
724
+ + " but no field or public setter was found" );
722
725
}
723
-
724
- throw new RuntimeException (
725
- "@DocumentId is annotated on property "
726
- + docIdProperty
727
- + " which provides no way to write to on class "
728
- + clazz .getName ());
729
726
}
730
727
}
731
728
@@ -798,10 +795,19 @@ T deserialize(
798
795
}
799
796
}
800
797
}
798
+ populateDocumentIdProperties (types , context , instance , deserialzedProperties );
799
+
800
+ return instance ;
801
+ }
801
802
802
- // Populate @DocumentId annotated fields. If there is a conflict (@DocumentId annotation is
803
- // applied to a property that is already deserialized from the firestore document)
804
- // a runtime exception will be thrown.
803
+ // Populate @DocumentId annotated fields. If there is a conflict (@DocumentId annotation is
804
+ // applied to a property that is already deserialized from the firestore document)
805
+ // a runtime exception will be thrown.
806
+ private void populateDocumentIdProperties (
807
+ Map <TypeVariable <Class <T >>, Type > types ,
808
+ DeserializeContext context ,
809
+ T instance ,
810
+ HashSet <String > deserialzedProperties ) {
805
811
for (String docIdPropertyName : documentIdPropertyNames ) {
806
812
if (deserialzedProperties .contains (docIdPropertyName )) {
807
813
String message =
@@ -839,7 +845,6 @@ T deserialize(
839
845
}
840
846
}
841
847
}
842
- return instance ;
843
848
}
844
849
845
850
private Type resolveType (Type type , Map <TypeVariable <Class <T >>, Type > types ) {
@@ -915,14 +920,7 @@ private void applyFieldAnnotations(Field field) {
915
920
916
921
if (field .isAnnotationPresent (DocumentId .class )) {
917
922
Class <?> fieldType = field .getType ();
918
- if (fieldType != String .class && fieldType != DocumentReference .class ) {
919
- throw new IllegalArgumentException (
920
- "Field "
921
- + field .getName ()
922
- + " is annotated with @DocumentId but is "
923
- + fieldType
924
- + " instead of String or DocumentReference." );
925
- }
923
+ ensureValidDocumentIdType ("Field" , "is" , fieldType );
926
924
documentIdPropertyNames .add (propertyName (field ));
927
925
}
928
926
}
@@ -944,14 +942,7 @@ private void applyGetterAnnotations(Method method) {
944
942
// Even though the value will be skipped, we still check for type matching for consistency.
945
943
if (method .isAnnotationPresent (DocumentId .class )) {
946
944
Class <?> returnType = method .getReturnType ();
947
- if (returnType != String .class && returnType != DocumentReference .class ) {
948
- throw new IllegalArgumentException (
949
- "Method "
950
- + method .getName ()
951
- + " is annotated with @DocumentId but returns "
952
- + returnType
953
- + " instead of String or DocumentReference." );
954
- }
945
+ ensureValidDocumentIdType ("Method" , "returns" , returnType );
955
946
documentIdPropertyNames .add (propertyName (method ));
956
947
}
957
948
}
@@ -967,18 +958,23 @@ private void applySetterAnnotations(Method method) {
967
958
968
959
if (method .isAnnotationPresent (DocumentId .class )) {
969
960
Class <?> paramType = method .getParameterTypes ()[0 ];
970
- if (paramType != String .class && paramType != DocumentReference .class ) {
971
- throw new IllegalArgumentException (
972
- "Method "
973
- + method .getName ()
974
- + " is annotated with @DocumentId but sets "
975
- + paramType
976
- + " instead of String or DocumentReference." );
977
- }
961
+ ensureValidDocumentIdType ("Method" , "sets" , paramType );
978
962
documentIdPropertyNames .add (propertyName (method ));
979
963
}
980
964
}
981
965
966
+ private void ensureValidDocumentIdType (String fieldDescription , String operation , Type type ) {
967
+ if (type != String .class && type != DocumentReference .class ) {
968
+ throw new IllegalArgumentException (
969
+ fieldDescription
970
+ + " is annotated with @DocumentId but "
971
+ + operation
972
+ + " "
973
+ + type
974
+ + " instead of String or DocumentReference." );
975
+ }
976
+ }
977
+
982
978
private static boolean shouldIncludeGetter (Method method ) {
983
979
if (!method .getName ().startsWith ("get" ) && !method .getName ().startsWith ("is" )) {
984
980
return false ;
0 commit comments