Skip to content

Package: RuleRegistry$DomainModelChangeListener

RuleRegistry$DomainModelChangeListener

nameinstructionbranchcomplexitylinemethod
RuleRegistry.DomainModelChangeListener(RuleRegistry)
M: 0 C: 27
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
notifyChange(ModelChangeNotification)
M: 3 C: 101
97%
M: 3 C: 15
83%
M: 3 C: 7
70%
M: 3 C: 17
85%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2019 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: * Edgar Mueller - initial API and implementation
13: * Johannes Faltermeier - registry setting based instead of feature based
14: * Lucas Koehler - adapt for segments and nested conditions
15: ******************************************************************************/
16: package org.eclipse.emf.ecp.view.internal.rule;
17:
18: import java.util.Collection;
19: import java.util.Collections;
20: import java.util.LinkedHashMap;
21: import java.util.LinkedHashSet;
22: import java.util.List;
23: import java.util.Map;
24: import java.util.Set;
25: import java.util.WeakHashMap;
26:
27: import org.eclipse.emf.common.notify.Notification;
28: import org.eclipse.emf.ecore.EObject;
29: import org.eclipse.emf.ecp.common.spi.BidirectionalMap;
30: import org.eclipse.emf.ecp.common.spi.UniqueSetting;
31: import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
32: import org.eclipse.emf.ecp.view.spi.model.ModelChangeListener;
33: import org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification;
34: import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
35: import org.eclipse.emf.ecp.view.spi.model.VElement;
36: import org.eclipse.emf.ecp.view.spi.rule.RuleConditionDmrUtil;
37: import org.eclipse.emf.ecp.view.spi.rule.model.Condition;
38: import org.eclipse.emf.ecp.view.spi.rule.model.Rule;
39: import org.eclipse.emfforms.spi.common.report.ReportService;
40: import org.eclipse.emfforms.spi.core.services.databinding.emf.EMFFormsDatabindingEMF;
41: import org.eclipse.emfforms.spi.core.services.structuralchange.EMFFormsStructuralChangeTester;
42:
43: /**
44: * Rule registry that maintains which {@link VElement}s
45: * are affected if a setting is changed.
46: *
47: * @author emueller
48: * @author jfaltermeier
49: *
50: * @param <T>
51: * the actual {@link Rule} type
52: */
53: public class RuleRegistry<T extends Rule> {
54:
55:         private final Map<UniqueSetting, BidirectionalMap<Condition, T>> settingToRules;
56:         private final BidirectionalMap<T, VElement> rulesToRenderables;
57:         private final Map<Condition, Set<UniqueSetting>> conditionToSettings;
58:         private final ViewModelContext context;
59:         private final Map<VDomainModelReference, Set<T>> dmrsToRules;
60:         private final DomainModelChangeListener domainModelChangeListener;
61:         private final ConditionServiceManager conditionServiceManager;
62:
63:         /**
64:          * Default constructor.
65:          *
66:          * @param context the view model context of the {@link RuleService} using this registry
67:          *
68:          */
69:         public RuleRegistry(ViewModelContext context) {
70:                 this.context = context;
71:                 settingToRules = new LinkedHashMap<UniqueSetting, BidirectionalMap<Condition, T>>();
72:                 rulesToRenderables = new BidirectionalMap<T, VElement>();
73:                 conditionToSettings = new LinkedHashMap<Condition, Set<UniqueSetting>>();
74:                 dmrsToRules = new WeakHashMap<VDomainModelReference, Set<T>>();
75:                 domainModelChangeListener = new DomainModelChangeListener();
76:                 context.registerDomainChangeListener(domainModelChangeListener);
77:                 conditionServiceManager = context.getService(ConditionServiceManager.class);
78:         }
79:
80:         /**
81:          * Creates a setting from the given {@link EObject} and the {@link Condition} and register it with the
82:          * {@link VElement}.
83:          *
84:          * @param renderable
85:          * the {@link VElement} to be updated in case the condition changes
86:          * @param rule
87:          * the parent rule holding the {@link Condition}
88:          * @param condition
89:          * contains the attribute that the condition is depending on
90:          * @param domainModel
91:          * the domain object that owns the attribute possibly being
92:          * changed
93:          * @return the registered {@link UniqueSetting UniqueSettings}
94:          */
95:         public Set<UniqueSetting> register(VElement renderable, T rule, Condition condition, EObject domainModel) {
96:                 rulesToRenderables.put(rule, renderable);
97:                 final Set<UniqueSetting> settings = conditionServiceManager.getConditionSettings(condition, domainModel);
98:                 for (final UniqueSetting setting : settings) {
99:                         mapSettingToRule(setting, condition, rule);
100:                 }
101:                 final Set<VDomainModelReference> domainModelReferences = conditionServiceManager
102:                         .getDomainModelReferences(condition);
103:                 mapDomainToDMRs(rule, domainModelReferences);
104:                 return settings;
105:         }
106:
107:         /**
108:          * Removes the given rule from the registry.
109:          *
110:          * @param rule
111:          * the rule to be removed
112:          * @return the {@link VElement} that belonged to the removed rule
113:          */
114:         public VElement removeRule(T rule) {
115:                 removeDomainModelChangeListener(rule);
116:
117:                 final Condition condition = rule.getCondition();
118:                 if (condition != null) {
119:                         return removeCondition(condition);
120:                 }
121:
122:                 final Collection<BidirectionalMap<Condition, T>> values = settingToRules.values();
123:                 for (final BidirectionalMap<Condition, T> bidirectionalMap : values) {
124:                         if (bidirectionalMap.removeByValue(rule) != null) {
125:                                 break;
126:                         }
127:                 }
128:
129:                 final VElement renderable = rulesToRenderables.getValue(rule);
130:                 rulesToRenderables.removeByKey(rule);
131:                 return renderable;
132:
133:         }
134:
135:         /**
136:          * Removes a rule from the dmrsToRules map. Consequently, the rule will no longer be re-registered when an
137:          * associated {@link VDomainModelReference}'s structure changes.
138:          *
139:          * @param rule The rule to remove.
140:          */
141:         private void removeDomainModelChangeListener(T rule) {
142:                 final Set<VDomainModelReference> dmrs = new LinkedHashSet<VDomainModelReference>(dmrsToRules.keySet());
143:                 for (final VDomainModelReference dmr : dmrs) {
144:                         final Set<T> rules = dmrsToRules.get(dmr);
145:                         if (rules != null) {
146:                                 rules.remove(rule);
147:                         }
148:                 }
149:         }
150:
151:         /**
152:          * Removes the given {@link VElement} from the registry.
153:          *
154:          * @param renderable
155:          * the renderable to be removed
156:          */
157:         public void removeRenderable(VElement renderable) {
158:                 final T rule = rulesToRenderables.removeByValue(renderable);
159:                 removeDomainModelChangeListener(rule);
160:         }
161:
162:         /**
163:          * Removes the given condition from the registry.
164:          *
165:          * @param condition
166:          * the condition to be removed
167:          * @return the {@link VElement} that belonged to the removed condition
168:          */
169:         public VElement removeCondition(Condition condition) {
170:                 VElement ret = null;
171:                 T rule = null;
172:                 final Set<UniqueSetting> settings = conditionToSettings.remove(condition);
173:                 if (settings == null) {
174:                         return ret;
175:                 }
176:                 for (final UniqueSetting setting : settings) {
177:                         final BidirectionalMap<Condition, T> rules = settingToRules.get(setting);
178:                         if (rules.keys().contains(condition)) {
179:                                 rule = rules.removeByKey(condition);
180:                         }
181:                         if (rules.keys().isEmpty()) {
182:                                 settingToRules.remove(setting);
183:                         }
184:                 }
185:
186:                 if (rule != null) {
187:                         removeDomainModelChangeListener(rule);
188:                         ret = rulesToRenderables.removeByKey(rule);
189:                 }
190:                 return ret;
191:         }
192:
193:         private void mapSettingToRule(final UniqueSetting uniqueSetting, Condition condition, T rule) {
194:                 if (!settingToRules.containsKey(uniqueSetting)) {
195:                         settingToRules.put(uniqueSetting, new BidirectionalMap<Condition, T>());
196:                 }
197:                 settingToRules.get(uniqueSetting).put(condition, rule);
198:                 if (!conditionToSettings.containsKey(condition)) {
199:                         conditionToSettings.put(condition, new LinkedHashSet<UniqueSetting>());
200:                 }
201:                 conditionToSettings.get(condition).add(uniqueSetting);
202:         }
203:
204:         /**
205:          * Returns the settings of this registry.
206:          *
207:          * @return the settings of this registry.
208:          */
209:         public Set<UniqueSetting> getSettings() {
210:                 return settingToRules.keySet();
211:         }
212:
213:         /**
214:          * Returns all rules that would be affected if the value of given setting is changed.
215:          *
216:          * @param setting
217:          * the setting
218:          * @return a list of {@link VElement}s that are affected of the setting change
219:          */
220:         public Map<T, VElement> getAffectedRenderables(final UniqueSetting setting) {
221:
222:                 final Map<T, VElement> result = new LinkedHashMap<T, VElement>();
223:                 final BidirectionalMap<Condition, T> bidirectionalMap = settingToRules.get(setting);
224:
225:                 if (bidirectionalMap == null) {
226:                         return Collections.emptyMap();
227:                 }
228:
229:                 for (final T rule : bidirectionalMap.values()) {
230:                         final VElement renderable = rulesToRenderables.getValue(rule);
231:                         result.put(rule, renderable);
232:                 }
233:
234:                 return result;
235:         }
236:
237:         private void mapDomainToDMRs(T rule, Set<VDomainModelReference> references) {
238:                 for (final VDomainModelReference reference : references) {
239:                         if (!dmrsToRules.containsKey(reference)) {
240:                                 dmrsToRules.put(reference, new LinkedHashSet<T>());
241:                         }
242:                         dmrsToRules.get(reference).add(rule);
243:                 }
244:         }
245:
246:         /**
247:          * Disposes this {@link RuleRegistry}.
248:          */
249:         public void dispose() {
250:                 context.unregisterDomainChangeListener(domainModelChangeListener);
251:         }
252:
253:         /**
254:          * A model change listener that re-registers affected DMRs and rules when the domain model changes.
255:          *
256:          * @author Lucas Koehler
257:          *
258:          */
259:         private class DomainModelChangeListener implements ModelChangeListener {
260:                 private final EMFFormsStructuralChangeTester structuralChangeTester;
261:                 private final EMFFormsDatabindingEMF databinding;
262:                 private final ReportService reportService;
263:
264:                 /**
265:                  * Creates a new {@link DomainModelChangeListener}.
266:                  */
267:                 DomainModelChangeListener() {
268:                         structuralChangeTester = context.getService(EMFFormsStructuralChangeTester.class);
269:                         databinding = context.getService(EMFFormsDatabindingEMF.class);
270:                         reportService = context.getService(ReportService.class);
271:                 }
272:
273:                 @Override
274:                 public void notifyChange(ModelChangeNotification notification) {
275:                         final Set<VDomainModelReference> dmrs = new LinkedHashSet<VDomainModelReference>(dmrsToRules.keySet());
276:•                        for (final VDomainModelReference dmr : dmrs) {
277:                                 final List<EObject> dmrRoots = RuleConditionDmrUtil.getDmrRootObject(databinding, reportService,
278:                                         context.getDomainModel(), dmr.eContainer());
279:
280:•                                for (final EObject dmrRoot : dmrRoots) {
281:•                                        if (structuralChangeTester.isStructureChanged(dmr, dmrRoot, notification)) {
282:                                                 // dmr has changed => re-register every rule associated with it
283:                                                 final Set<T> rules = dmrsToRules.remove(dmr);
284:•                                                if (rules == null) {
285:                                                         return;
286:                                                 }
287:
288:•                                                for (final T rule : rules) {
289:                                                         final VElement element = removeRule(rule);
290:•                                                        if (element == null) {
291:                                                                 return;
292:                                                         }
293:•                                                        if (notification.getRawNotification().getEventType() == Notification.SET
294:•                                                                && notification.getRawNotification().getNewValue() == null
295:•                                                                || notification.getRawNotification().getEventType() == Notification.UNSET) {
296:                                                                 continue;
297:                                                         }
298:                                                         register(element, rule, rule.getCondition(), dmrRoot);
299:                                                 }
300:                                                 // We can break out after the first change was detected, because the whole rule will be
301:                                                 // re-evaluated anyway
302:                                                 break;
303:                                         }
304:
305:                                 }
306:                         }
307:                 }
308:
309:         }
310: }