Skip to content

Package: UnsetService$1

UnsetService$1

nameinstructionbranchcomplexitylinemethod
notifyChange(ModelChangeNotification)
M: 0 C: 26
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 8
100%
M: 0 C: 1
100%
{...}
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%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2013 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.unset;
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.core.runtime.IStatus;
24: import org.eclipse.emf.common.notify.Notification;
25: import org.eclipse.emf.common.util.EList;
26: import org.eclipse.emf.common.util.TreeIterator;
27: import org.eclipse.emf.ecore.EObject;
28: import org.eclipse.emf.ecore.EStructuralFeature;
29: import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
30: import org.eclipse.emf.ecp.view.spi.context.ViewModelService;
31: import org.eclipse.emf.ecp.view.spi.model.ModelChangeListener;
32: import org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification;
33: import org.eclipse.emf.ecp.view.spi.model.VControl;
34: import org.eclipse.emf.ecp.view.spi.model.VElement;
35: import org.eclipse.emf.ecp.view.spi.model.VViewPackage;
36: import org.eclipse.emfforms.spi.common.report.AbstractReport;
37: import org.eclipse.emfforms.spi.common.report.ReportService;
38: import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
39: import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedReport;
40: import org.eclipse.emfforms.spi.core.services.databinding.EMFFormsDatabinding;
41: import org.eclipse.emfforms.spi.core.services.view.EMFFormsContextListener;
42: import org.eclipse.emfforms.spi.core.services.view.EMFFormsViewContext;
43:
44: /**
45: * Unset service that, once instantiated, synchronizes the visible state of a
46: * view and its children with the affected EStructuralFeature(s), i.e.
47: * setting/unsetting the value(s).
48: *
49: * @author jfaltermeier
50: *
51: */
52: public class UnsetService implements ViewModelService, EMFFormsContextListener {
53:
54:         private static final String DOMAIN_MODEL_NULL_EXCEPTION = "Domain model must not be null."; //$NON-NLS-1$
55:         private static final String VIEW_MODEL_NULL_EXCEPTION = "View model must not be null."; //$NON-NLS-1$
56:
57:         private ViewModelContext context;
58:         private ModelChangeListener viewChangeListener;
59:
60:         private final Map<EObject, Set<FeatureWrapper>> objectToFeatureMap;
61:         private final Map<FeatureWrapper, Set<VControl>> settingToControlMap;
62:
63:         /**
64:          * During init all controls that are hidden, either direct or by being
65:          * contained by a hidden parent, are collected here.
66:          */
67:         private final Set<VControl> hiddenControlsDuringInit;
68:         private ReportService reportService;
69:         private EMFFormsDatabinding emfFormsDatabinding;
70:
71:         /**
72:          * Default constructor for the unset service.
73:          */
74:         public UnsetService() {
75:                 objectToFeatureMap = new LinkedHashMap<EObject, Set<FeatureWrapper>>();
76:                 settingToControlMap = new LinkedHashMap<FeatureWrapper, Set<VControl>>();
77:                 hiddenControlsDuringInit = new LinkedHashSet<VControl>();
78:         }
79:
80:         /**
81:          * {@inheritDoc}
82:          *
83:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#instantiate(org.eclipse.emf.ecp.view.spi.context.ViewModelContext)
84:          */
85:         @Override
86:         public void instantiate(ViewModelContext context) {
87:                 this.context = context;
88:                 reportService = context.getService(ReportService.class);
89:                 emfFormsDatabinding = context.getService(EMFFormsDatabinding.class);
90:                 this.context.registerEMFFormsContextListener(this);
91:         }
92:
93:         private void initMaps(VElement view, boolean parentInvisible) {
94:                 if (!view.isVisible() || parentInvisible) {
95:                         if (view instanceof VControl) {
96:                                 hiddenControlsDuringInit.add((VControl) view);
97:                         }
98:                         parentInvisible = true;
99:                 }
100:
101:                 if (view instanceof VControl) {
102:                         addControlToMap((VControl) view);
103:                 } else {
104:                         final EList<EObject> children = view.eContents();
105:                         for (final EObject child : children) {
106:                                 if (child == null) {
107:                                         continue;
108:                                 }
109:
110:                                 if (child instanceof VElement) {
111:                                         initMaps((VElement) child, parentInvisible);
112:                                 }
113:                         }
114:                 }
115:         }
116:
117:         private void unsetInitialHiddenControls() {
118:                 for (final VControl control : hiddenControlsDuringInit) {
119:                         removeControlFromMapAndUnsetIfNeeded(control);
120:                 }
121:         }
122:
123:         private void addControlToMap(VControl control) {
124:                 if (control.getDomainModelReference() == null) {
125:                         reportService.report(
126:                                 new AbstractReport(String.format("The provided control [%1$s] has no defined DMR.", control), //$NON-NLS-1$
127:                                         IStatus.INFO));
128:                         return;
129:                 }
130:                 IObservableValue observableValue;
131:                 try {
132:                         observableValue = emfFormsDatabinding
133:                                 .getObservableValue(control.getDomainModelReference(), context.getDomainModel());
134:                 } catch (final DatabindingFailedException ex) {
135:                         reportService.report(new DatabindingFailedReport(ex));
136:                         return;
137:                 }
138:                 final EStructuralFeature structuralFeature = (EStructuralFeature) observableValue.getValueType();
139:                 final EObject eObject = (EObject) ((IObserving) observableValue).getObserved();
140:                 observableValue.dispose();
141:                 if (!objectToFeatureMap.containsKey(eObject)) {
142:                         objectToFeatureMap
143:                                 .put(eObject, new LinkedHashSet<FeatureWrapper>());
144:                 }
145:                 final Set<FeatureWrapper> features = objectToFeatureMap.get(eObject);
146:                 FeatureWrapper wrapper = null;
147:                 for (final FeatureWrapper w : features) {
148:                         if (w.isWrapperFor(structuralFeature)) {
149:                                 wrapper = w;
150:                                 break;
151:                         }
152:                 }
153:                 if (wrapper == null) {
154:                         wrapper = new FeatureWrapper(structuralFeature);
155:                         features.add(wrapper);
156:                 }
157:
158:                 if (!settingToControlMap.containsKey(wrapper)) {
159:                         settingToControlMap.put(wrapper, new LinkedHashSet<VControl>());
160:                 }
161:                 settingToControlMap.get(wrapper).add(control);
162:         }
163:
164:         private void removeControlFromMapAndUnsetIfNeeded(VControl control) {
165:                 IObservableValue observableValue;
166:                 try {
167:                         observableValue = emfFormsDatabinding
168:                                 .getObservableValue(control.getDomainModelReference(), context.getDomainModel());
169:                 } catch (final DatabindingFailedException ex) {
170:                         reportService.report(new DatabindingFailedReport(ex));
171:                         return;
172:                 }
173:                 final EStructuralFeature structuralFeature = (EStructuralFeature) observableValue.getValueType();
174:                 final EObject eObject = (EObject) ((IObserving) observableValue).getObserved();
175:                 observableValue.dispose();
176:                 final Set<FeatureWrapper> wrappers = objectToFeatureMap
177:                         .get(eObject);
178:                 FeatureWrapper wrapper = null;
179:                 if (wrappers == null) {
180:                         return;
181:                 }
182:                 for (final FeatureWrapper w : wrappers) {
183:                         if (w.isWrapperFor(structuralFeature)) {
184:                                 wrapper = w;
185:                         }
186:                 }
187:
188:                 final Set<VControl> visibleControls = settingToControlMap
189:                         .get(wrapper);
190:                 visibleControls.remove(control);
191:                 if (visibleControls.isEmpty() && eObject != null) {
192:                         eObject.eUnset(structuralFeature);
193:                 }
194:         }
195:
196:         /**
197:          * The given element just became visible.
198:          * If it is a control add it to the map.
199:          * If it is a container check if children became visible
200:          *
201:          * @param element
202:          */
203:         private void show(VElement element) {
204:                 if (element instanceof VControl) {
205:                         addControlToMap((VControl) element);
206:                         return;
207:                 }
208:
209:                 final EList<EObject> children = element.eContents();
210:                 for (final EObject child : children) {
211:                         if (child == null) {
212:                                 continue;
213:                         }
214:                         if (child instanceof VElement) {
215:                                 final VElement childElement = (VElement) child;
216:                                 if (childElement.isVisible()) {
217:                                         show(childElement);
218:                                 }
219:                         }
220:                 }
221:         }
222:
223:         /**
224:          * The given element just became invisible.
225:          * If it is a control remove it from map and unset if needed.
226:          * If it is a container hide all child controls.
227:          *
228:          *
229:          * @param element
230:          */
231:         private void hide(VElement element) {
232:                 if (element instanceof VControl) {
233:                         removeControlFromMapAndUnsetIfNeeded((VControl) element);
234:                         return;
235:                 }
236:
237:                 final TreeIterator<EObject> iterator = element.eAllContents();
238:                 while (iterator.hasNext()) {
239:                         final EObject object = iterator.next();
240:                         if (object == null) {
241:                                 continue;
242:                         }
243:                         if (object instanceof VControl) {
244:                                 removeControlFromMapAndUnsetIfNeeded((VControl) object);
245:                         }
246:                 }
247:         }
248:
249:         /**
250:          * {@inheritDoc}
251:          *
252:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#dispose()
253:          */
254:         @Override
255:         public void dispose() {
256:                 context.unregisterEMFFormsContextListener(this);
257:                 context.unregisterViewChangeListener(viewChangeListener);
258:         }
259:
260:         /**
261:          * {@inheritDoc}
262:          *
263:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelService#getPriority()
264:          */
265:         @Override
266:         public int getPriority() {
267:                 return 5;
268:         }
269:
270:         /**
271:          * Class wrapping an {@link EStructuralFeature} as a dedicated object that
272:          * can be used as key.
273:          *
274:          * @author jfaltermeier
275:          *
276:          */
277:         private class FeatureWrapper {
278:
279:                 private final EStructuralFeature feature;
280:
281:                 /**
282:                  * Default constructor.
283:                  *
284:                  * @param feature
285:                  * the feature to wrap
286:                  */
287:                 FeatureWrapper(EStructuralFeature feature) {
288:                         this.feature = feature;
289:                 }
290:
291:                 /**
292:                  * Whether this wrapper is mapped to the given feature.
293:                  *
294:                  * @param featureToCompare
295:                  * @return <code>true</code> if equals
296:                  */
297:                 public boolean isWrapperFor(EStructuralFeature featureToCompare) {
298:                         return feature.equals(featureToCompare);
299:                 }
300:
301:         }
302:
303:         /**
304:          * {@inheritDoc}
305:          *
306:          * @see org.eclipse.emfforms.spi.core.services.view.EMFFormsContextListener#childContextAdded(org.eclipse.emf.ecp.view.spi.model.VElement,
307:          * org.eclipse.emfforms.spi.core.services.view.EMFFormsViewContext)
308:          */
309:         @Override
310:         public void childContextAdded(VElement parentElement, EMFFormsViewContext childContext) {
311:                 // intentionally left empty
312:         }
313:
314:         /**
315:          * {@inheritDoc}
316:          *
317:          * @see org.eclipse.emfforms.spi.core.services.view.EMFFormsContextListener#childContextDisposed(org.eclipse.emfforms.spi.core.services.view.EMFFormsViewContext)
318:          */
319:         @Override
320:         public void childContextDisposed(EMFFormsViewContext childContext) {
321:                 // intentionally left empty
322:         }
323:
324:         /**
325:          * {@inheritDoc}
326:          *
327:          * @see org.eclipse.emfforms.spi.core.services.view.EMFFormsContextListener#contextInitialised()
328:          */
329:         @Override
330:         public void contextInitialised() {
331:
332:                 viewChangeListener = new ModelChangeListener() {
333:                         @Override
334:                         public void notifyChange(ModelChangeNotification notification) {
335:•                                if (notification.getStructuralFeature() == VViewPackage.eINSTANCE.getElement_Visible()) {
336:                                         final EObject notifier = notification.getNotifier();
337:                                         final Notification rawNotification = notification.getRawNotification();
338:•                                        if (rawNotification.getNewBooleanValue()) {
339:                                                 // isVisible set to true
340:                                                 show((VElement) notifier);
341:                                                 return;
342:                                         }
343:                                         // isVisible set to false
344:                                         hide((VElement) notifier);
345:
346:                                 }
347:                         }
348:                 };
349:                 context.registerViewChangeListener(viewChangeListener);
350:
351:                 final VElement view = context.getViewModel();
352:                 if (view == null) {
353:                         throw new IllegalStateException(VIEW_MODEL_NULL_EXCEPTION);
354:                 }
355:
356:                 final EObject domainModel = context.getDomainModel();
357:                 if (domainModel == null) {
358:                         throw new IllegalStateException(DOMAIN_MODEL_NULL_EXCEPTION);
359:                 }
360:
361:                 initMaps(view, false);
362:                 unsetInitialHiddenControls();
363:         }
364:
365:         /**
366:          * {@inheritDoc}
367:          *
368:          * @see org.eclipse.emfforms.spi.core.services.view.EMFFormsContextListener#contextDispose()
369:          */
370:         @Override
371:         public void contextDispose() {
372:                 // intentionally left empty
373:         }
374:
375: }