Skip to content

Package: ValidationServiceImpl

ValidationServiceImpl

nameinstructionbranchcomplexitylinemethod
ValidationServiceImpl()
M: 0 C: 34
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 8
100%
M: 0 C: 1
100%
addDiagnosticFilter(DiagnosticFilter)
M: 0 C: 8
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
addObjectFilter(ObjectFilter)
M: 0 C: 8
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
addSubTreeFilter(SubTreeFilter)
M: 0 C: 8
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
addValidator(Validator)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
cancel()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
doValidate(EObject)
M: 0 C: 15
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
getDiagnosticFilters()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getDiagnosticForEObject(EObject)
M: 1 C: 102
99%
M: 2 C: 14
88%
M: 2 C: 7
78%
M: 1 C: 21
95%
M: 0 C: 1
100%
getEValidatorForEObject(EObject)
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getObjectFilters()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getSubTreeFilters()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getValidationContext(EValidator)
M: 0 C: 26
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 7
100%
M: 0 C: 1
100%
invokeValidationResultListeners(EObject, Diagnostic, boolean)
M: 5 C: 24
83%
M: 1 C: 5
83%
M: 1 C: 3
75%
M: 2 C: 6
75%
M: 0 C: 1
100%
isBusy()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
isFiltered(EObject)
M: 0 C: 26
100%
M: 0 C: 6
100%
M: 0 C: 4
100%
M: 0 C: 6
100%
M: 0 C: 1
100%
isIgnored(EObject, Diagnostic)
M: 21 C: 6
22%
M: 5 C: 1
17%
M: 3 C: 1
25%
M: 4 C: 2
33%
M: 0 C: 1
100%
isSkipSubtree(EObject, Optional)
M: 21 C: 6
22%
M: 5 C: 1
17%
M: 3 C: 1
25%
M: 4 C: 2
33%
M: 0 C: 1
100%
registerValidationFilter(ValidationFilter)
M: 28 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
registerValidationResultListener(ValidationResultListener)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
removeDiagnosticFilter(DiagnosticFilter)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
removeObjectFilter(ObjectFilter)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
removeSubTreeFilter(SubTreeFilter)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
removeValidator(Validator)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
setSubstitutionLabelProvider(EValidator.SubstitutionLabelProvider)
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
unregisterValidationFilter(ValidationFilter)
M: 16 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
unregisterValidationResultListener(ValidationResultListener)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
validate(EObject)
M: 0 C: 10
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
validate(Iterator)
M: 15 C: 86
85%
M: 9 C: 13
59%
M: 9 C: 3
25%
M: 6 C: 22
79%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2017 EclipseSource Muenchen GmbH and others.
3: *
4: * All rights reserved. This program and the accompanying materials
5: * are made available under the terms of the Eclipse Public License 2.0
6: * which accompanies this distribution, and is available at
7: * https://www.eclipse.org/legal/epl-2.0/
8: *
9: * SPDX-License-Identifier: EPL-2.0
10: *
11: * Contributors:
12: * Mat Hansen - initial API and implementation
13: * Christian W. Damus - bug 526224
14: ******************************************************************************/
15: package org.eclipse.emfforms.common.internal.validation;
16:
17: import java.util.Iterator;
18: import java.util.LinkedHashMap;
19: import java.util.LinkedHashSet;
20: import java.util.List;
21: import java.util.Map;
22: import java.util.Set;
23:
24: import org.eclipse.emf.common.util.BasicDiagnostic;
25: import org.eclipse.emf.common.util.Diagnostic;
26: import org.eclipse.emf.common.util.DiagnosticChain;
27: import org.eclipse.emf.common.util.TreeIterator;
28: import org.eclipse.emf.ecore.EClass;
29: import org.eclipse.emf.ecore.EDataType;
30: import org.eclipse.emf.ecore.EObject;
31: import org.eclipse.emf.ecore.EStructuralFeature;
32: import org.eclipse.emf.ecore.EValidator;
33: import org.eclipse.emf.ecore.EValidator.SubstitutionLabelProvider;
34: import org.eclipse.emf.ecore.impl.DynamicEObjectImpl;
35: import org.eclipse.emf.ecore.util.Diagnostician;
36: import org.eclipse.emfforms.common.Optional;
37: import org.eclipse.emfforms.common.spi.validation.ValidationResultListener;
38: import org.eclipse.emfforms.common.spi.validation.ValidationService;
39: import org.eclipse.emfforms.common.spi.validation.Validator;
40: import org.eclipse.emfforms.common.spi.validation.exception.ValidationCanceledException;
41: import org.eclipse.emfforms.common.spi.validation.filter.DiagnosticFilter;
42: import org.eclipse.emfforms.common.spi.validation.filter.ObjectFilter;
43: import org.eclipse.emfforms.common.spi.validation.filter.SubTreeFilter;
44: import org.eclipse.emfforms.common.spi.validation.filter.ValidationFilter;
45:
46: /**
47: * The implementation of {@link ValidationService}.
48: *
49: * @author Mat Hansen <mhansen@eclipsesource.com>
50: */
51: public class ValidationServiceImpl implements ValidationService {
52:
53:         private final Set<Validator> validators = new LinkedHashSet<Validator>();
54:
55:         private final Set<ObjectFilter> objectFilters = new LinkedHashSet<ObjectFilter>();
56:         private final Set<SubTreeFilter> subTreeFilters = new LinkedHashSet<SubTreeFilter>();
57:         private final Set<DiagnosticFilter> diagnosticFilters = new LinkedHashSet<DiagnosticFilter>();
58:
59:         private final Set<ValidationResultListener> validationResultListeners = new LinkedHashSet<ValidationResultListener>();
60:
61:         private SubstitutionLabelProvider substitutionLabelProvider;
62:
63:         private boolean validationRunning;
64:         private boolean cancelationRequested;
65:
66:         private final Diagnostician diagnostician;
67:         private Map<Object, Object> validationContext;
68:
69:         /**
70:          * Default constructor.
71:          */
72:         public ValidationServiceImpl() {
73:                 diagnostician = new DiagnosticianImpl();
74:         }
75:
76:         private boolean isFiltered(EObject object) {
77:•                if (getObjectFilters().isEmpty()) {
78:                         return false;
79:                 }
80:•                for (final ObjectFilter filter : getObjectFilters()) {
81:•                        if (filter.skipValidation(object)) {
82:                                 return true;
83:                         }
84:                 }
85:                 return false;
86:         }
87:
88:         private boolean isSkipSubtree(EObject object, Optional<Diagnostic> diagnostic) {
89:•                if (getSubTreeFilters().isEmpty()) {
90:                         return false;
91:                 }
92:•                for (final SubTreeFilter filter : getSubTreeFilters()) {
93:•                        if (filter.skipSubtree(object, diagnostic)) {
94:                                 return true;
95:                         }
96:                 }
97:                 return false;
98:         }
99:
100:         private boolean isIgnored(EObject object, Diagnostic diagnostic) {
101:•                if (getDiagnosticFilters().isEmpty()) {
102:                         return false;
103:                 }
104:•                for (final DiagnosticFilter filter : getDiagnosticFilters()) {
105:•                        if (filter.ignoreDiagnostic(object, diagnostic)) {
106:                                 return true;
107:                         }
108:                 }
109:                 return false;
110:         }
111:
112:         private Diagnostic doValidate(EObject eObject) {
113:
114:                 Diagnostic diagnostic = null;
115:                 try {
116:
117:                         diagnostic = getDiagnosticForEObject(eObject);
118:                         return diagnostic;
119:
120:                 } finally {
121:                         invokeValidationResultListeners(eObject, diagnostic, false);
122:                 }
123:
124:         }
125:
126:         @Override
127:         public Diagnostic validate(EObject eObject) {
128:•                if (isFiltered(eObject)) {
129:                         return null;
130:                 }
131:                 return doValidate(eObject);
132:         }
133:
134:         @Override
135:         public Set<Diagnostic> validate(Iterator<EObject> eObjectsIterator) throws ValidationCanceledException {
136:
137:                 final Set<Diagnostic> diagnostics = new LinkedHashSet<Diagnostic>();
138:•                if (validationRunning) {
139:                         return diagnostics; // prevent re-entry
140:                 }
141:
142:                 validationRunning = true;
143:                 validationContext = new LinkedHashMap<Object, Object>(); // Shared context
144:                 try {
145:
146:                         final boolean isTreeIterator = eObjectsIterator instanceof TreeIterator<?>
147:•                        while (!cancelationRequested && eObjectsIterator.hasNext()) {
148:
149:                                 final EObject eObject = eObjectsIterator.next();
150:                                 Diagnostic diagnostic = null;
151:
152:                                 boolean isSubtreePruned = false;
153:•                                if (isTreeIterator && isSkipSubtree(eObject, Optional.ofNullable(diagnostic))) {
154:                                         ((TreeIterator<?>) eObjectsIterator).prune();
155:                                         isSubtreePruned = true;
156:                                 }
157:•                                if (isFiltered(eObject)) {
158:                                         continue;
159:                                 }
160:
161:                                 diagnostic = doValidate(eObject);
162:
163:•                                if (isTreeIterator && !isSubtreePruned && isSkipSubtree(eObject, Optional.of(diagnostic))) {
164:                                         ((TreeIterator<?>) eObjectsIterator).prune();
165:                                 }
166:•                                if (isIgnored(eObject, diagnostic)) {
167:                                         continue;
168:                                 }
169:
170:                                 diagnostics.add(diagnostic);
171:                                 invokeValidationResultListeners(eObject, diagnostic, true);
172:
173:                         }
174:                 } finally {
175:                         validationRunning = false;
176:                         validationContext = null; // Forget the shared context
177:                 }
178:
179:•                if (cancelationRequested) {
180:                         throw new ValidationCanceledException();
181:                 }
182:                 return diagnostics;
183:         }
184:
185:         private void invokeValidationResultListeners(EObject eObject, Diagnostic diagnostic, boolean passed) {
186:•                if (validationResultListeners.isEmpty()) {
187:                         return;
188:                 }
189:
190:•                for (final ValidationResultListener listener : validationResultListeners) {
191:•                        if (passed) {
192:                                 listener.afterValidate(eObject, diagnostic);
193:                         } else {
194:                                 listener.onValidate(eObject, diagnostic);
195:                         }
196:                 }
197:         }
198:
199:         /**
200:          * Returns a {@link EValidator} for the given {@link EObject}.
201:          *
202:          * @param eObject the {@link EObject} to retrieve a {@link EValidator} for
203:          * @return the {@link EValidator}
204:          */
205:         protected EValidator getEValidatorForEObject(EObject eObject) {
206:                 return diagnostician;
207:         }
208:
209:         /**
210:          * Computes the {@link Diagnostic} for the given eObject.
211:          *
212:          * @param object the {@link EObject} to validate
213:          * @return the {@link Diagnostic}
214:          */
215:         protected Diagnostic getDiagnosticForEObject(EObject object) {
216:
217:                 final EValidator eValidator = getEValidatorForEObject(object);
218:                 final BasicDiagnostic diagnostic = diagnostician.createDefaultDiagnostic(object);
219:                 final Map<Object, Object> context = getValidationContext(eValidator);
220:
221:                 eValidator.validate(object, diagnostic, context);
222:
223:                 final Map<EStructuralFeature, DiagnosticChain> diagnosticMap = new LinkedHashMap<EStructuralFeature, DiagnosticChain>();
224:•                for (final Diagnostic child : diagnostic.getChildren()) {
225:•                        if (DiagnosticChain.class.isInstance(child) && DiagnosticHelper.checkDiagnosticData(child)) {
226:                                 diagnosticMap.put(DiagnosticHelper.getEStructuralFeature(child.getData()),
227:                                         (DiagnosticChain) child);
228:                         }
229:                 }
230:
231:•                for (final Validator validator : validators) {
232:                         final List<Diagnostic> additionValidation = validator.validate(object);
233:•                        if (additionValidation == null) {
234:                                 continue;
235:                         }
236:•                        for (final Diagnostic additionDiagnostic : additionValidation) {
237:                                 if (diagnosticMap
238:•                                        .containsKey(DiagnosticHelper.getEStructuralFeature(additionDiagnostic.getData()))) {
239:                                         diagnosticMap.get(DiagnosticHelper.getEStructuralFeature(additionDiagnostic.getData()))
240:                                                 .add(additionDiagnostic);
241:                                 } else {
242:                                         // only add non-OK diagnostics
243:•                                        if (additionDiagnostic.getSeverity() != Diagnostic.OK) {
244:                                                 diagnostic.add(additionDiagnostic);
245:                                         }
246:                                 }
247:
248:                         }
249:                 }
250:                 return diagnostic;
251:         }
252:
253:         /**
254:          * Obtains the shared validation context for a validation of multiple objects
255:          * or a one-off context for validation of a single object. If I have a
256:          * {@link #setSubstitutionLabelProvider(SubstitutionLabelProvider) substitution label provider},
257:          * thn it will also be put into the context. Subclasses may extend this to populate
258:          * the context with anything else of interest.
259:          *
260:          * @param eValidator the validator to put into the context
261:          * @return the context
262:          */
263:         protected Map<Object, Object> getValidationContext(EValidator eValidator) {
264:•                final Map<Object, Object> result = validationContext == null
265:                         ? new LinkedHashMap<Object, Object>()
266:                         : validationContext;
267:
268:                 result.put(EValidator.class, eValidator);
269:•                if (substitutionLabelProvider != null) {
270:                         result.put(EValidator.SubstitutionLabelProvider.class, substitutionLabelProvider);
271:                 }
272:
273:                 return result;
274:         }
275:
276:         @Override
277:         public void addValidator(Validator validator) {
278:                 validators.add(validator);
279:         }
280:
281:         @Override
282:         public void removeValidator(Validator validator) {
283:                 validators.remove(validator);
284:         }
285:
286:         @Deprecated
287:         @Override
288:         public void registerValidationFilter(ValidationFilter filter) {
289:•                if (filter instanceof ObjectFilter) {
290:                         getObjectFilters().add((ObjectFilter) filter);
291:                 }
292:•                if (filter instanceof SubTreeFilter) {
293:                         getSubTreeFilters().add((SubTreeFilter) filter);
294:                 }
295:•                if (filter instanceof DiagnosticFilter) {
296:                         getDiagnosticFilters().add((DiagnosticFilter) filter);
297:                 }
298:         }
299:
300:         @Deprecated
301:         @Override
302:         public void unregisterValidationFilter(ValidationFilter filter) {
303:                 getObjectFilters().remove(filter);
304:                 getSubTreeFilters().remove(filter);
305:                 getDiagnosticFilters().remove(filter);
306:         }
307:
308:         @Override
309:         public void registerValidationResultListener(ValidationResultListener listener) {
310:                 validationResultListeners.add(listener);
311:         }
312:
313:         @Override
314:         public void unregisterValidationResultListener(ValidationResultListener listener) {
315:                 validationResultListeners.remove(listener);
316:         }
317:
318:         @Override
319:         public void setSubstitutionLabelProvider(SubstitutionLabelProvider substitutionLabelProvider) {
320:                 this.substitutionLabelProvider = substitutionLabelProvider;
321:         }
322:
323:         @Override
324:         public boolean isBusy() {
325:                 return validationRunning;
326:         }
327:
328:         @Override
329:         public void cancel() {
330:                 cancelationRequested = true;
331:         }
332:
333:         //
334:         // Nested types
335:         //
336:
337:         /**
338:          * A private diagnostician that uses the service's {@link SubstitutionLabelProvider}
339:          * for creating diagnostic messages and delegates validation of each object back
340:          * to the service.
341:          */
342:         private final class DiagnosticianImpl extends Diagnostician {
343:
344:                 DiagnosticianImpl() {
345:                         super(EValidator.Registry.INSTANCE);
346:                 }
347:
348:                 @Override
349:                 public String getObjectLabel(EObject eObject) {
350:                         return substitutionLabelProvider != null
351:                                 ? substitutionLabelProvider.getObjectLabel(eObject)
352:                                 : super.getObjectLabel(eObject);
353:                 }
354:
355:                 @Override
356:                 public String getFeatureLabel(EStructuralFeature eStructuralFeature) {
357:                         return substitutionLabelProvider != null
358:                                 ? substitutionLabelProvider.getFeatureLabel(eStructuralFeature)
359:                                 : super.getFeatureLabel(eStructuralFeature);
360:                 }
361:
362:                 @Override
363:                 public String getValueLabel(EDataType eDataType, Object value) {
364:                         return substitutionLabelProvider != null
365:                                 ? substitutionLabelProvider.getValueLabel(eDataType, value)
366:                                 : super.getValueLabel(eDataType, value);
367:                 }
368:
369:                 @Override
370:                 public boolean validate(EClass eClass, EObject eObject, DiagnosticChain diagnostics,
371:                         Map<Object, Object> context) {
372:                         EValidator eValidator;
373:                         EClass eType = eClass;
374:                         // short cut dynamic emf to avoid ClassCastExceptions in custom validators
375:                         if (eObject instanceof DynamicEObjectImpl) {
376:                                 eValidator = eValidatorRegistry.getEValidator(null);
377:                         } else {
378:                                 while ((eValidator = eValidatorRegistry.getEValidator(eType.getEPackage())) == null) {
379:                                         final List<EClass> eSuperTypes = eType.getESuperTypes();
380:                                         if (eSuperTypes.isEmpty()) {
381:                                                 eValidator = eValidatorRegistry.getEValidator(null);
382:                                                 break;
383:                                         }
384:                                         eType = eSuperTypes.get(0);
385:                                 }
386:                         }
387:                         return doValidate(eValidator, eType, eObject, diagnostics, context);
388:                 }
389:
390:                 // remove containment validation introduced in EMF 2.14 via iterator
391:                 @Override
392:                 public boolean validate(EObject eObject, DiagnosticChain diagnostics, Map<Object, Object> context) {
393:                         return validate(eObject.eClass(), eObject, diagnostics, context);
394:                 }
395:
396:         }
397:
398:         @Override
399:         public void addSubTreeFilter(SubTreeFilter subTreeFilter) {
400:•                if (subTreeFilter != null) {
401:                         getSubTreeFilters().add(subTreeFilter);
402:                 }
403:
404:         }
405:
406:         @Override
407:         public void addObjectFilter(ObjectFilter objectFilter) {
408:•                if (objectFilter != null) {
409:                         getObjectFilters().add(objectFilter);
410:                 }
411:
412:         }
413:
414:         @Override
415:         public void addDiagnosticFilter(DiagnosticFilter diagnosticFilter) {
416:•                if (diagnosticFilter != null) {
417:                         getDiagnosticFilters().add(diagnosticFilter);
418:                 }
419:
420:         }
421:
422:         @Override
423:         public void removeSubTreeFilter(SubTreeFilter subTreeFilter) {
424:                 getSubTreeFilters().remove(subTreeFilter);
425:
426:         }
427:
428:         @Override
429:         public void removeObjectFilter(ObjectFilter objectFilter) {
430:                 getObjectFilters().remove(objectFilter);
431:
432:         }
433:
434:         @Override
435:         public void removeDiagnosticFilter(DiagnosticFilter diagnosticFilter) {
436:                 getDiagnosticFilters().remove(diagnosticFilter);
437:
438:         }
439:
440:         /**
441:          * @return the objectFilters
442:          */
443:         Set<ObjectFilter> getObjectFilters() {
444:                 return objectFilters;
445:         }
446:
447:         /**
448:          * @return the subTreeFilters
449:          */
450:         Set<SubTreeFilter> getSubTreeFilters() {
451:                 return subTreeFilters;
452:         }
453:
454:         /**
455:          * @return the diagnosticFilters
456:          */
457:         Set<DiagnosticFilter> getDiagnosticFilters() {
458:                 return diagnosticFilters;
459:         }
460: }