Skip to content

Commit 60b003c

Browse files
committed
HV-840 Various fixes to the logic and other improvements
1 parent 65417ab commit 60b003c

13 files changed

+541
-367
lines changed

annotation-processor/src/main/java/org/hibernate/validator/ap/AbstractElementVisitor.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
import java.util.Collection;
1010
import java.util.Set;
11+
12+
import javax.lang.model.element.ElementVisitor;
1113
import javax.lang.model.util.ElementKindVisitor6;
1214

1315
import org.hibernate.validator.ap.checks.ConstraintCheckIssue;
@@ -16,7 +18,7 @@
1618
import org.hibernate.validator.ap.util.MessagerAdapter;
1719

1820
/**
19-
* An abstract {@link javax.lang.model.element.ElementVisitor} that should be used for implementation
21+
* An abstract {@link ElementVisitor} that should be used for implementation
2022
* of any other element visitors. The only method present in this class ({@link AbstractElementVisitor#reportIssues(Collection)}
2123
* is used to report found {@link ConstraintCheckIssue}s. Each {@link ConstraintCheckIssue} occurred will be reported using the
2224
* {@link javax.annotation.processing.Messager} API.

annotation-processor/src/main/java/org/hibernate/validator/ap/ClassVisitor.java

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public ClassVisitor(
6060
* Doesn't perform any checks at the moment but calls a visit methods on its own elements.
6161
*
6262
* @param element a class element to check
63+
* @param aVoid
6364
*/
6465
@Override
6566
public Void visitTypeAsClass(TypeElement element, Void aVoid) {
@@ -72,6 +73,7 @@ public Void visitTypeAsClass(TypeElement element, Void aVoid) {
7273
* Doesn't perform any checks at the moment but calls a visit methods on its own elements.
7374
*
7475
* @param element a class element to check
76+
* @param aVoid
7577
*/
7678
@Override
7779
public Void visitTypeAsInterface(TypeElement element, Void aVoid) {

annotation-processor/src/main/java/org/hibernate/validator/ap/checks/AbstractConstraintCheck.java

+3-9
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
* and <code>checkMethod()</code>.
2626
* </p>
2727
* <p>
28-
* All check methods not overridden will return an empty list.
28+
* All check methods not overridden will return an empty set.
2929
* </p>
3030
*
3131
* @author Gunnar Morling
@@ -34,27 +34,21 @@ public class AbstractConstraintCheck implements ConstraintCheck {
3434

3535
@Override
3636
public Set<ConstraintCheckIssue> checkField(VariableElement element, AnnotationMirror annotation) {
37-
3837
return Collections.emptySet();
3938
}
4039

4140
@Override
4241
public Set<ConstraintCheckIssue> checkMethod(ExecutableElement element, AnnotationMirror annotation) {
43-
4442
return Collections.emptySet();
4543
}
4644

4745
@Override
48-
public Set<ConstraintCheckIssue> checkAnnotationType(TypeElement element,
49-
AnnotationMirror annotation) {
50-
46+
public Set<ConstraintCheckIssue> checkAnnotationType(TypeElement element, AnnotationMirror annotation) {
5147
return Collections.emptySet();
5248
}
5349

5450
@Override
55-
public Set<ConstraintCheckIssue> checkNonAnnotationType(
56-
TypeElement element, AnnotationMirror annotation) {
57-
51+
public Set<ConstraintCheckIssue> checkNonAnnotationType(TypeElement element, AnnotationMirror annotation) {
5852
return Collections.emptySet();
5953
}
6054
}

annotation-processor/src/main/java/org/hibernate/validator/ap/classchecks/AbstractClassCheck.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
import java.util.Collection;
1010
import java.util.Collections;
11+
import java.util.Set;
12+
1113
import javax.lang.model.element.Element;
1214
import javax.lang.model.element.ExecutableElement;
1315

@@ -20,15 +22,15 @@
2022
* supported element types.
2123
* </p>
2224
* <p>
23-
* All check methods not overridden will return an empty list.
25+
* All check methods not overridden will return an empty set.
2426
* </p>
2527
*
2628
* @author Marko Bekhta
2729
*/
2830
public abstract class AbstractClassCheck implements ClassCheck {
2931

3032
@Override
31-
public Collection<ConstraintCheckIssue> checkMethod(ExecutableElement element) {
33+
public Set<ConstraintCheckIssue> checkMethod(ExecutableElement element) {
3234
return Collections.emptySet();
3335
}
3436

annotation-processor/src/main/java/org/hibernate/validator/ap/classchecks/AbstractMethodOverrideCheck.java

+72-95
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@
77
package org.hibernate.validator.ap.classchecks;
88

99

10-
import java.util.Collection;
1110
import java.util.Collections;
12-
import java.util.List;
13-
import java.util.Map;
11+
import java.util.Set;
1412

1513
import javax.lang.model.element.Element;
1614
import javax.lang.model.element.ElementKind;
@@ -22,13 +20,13 @@
2220
import javax.lang.model.util.Types;
2321

2422
import org.hibernate.validator.ap.checks.ConstraintCheckIssue;
25-
import org.hibernate.validator.ap.util.CollectionHelper;
2623
import org.hibernate.validator.ap.util.ConstraintHelper;
2724

2825
/**
2926
* Abstract base class for {@link ClassCheck} implementations that check overridden methods.
3027
*
3128
* @author Marko Bekhta
29+
* @author Guillaume Smet
3230
*/
3331
public abstract class AbstractMethodOverrideCheck extends AbstractClassCheck {
3432

@@ -47,19 +45,13 @@ public AbstractMethodOverrideCheck(Elements elementUtils, Types typeUtils, Const
4745
}
4846

4947
@Override
50-
public Collection<ConstraintCheckIssue> checkMethod(ExecutableElement currentMethod) {
48+
public Set<ConstraintCheckIssue> checkMethod(ExecutableElement currentMethod) {
5149
if ( !needToPerformAnyChecks( currentMethod ) ) {
5250
return Collections.emptySet();
5351
}
5452

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-
}
6153
// find if there's a method that was overridden by the current one.
62-
InheritanceTree overriddenMethodsTree = findAllOverriddenElements( currentTypeElement, currentMethod );
54+
MethodInheritanceTree overriddenMethodsTree = findAllOverriddenElements( currentMethod );
6355
if ( !overriddenMethodsTree.hasOverriddenMethods() ) {
6456
return Collections.emptySet();
6557
}
@@ -68,18 +60,17 @@ public Collection<ConstraintCheckIssue> checkMethod(ExecutableElement currentMet
6860
}
6961

7062
/**
71-
* Performs a real check of a method.
63+
* Performs the check of a method.
7264
*
7365
* @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
7567
*
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
7769
*/
78-
protected abstract Collection<ConstraintCheckIssue> checkMethodInternal(ExecutableElement currentMethod, InheritanceTree overriddenMethods);
70+
protected abstract Set<ConstraintCheckIssue> checkMethodInternal(ExecutableElement currentMethod, MethodInheritanceTree overriddenMethodsTree);
7971

8072
/**
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.
8374
*
8475
* @param currentMethod the method under investigation
8576
*
@@ -88,105 +79,93 @@ public Collection<ConstraintCheckIssue> checkMethod(ExecutableElement currentMet
8879
protected abstract boolean needToPerformAnyChecks(ExecutableElement currentMethod);
8980

9081
/**
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}.
9283
*
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
9585
*
96-
* @return an {@link InheritanceTree} containing overridden methods
86+
* @return a {@link MethodInheritanceTree} containing overridden methods
9787
*/
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();
10495
}
10596

10697
/**
107-
* A recursive part of {@link AbstractMethodOverrideCheck#findAllOverriddenElements(TypeElement, ExecutableElement)}.
98+
* Collect all the overridden elements of the inheritance tree.
10899
*
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
112103
*/
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;
126108
}
127109

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 ) {
130114
return;
131115
}
132116

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;
137121
}
122+
123+
collectOverriddenMethods( overridingMethod, superclassTypeElement, methodInheritanceTreeBuilder );
138124
}
139125

140126
/**
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.
146128
*
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
149132
*/
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;
161142
}
143+
else {
144+
newOverridingMethod = overridingMethod;
145+
}
146+
collectOverriddenMethodsInInterfaces( newOverridingMethod, interfaceTypeElement, methodInheritanceTreeBuilder );
162147
}
163-
164-
return elements;
165148
}
166149

167150
/**
168151
* Find a method that is overridden by the one passed to this function.
169152
*
170-
* @param currentTypeElement a class in which method is located
171-
* @param otherTypeElement a class/interface on which to look for overridden method
172153
* @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
175156
*/
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 ) {
182159
return null;
183160
}
184161

