1 /*
2  * Hibernate Validator, declare and validate application constraints
3  *
4  * License: Apache License, Version 2.0
5  * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6  */

7 package org.hibernate.validator.internal.metadata.core;
8
9 import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
10
11 import java.lang.invoke.MethodHandles;
12 import java.util.Map;
13
14 import org.hibernate.validator.internal.properties.Constrainable;
15 import org.hibernate.validator.internal.util.logging.Log;
16 import org.hibernate.validator.internal.util.logging.LoggerFactory;
17
18 /**
19  * An  {@code AnnotationProcessingOptions} instance keeps track of annotations which should be ignored as configuration source.
20  * The main validation source for Bean Validation is annotation and alternate configuration sources use this class
21  * to override/ignore existing annotations.
22  *
23  * @author Hardy Ferentschik
24  */

25 public class AnnotationProcessingOptionsImpl implements AnnotationProcessingOptions {
26
27     private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
28
29     /**
30      * Keeps track whether the 'ignore-annotations' flag is set on bean level in the xml configuration. If 'ignore-annotations'
31      * is not specified {@code true} is the default.
32      */

33     private final Map<Class<?>, Boolean> ignoreAnnotationDefaults = newHashMap();
34
35     /**
36      * Keeps track of explicitly excluded class level constraints.
37      */

38     private final Map<Class<?>, Boolean> annotationIgnoresForClasses = newHashMap();
39
40     /**
41      * Keeps track of explicitly excluded members (fields and properties).
42      */

43     private final Map<Constrainable, Boolean> annotationIgnoredForMembers = newHashMap();
44
45     /**
46      * Keeps track of explicitly excluded return value constraints for methods/constructors.
47      */

48     private final Map<Constrainable, Boolean> annotationIgnoresForReturnValues = newHashMap();
49
50     /**
51      * Keeps track of explicitly excluded cross parameter constraints for methods/constructors.
52      */

53     private final Map<Constrainable, Boolean> annotationIgnoresForCrossParameter = newHashMap();
54
55     /**
56      * Keeps track whether the 'ignore-annotations' flag is set on a method/constructor parameter
57      */

58     private final Map<ExecutableParameterKey, Boolean> annotationIgnoresForMethodParameter = newHashMap();
59
60     @Override
61     public boolean areMemberConstraintsIgnoredFor(Constrainable constrainable) {
62         Class<?> clazz = constrainable.getDeclaringClass();
63         Boolean annotationIgnoredForMember = annotationIgnoredForMembers.get( constrainable );
64         if ( annotationIgnoredForMember != null ) {
65             return annotationIgnoredForMember;
66         }
67         else {
68             return areAllConstraintAnnotationsIgnoredFor( clazz );
69         }
70     }
71
72     @Override
73     public boolean areReturnValueConstraintsIgnoredFor(Constrainable constrainable) {
74         Boolean annotationIgnoreForReturnValue = annotationIgnoresForReturnValues.get( constrainable );
75         if ( annotationIgnoreForReturnValue != null ) {
76             return annotationIgnoreForReturnValue;
77         }
78         else {
79             return areMemberConstraintsIgnoredFor( constrainable );
80         }
81     }
82
83     @Override
84     public boolean areCrossParameterConstraintsIgnoredFor(Constrainable constrainable) {
85         Boolean annotationIgnoreForCrossParameter = annotationIgnoresForCrossParameter.get( constrainable );
86         if ( annotationIgnoreForCrossParameter != null ) {
87             return annotationIgnoreForCrossParameter;
88         }
89         else {
90             return areMemberConstraintsIgnoredFor( constrainable );
91         }
92     }
93
94     @Override
95     public boolean areParameterConstraintsIgnoredFor(Constrainable constrainable, int index) {
96         ExecutableParameterKey key = new ExecutableParameterKey( constrainable, index );
97         Boolean annotationIgnoreForMethodParameter = annotationIgnoresForMethodParameter.get( key );
98         if ( annotationIgnoreForMethodParameter != null ) {
99             return annotationIgnoreForMethodParameter;
100         }
101         else {
102             return areMemberConstraintsIgnoredFor( constrainable );
103         }
104     }
105
106     @Override
107     public boolean areClassLevelConstraintsIgnoredFor(Class<?> clazz) {
108         boolean ignoreAnnotation;
109         Boolean annotationIgnoreForClass = annotationIgnoresForClasses.get( clazz );
110         if ( annotationIgnoreForClass != null ) {
111             ignoreAnnotation = annotationIgnoreForClass;
112         }
113         else {
114             ignoreAnnotation = areAllConstraintAnnotationsIgnoredFor( clazz );
115         }
116         if ( LOG.isDebugEnabled() && ignoreAnnotation ) {
117             LOG.debugf( "Class level annotation are getting ignored for %s.", clazz.getName() );
118         }
119         return ignoreAnnotation;
120     }
121
122     @Override
123     public void merge(AnnotationProcessingOptions annotationProcessingOptions) {
124         AnnotationProcessingOptionsImpl annotationProcessingOptionsImpl = (AnnotationProcessingOptionsImpl) annotationProcessingOptions;
125
126         // TODO rethink the "merging" of these options. It will depend on the order of merging (HF)
127         this.ignoreAnnotationDefaults.putAll( annotationProcessingOptionsImpl.ignoreAnnotationDefaults );
128         this.annotationIgnoresForClasses.putAll( annotationProcessingOptionsImpl.annotationIgnoresForClasses );
129         this.annotationIgnoredForMembers.putAll( annotationProcessingOptionsImpl.annotationIgnoredForMembers );
130         this.annotationIgnoresForReturnValues
131                 .putAll( annotationProcessingOptionsImpl.annotationIgnoresForReturnValues );
132         this.annotationIgnoresForCrossParameter
133                 .putAll( annotationProcessingOptionsImpl.annotationIgnoresForCrossParameter );
134         this.annotationIgnoresForMethodParameter.putAll( annotationProcessingOptionsImpl.annotationIgnoresForMethodParameter );
135     }
136
137     public void ignoreAnnotationConstraintForClass(Class<?> clazz, Boolean b) {
138         if ( b == null ) {
139             ignoreAnnotationDefaults.put( clazz, Boolean.TRUE );
140         }
141         else {
142             ignoreAnnotationDefaults.put( clazz, b );
143         }
144     }
145
146     public void ignoreConstraintAnnotationsOnMember(Constrainable member, Boolean b) {
147         annotationIgnoredForMembers.put( member, b );
148     }
149
150     public void ignoreConstraintAnnotationsForReturnValue(Constrainable member, Boolean b) {
151         annotationIgnoresForReturnValues.put( member, b );
152     }
153
154     public void ignoreConstraintAnnotationsForCrossParameterConstraint(Constrainable member, Boolean b) {
155         annotationIgnoresForCrossParameter.put( member, b );
156     }
157
158     public void ignoreConstraintAnnotationsOnParameter(Constrainable member, int index, Boolean b) {
159         ExecutableParameterKey key = new ExecutableParameterKey( member, index );
160         annotationIgnoresForMethodParameter.put( key, b );
161     }
162
163     public void ignoreClassLevelConstraintAnnotations(Class<?> clazz, boolean b) {
164         annotationIgnoresForClasses.put( clazz, b );
165     }
166
167     private boolean areAllConstraintAnnotationsIgnoredFor(Class<?> clazz) {
168         return ignoreAnnotationDefaults.containsKey( clazz ) && ignoreAnnotationDefaults.get( clazz );
169     }
170
171     public class ExecutableParameterKey {
172         private final Constrainable constrainable;
173         private final int index;
174
175         public ExecutableParameterKey(Constrainable constrainable, int index) {
176             this.constrainable = constrainable;
177             this.index = index;
178         }
179
180         @Override
181         public boolean equals(Object o) {
182             if ( this == o ) {
183                 return true;
184             }
185             if ( o == null || getClass() != o.getClass() ) {
186                 return false;
187             }
188
189             ExecutableParameterKey that = (ExecutableParameterKey) o;
190
191             if ( index != that.index ) {
192                 return false;
193             }
194             if ( constrainable != null ? !constrainable.equals( that.constrainable ) : that.constrainable != null ) {
195                 return false;
196             }
197
198             return true;
199         }
200
201         @Override
202         public int hashCode() {
203             int result = constrainable != null ? constrainable.hashCode() : 0;
204             result = 31 * result + index;
205             return result;
206         }
207     }
208 }
209