Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
GenericPropertyConstraint |
|
|
2.625;2,625 |
1 | /* |
|
2 | * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH |
|
3 | * |
|
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 | * you may not use this file except in compliance with the License. |
|
6 | * You may obtain a copy of the License at |
|
7 | * |
|
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 | * |
|
10 | * Unless required by applicable law or agreed to in writing, software |
|
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 | * See the License for the specific language governing permissions and |
|
14 | * limitations under the License. |
|
15 | */ |
|
16 | package de.smartics.properties.spi.core.constraint.jsr303; |
|
17 | ||
18 | import java.io.IOException; |
|
19 | import java.io.ObjectInputStream; |
|
20 | import java.lang.annotation.Annotation; |
|
21 | import java.util.List; |
|
22 | import java.util.Locale; |
|
23 | ||
24 | import javax.validation.ConstraintValidator; |
|
25 | import javax.validation.ConstraintValidatorContext; |
|
26 | import javax.validation.constraints.NotNull; |
|
27 | ||
28 | import org.hibernate.validator.constraints.NotBlank; |
|
29 | import org.hibernate.validator.internal.metadata.core.ConstraintHelper; |
|
30 | ||
31 | import de.smartics.properties.spi.core.constraint.AbstractPropertyConstraint; |
|
32 | import de.smartics.util.lang.Arguments; |
|
33 | import de.smartics.util.lang.NullArgumentException; |
|
34 | ||
35 | /** |
|
36 | * Generic wrapper for JSR-303 constraints. |
|
37 | * |
|
38 | * @param <A> the constraint annotation. |
|
39 | * @param <T> the type of the property that is validated. |
|
40 | */ |
|
41 | public class GenericPropertyConstraint<A extends Annotation, T> extends |
|
42 | AbstractPropertyConstraint<T> |
|
43 | { |
|
44 | // ********************************* Fields ********************************* |
|
45 | ||
46 | // --- constants ------------------------------------------------------------ |
|
47 | ||
48 | /** |
|
49 | * Appease PMD. |
|
50 | */ |
|
51 | private static final String UNCHECKED = "unchecked"; |
|
52 | ||
53 | /** |
|
54 | * The class version identifier. |
|
55 | * <p> |
|
56 | * The value of this constant is {@value}. |
|
57 | */ |
|
58 | private static final long serialVersionUID = 1L; |
|
59 | ||
60 | /** |
|
61 | * Helper to fetch validators for default constraints. |
|
62 | */ |
|
63 | 0 | private static final ConstraintHelper CONSTRAINTS = new ConstraintHelper(); |
64 | ||
65 | // --- members -------------------------------------------------------------- |
|
66 | ||
67 | /** |
|
68 | * The validator to validate values. |
|
69 | */ |
|
70 | private transient ConstraintValidator<A, T> validator; |
|
71 | ||
72 | // FIXME: Support list of validators. |
|
73 | ||
74 | /** |
|
75 | * The JSR-303 constraint annotation. |
|
76 | * |
|
77 | * @serial |
|
78 | */ |
|
79 | private final A constraintAnnotation; |
|
80 | ||
81 | /** |
|
82 | * Stores the validator class for serialization. |
|
83 | * |
|
84 | * @serial |
|
85 | */ |
|
86 | private final Class<? extends ConstraintValidator<A, T>> validatorClass; |
|
87 | ||
88 | // ****************************** Initializer ******************************* |
|
89 | ||
90 | // ****************************** Constructors ****************************** |
|
91 | ||
92 | /** |
|
93 | * Convenience constructor if the validator can be derived from the |
|
94 | * {@code constraintAnnotation}. |
|
95 | * |
|
96 | * @param constraintAnnotation the JSR-303 constraint annotation. |
|
97 | * @throws NullArgumentException if {@code constraintAnnotation} is |
|
98 | * <code>null</code>. |
|
99 | */ |
|
100 | @SuppressWarnings(UNCHECKED) |
|
101 | public GenericPropertyConstraint(final A constraintAnnotation) |
|
102 | throws NullArgumentException |
|
103 | { |
|
104 | 0 | this((ConstraintValidator<A, T>) createValidator(constraintAnnotation), |
105 | constraintAnnotation); |
|
106 | 0 | } |
107 | ||
108 | // /** |
|
109 | // * Convenience constructor if to provide the validator as a class. |
|
110 | // * |
|
111 | // * @param validatorClass the class of the validator to validate values. |
|
112 | // * @param constraintAnnotation the JSR-303 constraint annotation. |
|
113 | // * @throws NullArgumentException if {@code constraintAnnotation} is |
|
114 | // * <code>null</code>. |
|
115 | // */ |
|
116 | // public GenericPropertyConstraint( |
|
117 | // final Class<ConstraintValidator<A, T>> validatorClass, |
|
118 | // final A constraintAnnotation) throws NullArgumentException |
|
119 | // { |
|
120 | // this((ConstraintValidator<A, T>) createValidator(validatorClass), |
|
121 | // constraintAnnotation); |
|
122 | // } |
|
123 | ||
124 | /** |
|
125 | * Default constructor. |
|
126 | * |
|
127 | * @param validator the validator to validate values. |
|
128 | * @param constraintAnnotation the JSR-303 constraint annotation. |
|
129 | * @throws NullArgumentException if either {@code validator} or |
|
130 | * {@code constraintAnnotation} is <code>null</code>. |
|
131 | */ |
|
132 | @SuppressWarnings(UNCHECKED) |
|
133 | public GenericPropertyConstraint(final ConstraintValidator<A, T> validator, |
|
134 | final A constraintAnnotation) throws NullArgumentException |
|
135 | 0 | { |
136 | 0 | Arguments.checkNotNull("validator", validator); |
137 | 0 | Arguments.checkNotNull("constraintAnnotation", constraintAnnotation); |
138 | ||
139 | 0 | this.validator = validator; |
140 | 0 | this.constraintAnnotation = constraintAnnotation; |
141 | 0 | this.validatorClass = |
142 | (Class<? extends ConstraintValidator<A, T>>) validator.getClass(); |
|
143 | ||
144 | 0 | validator.initialize(constraintAnnotation); |
145 | 0 | } |
146 | ||
147 | // ****************************** Inner Classes ***************************** |
|
148 | ||
149 | // ********************************* Methods ******************************** |
|
150 | ||
151 | // --- init ----------------------------------------------------------------- |
|
152 | ||
153 | @SuppressWarnings({ UNCHECKED, "rawtypes" }) |
|
154 | private static <A extends Annotation, T> ConstraintValidator<A, T> createValidator( |
|
155 | final A constraintAnnotation) throws IllegalArgumentException |
|
156 | { |
|
157 | 0 | final javax.validation.Constraint annotation = |
158 | constraintAnnotation.annotationType().getAnnotation( |
|
159 | javax.validation.Constraint.class); |
|
160 | 0 | if (annotation != null) |
161 | { |
|
162 | 0 | final Class<? extends ConstraintValidator<?, ?>>[] validatorClass = |
163 | annotation.validatedBy(); |
|
164 | 0 | if (validatorClass.length > 0) |
165 | { |
|
166 | 0 | final Class<? extends ConstraintValidator<A, T>> type = |
167 | (Class<ConstraintValidator<A, T>>) validatorClass[0]; |
|
168 | 0 | final ConstraintValidator validator = |
169 | (ConstraintValidator) createValidator(type); |
|
170 | 0 | return validator; |
171 | } |
|
172 | else |
|
173 | { |
|
174 | 0 | final Class<? extends Annotation> annotationType = |
175 | constraintAnnotation.annotationType(); |
|
176 | 0 | final List<Class<? extends ConstraintValidator<? extends Annotation, ?>>> validators = |
177 | CONSTRAINTS.getBuiltInConstraints(annotationType); |
|
178 | 0 | if (!validators.isEmpty()) |
179 | { |
|
180 | 0 | final Class<? extends ConstraintValidator<A, T>> type = |
181 | (Class<? extends ConstraintValidator<A, T>>) validators.get(0); |
|
182 | 0 | final ConstraintValidator validator = |
183 | (ConstraintValidator) createValidator(type); |
|
184 | 0 | return validator; |
185 | } |
|
186 | } |
|
187 | } |
|
188 | ||
189 | 0 | throw new IllegalArgumentException( |
190 | "Cannot derive constraint validator from annotation: " |
|
191 | + constraintAnnotation); |
|
192 | } |
|
193 | ||
194 | private static <A extends Annotation, T> ConstraintValidator<A, T> createValidator( |
|
195 | final Class<? extends ConstraintValidator<A, T>> validatorClass) |
|
196 | throws IllegalArgumentException |
|
197 | { |
|
198 | 0 | Throwable cause = null; |
199 | try |
|
200 | { |
|
201 | 0 | final ConstraintValidator<A, T> validator = |
202 | (ConstraintValidator<A, T>) validatorClass.newInstance(); |
|
203 | 0 | return validator; |
204 | } |
|
205 | 0 | catch (final InstantiationException e) |
206 | { |
|
207 | 0 | cause = e; |
208 | // Fail at the end |
|
209 | } |
|
210 | 0 | catch (final IllegalAccessException e) |
211 | { |
|
212 | 0 | cause = e; |
213 | // Fail at the end |
|
214 | 0 | } |
215 | ||
216 | 0 | throw new IllegalArgumentException( |
217 | "Cannot instantiate constraint validator: " + validatorClass.getName(), |
|
218 | cause); |
|
219 | } |
|
220 | ||
221 | // --- get&set -------------------------------------------------------------- |
|
222 | ||
223 | // --- business ------------------------------------------------------------- |
|
224 | ||
225 | /** |
|
226 | * {@inheritDoc} |
|
227 | * <p> |
|
228 | * Override to provide more specific information the the string representation |
|
229 | * of the constraint annotation. |
|
230 | * </p> |
|
231 | * |
|
232 | * @see de.smartics.properties.api.core.domain.PropertyConstraint#getDescription(java.util.Locale) |
|
233 | */ |
|
234 | // CHECKSTYLE:OFF |
|
235 | @Override |
|
236 | public String getDescription(final Locale locale) // CHECKSTYLE:ON |
|
237 | { |
|
238 | 0 | return new ConstraintPrettifier(constraintAnnotation).toString(); |
239 | } |
|
240 | ||
241 | @Override |
|
242 | public final boolean isValid(final T value) |
|
243 | { |
|
244 | // Unsure if the context may be null, therefore we fake it: |
|
245 | 0 | final ConstraintValidatorContext context = |
246 | new PropertyConstraintValidatorContext(); |
|
247 | // new ConstraintValidatorContextImpl(PathImpl.createRootPath(), |
|
248 | // new ConstraintDescriptorImpl<A>(constraintAnnotation, |
|
249 | // new ConstraintHelper(), ElementType.FIELD, |
|
250 | // ConstraintOrigin.DEFINED_LOCALLY)); |
|
251 | 0 | final boolean valid = validator.isValid(value, context); |
252 | 0 | return valid; |
253 | } |
|
254 | ||
255 | @Override |
|
256 | public final boolean isMandatoryConstraint() |
|
257 | { |
|
258 | 0 | final Class<? extends Annotation> type = |
259 | constraintAnnotation.annotationType(); |
|
260 | 0 | return NotNull.class == type || NotBlank.class == type; |
261 | } |
|
262 | ||
263 | // --- object basics -------------------------------------------------------- |
|
264 | ||
265 | /** |
|
266 | * Reads the object from the given stream. |
|
267 | * |
|
268 | * @param in the stream to read from. |
|
269 | * @throws IOException on read problems. |
|
270 | * @throws ClassNotFoundException if a class cannot be found. |
|
271 | */ |
|
272 | private void readObject(final ObjectInputStream in) throws IOException, |
|
273 | ClassNotFoundException |
|
274 | { |
|
275 | 0 | in.defaultReadObject(); |
276 | ||
277 | try |
|
278 | { |
|
279 | 0 | this.validator = validatorClass.newInstance(); |
280 | } |
|
281 | 0 | catch (final Exception e) |
282 | { |
|
283 | 0 | throw new IOException("Cannot deserialize validator.", e); |
284 | 0 | } |
285 | 0 | } |
286 | } |