185-
for ( Element element : elementUtils.getAllMembers( otherTypeElement ) ) {
162+
TypeElement enclosingTypeElement = getEnclosingTypeElement( currentMethod );
163+
164+
for ( Element element : elementUtils.getAllMembers( typeElement ) ) {
186165
if ( !element.getKind().equals( ElementKind.METHOD ) ) {
187166
continue;
188167
}
189-
if ( elementUtils.overrides( currentMethod, (ExecutableElement) element, currentTypeElement ) ) {
168+
if ( elementUtils.overrides( currentMethod, (ExecutableElement) element, enclosingTypeElement ) ) {
190169
return (ExecutableElement) element;
191170
}
192171
}
@@ -195,36 +174,34 @@ private ExecutableElement getOverriddenElement(
195174
}
196175

197176
/**
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}.
201178
*
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}
203181
*/
204182
private TypeElement getEnclosingTypeElement(ExecutableElement currentMethod) {
205183
return (TypeElement) typeUtils.asElement( currentMethod.getEnclosingElement().asType() );
206184
}
207185

208186
/**
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}.
212189
*
190+
* @param currentMethod a method
213191
* @return a class/interface qualified name represented by {@link String} to which a method belongs to
214192
*/
215193
protected String getEnclosingTypeElementQualifiedName(ExecutableElement currentMethod) {
216194
return getEnclosingTypeElement( currentMethod ).getQualifiedName().toString();
217195
}
218196

219197
/**
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}.
223199
*
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
225202
*/
226203
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() );
228205
}
229206

230207
}

0 commit comments

Comments
 (0)