7
7
package org .hibernate .validator .ap .classchecks ;
8
8
9
9
10
- import java .util .Collection ;
11
10
import java .util .Collections ;
12
- import java .util .List ;
13
- import java .util .Map ;
11
+ import java .util .Set ;
14
12
15
13
import javax .lang .model .element .Element ;
16
14
import javax .lang .model .element .ElementKind ;
22
20
import javax .lang .model .util .Types ;
23
21
24
22
import org .hibernate .validator .ap .checks .ConstraintCheckIssue ;
25
- import org .hibernate .validator .ap .util .CollectionHelper ;
26
23
import org .hibernate .validator .ap .util .ConstraintHelper ;
27
24
28
25
/**
29
26
* Abstract base class for {@link ClassCheck} implementations that check overridden methods.
30
27
*
31
28
* @author Marko Bekhta
29
+ * @author Guillaume Smet
32
30
*/
33
31
public abstract class AbstractMethodOverrideCheck extends AbstractClassCheck {
34
32
@@ -47,19 +45,13 @@ public AbstractMethodOverrideCheck(Elements elementUtils, Types typeUtils, Const
47
45
}
48
46
49
47
@ Override
50
- public Collection <ConstraintCheckIssue > checkMethod (ExecutableElement currentMethod ) {
48
+ public Set <ConstraintCheckIssue > checkMethod (ExecutableElement currentMethod ) {
51
49
if ( !needToPerformAnyChecks ( currentMethod ) ) {
52
50
return Collections .emptySet ();
53
51
}
54
52
55
- TypeElement currentTypeElement = getEnclosingTypeElement ( currentMethod );
56
-
57
- // if current type is java.lang.Object then we can continue without doing any other checks
58
- if ( isJavaLangObjectOrNull ( currentTypeElement ) ) {
59
- return Collections .emptySet ();
60
- }
61
53
// find if there's a method that was overridden by the current one.
62
- InheritanceTree overriddenMethodsTree = findAllOverriddenElements ( currentTypeElement , currentMethod );
54
+ MethodInheritanceTree overriddenMethodsTree = findAllOverriddenElements ( currentMethod );
63
55
if ( !overriddenMethodsTree .hasOverriddenMethods () ) {
64
56
return Collections .emptySet ();
65
57
}
@@ -68,18 +60,17 @@ public Collection<ConstraintCheckIssue> checkMethod(ExecutableElement currentMet
68
60
}
69
61
70
62
/**
71
- * Performs a real check of a method.
63
+ * Performs the check of a method.
72
64
*
73
65
* @param currentMethod a method to check
74
- * @param overriddenMethods a map of overridden methods received by calling {@link AbstractMethodOverrideCheck#findAllOverriddenElements(TypeElement, ExecutableElement)}
66
+ * @param overriddenMethodsTree the {@link MethodInheritanceTree} of the method to check
75
67
*
76
- * @return a collection of issues if there are any, an empty collection otherwise
68
+ * @return a set of issues if there are any, an empty set otherwise
77
69
*/
78
- protected abstract Collection <ConstraintCheckIssue > checkMethodInternal (ExecutableElement currentMethod , InheritanceTree overriddenMethods );
70
+ protected abstract Set <ConstraintCheckIssue > checkMethodInternal (ExecutableElement currentMethod , MethodInheritanceTree overriddenMethodsTree );
79
71
80
72
/**
81
- * There can be situations in which no checks should be performed. So in such cases we will not look for any overridden
82
- * methods and do any work at all.
73
+ * There can be situations in which no checks should be performed. In such cases we will not perform any work at all.
83
74
*
84
75
* @param currentMethod the method under investigation
85
76
*
@@ -88,105 +79,93 @@ public Collection<ConstraintCheckIssue> checkMethod(ExecutableElement currentMet
88
79
protected abstract boolean needToPerformAnyChecks (ExecutableElement currentMethod );
89
80
90
81
/**
91
- * Find overridden methods from all super classes and all implemented interfaces. Results are returned as {@link InheritanceTree}
82
+ * Find overridden methods from all super classes and all implemented interfaces. Results are returned as a {@link MethodInheritanceTree}.
92
83
*
93
- * @param currentTypeElement the class in which the method is located
94
- * @param currentMethod the method for which we want to find the overridden methods
84
+ * @param overridingMethod the method for which we want to find the overridden methods
95
85
*
96
- * @return an {@link InheritanceTree } containing overridden methods
86
+ * @return a {@link MethodInheritanceTree } containing overridden methods
97
87
*/
98
- private InheritanceTree findAllOverriddenElements (
99
- TypeElement currentTypeElement ,
100
- ExecutableElement currentMethod ) {
101
- InheritanceTree tree = new InheritanceTree ( currentMethod , currentTypeElement );
102
- findAllOverriddenElementsRecursive ( currentTypeElement , currentMethod , tree );
103
- return tree ;
88
+ private MethodInheritanceTree findAllOverriddenElements (ExecutableElement overridingMethod ) {
89
+ TypeElement currentTypeElement = getEnclosingTypeElement ( overridingMethod );
90
+ MethodInheritanceTree .Builder methodInheritanceTreeBuilder = new MethodInheritanceTree .Builder ( overridingMethod );
91
+
92
+ collectOverriddenMethods ( overridingMethod , currentTypeElement , methodInheritanceTreeBuilder );
93
+
94
+ return methodInheritanceTreeBuilder .build ();
104
95
}
105
96
106
97
/**
107
- * A recursive part of {@link AbstractMethodOverrideCheck#findAllOverriddenElements(TypeElement, ExecutableElement)} .
98
+ * Collect all the overridden elements of the inheritance tree .
108
99
*
109
- * @param currentTypeElement the class in which the method is located
110
- * @param currentMethod the method for which we want to find the overridden methods
111
- * @param tree a resulting inheritance tree
100
+ * @param overridingMethod the method for which we want to find the overridden methods
101
+ * @param currentTypeElement the class we are analyzing
102
+ * @param methodInheritanceTreeBuilder the method inheritance tree builder
112
103
*/
113
- private void findAllOverriddenElementsRecursive (
114
- TypeElement currentTypeElement ,
115
- ExecutableElement currentMethod ,
116
- InheritanceTree tree ) {
117
-
118
- // look for implemented interfaces
119
- for ( Map .Entry <TypeElement , ExecutableElement > entry : findOverriddenMethodInInterfacesPairs (
120
- currentTypeElement ,
121
- currentTypeElement .getInterfaces (),
122
- currentMethod
123
- ).entrySet () ) {
124
- tree .addNode ( entry .getValue (), entry .getKey () );
125
- findAllOverriddenElementsRecursive ( entry .getKey (), entry .getValue (), tree );
104
+ private void collectOverriddenMethods ( ExecutableElement overridingMethod , TypeElement currentTypeElement ,
105
+ MethodInheritanceTree .Builder methodInheritanceTreeBuilder ) {
106
+ if ( isJavaLangObjectOrNull ( currentTypeElement ) ) {
107
+ return ;
126
108
}
127
109
128
- TypeElement superType = (TypeElement ) typeUtils .asElement ( currentTypeElement .getSuperclass () );
129
- if ( isJavaLangObjectOrNull ( superType ) ) {
110
+ collectOverriddenMethodsInInterfaces ( overridingMethod , currentTypeElement , methodInheritanceTreeBuilder );
111
+
112
+ TypeElement superclassTypeElement = (TypeElement ) typeUtils .asElement ( currentTypeElement .getSuperclass () );
113
+ if ( superclassTypeElement == null ) {
130
114
return ;
131
115
}
132
116
133
- ExecutableElement element = getOverriddenElement ( currentTypeElement , superType , currentMethod );
134
- if ( element != null ) {
135
- tree . addNode ( element , superType , currentTypeElement );
136
- findAllOverriddenElementsRecursive ( superType , element , tree ) ;
117
+ ExecutableElement overriddenMethod = getOverriddenMethod ( overridingMethod , superclassTypeElement );
118
+ if ( overriddenMethod != null ) {
119
+ methodInheritanceTreeBuilder . addOverriddenMethod ( overridingMethod , overriddenMethod );
120
+ overridingMethod = overriddenMethod ;
137
121
}
122
+
123
+ collectOverriddenMethods ( overridingMethod , superclassTypeElement , methodInheritanceTreeBuilder );
138
124
}
139
125
140
126
/**
141
- * Find pairs of enclosing type {@link TypeElement} and overridden method {@link ExecutableElement} from implemented interfaces.
142
- *
143
- * @param currentTypeElement the class in which the method is located
144
- * @param interfaces a list of implemented interfaces
145
- * @param currentMethod the method for which we want to find the overridden methods
127
+ * Collect overridden methods in the interfaces of a given type.
146
128
*
147
- * @return a map of pairs of overridden methods (map key - an enclosing type, map value - overridden method in that type)
148
- * if there are any, an empty map otherwise
129
+ * @param overridingMethod the method for which we want to find the overridden methods
130
+ * @param currentTypeElement the class we are currently analyzing
131
+ * @param methodInheritanceTreeBuilder the method inheritance tree builder
149
132
*/
150
- private Map <TypeElement , ExecutableElement > findOverriddenMethodInInterfacesPairs (
151
- TypeElement currentTypeElement ,
152
- List <? extends TypeMirror > interfaces ,
153
- ExecutableElement currentMethod ) {
154
- Map <TypeElement ,ExecutableElement > elements = CollectionHelper .newHashMap ();
155
-
156
- for ( TypeMirror anInterface : interfaces ) {
157
- TypeElement implementedInterface = (TypeElement ) typeUtils .asElement ( anInterface );
158
- ExecutableElement element = getOverriddenElement ( currentTypeElement , implementedInterface , currentMethod );
159
- if ( element != null ) {
160
- elements .put ( implementedInterface , element );
133
+ private void collectOverriddenMethodsInInterfaces (ExecutableElement overridingMethod , TypeElement currentTypeElement ,
134
+ MethodInheritanceTree .Builder methodInheritanceTreeBuilder ) {
135
+ for ( TypeMirror implementedInterface : currentTypeElement .getInterfaces () ) {
136
+ TypeElement interfaceTypeElement = (TypeElement ) typeUtils .asElement ( implementedInterface );
137
+ ExecutableElement overriddenMethod = getOverriddenMethod ( overridingMethod , interfaceTypeElement );
138
+ ExecutableElement newOverridingMethod ;
139
+ if ( overriddenMethod != null ) {
140
+ methodInheritanceTreeBuilder .addOverriddenMethod ( overridingMethod , overriddenMethod );
141
+ newOverridingMethod = overriddenMethod ;
161
142
}
143
+ else {
144
+ newOverridingMethod = overridingMethod ;
145
+ }
146
+ collectOverriddenMethodsInInterfaces ( newOverridingMethod , interfaceTypeElement , methodInheritanceTreeBuilder );
162
147
}
163
-
164
- return elements ;
165
148
}
166
149
167
150
/**
168
151
* Find a method that is overridden by the one passed to this function.
169
152
*
170
- * @param currentTypeElement a class in which method is located
171
- * @param otherTypeElement a class/interface on which to look for overridden method
172
153
* @param currentMethod the method for which we want to find the overridden methods
173
- *
174
- * @return an overridden method if there's one, and {@code null} otherwise
154
+ * @param typeElement the class or interface analyzed
155
+ * @return the overridden method if there is one, and {@code null} otherwise
175
156
*/
176
- private ExecutableElement getOverriddenElement (
177
- TypeElement currentTypeElement ,
178
- TypeElement otherTypeElement ,
179
- ExecutableElement currentMethod ) {
180
-
181
- if ( isJavaLangObjectOrNull ( otherTypeElement ) ) {
157
+ private ExecutableElement getOverriddenMethod (ExecutableElement currentMethod , TypeElement typeElement ) {
158
+ if ( typeElement == null ) {
182
159
return null ;
183
160
}
184
161
185
- for ( Element element : elementUtils .getAllMembers ( otherTypeElement ) ) {
162
+ TypeElement enclosingTypeElement = getEnclosingTypeElement ( currentMethod );
163
+
164
+ for ( Element element : elementUtils .getAllMembers ( typeElement ) ) {
186
165
if ( !element .getKind ().equals ( ElementKind .METHOD ) ) {
187
166
continue ;
188
167
}
189
- if ( elementUtils .overrides ( currentMethod , (ExecutableElement ) element , currentTypeElement ) ) {
168
+ if ( elementUtils .overrides ( currentMethod , (ExecutableElement ) element , enclosingTypeElement ) ) {
190
169
return (ExecutableElement ) element ;
191
170
}
192
171
}
@@ -195,36 +174,34 @@ private ExecutableElement getOverriddenElement(
195
174
}
196
175
197
176
/**
198
- * Find a {@link TypeElement} that enclose a given {@link ExecutableElement}.
199
- *
200
- * @param currentMethod a method that you want to find class/interface it belongs to
177
+ * Find the {@link TypeElement} that contains a given {@link ExecutableElement}.
201
178
*
202
- * @return a class/interface represented by {@link TypeElement} to which a method belongs to
179
+ * @param currentMethod a method
180
+ * @return the class/interface containing the method represented by a {@link TypeElement}
203
181
*/
204
182
private TypeElement getEnclosingTypeElement (ExecutableElement currentMethod ) {
205
183
return (TypeElement ) typeUtils .asElement ( currentMethod .getEnclosingElement ().asType () );
206
184
}
207
185
208
186
/**
209
- * Find a {@link String} representation of qualified name ({@link Name}) of corresponding {@link TypeElement} that enclose a given {@link ExecutableElement}.
210
- *
211
- * @param currentMethod a method that you want to find class/interface qualified name it belongs to
187
+ * Find a {@link String} representation of qualified name ({@link Name}) of corresponding {@link TypeElement} that
188
+ * contains a given {@link ExecutableElement}.
212
189
*
190
+ * @param currentMethod a method
213
191
* @return a class/interface qualified name represented by {@link String} to which a method belongs to
214
192
*/
215
193
protected String getEnclosingTypeElementQualifiedName (ExecutableElement currentMethod ) {
216
194
return getEnclosingTypeElement ( currentMethod ).getQualifiedName ().toString ();
217
195
}
218
196
219
197
/**
220
- * Determine if provided type element ({@link TypeElement} represents a {@link java.lang.Object} or is {@code null}
221
- *
222
- * @param typeElement an element to check
198
+ * Determine if the provided {@link TypeElement} represents a {@link java.lang.Object} or is {@code null}.
223
199
*
224
- * @return {@code true} if provided element is {@link java.lang.Object} or is {@code null}, {@code false} otherwise
200
+ * @param typeElement the element to check
201
+ * @return {@code true} if the provided element is {@link java.lang.Object} or is {@code null}, {@code false} otherwise
225
202
*/
226
203
private boolean isJavaLangObjectOrNull (TypeElement typeElement ) {
227
- return typeElement == null || JAVA_LANG_OBJECT .equals ( typeElement .toString () );
204
+ return typeElement == null || JAVA_LANG_OBJECT .contentEquals ( typeElement .getQualifiedName () );
228
205
}
229
206
230
207
}
0 commit comments