/
LiveValidationTrigger.java
141 lines (124 loc) · 4.87 KB
/
LiveValidationTrigger.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*******************************************************************************
* Copyright (c) 2015, 2023 Obeo
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.emf.ecoretools.design.service;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.impl.EStringToStringMapEntryImpl;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.emf.transaction.NotificationFilter;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.sirius.business.api.session.ModelChangeTrigger;
import org.eclipse.sirius.ext.base.Option;
import org.eclipse.sirius.ext.base.Options;
public class LiveValidationTrigger implements ModelChangeTrigger {
public static final NotificationFilter IS_ECORE_CHANGE = new NotificationFilter.Custom() {
public boolean matches(Notification notification) {
return (!notification.isTouch() && notification.getFeature() instanceof EStructuralFeature
&& ((EStructuralFeature) notification.getFeature()).getEContainingClass()
.getEPackage() == EcorePackage.eINSTANCE);
}
};
private TransactionalEditingDomain domain;
/**
* We need to be triggered before the refresh mechanism takes place so that the
* diagnostic attachements are up-to date when computing colors.
*/
public static final int PRIORITY = 0;
public LiveValidationTrigger(TransactionalEditingDomain domain) {
this.domain = domain;
}
public Option<Command> localChangesAboutToCommit(Collection<Notification> notifications) {
final Set<EObject> changedEcoreObjects = new LinkedHashSet<>();
for (Notification notif : notifications) {
Object obj = notif.getNotifier();
if (obj instanceof EObject && ((EObject) obj).eClass() != null
&& ((EObject) obj).eClass().getEPackage() == EcorePackage.eINSTANCE) {
changedEcoreObjects.add((EObject) obj);
}
}
if (changedEcoreObjects.size() > 0) {
Command revalidateEObjects = new RecordingCommand(domain) {
@Override
protected void doExecute() {
Set<EObject> containers = new LinkedHashSet<>();
for (EObject eObj : changedEcoreObjects) {
revalidate(eObj);
EObject container = eObj.eContainer();
if (container != null) {
containers.add(container);
/*
* in the case of a changed EParameter or annotationwe really want to include
* the parent EClass in the revalidation.
*/
if ((eObj instanceof EParameter || eObj instanceof EStringToStringMapEntryImpl)
&& container.eContainer() instanceof EClass) {
containers.add(container.eContainer());
}
}
}
/*
* When an Ecore object changes it is likely the container might have a new
* validation error (or an error might be gone) even if it has suffered no
* change itself. Example : two EStructural features having the same name and
* contained in the same EClass will trigger an error on such EClass. This state
* should be updated when one of the EStructuralFeature got renamed.
*/
for (EObject container : containers) {
revalidate(container);
}
}
protected void revalidate(EObject eObj) {
DiagnosticAttachment diag = DiagnosticAttachment.getAttachment(eObj);
/*
* If the EObject had a validation marker, then we need to update its state,
* otherwise nobody cared about its validation status, no need to pre-compute
* it.
*/
if (diag != null) {
try {
/*
* anything could happen within the validate. We make sure we won't fail the
* whole post-process in this case.
*/
Diagnostic diagnostic = Diagnostician.INSTANCE.validate(eObj);
/*
* We attach the result of the validation on the EObject through the eAdapters
* list. It might be consumed by some modelers to change colors or update
* tooltips.
*/
diag.setDiagnostic(diagnostic);
} catch (Throwable e) {
/*
* Anything which happens here might not be a concern.
*/
}
}
}
};
return Options.newSome(revalidateEObjects);
}
return Options.newNone();
}
public int priority() {
return PRIORITY;
}
}