@@ -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 ) {
@@ -916,14 +921,7 @@ private void applyFieldAnnotations(Field field) {
916
921
917
922
if (field .isAnnotationPresent (DocumentId .class )) {
918
923
Class <?> fieldType = field .getType ();
919
- if (fieldType != String .class && fieldType != DocumentReference .class ) {
920
- throw new IllegalArgumentException (
921
- "Field "
922
- + field .getName ()
923
- + " is annotated with @DocumentId but is "
924
- + fieldType
925
- + " instead of String or DocumentReference." );
926
- }
924
+ ensureValidDocumentIdType ("Field" , "is" , fieldType );
927
925
documentIdPropertyNames .add (propertyName (field ));
928
926
}
929
927
}
@@ -945,14 +943,7 @@ private void applyGetterAnnotations(Method method) {
945
943
// Even though the value will be skipped, we still check for type matching for consistency.
946
944
if (method .isAnnotationPresent (DocumentId .class )) {
947
945
Class <?> returnType = method .getReturnType ();
948
- if (returnType != String .class && returnType != DocumentReference .class ) {
949
- throw new IllegalArgumentException (
950
- "Method "
951
- + method .getName ()
952
- + " is annotated with @DocumentId but returns "
953
- + returnType
954
- + " instead of String or DocumentReference." );
955
- }
946
+ ensureValidDocumentIdType ("Method" , "returns" , returnType );
956
947
documentIdPropertyNames .add (propertyName (method ));
957
948
}
958
949
}
@@ -968,18 +959,23 @@ private void applySetterAnnotations(Method method) {
968
959
969
960
if (method .isAnnotationPresent (DocumentId .class )) {
970
961
Class <?> paramType = method .getParameterTypes ()[0 ];
971
- if (paramType != String .class && paramType != DocumentReference .class ) {
972
- throw new IllegalArgumentException (
973
- "Method "
974
- + method .getName ()
975
- + " is annotated with @DocumentId but sets "
976
- + paramType
977
- + " instead of String or DocumentReference." );
978
- }
962
+ ensureValidDocumentIdType ("Method" , "sets" , paramType );
979
963
documentIdPropertyNames .add (propertyName (method ));
980
964
}
981
965
}
982
966
967
+ private void ensureValidDocumentIdType (String fieldDescription , String operation , Type type ) {
968
+ if (type != String .class && type != DocumentReference .class ) {
969
+ throw new IllegalArgumentException (
970
+ fieldDescription
971
+ + " is annotated with @DocumentId but "
972
+ + operation
973
+ + " "
974
+ + type
975
+ + " instead of String or DocumentReference." );
976
+ }
977
+ }
978
+
983
979
private static boolean shouldIncludeGetter (Method method ) {
984
980
if (!method .getName ().startsWith ("get" ) && !method .getName ().startsWith ("is" )) {
985
981
return false ;
0 commit comments