1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package eu.ehri.project.persistence;
21
22 import com.google.common.collect.ListMultimap;
23 import com.google.common.collect.Lists;
24 import com.tinkerpop.blueprints.CloseableIterable;
25 import com.tinkerpop.blueprints.Vertex;
26 import eu.ehri.project.core.GraphManager;
27 import eu.ehri.project.exceptions.ValidationError;
28 import eu.ehri.project.models.annotations.EntityType;
29 import eu.ehri.project.models.utils.ClassUtils;
30
31 import java.text.MessageFormat;
32 import java.util.Collection;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Optional;
36 import java.util.Set;
37
38
39
40
41 public final class BundleValidator {
42
43 private final GraphManager manager;
44 private final List<String> scopes;
45
46 public BundleValidator(GraphManager manager, Collection<String> scopes) {
47 this.manager = manager;
48 this.scopes = Lists.newArrayList(Optional.ofNullable(scopes)
49 .orElse(Lists.newArrayList()));
50 }
51
52
53
54
55
56
57
58 Bundle validateForCreate(Bundle bundle) throws ValidationError {
59 validateData(bundle);
60 Bundle withIds = bundle.generateIds(scopes);
61 ErrorSet createErrors = validateTreeForCreate(withIds);
62 if (!createErrors.isEmpty()) {
63 throw new ValidationError(bundle, createErrors);
64 }
65 return withIds;
66 }
67
68
69
70
71
72
73
74 Bundle validateForUpdate(Bundle bundle) throws ValidationError {
75 validateData(bundle);
76 Bundle withIds = bundle.generateIds(scopes);
77 ErrorSet updateErrors = validateTreeForUpdate(withIds);
78 if (!updateErrors.isEmpty()) {
79 throw new ValidationError(bundle, updateErrors);
80 }
81 return withIds;
82 }
83
84
85
86
87
88
89
90
91 private void validateData(Bundle bundle) throws ValidationError {
92 ErrorSet es = validateTreeData(bundle);
93 if (!es.isEmpty()) {
94 throw new ValidationError(bundle, es);
95 }
96 }
97
98 private enum ValidationType {
99 data, create, update
100 }
101
102
103
104
105
106
107
108
109
110 private ErrorSet validateTreeForUpdate(Bundle bundle) {
111 ErrorSet.Builder builder = new ErrorSet.Builder();
112 if (bundle.getId() == null)
113 builder.addError(Bundle.ID_KEY, Messages
114 .getString("BundleValidator.missingIdForUpdate"));
115 checkUniquenessOnUpdate(bundle, builder);
116 checkChildren(bundle, builder, ValidationType.update);
117 return builder.build();
118 }
119
120
121
122
123
124
125
126
127
128 private ErrorSet validateTreeForCreate(Bundle bundle) {
129 ErrorSet.Builder builder = new ErrorSet.Builder();
130 checkIntegrity(bundle, builder);
131 checkUniqueness(bundle, builder);
132 checkChildren(bundle, builder, ValidationType.create);
133 return builder.build();
134 }
135
136
137
138
139
140
141
142
143 private ErrorSet validateTreeData(Bundle bundle) {
144 ErrorSet.Builder builder = new ErrorSet.Builder();
145 checkFields(bundle, builder);
146 checkEntityType(bundle, builder);
147 checkChildren(bundle, builder, ValidationType.data);
148 return builder.build();
149 }
150
151 private void checkChildren(Bundle bundle,
152 ErrorSet.Builder builder, ValidationType type) {
153 for (Map.Entry<String, Bundle> entry : bundle.getDependentRelations().entries()) {
154 switch (type) {
155 case data:
156 builder.addRelation(entry.getKey(), validateTreeData(entry.getValue()));
157 break;
158 case create:
159 builder.addRelation(entry.getKey(), validateTreeForCreate(entry.getValue()));
160 break;
161 case update:
162 builder.addRelation(entry.getKey(), validateTreeForUpdate(entry.getValue()));
163 break;
164 }
165 }
166 }
167
168
169
170
171 private static void checkFields(Bundle bundle, ErrorSet.Builder builder) {
172 for (String key : ClassUtils.getMandatoryPropertyKeys(bundle.getBundleJavaClass())) {
173 checkField(bundle, builder, key);
174 }
175 Map<String, Set<String>> enumPropertyKeys = ClassUtils.getEnumPropertyKeys(bundle.getBundleJavaClass());
176 for (Map.Entry<String, Set<String>> entry : enumPropertyKeys.entrySet()) {
177 checkValueInRange(bundle, builder, entry.getKey(), entry.getValue());
178 }
179 }
180
181 private static void checkValueInRange(Bundle bundle, ErrorSet.Builder builder, String key, Collection<String> values) {
182 Object dataValue = bundle.getDataValue(key);
183 if (dataValue != null) {
184 if (!values.contains(dataValue.toString())) {
185 builder.addError(key,
186 MessageFormat.format(Messages.getString("BundleValidator.invalidFieldValue"), values, dataValue));
187 }
188 }
189 }
190
191
192
193
194
195
196 private static void checkField(Bundle bundle, ErrorSet.Builder builder, String name) {
197 if (!bundle.getData().containsKey(name)) {
198 builder.addError(name, Messages.getString("BundleValidator.missingField"));
199 } else {
200 Object value = bundle.getData().get(name);
201 if (value == null) {
202 builder.addError(name, Messages.getString("BundleValidator.emptyField"));
203 } else if (value instanceof String) {
204 if (((String) value).trim().isEmpty()) {
205 builder.addError(name, Messages.getString("BundleValidator.emptyField"));
206 }
207 }
208 }
209 }
210
211
212
213
214 private static void checkEntityType(Bundle bundle, ErrorSet.Builder builder) {
215 EntityType annotation = bundle.getBundleJavaClass().getAnnotation(EntityType.class);
216 if (annotation == null) {
217 builder.addError(Bundle.TYPE_KEY, MessageFormat.format(Messages
218 .getString("BundleValidator.missingTypeAnnotation"),
219 bundle.getBundleJavaClass().getName()));
220 }
221 }
222
223
224
225
226
227 private void checkIntegrity(Bundle bundle, ErrorSet.Builder builder) {
228 if (bundle.getId() == null) {
229 builder.addError("id", MessageFormat.format(Messages.getString("BundleValidator.missingIdForCreate"),
230 bundle.getId()));
231 }
232 if (manager.exists(bundle.getId())) {
233 ListMultimap<String, String> idErrors = bundle
234 .getType().getIdGen().handleIdCollision(scopes, bundle);
235 for (Map.Entry<String, String> entry : idErrors.entries()) {
236 builder.addError(entry.getKey(), entry.getValue());
237 }
238 }
239 }
240
241
242
243
244 private void checkUniqueness(Bundle bundle, ErrorSet.Builder builder) {
245 for (String ukey : bundle.getUniquePropertyKeys()) {
246 Object uval = bundle.getDataValue(ukey);
247 if (uval != null) {
248 try (CloseableIterable<Vertex> vertices = manager.getVertices(ukey, uval, bundle.getType())) {
249 if (vertices.iterator().hasNext()) {
250 builder.addError(ukey, MessageFormat.format(Messages
251 .getString("BundleValidator.uniquenessError"), uval));
252 }
253 }
254 }
255 }
256 }
257
258
259
260
261
262 private void checkUniquenessOnUpdate(Bundle bundle, ErrorSet.Builder builder) {
263 for (String ukey : bundle.getUniquePropertyKeys()) {
264 Object uval = bundle.getDataValue(ukey);
265 if (uval != null) {
266 try (CloseableIterable<Vertex> vertices = manager.getVertices(ukey, uval, bundle.getType())) {
267 if (vertices.iterator().hasNext()) {
268 Vertex v = vertices.iterator().next();
269
270 if (!manager.getId(v).equals(bundle.getId())) {
271 builder.addError(ukey, MessageFormat.format(Messages
272 .getString("BundleValidator.uniquenessError"), uval));
273
274 }
275 }
276 }
277 }
278 }
279 }
280 }