6
6
*/
7
7
package org .hibernate .jpamodelgen .annotation ;
8
8
9
- import java .beans .Introspector ;
10
9
import java .util .ArrayList ;
11
10
import java .util .Collection ;
12
11
import java .util .HashMap ;
13
12
import java .util .List ;
14
13
import java .util .Map ;
14
+ import java .util .StringTokenizer ;
15
15
import javax .lang .model .element .AnnotationMirror ;
16
16
import javax .lang .model .element .AnnotationValue ;
17
17
import javax .lang .model .element .Element ;
43
43
import org .hibernate .query .sqm .tree .SqmStatement ;
44
44
import org .hibernate .query .sqm .tree .expression .SqmParameter ;
45
45
46
+ import static java .beans .Introspector .decapitalize ;
46
47
import static java .util .Collections .emptyList ;
47
48
import static java .util .stream .Collectors .toList ;
48
49
import static javax .lang .model .util .ElementFilter .fieldsIn ;
49
50
import static javax .lang .model .util .ElementFilter .methodsIn ;
51
+ import static org .hibernate .internal .util .StringHelper .qualify ;
50
52
import static org .hibernate .jpamodelgen .annotation .QueryMethod .isOrderParam ;
51
53
import static org .hibernate .jpamodelgen .annotation .QueryMethod .isPageParam ;
52
54
import static org .hibernate .jpamodelgen .util .Constants .SESSION_TYPES ;
@@ -109,6 +111,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
109
111
*/
110
112
private String sessionType = Constants .ENTITY_MANAGER ;
111
113
114
+ private final Map <String ,String > memberTypes = new HashMap <>();
115
+
112
116
public AnnotationMetaEntity (TypeElement element , Context context ) {
113
117
this .element = element ;
114
118
this .context = context ;
@@ -124,6 +128,10 @@ public static AnnotationMetaEntity create(TypeElement element, Context context,
124
128
return annotationMetaEntity ;
125
129
}
126
130
131
+ public @ Nullable String getMemberType (String entityType , String memberName ) {
132
+ return memberTypes .get ( qualify (entityType , memberName ) );
133
+ }
134
+
127
135
public AccessTypeInformation getEntityAccessTypeInfo () {
128
136
return entityAccessTypeInfo ;
129
137
}
@@ -739,19 +747,9 @@ private int countNaturalIdFields(TypeElement entity) {
739
747
}
740
748
741
749
private @ Nullable FieldType validateFinderParameter (TypeElement entity , VariableElement param ) {
742
- final String entityClassName = entity .getQualifiedName ().toString ();
743
- determineAccessTypeForHierarchy ( entity , context );
744
- final AccessType accessType = castNonNull ( context .getAccessTypeInfo ( entityClassName ) ).getAccessType ();
750
+ final AccessType accessType = getAccessType (entity );
745
751
for ( Element member : entity .getEnclosedElements () ) {
746
- if ( fieldMatchesParameter ( param , member , accessType ) ) {
747
- final String memberType = memberType ( member ).toString ();
748
- final String paramType = param .asType ().toString ();
749
- if ( !memberType .equals (paramType ) ) {
750
- context .message ( param ,
751
- "matching field has type '" + memberType
752
- + "' in entity class '" + entityClassName + "'" ,
753
- Diagnostic .Kind .ERROR );
754
- }
752
+ if ( fieldMatchesParameter ( entity , param , member , accessType ) ) {
755
753
if ( containsAnnotation ( member , Constants .ID , Constants .EMBEDDED_ID ) ) {
756
754
return FieldType .ID ;
757
755
}
@@ -773,37 +771,107 @@ else if ( containsAnnotation( member, Constants.NATURAL_ID ) ) {
773
771
}
774
772
}
775
773
context .message ( param ,
776
- "no matching field named '" + param .getSimpleName ()
777
- + "' in entity class '" + entityClassName + "'" ,
774
+ "no matching field named '"
775
+ + param .getSimpleName ().toString ().replace ('$' , '.' )
776
+ + "' in entity class '" + entity + "'" ,
778
777
Diagnostic .Kind .ERROR );
779
778
return null ;
780
779
}
781
780
781
+ private AccessType getAccessType (TypeElement entity ) {
782
+ final String entityClassName = entity .getQualifiedName ().toString ();
783
+ determineAccessTypeForHierarchy (entity , context );
784
+ return castNonNull ( context .getAccessTypeInfo ( entityClassName ) ).getAccessType ();
785
+ }
786
+
782
787
private static TypeMirror memberType (Element member ) {
783
- if (member .getKind () == ElementKind .METHOD ) {
784
- ExecutableElement method = (ExecutableElement ) member ;
788
+ if ( member .getKind () == ElementKind .METHOD ) {
789
+ final ExecutableElement method = (ExecutableElement ) member ;
785
790
return method .getReturnType ();
786
791
}
787
792
else {
788
793
return member .asType ();
789
794
}
790
795
}
791
796
792
- private static boolean fieldMatchesParameter (VariableElement param , Element member , AccessType accessType ) {
793
- final Name name = member .getSimpleName ();
794
- final Name paramName = param .getSimpleName ();
795
- if ( accessType == AccessType .FIELD ) {
796
- return name .contentEquals (paramName );
797
+ private boolean fieldMatchesParameter (TypeElement entityType , VariableElement param , Element member , AccessType accessType ) {
798
+ final StringTokenizer tokens = new StringTokenizer ( param .getSimpleName ().toString (), "$" );
799
+ return fieldMatchesParameter ( entityType , param , member , accessType , tokens , tokens .nextToken () );
800
+ }
801
+
802
+ private boolean fieldMatchesParameter (
803
+ TypeElement entityType ,
804
+ VariableElement param ,
805
+ Element member ,
806
+ AccessType accessType ,
807
+ StringTokenizer tokens ,
808
+ String token ) {
809
+ final Name memberName = member .getSimpleName ();
810
+ final TypeMirror type ;
811
+ if ( accessType == AccessType .FIELD && member .getKind () == ElementKind .FIELD ) {
812
+ if ( !fieldMatches (token , memberName ) ) {
813
+ return false ;
814
+ }
815
+ else {
816
+ type = member .asType ();
817
+ }
818
+ }
819
+ else if ( accessType == AccessType .PROPERTY && member .getKind () == ElementKind .METHOD ) {
820
+ if ( !getterMatches (token , memberName ) ) {
821
+ return false ;
822
+ }
823
+ else {
824
+ final ExecutableElement method = (ExecutableElement ) member ;
825
+ type = method .getReturnType ();
826
+ }
797
827
}
798
828
else {
799
- if ( name .length () > 3 && name .subSequence (0 ,3 ).toString ().equals ("get" )) {
800
- final String propertyName = Introspector .decapitalize (name .subSequence (3 , name .length ()).toString ());
801
- return paramName .contentEquals (propertyName );
829
+ return false ;
830
+ }
831
+
832
+ if ( tokens .hasMoreTokens () ) {
833
+ final String nextToken = tokens .nextToken ();
834
+ if ( type .getKind () == TypeKind .DECLARED ) {
835
+ final DeclaredType declaredType = (DeclaredType ) type ;
836
+ final TypeElement memberType = (TypeElement ) declaredType .asElement ();
837
+ memberTypes .put ( qualify (entityType .getQualifiedName ().toString (), memberName .toString ()),
838
+ memberType .getQualifiedName ().toString () );
839
+ final AccessType memberAccessType = getAccessType (memberType );
840
+ for ( Element entityMember : memberType .getEnclosedElements () ) {
841
+ if ( fieldMatchesParameter (memberType , param , entityMember , memberAccessType , tokens , nextToken ) ) {
842
+ return true ;
843
+ }
844
+ }
802
845
}
803
- if ( name .length () > 2 && name .subSequence (0 ,2 ).toString ().equals ("is" )) {
804
- final String propertyName = Introspector .decapitalize (name .subSequence (2 , name .length ()).toString ());
805
- return paramName .contentEquals (propertyName );
846
+ return false ;
847
+ }
848
+ else {
849
+ final String memberType = memberType ( member ).toString ();
850
+ final String paramType = param .asType ().toString ();
851
+ if ( !isLegalAssignment ( paramType , memberType ) ) {
852
+ context .message ( param ,
853
+ "matching field has type '" + memberType
854
+ + "' in entity class '" + entityType + "'" ,
855
+ Diagnostic .Kind .ERROR );
806
856
}
857
+ return true ;
858
+ }
859
+ }
860
+
861
+ private static boolean fieldMatches (String token , Name fieldName ) {
862
+ return fieldName .contentEquals (token );
863
+ }
864
+
865
+ private static boolean getterMatches (String token , Name methodName ) {
866
+ if ( methodName .length () > 3 && methodName .subSequence (0 ,3 ).toString ().equals ("get" )) {
867
+ final String propertyName = decapitalize (methodName .subSequence (3 , methodName .length ()).toString ());
868
+ return token .equals (propertyName );
869
+ }
870
+ else if ( methodName .length () > 2 && methodName .subSequence (0 ,2 ).toString ().equals ("is" )) {
871
+ final String propertyName = decapitalize (methodName .subSequence (2 , methodName .length ()).toString ());
872
+ return token .equals (propertyName );
873
+ }
874
+ else {
807
875
return false ;
808
876
}
809
877
}
0 commit comments