Skip to content

Package: ViewModelContextImpl$2

ViewModelContextImpl$2

nameinstructionbranchcomplexitylinemethod
serviceChanged(ServiceEvent)
M: 0 C: 26
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 5
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-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: * Eugen Neufeld - initial API and implementation
13: * Christian W. Damus - bugs 527740, 533522, 545686, 527686, 548592
14: ******************************************************************************/
15: package org.eclipse.emf.ecp.view.internal.context;
16:
17: import static java.util.Collections.singleton;
18:
19: import java.util.Collections;
20: import java.util.Comparator;
21: import java.util.Iterator;
22: import java.util.LinkedHashMap;
23: import java.util.LinkedHashSet;
24: import java.util.List;
25: import java.util.Map;
26: import java.util.Set;
27: import java.util.SortedSet;
28: import java.util.TreeSet;
29: import java.util.concurrent.ConcurrentHashMap;
30: import java.util.concurrent.ConcurrentMap;
31: import java.util.concurrent.ConcurrentSkipListSet;
32: import java.util.concurrent.CopyOnWriteArrayList;
33: import java.util.concurrent.CopyOnWriteArraySet;
34: import java.util.concurrent.atomic.AtomicInteger;
35:
36: import org.eclipse.emf.common.command.BasicCommandStack;
37: import org.eclipse.emf.common.notify.AdapterFactory;
38: import org.eclipse.emf.common.notify.Notification;
39: import org.eclipse.emf.common.notify.Notifier;
40: import org.eclipse.emf.common.util.TreeIterator;
41: import org.eclipse.emf.common.util.URI;
42: import org.eclipse.emf.ecore.EObject;
43: import org.eclipse.emf.ecore.EStructuralFeature.Setting;
44: import org.eclipse.emf.ecore.resource.Resource;
45: import org.eclipse.emf.ecore.resource.ResourceSet;
46: import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
47: import org.eclipse.emf.ecore.util.EContentAdapter;
48: import org.eclipse.emf.ecore.util.EcoreUtil;
49: import org.eclipse.emf.ecp.common.spi.UniqueSetting;
50: import org.eclipse.emf.ecp.view.spi.context.EMFFormsLegacyServicesManager;
51: import org.eclipse.emf.ecp.view.spi.context.GlobalViewModelService;
52: import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
53: import org.eclipse.emf.ecp.view.spi.context.ViewModelContextDisposeListener;
54: import org.eclipse.emf.ecp.view.spi.context.ViewModelService;
55: import org.eclipse.emf.ecp.view.spi.context.ViewModelServiceNotAvailableReport;
56: import org.eclipse.emf.ecp.view.spi.context.ViewModelServiceProvider;
57: import org.eclipse.emf.ecp.view.spi.model.ModelChangeAddRemoveListener;
58: import org.eclipse.emf.ecp.view.spi.model.ModelChangeListener;
59: import org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification;
60: import org.eclipse.emf.ecp.view.spi.model.VControl;
61: import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
62: import org.eclipse.emf.ecp.view.spi.model.VDomainModelReferenceSegment;
63: import org.eclipse.emf.ecp.view.spi.model.VElement;
64: import org.eclipse.emf.ecp.view.spi.model.VView;
65: import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
66: import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
67: import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
68: import org.eclipse.emfforms.common.Optional;
69: import org.eclipse.emfforms.spi.common.report.AbstractReport;
70: import org.eclipse.emfforms.spi.common.report.ReportService;
71: import org.eclipse.emfforms.spi.core.services.controlmapper.EMFFormsSettingToControlMapper;
72: import org.eclipse.emfforms.spi.core.services.domainexpander.EMFFormsDomainExpander;
73: import org.eclipse.emfforms.spi.core.services.domainexpander.EMFFormsExpandingFailedException;
74: import org.eclipse.emfforms.spi.core.services.view.EMFFormsContextListener;
75: import org.eclipse.emfforms.spi.core.services.view.EMFFormsViewServiceManager;
76: import org.eclipse.emfforms.spi.core.services.view.RootDomainModelChangeListener;
77: import org.osgi.framework.Bundle;
78: import org.osgi.framework.BundleContext;
79: import org.osgi.framework.FrameworkUtil;
80: import org.osgi.framework.ServiceEvent;
81: import org.osgi.framework.ServiceListener;
82: import org.osgi.framework.ServiceReference;
83:
84: /**
85: * The Class ViewModelContextImpl.
86: *
87: * @author Eugen Neufeld
88: */
89: public class ViewModelContextImpl implements ViewModelContext {
90:
91:         static {
92:                 final Bundle bundle = FrameworkUtil.getBundle(ViewModelContextImpl.class);
93:                 if (bundle != null) {
94:                         bundleContext = bundle.getBundleContext();
95:                 }
96:         }
97:         private static BundleContext bundleContext;
98:         private static final String MODEL_CHANGE_LISTENER_MUST_NOT_BE_NULL = "ModelChangeAddRemoveListener must not be null."; //$NON-NLS-1$
99:
100:         private static final String ROOT_DOMAIN_MODEL_CHANGE_LISTENER_MUST_NOT_BE_NULL = "RootDomainModelChangeListener must not be null."; //$NON-NLS-1$
101:
102:         private static final String THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED = "The ViewModelContext was already disposed."; //$NON-NLS-1$
103:
104:         /** The view. */
105:         private final VElement view;
106:
107:         /** The domain object. */
108:         private EObject domainObject;
109:
110:         /** The root domain model change listeners. Needs to be thread safe. */
111:         private final List<RootDomainModelChangeListener> rootDomainModelChangeListeners = new CopyOnWriteArrayList<RootDomainModelChangeListener>();
112:
113:         /** The domain model content adapter. */
114:         private final DomainModelContentAdapter domainModelContentAdapter;
115:
116:         /** The view model content adapter. */
117:         private final ViewModelContentAdapter viewModelContentAdapter;
118:
119:         private final Set<EMFFormsContextListener> contextListeners = new CopyOnWriteArraySet<EMFFormsContextListener>();
120:
121:         /** The view services. */
122:         private final SortedSet<ViewModelService> viewServices = new TreeSet<ViewModelService>(
123:                 new Comparator<ViewModelService>() {
124:
125:                         @Override
126:                         public int compare(ViewModelService arg0, ViewModelService arg1) {
127:                                 if (arg0.getPriority() == arg1.getPriority()) {
128:                                         /* compare would return 0, meaning the services are identical -> 1 service would get lost */
129:                                         return arg0.getClass().getName().compareTo(arg1.getClass().getName());
130:                                 }
131:                                 return arg0.getPriority() - arg1.getPriority();
132:                         }
133:                 });
134:
135:         /** Provider of view service overrides. */
136:         private final ViewModelServiceProvider viewServiceProvider;
137:
138:         /**
139:          * The disposed state.
140:          */
141:         private boolean isDisposed;
142:
143:         /**
144:          * The context map.
145:          */
146:         private final Map<String, Object> keyObjectMap = new LinkedHashMap<String, Object>();
147:
148:         /**
149:          * Whether the context is being disposed.
150:          */
151:         private boolean isDisposing;
152:
153:         private Resource resource;
154:
155:         private final Map<EObject, Set<ViewModelContext>> childContexts = new LinkedHashMap<EObject, Set<ViewModelContext>>();
156:         private final Map<ViewModelContext, VElement> childContextUsers = new LinkedHashMap<ViewModelContext, VElement>();
157:
158:         private final ViewModelContext parentContext;
159:
160:         private final Set<ViewModelContextDisposeListener> disposeListeners = new LinkedHashSet<ViewModelContextDisposeListener>();
161:
162:         // TODO replace with eclipse context?!
163:         private final Map<Class<?>, Object> serviceMap = new LinkedHashMap<Class<?>, Object>();
164:
165:         private final Map<ServiceReference<?>, Class<?>> usedOSGiServices = new LinkedHashMap<ServiceReference<?>, Class<?>>();
166:
167:         private ServiceListener serviceListener;
168:
169:         private final VElement parentVElement;
170:
171:         private boolean pause = false;
172:
173:         /**
174:          * Instantiates a new view model context impl.
175:          *
176:          * @param view the view
177:          * @param domainObject the domain object
178:          */
179:         public ViewModelContextImpl(VElement view, EObject domainObject) {
180:                 this(view, domainObject, ViewModelServiceProvider.NULL);
181:         }
182:
183:         /**
184:          * Instantiates a new view model context impl.
185:          *
186:          * @param view the view
187:          * @param domainObject the domain object
188:          * @param parent The parent {@link ViewModelContext}
189:          * @param parentVElement the parent {@link VElement}
190:          */
191:         public ViewModelContextImpl(VElement view, EObject domainObject, ViewModelContext parent,
192:                 VElement parentVElement) {
193:                 this(view, domainObject, parent, parentVElement, ViewModelServiceProvider.NULL);
194:         }
195:
196:         /**
197:          * Instantiates a new view model context impl.
198:          *
199:          * @param view the view
200:          * @param domainObject the domain object
201:          * @param modelServices an array of services to use in the {@link ViewModelContext}
202:          *
203:          * @deprecated As of 1.16, use the {@link #ViewModelContextImpl(VElement, EObject, ViewModelServiceProvider)} API
204:          */
205:         @Deprecated
206:         public ViewModelContextImpl(VElement view, EObject domainObject, ViewModelService... modelServices) {
207:                 this(view, domainObject, new ArrayOnceViewModelServiceProvider(modelServices));
208:         }
209:
210:         /**
211:          * Instantiates a new view model context impl.
212:          *
213:          * @param view the view
214:          * @param domainObject the domain object
215:          * @param modelServiceProvider a provider of services to use in the {@link ViewModelContext}. May be {@code null} if
216:          * local service overrides are not needed
217:          *
218:          * @since 1.16
219:          */
220:         public ViewModelContextImpl(VElement view, EObject domainObject, ViewModelServiceProvider modelServiceProvider) {
221:                 this(view, domainObject, null, null, modelServiceProvider);
222:         }
223:
224:         /**
225:          * Instantiates a new view model context impl with a parent context.
226:          *
227:          * @param view the view
228:          * @param domainObject the domain object
229:          * @param parent The parent {@link ViewModelContext}
230:          * @param parentVElement The parent {@link VElement}
231:          * @param modelServices an array of services to use in the {@link ViewModelContext}
232:          *
233:          * @deprecated As of 1.16, use the
234:          * {@link #ViewModelContextImpl(VElement, EObject, ViewModelContext, VElement, ViewModelServiceProvider)}
235:          * API
236:          */
237:         @Deprecated
238:         public ViewModelContextImpl(VElement view, EObject domainObject, ViewModelContext parent,
239:                 VElement parentVElement, ViewModelService... modelServices) {
240:
241:                 this(view, domainObject, parent, parentVElement, new ArrayOnceViewModelServiceProvider(modelServices));
242:         }
243:
244:         /**
245:          * Instantiates a new view model context impl with a parent context.
246:          *
247:          * @param view the view
248:          * @param domainObject the domain object
249:          * @param parent The parent {@link ViewModelContext}
250:          * @param parentVElement The parent {@link VElement}
251:          * @param modelServiceProvider a provider of services to use in the {@link ViewModelContext}. May be {@code null} if
252:          * local service overrides are not needed
253:          *
254:          * @since 1.16
255:          */
256:         public ViewModelContextImpl(VElement view, EObject domainObject, ViewModelContext parent,
257:                 VElement parentVElement, ViewModelServiceProvider modelServiceProvider) {
258:
259:                 this(view, domainObject, parent, parentVElement, modelServiceProvider, null);
260:         }
261:
262:         /**
263:          * Instantiates a new view model context with initial {@linkplain #getContextValue(String) context values}.
264:          *
265:          * @param view the view
266:          * @param domainObject the domain object
267:          * @param contextValues initial context values to set
268:          *
269:          * @since 1.21
270:          * @see #getContextValue(String)
271:          */
272:         public ViewModelContextImpl(VElement view, EObject domainObject, Map<String, ?> contextValues) {
273:                 this(view, domainObject, ViewModelServiceProvider.NULL, contextValues);
274:         }
275:
276:         /**
277:          * Instantiates a new view model context with initial {@linkplain #getContextValue(String) context values}.
278:          *
279:          * @param view the view
280:          * @param domainObject the domain object
281:          * @param modelServiceProvider a provider of services to use in the {@link ViewModelContext}. May be {@code null} if
282:          * local service overrides are not needed
283:          * @param contextValues initial context values to set
284:          *
285:          * @since 1.21
286:          * @see #getContextValue(String)
287:          */
288:         public ViewModelContextImpl(VElement view, EObject domainObject, ViewModelServiceProvider modelServiceProvider,
289:                 Map<String, ?> contextValues) {
290:
291:                 this(view, domainObject, null, null, modelServiceProvider, contextValues);
292:         }
293:
294:         /**
295:          * Internal constructor to which all others ultimately delegate.
296:          */
297:         private ViewModelContextImpl(VElement view, EObject domainObject, ViewModelContext parent,
298:                 VElement parentVElement, ViewModelServiceProvider modelServiceProvider, Map<String, ?> contextValues) {
299:
300:                 this.view = view;
301:                 this.domainObject = domainObject;
302:                 parentContext = parent;
303:                 if (modelServiceProvider == null) {
304:                         viewServiceProvider = ViewModelServiceProvider.NULL;
305:                 } else {
306:                         viewServiceProvider = modelServiceProvider;
307:                 }
308:                 this.parentVElement = parentVElement;
309:
310:                 // This must be done before instantiating services that may use the context values
311:                 if (contextValues != null) {
312:                         if (parentContext != null) {
313:                                 // Values go up to the parent
314:                                 contextValues.forEach(parentContext::putContextValue);
315:                         } else {
316:                                 // I am the root
317:                                 keyObjectMap.putAll(contextValues);
318:                         }
319:                 }
320:
321:                 viewModelContentAdapter = new ViewModelContentAdapter();
322:
323:                 if (parentContext == null) {
324:                         domainModelContentAdapter = new DomainModelContentAdapter();
325:                 } else {
326:                         domainModelContentAdapter = null;
327:                 }
328:
329:                 instantiate();
330:         }
331:
332:         /**
333:          * Resolve all domain model references for a given resolvable and a given domain model root.
334:          *
335:          * @param resolvable The EObject to resolve all {@link VDomainModelReference domain model references} of.
336:          * @param domainModelRoot the domain model used for the resolving.
337:          * @throws EMFFormsExpandingFailedException If the domain expansion fails.
338:          */
339:         private void resolveDomainReferences(EObject resolvable, EObject domainModelRoot) {
340:                 // Get domain expander service
341:                 final EMFFormsDomainExpander domainExpander = getService(EMFFormsDomainExpander.class);
342:                 if (domainExpander == null) {
343:                         return;
344:                 }
345:                 expandAndInitDMR(domainModelRoot, domainExpander, resolvable);
346:                 // Iterate over all domain model references of the given EObject.
347:                 final TreeIterator<EObject> eAllContents = resolvable.eAllContents();
348:                 while (eAllContents.hasNext()) {
349:                         final EObject eObject = eAllContents.next();
350:                         expandAndInitDMR(domainModelRoot, domainExpander, eObject);
351:                 }
352:         }
353:
354:         private void expandAndInitDMR(EObject domainModelRoot, final EMFFormsDomainExpander domainExpander,
355:                 final EObject eObject) {
356:                 if (VDomainModelReference.class.isInstance(eObject)
357:                         && !VDomainModelReference.class.isInstance(eObject.eContainer())
358:                         && !VDomainModelReferenceSegment.class.isInstance(eObject.eContainer())) {
359:                         final VDomainModelReference domainModelReference = VDomainModelReference.class.cast(eObject);
360:                         try {
361:                                 domainExpander.prepareDomainObject(domainModelReference, domainModelRoot);
362:                         } catch (final EMFFormsExpandingFailedException ex) {
363:                                 getServiceWithoutLog(ReportService.class).report(new AbstractReport(ex));
364:                         }
365:                 }
366:         }
367:
368:         /**
369:          * Instantiate.
370:          */
371:         private void instantiate() {
372:                 addResourceIfNecessary();
373:
374:                 resolveDomainReferences(getViewModel(), getDomainModel());
375:
376:                 // Only the root context has this adapter to attach it to the model. Child contents
377:                 // all push listeners up to the root and expect their own domain object to be in
378:                 // the content tree of the root context's domain object
379:                 if (domainModelContentAdapter != null) {
380:                         domainObject.eAdapters().add(domainModelContentAdapter);
381:                 }
382:
383:                 // Generate local view services from our provider. Do this before loading
384:                 // contributed immediate services because these may create child contexts
385:                 // even before we are ready and these provided services need to be instantiated
386:                 // in me before that (otherwise, an array "once" provider may be consumed by
387:                 // a child context before I can get to it)
388:                 viewServices.addAll(viewServiceProvider.getViewModelServices(view, domainObject));
389:
390:                 loadImmediateServices();
391:
392:                 view.eAdapters().add(viewModelContentAdapter);
393:
394:                 for (final ViewModelService viewService : viewServices) {
395:                         viewService.instantiate(this);
396:                 }
397:
398:                 // If I have a parent, then I should be added to it if I wasn't already.
399:                 // e.g., if this is the re-instantiate after dispose on changing the domain
400:                 // model, then I need to be added back as a child because I was removed
401:                 if (parentContext instanceof ViewModelContextImpl) {
402:                         ((ViewModelContextImpl) parentContext).addChildContext(getParentVElement(), getDomainModel(), this);
403:                 }
404:
405:                 for (final EMFFormsContextListener contextListener : contextListeners) {
406:                         contextListener.contextInitialised();
407:                 }
408:         }
409:
410:         private void loadImmediateServices() {
411:                 final Bundle bundle = FrameworkUtil.getBundle(getClass());
412:
413:                 if (bundle != null) {
414:                         final BundleContext bundleContext = bundle.getBundleContext();
415:                         serviceReferenceLegacy = bundleContext
416:                                 .getServiceReference(EMFFormsLegacyServicesManager.class);
417:                         if (serviceReferenceLegacy != null) {
418:                                 final EMFFormsLegacyServicesManager legacyServicesFactory = bundleContext
419:                                         .getService(serviceReferenceLegacy);
420:                                 legacyServicesFactory.instantiate();
421:                         }
422:
423:                         servicesManager = getService(EMFFormsViewServiceManager.class);
424:
425:                         for (final Class<?> localImmediateService : servicesManager.getAllLocalImmediateServiceTypes()) {
426:                                 final Optional<?> service = servicesManager.createLocalImmediateService(localImmediateService, this);
427:                                 if (!service.isPresent()) {
428:                                         // error case?
429:                                         continue;
430:                                 }
431:                                 serviceMap.put(localImmediateService, service.get());
432:
433:                         }
434:
435:                         if (parentContext == null) {
436:                                 for (final Class<?> globalImmediateService : servicesManager.getAllGlobalImmediateServiceTypes()) {
437:                                         final Optional<?> service = servicesManager.createGlobalImmediateService(globalImmediateService,
438:                                                 this);
439:                                         if (!service.isPresent()) {
440:                                                 // error case?
441:                                                 continue;
442:                                         }
443:                                         final Object serviceObject = service.get();
444:                                         serviceMap.put(globalImmediateService, serviceObject);
445:
446:                                 }
447:                         }
448:
449:                         serviceListener = new ServiceListener() {
450:
451:                                 @Override
452:                                 public void serviceChanged(ServiceEvent event) {
453:•                                        if (event.getType() == ServiceEvent.UNREGISTERING &&
454:•                                                usedOSGiServices.containsKey(event.getServiceReference())) {
455:                                                 final Class<?> remove = usedOSGiServices.remove(event.getServiceReference());
456:                                                 serviceMap.remove(remove);
457:                                         }
458:                                 }
459:                         };
460:                         bundleContext.addServiceListener(serviceListener);
461:
462:                 }
463:         }
464:
465:         private void addResourceIfNecessary() {
466:                 if (domainObject.eResource() != null) {
467:                         return;
468:                 }
469:                 final EObject rootObject = EcoreUtil.getRootContainer(domainObject);
470:                 final ResourceSet rs = new ResourceSetImpl();
471:                 final ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(new AdapterFactory[] {
472:                         new ReflectiveItemProviderAdapterFactory(),
473:                         new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE) });
474:                 final AdapterFactoryEditingDomain domain = new AdapterFactoryEditingDomain(
475:                         adapterFactory,
476:                         new BasicCommandStack(), rs);
477:                 rs.eAdapters().add(new AdapterFactoryEditingDomain.EditingDomainProvider(domain));
478:                 resource = rs.createResource(URI.createURI("VIRTAUAL_URI")); //$NON-NLS-1$
479:                 if (resource != null) {
480:                         resource.getContents().add(rootObject);
481:                 }
482:         }
483:
484:         /**
485:          * {@inheritDoc}
486:          *
487:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getControlsFor(org.eclipse.emf.ecore.EStructuralFeature.Setting)
488:          * @deprecated
489:          */
490:         @Deprecated
491:         @Override
492:         public Set<VControl> getControlsFor(Setting setting) {
493:                 final Set<VElement> elements = getService(EMFFormsSettingToControlMapper.class)
494:                         .getControlsFor(UniqueSetting.createSetting(setting));
495:                 final Set<VControl> controls = new LinkedHashSet<VControl>();
496:                 for (final VElement element : elements) {
497:                         if (VControl.class.isInstance(element)) {
498:                                 controls.add((VControl) element);
499:                         }
500:                 }
501:                 return controls;
502:         }
503:
504:         /**
505:          * {@inheritDoc}
506:          *
507:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getControlsFor(org.eclipse.emf.ecp.common.spi.UniqueSetting)
508:          * @deprecated
509:          */
510:         @Deprecated
511:         @Override
512:         public Set<VElement> getControlsFor(UniqueSetting setting) {
513:                 return getService(EMFFormsSettingToControlMapper.class).getControlsFor(setting);
514:         }
515:
516:         /**
517:          * {@inheritDoc}
518:          *
519:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getViewModel()
520:          */
521:         @Override
522:         public VElement getViewModel() {
523:                 if (isDisposed) {
524:                         throw new IllegalStateException(THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED);
525:                 }
526:                 return view;
527:         }
528:
529:         /**
530:          * {@inheritDoc}
531:          *
532:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getDomainModel()
533:          */
534:         @Override
535:         public EObject getDomainModel() {
536:                 if (isDisposed) {
537:                         throw new IllegalStateException(THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED);
538:                 }
539:                 return domainObject;
540:         }
541:
542:         /**
543:          * Dispose.
544:          */
545:         @Override
546:         public void dispose() {
547:                 if (isDisposed || isDisposing) {
548:                         return;
549:                 }
550:                 isDisposing = true;
551:                 for (final ViewModelContextDisposeListener listener : disposeListeners) {
552:                         listener.contextDisposed(this);
553:                 }
554:                 disposeListeners.clear();
555:                 
556:                 innerDispose();
557:                 viewModelContentAdapter.dispose();
558:                 if (domainModelContentAdapter != null) {
559:                         domainModelContentAdapter.dispose();
560:                 }
561:                 rootDomainModelChangeListeners.clear();
562:
563:                 isDisposing = false;
564:                 isDisposed = true;
565:                 if (serviceReferenceLegacy != null) {
566:                         bundleContext.ungetService(serviceReferenceLegacy);
567:                 }
568:
569:         }
570:
571:         private void releaseOSGiServices() {
572:                 final Bundle bundle = FrameworkUtil.getBundle(getClass());
573:                 if (bundle != null) {
574:                         final BundleContext bundleContext = bundle.getBundleContext();
575:                         for (final ServiceReference<?> serviceReference : usedOSGiServices.keySet()) {
576:                                 bundleContext.ungetService(serviceReference);
577:                         }
578:                 }
579:                 usedOSGiServices.clear();
580:         }
581:
582:         @Override
583:         public void registerViewChangeListener(ModelChangeListener modelChangeListener) {
584:                 if (isDisposed) {
585:                         throw new IllegalStateException(THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED);
586:                 }
587:                 if (modelChangeListener == null) {
588:                         throw new IllegalArgumentException(MODEL_CHANGE_LISTENER_MUST_NOT_BE_NULL);
589:                 }
590:                 viewModelContentAdapter.add(modelChangeListener);
591:         }
592:
593:         @Override
594:         public void unregisterViewChangeListener(ModelChangeListener modelChangeListener) {
595:                 // if (isDisposed) {
596:                 // throw new IllegalStateException("The ViewModelContext was already disposed.");
597:                 // }
598:                 viewModelContentAdapter.remove(modelChangeListener);
599:         }
600:
601:         @Override
602:         public void registerDomainChangeListener(ModelChangeListener modelChangeListener) {
603:                 if (isDisposed) {
604:                         throw new IllegalStateException(THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED);
605:                 }
606:                 if (modelChangeListener == null) {
607:                         throw new IllegalArgumentException(MODEL_CHANGE_LISTENER_MUST_NOT_BE_NULL);
608:                 }
609:                 if (parentContext == null) {
610:                         domainModelContentAdapter.add(modelChangeListener);
611:                 } else {
612:                         parentContext.registerDomainChangeListener(modelChangeListener);
613:                 }
614:         }
615:
616:         @Override
617:         public void unregisterDomainChangeListener(ModelChangeListener modelChangeListener) {
618:                 // if (isDisposed) {
619:                 // throw new IllegalStateException("The ViewModelContext was already disposed.");
620:                 // }
621:                 if (modelChangeListener == null) {
622:                         // ConcurrentSkipListSet doesn't allow nulls but balks on attempts to remove them, too
623:                         return;
624:                 }
625:                 if (parentContext == null) {
626:                         domainModelContentAdapter.remove(modelChangeListener);
627:                 } else {
628:                         parentContext.unregisterDomainChangeListener(modelChangeListener);
629:                 }
630:         }
631:
632:         /**
633:          * {@inheritDoc}
634:          *
635:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#hasService(java.lang.Class)
636:          */
637:         @Override
638:         public <T> boolean hasService(Class<T> serviceType) {
639:                 return getServiceWithoutLog(serviceType) != null;
640:         }
641:
642:         /**
643:          * {@inheritDoc}
644:          *
645:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getService(java.lang.Class)
646:          */
647:         @Override
648:         public <T> T getService(Class<T> serviceType) {
649:                 final T service = getServiceWithoutLog(serviceType);
650:                 if (service == null) {
651:                         report(new ViewModelServiceNotAvailableReport(serviceType));
652:                 }
653:                 return service;
654:         }
655:
656:         /**
657:          * Retrieve an {@link ViewModelService} of type {@code serviceType} without reporting an error if none is found.
658:          *
659:          * @param <T>
660:          * the type of the desired service
661:          *
662:          * @param serviceType
663:          * the type of the service to be retrieved
664:          * @return the service
665:          */
666:         @SuppressWarnings("unchecked")
667:         protected <T> T getServiceWithoutLog(Class<T> serviceType) {
668:                 // legacy stuff
669:                 for (final ViewModelService service : viewServices) {
670:                         if (serviceType.isInstance(service)) {
671:                                 return (T) service;
672:                         }
673:                 }
674:                 // First check local services
675:                 final T localService = getLocalService(serviceType);
676:                 if (localService != null) {
677:                         return localService;
678:                 }
679:                 // If context is the root, check global services to be instanciated
680:                 if (servicesManager != null && parentContext == null) {
681:                         final Optional<T> lazyService = servicesManager.createGlobalLazyService(serviceType, this);
682:                         if (lazyService.isPresent()) {
683:                                 final T t = lazyService.get();
684:                                 serviceMap.put(serviceType, t);
685:                                 return t;
686:                         }
687:                 }
688:                 // Check the parent context
689:                 if (parentContext != null && parentContext.hasService(serviceType)) {
690:                         return parentContext.getService(serviceType);
691:                 }
692:                 // Check OSGi services
693:                 if (bundleContext != null) {
694:                         final ServiceReference<T> serviceReference = bundleContext.getServiceReference(serviceType);
695:                         if (serviceReference != null) {
696:                                 usedOSGiServices.put(serviceReference, serviceType);
697:                                 final T service = bundleContext.getService(serviceReference);
698:                                 serviceMap.put(serviceType, service);
699:                                 return service;
700:                         }
701:                 }
702:                 return null;
703:         }
704:
705:         /**
706:          * @return a instance of a local service or a local service service, which can be created or null if neither exists.
707:          */
708:         @SuppressWarnings("unchecked")
709:         private <T> T getLocalService(Class<T> serviceType) {
710:                 if (serviceMap.containsKey(serviceType)) {
711:                         return (T) serviceMap.get(serviceType);
712:                 } else if (servicesManager != null) {
713:                         final Optional<T> lazyService = servicesManager.createLocalLazyService(serviceType, this);
714:                         if (lazyService.isPresent()) {
715:                                 final T t = lazyService.get();
716:                                 serviceMap.put(serviceType, t);
717:                                 return t;
718:                         }
719:                 }
720:                 return null;
721:         }
722:
723:         private void report(AbstractReport report) {
724:                 final ReportService reportService = getServiceWithoutLog(ReportService.class);
725:                 if (reportService != null) {
726:                         reportService.report(report);
727:                 }
728:         }
729:
730:         /**
731:          * The content adapter for the view model.
732:          */
733:         private class ViewModelContentAdapter extends EContentAdapter {
734:
735:                 /** The view model change listener. Needs to be thread safe. */
736:                 private final List<ModelChangeListener> viewModelChangeListeners = new CopyOnWriteArrayList<ModelChangeListener>();
737:
738:                 @Override
739:                 public void notifyChanged(Notification notification) {
740:                         super.notifyChanged(notification);
741:                         // do not notify while being disposed
742:                         if (isDisposing || isDisposed || pause) {
743:                                 return;
744:                         }
745:                         if (notification.isTouch()) {
746:                                 return;
747:                         }
748:                         final ModelChangeNotification modelChangeNotification = new ModelChangeNotification(notification);
749:                         for (final ModelChangeListener modelChangeListener : viewModelChangeListeners) {
750:                                 modelChangeListener.notifyChange(modelChangeNotification);
751:                         }
752:                 }
753:
754:                 @Override
755:                 protected void addAdapter(Notifier notifier) {
756:                         super.addAdapter(notifier);
757:                         // do not notify while being disposed
758:                         if (isDisposing || isDisposed || pause) {
759:                                 return;
760:                         }
761:                         for (final ModelChangeListener modelChangeListener : viewModelChangeListeners) {
762:                                 if (ModelChangeAddRemoveListener.class.isInstance(modelChangeListener)) {
763:                                         ModelChangeAddRemoveListener.class.cast(modelChangeListener).notifyAdd(notifier);
764:                                 }
765:                         }
766:                 }
767:
768:                 @Override
769:                 protected void removeAdapter(Notifier notifier) {
770:                         super.removeAdapter(notifier);
771:                         // do not notify while being disposed
772:                         if (isDisposing || isDisposed || pause) {
773:                                 return;
774:                         }
775:                         if (VElement.class.isInstance(notifier)) {
776:                                 VElement.class.cast(notifier).setDiagnostic(null);
777:                         }
778:                         for (final ModelChangeListener modelChangeListener : viewModelChangeListeners) {
779:                                 if (ModelChangeAddRemoveListener.class.isInstance(modelChangeListener)) {
780:                                         ModelChangeAddRemoveListener.class.cast(modelChangeListener).notifyRemove(notifier);
781:                                 }
782:                         }
783:                 }
784:
785:                 void add(ModelChangeListener viewModelChangeListener) {
786:                         viewModelChangeListeners.add(viewModelChangeListener);
787:
788:                         // do not notify while being disposed
789:                         if (isDisposing || isDisposed || !view.eAdapters().contains(this)
790:                                 || !(viewModelChangeListener instanceof ModelChangeAddRemoveListener)) {
791:                                 return;
792:                         }
793:
794:                         // This listener needs to discover all of the view model as new
795:                         final ModelChangeAddRemoveListener addRemove = (ModelChangeAddRemoveListener) viewModelChangeListener;
796:                         for (final Iterator<EObject> iter = EcoreUtil.getAllContents(singleton(view), resolve()); iter.hasNext();) {
797:                                 addRemove.notifyAdd(iter.next());
798:                         }
799:                 }
800:
801:                 void remove(ModelChangeListener viewModelChangeListener) {
802:                         viewModelChangeListeners.remove(viewModelChangeListener);
803:
804:                         // do not notify while being disposed
805:                         if (isDisposing || !view.eAdapters().contains(this)
806:                                 || !(viewModelChangeListener instanceof ModelChangeAddRemoveListener)) {
807:                                 return;
808:                         }
809:
810:                         // This listener needs to un-discover all of the view model
811:                         final ModelChangeAddRemoveListener addRemove = (ModelChangeAddRemoveListener) viewModelChangeListener;
812:                         for (final Iterator<EObject> iter = EcoreUtil.getAllContents(singleton(view), resolve()); iter.hasNext();) {
813:                                 addRemove.notifyRemove(iter.next());
814:                         }
815:                 }
816:
817:                 void dispose() {
818:                         viewModelChangeListeners.clear();
819:                 }
820:         }
821:
822:         /**
823:          * The content adapter for the domain model.
824:          */
825:         private class DomainModelContentAdapter extends EContentAdapter {
826:
827:                 /** The domain model change listeners. Needs to be thread safe. */
828:                 private final GroupedListenerList<ModelChangeListener> domainModelChangeListeners =
829:                         // needed to make sure that all data operations are done before any validation etc provided by services
830:                         // happens
831:                         new GroupedListenerList<ModelChangeListener>(VDomainModelReference.class);
832:
833:                 @Override
834:                 public void notifyChanged(Notification notification) {
835:                         super.notifyChanged(notification);
836:
837:                         // do not notify while being disposed
838:                         if (isDisposing || isDisposed || pause) {
839:                                 return;
840:                         }
841:
842:                         final ModelChangeNotification modelChangeNotification = new ModelChangeNotification(notification);
843:                         for (final ModelChangeListener modelChangeListener : domainModelChangeListeners) {
844:                                 modelChangeListener.notifyChange(modelChangeNotification);
845:                         }
846:                 }
847:
848:                 @Override
849:                 protected void addAdapter(Notifier notifier) {
850:                         super.addAdapter(notifier);
851:                         // do not notify while being disposed
852:                         if (isDisposing || isDisposed || pause) {
853:                                 return;
854:                         }
855:                         for (final ModelChangeListener modelChangeListener : domainModelChangeListeners) {
856:                                 if (ModelChangeAddRemoveListener.class.isInstance(modelChangeListener)) {
857:                                         ModelChangeAddRemoveListener.class.cast(modelChangeListener).notifyAdd(notifier);
858:                                 }
859:                         }
860:                 }
861:
862:                 @Override
863:                 protected void removeAdapter(Notifier notifier) {
864:                         super.removeAdapter(notifier);
865:                         // do not notify while being disposed
866:                         if (isDisposing || isDisposed || pause) {
867:                                 return;
868:                         }
869:                         for (final ModelChangeListener modelChangeListener : domainModelChangeListeners) {
870:                                 if (ModelChangeAddRemoveListener.class.isInstance(modelChangeListener)) {
871:                                         ModelChangeAddRemoveListener.class.cast(modelChangeListener).notifyRemove(notifier);
872:                                 }
873:                         }
874:                 }
875:
876:                 void dispose() {
877:                         domainModelChangeListeners.clear();
878:                 }
879:
880:                 void add(ModelChangeListener domainModelChangeListener) {
881:                         domainModelChangeListeners.add(domainModelChangeListener);
882:
883:                         // do not notify while being disposed
884:                         if (isDisposing || !domainObject.eAdapters().contains(this)
885:                                 || !(domainModelChangeListener instanceof ModelChangeAddRemoveListener)) {
886:                                 return;
887:                         }
888:
889:                         // This listener needs to discover all of the domain model as new
890:                         final ModelChangeAddRemoveListener addRemove = (ModelChangeAddRemoveListener) domainModelChangeListener;
891:                         for (final Iterator<EObject> iter = EcoreUtil.getAllContents(singleton(domainObject), resolve()); iter
892:                                 .hasNext();) {
893:                                 addRemove.notifyAdd(iter.next());
894:                         }
895:                 }
896:
897:                 void remove(ModelChangeListener domainModelChangeListener) {
898:                         domainModelChangeListeners.remove(domainModelChangeListener);
899:
900:                         // do not notify while being disposed
901:                         if (isDisposing || !domainObject.eAdapters().contains(this)
902:                                 || !(domainModelChangeListener instanceof ModelChangeAddRemoveListener)) {
903:                                 return;
904:                         }
905:
906:                         // This listener needs to un-discover all of the domain model
907:                         final ModelChangeAddRemoveListener addRemove = (ModelChangeAddRemoveListener) domainModelChangeListener;
908:                         for (final Iterator<EObject> iter = EcoreUtil.getAllContents(singleton(domainObject), resolve()); iter
909:                                 .hasNext();) {
910:                                 addRemove.notifyRemove(iter.next());
911:                         }
912:                 }
913:
914:         }
915:
916:         private final Set<Object> users = new LinkedHashSet<Object>();
917:
918:         private EMFFormsViewServiceManager servicesManager;
919:         private ServiceReference<EMFFormsLegacyServicesManager> serviceReferenceLegacy;
920:
921:         /**
922:          * Inner method for registering context users (not {@link ViewModelService}).
923:          *
924:          * @param user the user of the context
925:          */
926:         @Override
927:         public void addContextUser(Object user) {
928:                 users.add(user);
929:         }
930:
931:         /**
932:          * Inner method for unregistering the context user.
933:          *
934:          * @param user the user of the context
935:          */
936:         @Override
937:         public void removeContextUser(Object user) {
938:                 users.remove(user);
939:                 // Every renderer is registered here, as it needs to know when the view model changes (rules, validations, etc).
940:                 // If no listener is left, we can dispose the context
941:                 // if (users.isEmpty() || users.size() == 1 && parentContext != null && users.contains(parentContext)) {
942:                 if (users.isEmpty()) {
943:                         dispose();
944:                 }
945:         }
946:
947:         /**
948:          * {@inheritDoc}
949:          *
950:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getContextValue(java.lang.String)
951:          */
952:         @Override
953:         public Object getContextValue(String key) {
954:                 if (parentContext != null) {
955:                         return parentContext.getContextValue(key);
956:                 }
957:                 return keyObjectMap.get(key);
958:         }
959:
960:         /**
961:          * {@inheritDoc}
962:          *
963:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#putContextValue(java.lang.String, java.lang.Object)
964:          */
965:         @Override
966:         public void putContextValue(String key, Object value) {
967:                 if (parentContext != null) {
968:                         parentContext.putContextValue(key, value);
969:                         return;
970:                 }
971:                 keyObjectMap.put(key, value);
972:         }
973:
974:         private void addChildContext(VElement parentElement, EObject eObject, ViewModelContext childContext) {
975:                 if (hasChildContext(parentElement, eObject, childContext)) {
976:                         return;
977:                 }
978:
979:                 if (!childContexts.containsKey(eObject)) {
980:                         childContexts.put(eObject, new LinkedHashSet<ViewModelContext>());
981:                 }
982:                 childContexts.get(eObject).add(childContext);
983:                 childContextUsers.put(childContext, parentElement);
984:
985:                 for (final EMFFormsContextListener contextListener : contextListeners) {
986:                         contextListener.childContextAdded(parentElement, childContext);
987:                 }
988:                 // notify all global View model services
989:                 for (final ViewModelService viewModelService : viewServices) {
990:                         if (GlobalViewModelService.class.isInstance(viewModelService)) {
991:                                 GlobalViewModelService.class.cast(viewModelService).childViewModelContextAdded(childContext);
992:                         }
993:                 }
994:
995:         }
996:
997:         /**
998:          * Query whether I have a valid child context for the given domain {@code object} under
999:          * the given parent view-model element.
1000:          *
1001:          * @param parentElement the parent view-model element
1002:          * @param object a domain model element
1003:          * @param childContext a child context expected possibly for that element
1004:          * @return {@code true} if the given context is currently registered as a child
1005:          * context for the {@code object}; {@code false}, otherwise
1006:          */
1007:         private boolean hasChildContext(VElement parentElement, EObject object, ViewModelContext childContext) {
1008:                 boolean result = childContexts.getOrDefault(object, Collections.emptySet()).contains(childContext);
1009:                 result = result && childContextUsers.get(childContext) == parentElement;
1010:                 return result;
1011:         }
1012:
1013:         private void removeChildContext(EObject eObject, ViewModelContext context) {
1014:                 final Set<ViewModelContext> children = childContexts.get(eObject);
1015:                 final boolean removed = children != null && children.remove(context);
1016:
1017:                 if (!removed) {
1018:                         return;
1019:                 }
1020:
1021:                 childContextUsers.remove(context);
1022:                 if (children.size() == 0) {
1023:                         childContexts.remove(eObject);
1024:                 }
1025:
1026:                 for (final EMFFormsContextListener contextListener : contextListeners) {
1027:                         contextListener.childContextDisposed(context);
1028:                 }
1029:         }
1030:
1031:         /**
1032:          * Obtains the provider of local view-model service overrides.
1033:          *
1034:          * @return the local view-model service provider
1035:          *
1036:          * @since 1.16
1037:          */
1038:         protected ViewModelServiceProvider getViewModelServiceProvider() {
1039:                 return viewServiceProvider;
1040:         }
1041:
1042:         @Deprecated
1043:         @Override
1044:         public ViewModelContext getChildContext(final EObject eObject, VElement parent, VView vView,
1045:                 ViewModelService... viewModelServices) {
1046:
1047:                 return getChildContext(eObject, parent, vView, new ArrayOnceViewModelServiceProvider(viewModelServices));
1048:         }
1049:
1050:         @Override
1051:         public ViewModelContext getChildContext(final EObject eObject, VElement parent, VView vView,
1052:                 ViewModelServiceProvider viewModelServiceProvider) {
1053:
1054:                 final Set<ViewModelContext> contexts = childContexts.get(eObject);
1055:                 if (contexts != null) {
1056:                         for (final ViewModelContext context : contexts) {
1057:                                 // TODO change to use bidirectional map
1058:                                 if (childContextUsers.get(context).equals(parent)) {
1059:                                         return context;
1060:                                 }
1061:                         }
1062:                 }
1063:
1064:                 // no context found -> create a new one
1065:
1066:                 ViewModelServiceProvider serviceProvider = getViewModelServiceProvider();
1067:                 if (viewModelServiceProvider != null) {
1068:                         // Compose the client's provided service first as they must override ours
1069:                         serviceProvider = new ViewModelServiceProvider.Composed(
1070:                                 viewModelServiceProvider, serviceProvider);
1071:                 }
1072:
1073:                 final ViewModelContext childContext = new ViewModelContextImpl(vView, eObject, this, parent, serviceProvider);
1074:                 childContext.registerDisposeListener(new ViewModelContextDisposeListener() {
1075:
1076:                         @Override
1077:                         public void contextDisposed(ViewModelContext viewModelContext) {
1078:                                 // If the child context had had its domain object changed along the way,
1079:                                 // then re-mapped it, so be sure to use it here for accuracy (it is still
1080:                                 // accessible while disposal is in progress)
1081:                                 removeChildContext(viewModelContext.getDomainModel(), viewModelContext);
1082:                         }
1083:                 });
1084:                 addChildContext(parent, eObject, childContext);
1085:                 return childContext;
1086:         }
1087:
1088:         @Override
1089:         public ViewModelContext getParentContext() {
1090:                 return parentContext;
1091:         }
1092:
1093:         @Override
1094:         public VElement getParentVElement() {
1095:                 return parentVElement;
1096:         }
1097:
1098:         /**
1099:          * {@inheritDoc}
1100:          *
1101:          * @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#registerDisposeListener(org.eclipse.emf.ecp.view.spi.context.ViewModelContextDisposeListener)
1102:          */
1103:         @Override
1104:         public void registerDisposeListener(ViewModelContextDisposeListener listener) {
1105:                 disposeListeners.add(listener);
1106:         }
1107:
1108:         @Override
1109:         public void registerEMFFormsContextListener(EMFFormsContextListener contextListener) {
1110:                 contextListeners.add(contextListener);
1111:         }
1112:
1113:         @Override
1114:         public void unregisterEMFFormsContextListener(EMFFormsContextListener contextListener) {
1115:                 contextListeners.remove(contextListener);
1116:         }
1117:
1118:         @Override
1119:         public void changeDomainModel(EObject newDomainModel) {
1120:                 if (isDisposed) {
1121:                         throw new IllegalStateException(THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED);
1122:                 }
1123:
1124:                 if (newDomainModel == domainObject) {
1125:                         // Nothing to do
1126:                         return;
1127:                 }
1128:
1129:                 innerDispose();
1130:
1131:                 final EObject oldObject = domainObject;
1132:                 domainObject = newDomainModel;
1133:
1134:                 // if I have a parent, it tracks me now under the wrong key
1135:                 if (parentContext instanceof ViewModelContextImpl) {
1136:                         ((ViewModelContextImpl) parentContext).updateChildContext(oldObject, domainObject, this);
1137:                 }
1138:
1139:                 instantiate();
1140:
1141:                 for (final RootDomainModelChangeListener listener : rootDomainModelChangeListeners) {
1142:                         listener.notifyChange();
1143:                 }
1144:         }
1145:
1146:         /**
1147:          * Re-key a child context under its new domain object.
1148:          *
1149:          * @param oldDomainObject the domain object under which I currently track the child context
1150:          * @param newDomainObject the new domain object of the child context
1151:          * @param childContext the child context to re-key
1152:          */
1153:         private void updateChildContext(EObject oldDomainObject, EObject newDomainObject, ViewModelContext childContext) {
1154:                 final Set<ViewModelContext> oldSibs = childContexts.get(oldDomainObject);
1155:                 if (oldSibs != null) {
1156:                         oldSibs.remove(childContext);
1157:                 }
1158:                 final Set<ViewModelContext> newSibs = childContexts.computeIfAbsent(newDomainObject,
1159:                         __ -> new LinkedHashSet<ViewModelContext>());
1160:                 newSibs.add(childContext);
1161:         }
1162:
1163:         private void innerDispose() {
1164:                 if (resource != null) {
1165:                         resource.getContents().remove(domainObject);
1166:                 }
1167:
1168:                 view.eAdapters().remove(viewModelContentAdapter);
1169:                 if (domainModelContentAdapter != null) {
1170:                         domainObject.eAdapters().remove(domainModelContentAdapter);
1171:                 }
1172:
1173:                 for (final ViewModelService viewService : viewServices) {
1174:                         viewService.dispose();
1175:                 }
1176:                 viewServices.clear();
1177:
1178:                 for (final EMFFormsContextListener contextListener : contextListeners) {
1179:                         contextListener.contextDispose();
1180:                 }
1181:
1182:                 // TODO Child context disposing necessary?
1183:                 final Set<ViewModelContext> toDispose = new LinkedHashSet<ViewModelContext>(childContextUsers.keySet());
1184:                 for (final ViewModelContext vmc : toDispose) {
1185:                         vmc.dispose();
1186:                 }
1187:                 childContextUsers.clear();
1188:                 childContexts.clear();
1189:
1190:                 // Remove me from my parent context, if any
1191:                 if (parentContext instanceof ViewModelContextImpl) {
1192:                         ((ViewModelContextImpl) parentContext).removeChildContext(getDomainModel(), this);
1193:                 }
1194:
1195:                 releaseOSGiServices();
1196:                 serviceMap.clear();
1197:
1198:                 final Bundle bundle = FrameworkUtil.getBundle(getClass());
1199:                 if (bundle != null) {
1200:                         final BundleContext bundleContext = bundle.getBundleContext();
1201:                         bundleContext.removeServiceListener(serviceListener);
1202:                         serviceListener = null;
1203:                 }
1204:         }
1205:
1206:         /**
1207:          * {@inheritDoc}
1208:          *
1209:          * @see org.eclipse.emfforms.spi.core.services.view.EMFFormsViewContext#registerRootDomainModelChangeListener(org.eclipse.emfforms.spi.core.services.view.RootDomainModelChangeListener)
1210:          */
1211:         @Override
1212:         public void registerRootDomainModelChangeListener(RootDomainModelChangeListener rootDomainModelChangeListener) {
1213:                 if (isDisposed) {
1214:                         throw new IllegalStateException(THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED);
1215:                 }
1216:                 if (rootDomainModelChangeListener == null) {
1217:                         throw new IllegalArgumentException(ROOT_DOMAIN_MODEL_CHANGE_LISTENER_MUST_NOT_BE_NULL);
1218:                 }
1219:                 rootDomainModelChangeListeners.add(rootDomainModelChangeListener);
1220:         }
1221:
1222:         /**
1223:          * {@inheritDoc}
1224:          *
1225:          * @see org.eclipse.emfforms.spi.core.services.view.EMFFormsViewContext#unregisterRootDomainModelChangeListener(org.eclipse.emfforms.spi.core.services.view.RootDomainModelChangeListener)
1226:          */
1227:         @Override
1228:         public void unregisterRootDomainModelChangeListener(RootDomainModelChangeListener rootDomainModelChangeListener) {
1229:                 rootDomainModelChangeListeners.remove(rootDomainModelChangeListener);
1230:         }
1231:
1232:         /**
1233:          * A collection of listeners grouped by class. Work-around for the absence of priority
1234:          * support (<a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=460158">bug 460158</a>).
1235:          *
1236:          * @author Christian W. Damus
1237:          *
1238:          * @param <T> the type of listener stored in the list
1239:          * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=460158">bug 460158</a>
1240:          */
1241:         private static final class GroupedListenerList<T> implements Iterable<T> {
1242:                 private final Set<T> listeners = new ConcurrentSkipListSet<T>(comparator());
1243:
1244:                 private final AtomicInteger nextGroup = new AtomicInteger();
1245:                 private final ConcurrentMap<Class<?>, Integer> groups = new ConcurrentHashMap<Class<?>, Integer>();
1246:
1247:                 private final Class<?> importantGroup;
1248:
1249:                 /**
1250:                  * Initializes with an important group that must be first in the iteration order.
1251:                  */
1252:                 GroupedListenerList(Class<?> importantGroup) {
1253:                         super();
1254:
1255:                         this.importantGroup = importantGroup;
1256:                         seedImportantGroup();
1257:                 }
1258:
1259:                 /**
1260:                  * Sort elements by group first and then arbitrarily within each group.
1261:                  *
1262:                  * @return a grouping comparator
1263:                  */
1264:                 private Comparator<T> comparator() {
1265:                         return new Comparator<T>() {
1266:                                 @Override
1267:                                 public int compare(T o1, T o2) {
1268:                                         if (o1 == o2) {
1269:                                                 return 0;
1270:                                         }
1271:
1272:                                         final int group1 = getGroup(o1);
1273:                                         final int group2 = getGroup(o2);
1274:                                         int result = group1 - group2;
1275:
1276:                                         if (result == 0) {
1277:                                                 // Same group. Arbitrary ordering by address
1278:                                                 result = System.identityHashCode(o1) - System.identityHashCode(o2);
1279:                                         }
1280:
1281:                                         return result;
1282:                                 }
1283:                         };
1284:                 }
1285:
1286:                 private void seedImportantGroup() {
1287:                         if (importantGroup != null) {
1288:                                 // Seed the map with a lowest-order group
1289:                                 groups.put(importantGroup, nextGroup.getAndIncrement());
1290:                         }
1291:                 }
1292:
1293:                 private int getGroup(T listener) {
1294:                         final Class<?> groupKey = importantGroup != null && importantGroup.isInstance(listener)
1295:                                 ? importantGroup
1296:                                 : listener.getClass();
1297:
1298:                         Integer result = groups.get(groupKey);
1299:                         if (result == null) {
1300:                                 result = nextGroup.getAndIncrement();
1301:                                 final Integer collision = groups.putIfAbsent(groupKey, result);
1302:                                 if (collision != null) {
1303:                                         result = collision;
1304:                                 }
1305:                         }
1306:
1307:                         return result;
1308:                 }
1309:
1310:                 void add(T listener) {
1311:                         listeners.add(listener);
1312:                         // need to add group as soon as listener added to correctly calculate order
1313:                         getGroup(listener);
1314:                 }
1315:
1316:                 void remove(T listener) {
1317:                         listeners.remove(listener);
1318:                 }
1319:
1320:                 void clear() {
1321:                         listeners.clear();
1322:                         groups.clear();
1323:                         seedImportantGroup();
1324:                 }
1325:
1326:                 @Override
1327:                 public Iterator<T> iterator() {
1328:                         return listeners.iterator();
1329:                 }
1330:         }
1331:
1332:         @Override
1333:         public void pause() {
1334:                 pause = true;
1335:         }
1336:
1337:         @Override
1338:         public void reactivate() {
1339:                 pause = false;
1340:         }
1341: }