Skip to content

Package: StackItemViewService$StackDomainChangeListener

StackItemViewService$StackDomainChangeListener

nameinstructionbranchcomplexitylinemethod
StackItemViewService.StackDomainChangeListener(StackItemViewService)
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%
notifyChange(ModelChangeNotification)
M: 0 C: 19
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 6
100%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2016 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: ******************************************************************************/
14: package org.eclipse.emf.ecp.view.internal.stack.ui.swt;
15:
16: import java.util.LinkedHashMap;
17: import java.util.LinkedHashSet;
18: import java.util.Map;
19: import java.util.Set;
20:
21: import org.eclipse.core.databinding.observable.IObserving;
22: import org.eclipse.core.databinding.observable.value.IObservableValue;
23: import org.eclipse.emf.common.util.Enumerator;
24: import org.eclipse.emf.common.util.TreeIterator;
25: import org.eclipse.emf.ecore.EObject;
26: import org.eclipse.emf.ecore.EStructuralFeature;
27: import org.eclipse.emf.ecore.EStructuralFeature.Setting;
28: import org.eclipse.emf.ecore.EcorePackage;
29: import org.eclipse.emf.ecore.InternalEObject;
30: import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
31: import org.eclipse.emf.ecp.view.spi.context.ViewModelService;
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.stack.model.VStackItem;
37: import org.eclipse.emf.ecp.view.spi.stack.model.VStackLayout;
38: import org.eclipse.emfforms.spi.common.report.ReportService;
39: import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
40: import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedReport;
41: import org.eclipse.emfforms.spi.core.services.databinding.EMFFormsDatabinding;
42: import org.eclipse.emfforms.spi.core.services.structuralchange.EMFFormsStructuralChangeTester;
43:
44: /**
45: * {@link ViewModelService} evaluating changes on the {@link VDomainModelReference} of the {@link VStackLayout} based on
46: * the given value in the available {@link VStackItem VStackItems}. Sets the top element of the VStackLayout
47: * accordingly.
48: *
49: * @author jfaltermeier
50: *
51: */
52: public class StackItemViewService implements ViewModelService {
53:
54:         private ModelChangeListener domainListener;
55:         private ModelChangeListener stackItemsDomainListener;
56:         private ViewModelContext context;
57:         private VElement viewModel;
58:         private EObject domain;
59:
60:         private Map<EObject, Map<EStructuralFeature, Set<VStackLayout>>> registry;
61:
62:         /**
63:          * {@inheritDoc}
64:          *
65:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#instantiate(org.eclipse.emf.ecp.view.spi.context.ViewModelContext)
66:          */
67:         @Override
68:         public void instantiate(ViewModelContext context) {
69:                 this.context = context;
70:                 viewModel = context.getViewModel();
71:                 domain = context.getDomainModel();
72:
73:                 registry = new LinkedHashMap<EObject, Map<EStructuralFeature, Set<VStackLayout>>>();
74:
75:                 initRegistry(viewModel);
76:                 evaluateRegistry();
77:                 domainListener = createDomainListener();
78:                 context.registerDomainChangeListener(domainListener);
79:         }
80:
81:         private void initRegistry(VElement viewModel) {
82:                 final Set<VStackLayout> stacks = new LinkedHashSet<VStackLayout>();
83:
84:                 final TreeIterator<EObject> iterator = viewModel.eAllContents();
85:                 while (iterator.hasNext()) {
86:                         final EObject current = iterator.next();
87:                         if (VStackLayout.class.isInstance(current)) {
88:                                 stacks.add(VStackLayout.class.cast(current));
89:                         }
90:                 }
91:
92:                 final Map<VStackLayout, Setting> stackToSetting = new LinkedHashMap<VStackLayout, Setting>();
93:                 for (final VStackLayout stack : stacks) {
94:                         final VDomainModelReference dmr = stack.getDomainModelReference();
95:                         if (dmr == null) {
96:                                 continue;
97:                         }
98:                         final Setting setting = addToRegistry(stack, dmr);
99:                         if (setting == null) {
100:                                 // TODO JF how to handle?
101:                                 return;
102:                         }
103:                         stackToSetting.put(stack, setting);
104:                 }
105:                 stackItemsDomainListener = new StackItemsModelChangeListener(stackToSetting);
106:                 context.registerDomainChangeListener(stackItemsDomainListener);
107:         }
108:
109:         private Setting addToRegistry(VStackLayout stack, VDomainModelReference dmr) {
110:                 IObservableValue observableValue;
111:                 try {
112:                         observableValue = context.getService(EMFFormsDatabinding.class).getObservableValue(dmr, domain);
113:                 } catch (final DatabindingFailedException ex) {
114:                         context.getService(ReportService.class).report(new DatabindingFailedReport(ex));
115:                         // TODO JF how to handle?
116:                         return null;
117:                 }
118:                 final EObject eObject = (EObject) ((IObserving) observableValue).getObserved();
119:                 final EStructuralFeature structuralFeature = (EStructuralFeature) observableValue.getValueType();
120:                 observableValue.dispose();
121:                 if (!eObject.eClass().getEAllStructuralFeatures().contains(structuralFeature)) {
122:                         return null;
123:                 }
124:
125:                 addToRegistry(eObject, structuralFeature, stack);
126:                 return ((InternalEObject) eObject).eSetting(structuralFeature);
127:         }
128:
129:         private void addToRegistry(EObject object, final EStructuralFeature domainModelEFeature, final VStackLayout stack) {
130:                 if (!registry.containsKey(object)) {
131:                         registry.put(object, new LinkedHashMap<EStructuralFeature, Set<VStackLayout>>());
132:                 }
133:                 final Map<EStructuralFeature, Set<VStackLayout>> featureToLayoutMap = registry.get(object);
134:                 if (!featureToLayoutMap.containsKey(domainModelEFeature)) {
135:                         featureToLayoutMap.put(domainModelEFeature, new LinkedHashSet<VStackLayout>());
136:                 }
137:                 featureToLayoutMap.get(domainModelEFeature).add(stack);
138:         }
139:
140:         private boolean doesRegistryContain(EObject object, final EStructuralFeature domainModelEFeature) {
141:                 if (!registry.containsKey(object)) {
142:                         return false;
143:                 }
144:                 final Map<EStructuralFeature, Set<VStackLayout>> featureToStackMap = registry.get(object);
145:                 return featureToStackMap.containsKey(domainModelEFeature);
146:         }
147:
148:         private void evaluateRegistry() {
149:                 for (final EObject object : registry.keySet()) {
150:                         final Map<EStructuralFeature, Set<VStackLayout>> featureToStacksMap = registry.get(object);
151:                         for (final EStructuralFeature feature : featureToStacksMap.keySet()) {
152:                                 evaluate(object, feature);
153:                         }
154:                 }
155:         }
156:
157:         private void evaluate(EObject object, final EStructuralFeature domainModelEFeature) {
158:                 final Object currentValue = object.eGet(domainModelEFeature);
159:                 final Set<VStackLayout> stacks = registry.get(object).get(domainModelEFeature);
160:                 for (final VStackLayout stack : stacks) {
161:                         boolean topElementSet = false;
162:                         for (final VStackItem item : stack.getStackItems()) {
163:                                 if (currentValue == null) {
164:                                         if (currentValue == item.getValue()) {
165:                                                 stack.setTopElement(item);
166:                                                 topElementSet = true;
167:                                                 break;
168:                                         }
169:                                 } else {
170:                                         if (EcorePackage.eINSTANCE.getEEnum().isInstance(domainModelEFeature.getEType())) {
171:                                                 if (Enumerator.class.cast(currentValue).getLiteral().equals(item.getValue())) {
172:                                                         stack.setTopElement(item);
173:                                                         topElementSet = true;
174:                                                         break;
175:                                                 }
176:                                         }
177:                                         if (currentValue.equals(item.getValue())) {
178:                                                 stack.setTopElement(item);
179:                                                 topElementSet = true;
180:                                                 break;
181:                                         }
182:                                 }
183:                         }
184:                         if (!topElementSet) {
185:                                 stack.setTopElement(null);
186:                         }
187:                 }
188:         }
189:
190:         private ModelChangeListener createDomainListener() {
191:                 return new StackDomainChangeListener();
192:         }
193:
194:         /**
195:          * {@inheritDoc}
196:          *
197:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#dispose()
198:          */
199:         @Override
200:         public void dispose() {
201:                 viewModel = null;
202:                 domain = null;
203:
204:                 registry.clear();
205:                 registry = null;
206:
207:                 context.unregisterDomainChangeListener(stackItemsDomainListener);
208:                 stackItemsDomainListener = null;
209:
210:                 context.unregisterDomainChangeListener(domainListener);
211:                 domainListener = null;
212:                 context = null;
213:         }
214:
215:         /**
216:          * {@inheritDoc}
217:          *
218:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#getPriority()
219:          */
220:         @Override
221:         public int getPriority() {
222:                 return 7;
223:         }
224:
225:         /**
226:          * {@link ModelChangeListener} that updates the registry and reevaluates affected {@link VStackLayout VStackLayouts}
227:          * when the domain model is changed.
228:          *
229:          * @author Lucas Koehler
230:          *
231:          */
232:         private class StackItemsModelChangeListener implements ModelChangeListener {
233:                 private final Map<VStackLayout, Setting> stackToSetting;
234:
235:                 /**
236:                  * Constructs a new {@link StackItemsModelChangeListener}.
237:                  *
238:                  * @param stackToSetting The map of stacks to their current registered settings
239:                  */
240:                 StackItemsModelChangeListener(Map<VStackLayout, Setting> stackToSetting) {
241:                         this.stackToSetting = stackToSetting;
242:                 }
243:
244:                 /**
245:                  * {@inheritDoc}
246:                  *
247:                  * @see org.eclipse.emf.ecp.view.spi.model.ModelChangeListener#notifyChange(org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification)
248:                  */
249:                 @Override
250:                 public void notifyChange(ModelChangeNotification notification) {
251:                         final EMFFormsStructuralChangeTester tester = context.getService(EMFFormsStructuralChangeTester.class);
252:                         for (final VStackLayout stack : stackToSetting.keySet()) {
253:                                 if (tester.isStructureChanged(stack.getDomainModelReference(), context.getDomainModel(),
254:                                         notification)) {
255:                                         removeOutdatedEntriesFromRegistry(stack);
256:                                         addCurrentEntriesToRegistry(stack);
257:                                         final Setting setting = stackToSetting.get(stack);
258:                                         evaluate(setting.getEObject(), setting.getEStructuralFeature());
259:                                 }
260:                         }
261:
262:                 }
263:
264:                 private void removeOutdatedEntriesFromRegistry(VStackLayout stack) {
265:                         final Setting setting = stackToSetting.get(stack);
266:                         final Map<EStructuralFeature, Set<VStackLayout>> featureToStackMap = registry.get(setting.getEObject());
267:                         final Set<VStackLayout> stacks = featureToStackMap.get(setting.getEStructuralFeature());
268:                         stacks.remove(stack);
269:                         if (!stacks.isEmpty()) {
270:                                 return;
271:                         }
272:                         featureToStackMap.remove(setting.getEStructuralFeature());
273:                         if (!featureToStackMap.isEmpty()) {
274:                                 return;
275:                         }
276:                         registry.remove(setting.getEObject());
277:                 }
278:
279:                 private void addCurrentEntriesToRegistry(VStackLayout stack) {
280:                         // TODO setting may be null. see TODOs above
281:                         final Setting setting = addToRegistry(stack, stack.getDomainModelReference());
282:                         stackToSetting.put(stack, setting);
283:                 }
284:         }
285:
286:         /**
287:          * {@link ModelChangeListener} reacting on changes in the domain.
288:          *
289:          * @author jfaltermeier
290:          *
291:          */
292:         private class StackDomainChangeListener implements ModelChangeListener {
293:                 @Override
294:                 public void notifyChange(ModelChangeNotification notification) {
295:                         final EObject notifier = notification.getNotifier();
296:                         final EStructuralFeature feature = notification.getStructuralFeature();
297:•                        if (!doesRegistryContain(notifier, feature)) {
298:                                 return;
299:                         }
300:                         evaluate(notifier, feature);
301:                 }
302:         }
303:
304: }