Skip to content

Package: DiagnosticCache

DiagnosticCache

nameinstructionbranchcomplexitylinemethod
DiagnosticCache(Notifier)
M: 9 C: 60
87%
M: 1 C: 5
83%
M: 1 C: 3
75%
M: 1 C: 14
93%
M: 0 C: 1
100%
createdCachedTreeNode(Diagnostic)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
depthFirstCacheUpdate(EObject)
M: 3 C: 19
86%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 1 C: 5
83%
M: 0 C: 1
100%
deregisterValidationListener(DiagnosticCache.ValidationListener)
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%
dispose()
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%
getDefaultValue()
M: 0 C: 2
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getDiagnostic(Object)
M: 2 C: 47
96%
M: 1 C: 3
75%
M: 1 C: 2
67%
M: 1 C: 11
92%
M: 0 C: 1
100%
getObjects()
M: 0 C: 8
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getOwnValue(Object)
M: 6 C: 14
70%
M: 2 C: 2
50%
M: 2 C: 1
33%
M: 2 C: 4
67%
M: 0 C: 1
100%
handleRemove(EObject, DiagnosticCache)
M: 0 C: 41
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 9
100%
M: 0 C: 1
100%
init(Notifier)
M: 19 C: 99
84%
M: 2 C: 10
83%
M: 2 C: 5
71%
M: 5 C: 28
85%
M: 0 C: 1
100%
isInitializing()
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%
lambda$3(EObject)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$4(CachedTreeNode, EObject)
M: 0 C: 7
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
notifyValidationListeners(Set, boolean)
M: 0 C: 17
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
registerValidationListener(DiagnosticCache.ValidationListener)
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%
reinit(Notifier)
M: 13 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
updateCache(EObject, DiagnosticCache)
M: 13 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
updateCache(Set, DiagnosticCache)
M: 0 C: 33
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 7
100%
M: 0 C: 1
100%
updateCacheWithoutRefresh(EObject, DiagnosticCache)
M: 0 C: 18
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
updateCacheWithoutRefresh(Set, DiagnosticCache)
M: 0 C: 33
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 7
100%
M: 0 C: 1
100%
updateParentNode(Object, Object, Diagnostic)
M: 0 C: 9
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2018 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: * Johannes Faltermeier - initial API and implementation
13: * Christian W. Damus - bug 533522
14: ******************************************************************************/
15: package org.eclipse.emfforms.spi.swt.treemasterdetail.diagnostic;
16:
17: import java.util.Collection;
18: import java.util.Collections;
19: import java.util.Comparator;
20: import java.util.HashMap;
21: import java.util.Iterator;
22: import java.util.LinkedHashSet;
23: import java.util.List;
24: import java.util.Map;
25: import java.util.Set;
26: import java.util.TreeSet;
27: import java.util.concurrent.CopyOnWriteArraySet;
28:
29: import org.eclipse.emf.common.notify.Notification;
30: import org.eclipse.emf.common.notify.Notifier;
31: import org.eclipse.emf.common.util.BasicDiagnostic;
32: import org.eclipse.emf.common.util.Diagnostic;
33: import org.eclipse.emf.common.util.EList;
34: import org.eclipse.emf.common.util.TreeIterator;
35: import org.eclipse.emf.ecore.EObject;
36: import org.eclipse.emf.ecore.EReference;
37: import org.eclipse.emf.ecore.EStructuralFeature.Setting;
38: import org.eclipse.emf.ecore.EValidator;
39: import org.eclipse.emf.ecore.resource.Resource;
40: import org.eclipse.emf.ecore.resource.ResourceSet;
41: import org.eclipse.emf.ecore.util.Diagnostician;
42: import org.eclipse.emf.ecore.util.EContentAdapter;
43: import org.eclipse.emf.ecore.util.EObjectValidator;
44: import org.eclipse.emf.ecore.util.EcoreUtil;
45: import org.eclipse.emf.ecp.common.spi.cachetree.AbstractCachedTree;
46: import org.eclipse.emf.ecp.common.spi.cachetree.CachedTreeNode;
47: import org.eclipse.emf.ecp.common.spi.cachetree.IExcludedObjectsCallback;
48:
49: /**
50: * Cache for diagnostic results.
51: *
52: * @author Johannes Faltermeier
53: * @since 1.10
54: *
55: */
56: public class DiagnosticCache extends AbstractCachedTree<Diagnostic> {
57:
58:         private final Set<ValidationListener> validationListeners = new CopyOnWriteArraySet<ValidationListener>();
59:
60:         private ValidationChangeListener validationChangeListener;
61:
62:         private Notifier input;
63:
64:         private boolean initializing;
65:
66:         /**
67:          * Default constructor.
68:          *
69:          * @param input the input
70:          */
71:         public DiagnosticCache(Notifier input) {
72:                 super(new IExcludedObjectsCallback() {
73:
74:                         @Override
75:                         public boolean isExcluded(Object object) {
76:                                 return false;
77:                         }
78:                 });
79:
80:                 init(input);
81:
82:                 final Set<EObject> rootObjects = new LinkedHashSet<>();
83:•                if (ResourceSet.class.isInstance(input)) {
84:                         final EList<Resource> resources = ResourceSet.class.cast(input).getResources();
85:                         resources.stream().map(Resource::getContents).forEach(rootObjects::addAll);
86:•                } else if (Resource.class.isInstance(input)) {
87:                         rootObjects.addAll(Resource.class.cast(input).getContents());
88:•                } else if (EObject.class.isInstance(input)) {
89:                         rootObjects.add(EObject.class.cast(input));
90:                 } else {
91:                         return;
92:                 }
93:
94:                 // The implementation builds the cache tree on initialization, but does not propagate validation results
95:                 // of children to their parents. This means, the own diagnostic for every EObject in the containment history is
96:                 // known but nodes do not know about the diagnostics of their children, yet. As a consequence, validation errors
97:                 // can only be shown where they occur.
98:                 // In order to show the errors at the parents, we walk down the containment hierarchy in a depth-first search
99:                 // approach and propagate the validation results upwards. On the way up, each node propagates its aggregated
100:                 // validation result which is the most severe diagnostic of itself and its children. With this we only need to
101:                 // propagate the results one time from bottom to the top.
102:                 rootObjects.forEach(this::depthFirstCacheUpdate);
103:         }
104:
105:         private void init(Notifier input) {
106:                 final boolean wasInitializing = initializing;
107:                 initializing = true;
108:
109:                 try {
110:•                        if (input == null) {
111:                                 return;
112:                         }
113:                         this.input = input;
114:                         validationChangeListener = new ValidationChangeListener(input);
115:                         TreeIterator<Object> allContents;
116:                         final Set<EObject> externalReferences = new LinkedHashSet<EObject>();
117:•                        if (ResourceSet.class.isInstance(input)) {
118:                                 final ResourceSet resourceSet = ResourceSet.class.cast(input);
119:                                 allContents = EcoreUtil.getAllContents(resourceSet, false);
120:                                 final Map<EObject, Collection<Setting>> map = EcoreUtil.ExternalCrossReferencer.find(resourceSet);
121:                                 externalReferences.addAll(map.keySet());
122:•                        } else if (Resource.class.isInstance(input)) {
123:                                 final Resource resource = Resource.class.cast(input);
124:                                 allContents = EcoreUtil.getAllContents(resource, false);
125:                                 final Map<EObject, Collection<Setting>> map = EcoreUtil.ExternalCrossReferencer.find(resource);
126:                                 externalReferences.addAll(map.keySet());
127:•                        } else if (EObject.class.isInstance(input)) {
128:                                 final Set<EObject> set = Collections.singleton(EObject.class.cast(input));
129:                                 allContents = EcoreUtil.getAllContents(set, false);
130:                                 final Map<EObject, Collection<Setting>> map = EcoreUtil.ExternalCrossReferencer.find(set);
131:                                 externalReferences.addAll(map.keySet());
132:                         } else {
133:                                 return;
134:                         }
135:
136:•                        while (allContents.hasNext()) {
137:                                 final Object next = allContents.next();
138:•                                if (!EObject.class.isInstance(next)) {
139:                                         continue;
140:                                 }
141:                                 updateCacheWithoutRefresh(EObject.class.cast(next), this);
142:                         }
143:                         externalReferences.forEach(e -> updateCacheWithoutRefresh(e, this));
144:                 } finally {
145:                         initializing = wasInitializing;
146:                 }
147:         }
148:
149:         /**
150:          * Recursively walks down the containment hierarchy of the given root object and puts the children's diagnostics
151:          * into the cache of root's cached tree node. Afterwards, the most severe diagnostic of root's subtree (including
152:          * root) is returned which allows caching it in root's parent.
153:          *
154:          * @param root The root object of the current depth first search
155:          * @return The most severe Diagnostic in the subtree of the root object
156:          */
157:         private Diagnostic depthFirstCacheUpdate(EObject root) {
158:                 final CachedTreeNode<Diagnostic> treeNode = getNodes().get(root);
159:•                if (treeNode == null) {
160:                         return getDefaultValue();
161:                 }
162:                 root.eContents().stream()
163:                         .forEach(c -> treeNode.putIntoCache(c, depthFirstCacheUpdate(c)));
164:                 return treeNode.getDisplayValue();
165:         }
166:
167:         /**
168:          * Queries whether the cache is in the process of initializing itself. This is useful
169:          * to avoid doing redundant work, such as back-tracking up an EMF containment tree
170:          * to update parents that have already been covered during the initialization.
171:          *
172:          * @return whether the cache is in the process of initializations
173:          *
174:          * @since 1.17
175:          */
176:         protected final boolean isInitializing() {
177:                 return initializing;
178:         }
179:
180:         @Override
181:         public Diagnostic getDefaultValue() {
182:                 return Diagnostic.OK_INSTANCE;
183:         }
184:
185:         @Override
186:         protected CachedTreeNode<Diagnostic> createdCachedTreeNode(Diagnostic value) {
187:                 return new DiagnosticTreeNode(value);
188:         }
189:
190:         /**
191:          * @since 1.17
192:          */
193:         @Override
194:         protected void updateParentNode(Object parent, Object object, Diagnostic value) {
195:                 // In the initial walk over the contents, we will already have processed
196:                 // the containment chain
197:•                if (!isInitializing()) {
198:                         super.updateParentNode(parent, object, value);
199:                 }
200:         }
201:
202:         /**
203:          * @return the objects with cached values.
204:          */
205:         public Set<Object> getObjects() {
206:                 return Collections.unmodifiableSet(new LinkedHashSet<Object>(getNodes().keySet()));
207:         }
208:
209:         /**
210:          * @param o the object
211:          * @return the objects diagnostic
212:          */
213:         public Diagnostic getOwnValue(Object o) {
214:•                if (o == null) {
215:                         return getDefaultValue();
216:                 }
217:                 final CachedTreeNode<Diagnostic> treeNode = getNodes().get(o);
218:•                if (treeNode == null) {
219:                         /* there is no entry in the cache */
220:                         return getDefaultValue();
221:                 }
222:                 return treeNode.getOwnValue();
223:         }
224:
225:         /**
226:          * Disposes this cache.
227:          */
228:         public void dispose() {
229:                 validationChangeListener.dispose();
230:         }
231:
232:         /**
233:          * Does a reinit of this cache <b>if</b> the given notifier is different than the current one.
234:          *
235:          * @param notifier the notifier
236:          */
237:         public void reinit(Notifier notifier) {
238:•                if (input == notifier) {
239:                         return;
240:                 }
241:                 dispose();
242:                 clear();
243:                 init(notifier);
244:         }
245:
246:         /**
247:          * @param listener the {@link ValidationListener} to register
248:          */
249:         public void registerValidationListener(ValidationListener listener) {
250:                 validationListeners.add(listener);
251:         }
252:
253:         /**
254:          *
255:          * @param listener the {@link ValidationListener} to deregister
256:          */
257:         public void deregisterValidationListener(ValidationListener listener) {
258:                 validationListeners.remove(listener);
259:         }
260:
261:         /**
262:          * UPdates the cache and notifies listeners that this was a potential structure change.
263:          *
264:          * @param element the changed element
265:          * @param cache the cache
266:          * @deprecated please use {@link DiagnosticCache#updateCache(Set, DiagnosticCache)} instead
267:          */
268:         @Deprecated
269:         protected void updateCache(EObject element, DiagnosticCache cache) {
270:                 final Diagnostic diagnostic = getDiagnostic(element);
271:                 final Set<EObject> update = cache.update(element, diagnostic);
272:                 notifyValidationListeners(update, true);
273:
274:         }
275:
276:         /**
277:          * Updates the cache and notifies listeners that this was a potential structure change.
278:          *
279:          * @param elements the changed elements
280:          * @param cache the cache
281:          * @since 1.25
282:          */
283:         protected void updateCache(Set<EObject> elements, DiagnosticCache cache) {
284:                 final Set<EObject> updates = new LinkedHashSet<EObject>(elements);
285:•                for (final EObject element : elements) {
286:                         final Diagnostic diagnostic = getDiagnostic(element);
287:                         final Set<EObject> update = cache.update(element, diagnostic);
288:                         updates.addAll(update);
289:                 }
290:                 notifyValidationListeners(updates, true);
291:
292:         }
293:
294:         /**
295:          * Remove the given object from the cache.
296:          *
297:          * @param object the EObject to remove
298:          * @param cache the DiagnosticCache from which the object should be removed
299:          * @since 1.25
300:          */
301:         protected void handleRemove(EObject object, DiagnosticCache cache) {
302:                 final Set<EObject> toRemove = new LinkedHashSet<EObject>();
303:                 toRemove.add(object);
304:                 final TreeIterator<EObject> iterator = EcoreUtil.getAllContents(object, false);
305:•                while (iterator.hasNext()) {
306:                         toRemove.add(iterator.next());
307:                 }
308:•                for (final EObject eObject : toRemove) {
309:                         cache.remove(eObject);
310:                 }
311:                 notifyValidationListeners(toRemove, true);
312:         }
313:
314:         /**
315:          * Validate given object and return the result of the validation.
316:          *
317:          * @param object the object to validate
318:          * @return the validation result
319:          * @since 1.25
320:          */
321:         protected static Diagnostic getDiagnostic(Object object) {
322:•                if (!EObject.class.isInstance(object)) {
323:                         return Diagnostic.OK_INSTANCE;
324:                 }
325:                 final EObject eObject = EObject.class.cast(object);
326:                 EValidator validator = EValidator.Registry.INSTANCE.getEValidator(eObject.eClass().getEPackage());
327:                 final BasicDiagnostic diagnostics = Diagnostician.INSTANCE.createDefaultDiagnostic(eObject);
328:
329:•                if (validator == null) {
330:                         validator = new EObjectValidator();
331:                 }
332:                 final Map<Object, Object> context = new HashMap<Object, Object>();
333:                 context.put(EValidator.SubstitutionLabelProvider.class, Diagnostician.INSTANCE);
334:                 context.put(EValidator.class, validator);
335:
336:                 validator.validate(eObject, diagnostics, context);
337:                 return diagnostics;
338:         }
339:
340:         /**
341:          * Notify the registered validation listeners that a validation occurred.
342:          *
343:          * @param updatedObjects the objects that changed
344:          * @param potentialStructuralChange whether the validation was caused by a structural change
345:          * @since 1.25
346:          */
347:         protected void notifyValidationListeners(final Set<EObject> updatedObjects, boolean potentialStructuralChange) {
348:•                for (final ValidationListener validationListener : validationListeners) {
349:                         validationListener.revalidationOccurred(updatedObjects, potentialStructuralChange);
350:                 }
351:         }
352:
353:         /**
354:          * Updates the cache and notifes listeners that this change was not a structure change.
355:          *
356:          * @param element the element
357:          * @param cache the cache
358:          * @deprecated please use {@link DiagnosticCache#updateCacheWithoutRefresh(Set, DiagnosticCache)} instead
359:          */
360:         @Deprecated
361:         protected void updateCacheWithoutRefresh(EObject element, DiagnosticCache cache) {
362:                 final Diagnostic diagnostic = getDiagnostic(element);
363:                 final Set<EObject> update = cache.update(element, diagnostic);
364:                 notifyValidationListeners(update, false);
365:                 notifyValidationListeners(Collections.singleton(element), false);
366:         }
367:
368:         /**
369:          * Updates the cache and notifes listeners that this change was not a structure change.
370:          *
371:          * @param elements the elements
372:          * @param cache the cache
373:          * @since 1.25
374:          */
375:         protected void updateCacheWithoutRefresh(Set<EObject> elements, DiagnosticCache cache) {
376:                 final Set<EObject> updates = new LinkedHashSet<EObject>(elements);
377:•                for (final EObject element : elements) {
378:                         final Diagnostic diagnostic = getDiagnostic(element);
379:                         final Set<EObject> update = cache.update(element, diagnostic);
380:                         updates.addAll(update);
381:                 }
382:                 notifyValidationListeners(updates, false);
383:         }
384:
385:         /**
386:          * Tree node for diagnostics.
387:          *
388:          * @author Johannes Faltermeier
389:          *
390:          */
391:         private class DiagnosticTreeNode extends CachedTreeNode<Diagnostic> {
392:
393:                 private final Set<Diagnostic> diagnosticSet = new TreeSet<Diagnostic>(new Comparator<Diagnostic>() {
394:
395:                         @Override
396:                         public int compare(Diagnostic o1, Diagnostic o2) {
397:                                 if (o1.getSeverity() == o2.getSeverity()) {
398:                                         if (o1 == o2) {
399:                                                 return 0;
400:                                         }
401:                                         return getHashCode(o1) - getHashCode(o2);
402:                                 }
403:                                 return -1 * Integer.class.cast(o1.getSeverity()).compareTo(o2.getSeverity());
404:                         }
405:
406:                         private int getHashCode(Diagnostic o) {
407:                                 if (o.getData().isEmpty()) {
408:                                         return 0;
409:                                 }
410:                                 return o.getData().hashCode();
411:                         }
412:
413:                 });
414:
415:                 DiagnosticTreeNode(Diagnostic initialValue) {
416:                         super(initialValue);
417:                 }
418:
419:                 @Override
420:                 public void putIntoCache(Object key, Diagnostic value) {
421:                         boolean updateRequired = true;
422:
423:                         if (getCache().containsKey(key)) {
424:                                 final Diagnostic diagnostic = getCache().get(key);
425:                                 if (diagnostic.getSeverity() == value.getSeverity()) {
426:                                         updateRequired = false;
427:                                 }
428:                                 diagnosticSet.remove(diagnostic);
429:                         }
430:                         getCache().put(key, value);
431:                         diagnosticSet.add(value);
432:
433:                         if (updateRequired) {
434:                                 update();
435:                         }
436:                 }
437:
438:                 @Override
439:                 public void update() {
440:                         final Iterator<Diagnostic> iterator = diagnosticSet.iterator();
441:                         if (iterator.hasNext()) {
442:                                 final Diagnostic mostSevereDiagnostic = iterator.next();
443:                                 setChildValue(mostSevereDiagnostic);
444:                         } else {
445:                                 setChildValue(getDefaultValue());
446:                         }
447:                 }
448:
449:                 @Override
450:                 public Diagnostic getDisplayValue() {
451:                         if (getChildValue() == null) {
452:                                 return getOwnValue();
453:                         }
454:                         return getOwnValue().getSeverity() > getChildValue().getSeverity() ? getOwnValue() : getChildValue();
455:                 }
456:
457:                 /**
458:                  * {@inheritDoc}
459:                  *
460:                  * @see org.eclipse.emf.ecp.common.spi.cachetree.CachedTreeNode#removeFromCache(java.lang.Object)
461:                  */
462:                 @Override
463:                 public void removeFromCache(Object key) {
464:                         final Diagnostic diagnostic = getCache().remove(key);
465:                         if (diagnostic != null) {
466:                                 diagnosticSet.remove(diagnostic);
467:                         }
468:                         update();
469:                 }
470:         }
471:
472:         /**
473:          *
474:          * An adapter which will update the cache.
475:          *
476:          * @author Johannes Faltermeier
477:          *
478:          */
479:         private final class ValidationChangeListener extends EContentAdapter {
480:
481:                 private final Notifier parent;
482:
483:                 ValidationChangeListener(Notifier parent) {
484:                         this.parent = parent;
485:                         parent.eAdapters().add(this);
486:                 }
487:
488:                 @Override
489:                 public void notifyChanged(Notification notification) {
490:                         super.notifyChanged(notification);
491:                         if (notification.isTouch() && notification.getEventType() < Notification.EVENT_TYPE_COUNT) {
492:                                 /* if touch and non-user-defined event type break */
493:                                 return;
494:                         }
495:                         handleStructuralChangeNotification(notification);
496:                         if (!EObject.class.isInstance(notification.getNotifier())) {
497:                                 return;
498:                         }
499:                         updateCacheWithoutRefresh(Collections.singleton(EObject.class.cast(notification.getNotifier())),
500:                                 DiagnosticCache.this);
501:                 }
502:
503:                 void dispose() {
504:                         parent.eAdapters().remove(this);
505:                 }
506:
507:                 private void handleStructuralChangeNotification(Notification notification) {
508:                         switch (notification.getEventType()) {
509:                         case Notification.REMOVE: {
510:                                 handleSingleRemove(notification);
511:                                 break;
512:                         }
513:                         case Notification.REMOVE_MANY: {
514:                                 @SuppressWarnings("unchecked")
515:                                 final List<Object> deleted = (List<Object>) notification.getOldValue();
516:                                 if (deleted.isEmpty() || !EObject.class.isInstance(deleted.get(0))) {
517:                                         break;
518:                                 }
519:                                 for (final Object oldValue : deleted) {
520:                                         handleRemove(EObject.class.cast(oldValue), DiagnosticCache.this);
521:                                 }
522:                                 break;
523:                         }
524:                         case Notification.ADD: {
525:                                 handleAdd(notification);
526:                                 break;
527:                         }
528:                         case Notification.ADD_MANY: {
529:                                 @SuppressWarnings("unchecked")
530:                                 final List<Object> added = (List<Object>) notification.getNewValue();
531:                                 if (added.isEmpty() || !EObject.class.isInstance(added.get(0))) {
532:                                         break;
533:                                 }
534:                                 final Set<EObject> toValidate = new LinkedHashSet<EObject>();
535:                                 final Set<EObject> addedSet = new LinkedHashSet<EObject>();
536:                                 for (final Object newValue : added) {
537:                                         final TreeIterator<EObject> iterator = EcoreUtil.getAllContents(EObject.class.cast(newValue),
538:                                                 false);
539:                                         while (iterator.hasNext()) {
540:                                                 toValidate.add(iterator.next());
541:                                         }
542:                                         addedSet.add(EObject.class.cast(newValue));
543:                                 }
544:                                 updateCacheWithoutRefresh(toValidate, DiagnosticCache.this);
545:                                 updateCache(addedSet, DiagnosticCache.this);
546:                                 break;
547:
548:                         }
549:                         case Notification.SET: {
550:                                 if (!EReference.class.isInstance(notification.getFeature())
551:                                         || !EReference.class.cast(notification.getFeature()).isContainment()) {
552:                                         break;
553:                                 }
554:                                 handleAdd(notification);
555:
556:                                 break;
557:                         }
558:                         default:
559:                                 break;
560:                         }
561:                 }
562:
563:                 private void handleSingleRemove(Notification notification) {
564:                         final Object oldValue = notification.getOldValue();
565:                         if (!EObject.class.isInstance(oldValue)) {
566:                                 return;
567:                         }
568:                         handleRemove(EObject.class.cast(oldValue), DiagnosticCache.this);
569:                 }
570:
571:                 private void handleAdd(Notification notification) {
572:                         final Object newValue = notification.getNewValue();
573:                         if (!EObject.class.isInstance(newValue)) {
574:                                 return;
575:                         }
576:                         final Set<EObject> toValidate = new LinkedHashSet<EObject>();
577:                         final TreeIterator<EObject> iterator = EcoreUtil.getAllContents(EObject.class.cast(newValue), false);
578:                         while (iterator.hasNext()) {
579:                                 toValidate.add(iterator.next());
580:                         }
581:                         updateCacheWithoutRefresh(toValidate, DiagnosticCache.this);
582:                         updateCache(Collections.singleton(EObject.class.cast(newValue)), DiagnosticCache.this);
583:                 }
584:         }
585:
586:         /**
587:          * Notified when the validation changes.
588:          *
589:          */
590:         public interface ValidationListener {
591:                 /**
592:                  * Called when a revalidation for the object has happened.
593:                  *
594:                  * @param object the object
595:                  * @param potentialStructuralChange whether this was caused by a structural change.
596:                  */
597:                 void revalidationOccurred(Collection<EObject> object, boolean potentialStructuralChange);
598:         }
599:
600: }