1
|
/**
|
2
|
* Copyright (C) 2009 EDIT
|
3
|
* European Distributed Institute of Taxonomy
|
4
|
* http://www.e-taxonomy.eu
|
5
|
*
|
6
|
* The contents of this file are subject to the Mozilla Public License Version 1.1
|
7
|
* See LICENSE.TXT at the top of this package for the full license terms.
|
8
|
*/
|
9
|
package eu.etaxonomy.cdm.model.validation;
|
10
|
|
11
|
import java.util.List;
|
12
|
import java.util.Set;
|
13
|
|
14
|
import javax.persistence.Entity;
|
15
|
import javax.persistence.FetchType;
|
16
|
import javax.persistence.ManyToOne;
|
17
|
import javax.validation.ConstraintValidator;
|
18
|
import javax.validation.ConstraintViolation;
|
19
|
import javax.validation.metadata.ConstraintDescriptor;
|
20
|
import javax.xml.bind.annotation.XmlAccessType;
|
21
|
import javax.xml.bind.annotation.XmlAccessorType;
|
22
|
import javax.xml.bind.annotation.XmlElement;
|
23
|
import javax.xml.bind.annotation.XmlRootElement;
|
24
|
import javax.xml.bind.annotation.XmlType;
|
25
|
|
26
|
import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
|
27
|
import org.hibernate.annotations.Cascade;
|
28
|
import org.hibernate.annotations.CascadeType;
|
29
|
import org.hibernate.annotations.Type;
|
30
|
|
31
|
import eu.etaxonomy.cdm.model.common.CdmBase;
|
32
|
import eu.etaxonomy.cdm.model.common.ICdmBase;
|
33
|
import eu.etaxonomy.cdm.model.common.ISelfDescriptive;
|
34
|
|
35
|
/**
|
36
|
* An {@code EntityConstraintViolation} represents a single error resulting from
|
37
|
* the validation of an entity. It basically is a database model for the
|
38
|
* {@link ConstraintValidator} class of the javax.validation framework.
|
39
|
*
|
40
|
* @author ayco_holleman
|
41
|
*
|
42
|
*/
|
43
|
|
44
|
@XmlAccessorType(XmlAccessType.FIELD)
|
45
|
@XmlType(name = "EntityConstraintViolation", propOrder = { "PropertyPath", "UserFriendlyFieldName", "InvalidValue",
|
46
|
"Severity", "Message", "Validator", "ValidationGroup", "EntityValidation" })
|
47
|
@XmlRootElement(name = "EntityConstraintViolation")
|
48
|
@Entity
|
49
|
public class EntityConstraintViolation extends CdmBase {
|
50
|
private static final long serialVersionUID = 6685798691716413950L;
|
51
|
|
52
|
private static final Logger logger = LogManager.getLogger(EntityConstraintViolation.class);
|
53
|
|
54
|
public static EntityConstraintViolation newInstance() {
|
55
|
return new EntityConstraintViolation();
|
56
|
}
|
57
|
|
58
|
public static <T extends ICdmBase> EntityConstraintViolation newInstance(T entity, ConstraintViolation<T> error) {
|
59
|
EntityConstraintViolation violation = newInstance();
|
60
|
violation.setSeverity(Severity.getSeverity(error));
|
61
|
String propPath = error.getPropertyPath() == null ? "-" : error.getPropertyPath().toString();
|
62
|
violation.setPropertyPath(propPath);
|
63
|
violation.setInvalidValue(error.getInvalidValue() == null ? "NULL" : error.getInvalidValue().toString());
|
64
|
violation.setMessage(error.getMessage());
|
65
|
/*
|
66
|
* Since I have changed CdmBase to implement ISelfDescriptive, this is a
|
67
|
* redundant check, since only instances of CdmBase can be validated
|
68
|
* using the validation infrastructure. However, until Andreas Mueller
|
69
|
* decides that it is actually useful and appropriate that CdmBase
|
70
|
* should implement this interface, this check should be made, so that
|
71
|
* nothing breaks if the "implements ISelfDescriptive" is removed from
|
72
|
* the class declaration of CdmBase.
|
73
|
*/
|
74
|
if (entity instanceof ISelfDescriptive) {
|
75
|
ISelfDescriptive selfDescriptive = (ISelfDescriptive) entity;
|
76
|
violation.setUserFriendlyFieldName(selfDescriptive.getUserFriendlyFieldName(propPath));
|
77
|
} else {
|
78
|
violation.setUserFriendlyFieldName(propPath);
|
79
|
}
|
80
|
ConstraintDescriptor<?> metadata = error.getConstraintDescriptor();
|
81
|
List<?> validators = metadata.getConstraintValidatorClasses();
|
82
|
violation.setValidator(validators.isEmpty() ? null : ((Class<?>) validators.iterator().next()).getName());
|
83
|
Set<Class<?>> validationGroups = metadata.getGroups();
|
84
|
|
85
|
// See spec for getGroups(): The set of groups the constraint is applied
|
86
|
// on. If the constraint declares no group, a set with only the Default
|
87
|
// group is returned.
|
88
|
assert (validationGroups != null && validationGroups.size() > 0);
|
89
|
|
90
|
String validationGroup = validationGroups.iterator().next().getName();
|
91
|
if (validationGroups.size() > 1) {
|
92
|
if (logger.isDebugEnabled()) {
|
93
|
String fmt = "Constraint %s belongs to multiple validation groups. Will use %s to create instance";
|
94
|
String msg = String.format(fmt, violation.getValidator(), validationGroup);
|
95
|
logger.debug(msg);
|
96
|
}
|
97
|
}
|
98
|
violation.setValidationGroup(validationGroup);
|
99
|
return violation;
|
100
|
}
|
101
|
|
102
|
@XmlElement(name = "PropertyPath")
|
103
|
private String propertyPath;
|
104
|
|
105
|
@XmlElement(name = "UserFriendlyFieldName")
|
106
|
private String userFriendlyFieldName;
|
107
|
|
108
|
@XmlElement(name = "InvalidValue")
|
109
|
private String invalidValue;
|
110
|
|
111
|
@XmlElement(name = "Severity")
|
112
|
@Type(type = "eu.etaxonomy.cdm.hibernate.SeverityUserType")
|
113
|
private Severity severity = Severity.ERROR;
|
114
|
|
115
|
@XmlElement(name = "Message")
|
116
|
private String message;
|
117
|
|
118
|
@XmlElement(name = "Validator")
|
119
|
private String validator;
|
120
|
|
121
|
@XmlElement(name = "ValidationGroup")
|
122
|
private String validationGroup;
|
123
|
|
124
|
@XmlElement(name = "EntityValidation")
|
125
|
@ManyToOne(fetch = FetchType.LAZY)
|
126
|
@Cascade({ CascadeType.SAVE_UPDATE })
|
127
|
private EntityValidation entityValidation;
|
128
|
|
129
|
protected EntityConstraintViolation() {
|
130
|
}
|
131
|
|
132
|
/**
|
133
|
* Get the path from the root bean to the field with the invalid value.
|
134
|
* Ordinarily this simply is the simple name of the field of the validated
|
135
|
* entity (see {@link EntityValidation#getValidatedEntityClass()}). Only if
|
136
|
* you have used @Valid annotations, and the error was in a parent or child
|
137
|
* entity, will this be a dot-separated path (e.g. "addresses[0].street" or
|
138
|
* "company.name").
|
139
|
*/
|
140
|
public String getPropertyPath() {
|
141
|
return propertyPath;
|
142
|
}
|
143
|
|
144
|
public void setPropertyPath(String propertyPath) {
|
145
|
this.propertyPath = propertyPath;
|
146
|
}
|
147
|
|
148
|
/**
|
149
|
* A user-friendly name for the property path.
|
150
|
*/
|
151
|
public String getUserFriendlyFieldName() {
|
152
|
return userFriendlyFieldName;
|
153
|
}
|
154
|
|
155
|
public void setUserFriendlyFieldName(String userFriendlyFieldName) {
|
156
|
this.userFriendlyFieldName = userFriendlyFieldName;
|
157
|
}
|
158
|
|
159
|
/**
|
160
|
* Get the value that violated the constraint.
|
161
|
*
|
162
|
* @return
|
163
|
*/
|
164
|
public String getInvalidValue() {
|
165
|
return invalidValue;
|
166
|
}
|
167
|
|
168
|
public void setInvalidValue(String invalidValue) {
|
169
|
this.invalidValue = invalidValue;
|
170
|
}
|
171
|
|
172
|
/**
|
173
|
* Get the severity of the constraint violation.
|
174
|
*
|
175
|
* @return
|
176
|
*/
|
177
|
public Severity getSeverity() {
|
178
|
return severity == null ? Severity.ERROR : severity;
|
179
|
}
|
180
|
|
181
|
public void setSeverity(Severity severity) {
|
182
|
this.severity = severity;
|
183
|
}
|
184
|
|
185
|
/**
|
186
|
* Get the error message associated with the constraint violation.
|
187
|
*
|
188
|
* @return The error message
|
189
|
*/
|
190
|
public String getMessage() {
|
191
|
return message;
|
192
|
}
|
193
|
|
194
|
public void setMessage(String message) {
|
195
|
this.message = message;
|
196
|
}
|
197
|
|
198
|
/**
|
199
|
* Get the fully qualified class name of the {@link ConstraintValidator}
|
200
|
* responsible for invalidating the entity.
|
201
|
*
|
202
|
* @param validator
|
203
|
*/
|
204
|
public String getValidator() {
|
205
|
return validator;
|
206
|
}
|
207
|
|
208
|
public void setValidator(String validator) {
|
209
|
this.validator = validator;
|
210
|
}
|
211
|
|
212
|
/**
|
213
|
* @return the validationGroup
|
214
|
*/
|
215
|
public String getValidationGroup() {
|
216
|
return validationGroup;
|
217
|
}
|
218
|
|
219
|
/**
|
220
|
* @param validationGroup
|
221
|
* the validationGroup to set
|
222
|
*/
|
223
|
public void setValidationGroup(String validationGroup) {
|
224
|
this.validationGroup = validationGroup;
|
225
|
}
|
226
|
|
227
|
public EntityValidation getEntityValidation() {
|
228
|
return entityValidation;
|
229
|
}
|
230
|
|
231
|
public void setEntityValidation(EntityValidation entityValidation) {
|
232
|
this.entityValidation = entityValidation;
|
233
|
}
|
234
|
|
235
|
@Override
|
236
|
public boolean equals(Object obj) {
|
237
|
if (super.equals(obj)) {
|
238
|
return true;
|
239
|
}
|
240
|
EntityConstraintViolation other = (EntityConstraintViolation) obj;
|
241
|
if (!equals(invalidValue, other.invalidValue)) {
|
242
|
return false;
|
243
|
}
|
244
|
if (!equals(propertyPath, other.propertyPath)) {
|
245
|
return false;
|
246
|
}
|
247
|
if (!equals(validator, other.validator)) {
|
248
|
return false;
|
249
|
}
|
250
|
if (!equals(validationGroup, other.validationGroup)) {
|
251
|
return false;
|
252
|
}
|
253
|
return true;
|
254
|
}
|
255
|
|
256
|
@Override
|
257
|
public int hashCode() {
|
258
|
int hash = 17;
|
259
|
hash = (hash * 31) + (invalidValue == null ? 0 : invalidValue.hashCode());
|
260
|
hash = (hash * 31) + (propertyPath == null ? 0 : propertyPath.hashCode());
|
261
|
hash = (hash * 31) + (validator == null ? 0 : validator.hashCode());
|
262
|
hash = (hash * 31) + (validationGroup == null ? 0 : validationGroup.hashCode());
|
263
|
return hash | super.hashCode();
|
264
|
}
|
265
|
|
266
|
private static boolean equals(Object o1, Object o2) {
|
267
|
if (o1 == null) {
|
268
|
if (o2 == null) {
|
269
|
return true;
|
270
|
}
|
271
|
return false;
|
272
|
}
|
273
|
return o2 != null && o1.equals(o1);
|
274
|
}
|
275
|
|
276
|
}
|