blob: 55fbbaa48517d0e530c1b89d8c236f8a0f60ded2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2016 EclipseSource Muenchen GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eugen Neufeld - initial API and implementation
******************************************************************************/
package org.eclipse.emf.ecp.view.internal.context;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecp.common.spi.UniqueSetting;
import org.eclipse.emf.ecp.view.spi.context.EMFFormsLegacyServicesManager;
import org.eclipse.emf.ecp.view.spi.context.GlobalViewModelService;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContextDisposeListener;
import org.eclipse.emf.ecp.view.spi.context.ViewModelService;
import org.eclipse.emf.ecp.view.spi.context.ViewModelServiceNotAvailableReport;
import org.eclipse.emf.ecp.view.spi.model.ModelChangeAddRemoveListener;
import org.eclipse.emf.ecp.view.spi.model.ModelChangeListener;
import org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification;
import org.eclipse.emf.ecp.view.spi.model.VControl;
import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
import org.eclipse.emf.ecp.view.spi.model.VElement;
import org.eclipse.emf.ecp.view.spi.model.VView;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emfforms.common.Optional;
import org.eclipse.emfforms.spi.common.report.AbstractReport;
import org.eclipse.emfforms.spi.common.report.ReportService;
import org.eclipse.emfforms.spi.core.services.controlmapper.EMFFormsSettingToControlMapper;
import org.eclipse.emfforms.spi.core.services.domainexpander.EMFFormsDomainExpander;
import org.eclipse.emfforms.spi.core.services.domainexpander.EMFFormsExpandingFailedException;
import org.eclipse.emfforms.spi.core.services.view.EMFFormsContextListener;
import org.eclipse.emfforms.spi.core.services.view.EMFFormsViewServiceManager;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
/**
* The Class ViewModelContextImpl.
*
* @author Eugen Neufeld
*/
public class ViewModelContextImpl implements ViewModelContext {
private static final String MODEL_CHANGE_LISTENER_MUST_NOT_BE_NULL = "ModelChangeAddRemoveListener must not be null."; //$NON-NLS-1$
private static final String THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED = "The ViewModelContext was already disposed."; //$NON-NLS-1$
/** The view. */
private final VElement view;
/** The domain object. */
private final EObject domainObject;
/** The view model change listener. Needs to be thread safe. */
private final List<ModelChangeListener> viewModelChangeListener = new CopyOnWriteArrayList<ModelChangeListener>();
/** The domain model change listener. Needs to be thread safe. */
private final List<ModelChangeListener> domainModelChangeListener = new CopyOnWriteArrayList<ModelChangeListener>();
/** The domain model content adapter. */
private EContentAdapter domainModelContentAdapter;
/** The view model content adapter. */
private EContentAdapter viewModelContentAdapter;
private final Set<EMFFormsContextListener> contextListeners = new CopyOnWriteArraySet<EMFFormsContextListener>();
/** The view services. */
private final SortedSet<ViewModelService> viewServices = new TreeSet<ViewModelService>(
new Comparator<ViewModelService>() {
@Override
public int compare(ViewModelService arg0, ViewModelService arg1) {
if (arg0.getPriority() == arg1.getPriority()) {
/* compare would return 0, meaning the services are identical -> 1 service would get lost */
return arg0.getClass().getName().compareTo(arg1.getClass().getName());
}
return arg0.getPriority() - arg1.getPriority();
}
});
/**
* The disposed state.
*/
private boolean isDisposed;
/**
* The context map.
*/
private final Map<String, Object> keyObjectMap = new LinkedHashMap<String, Object>();
/**
* Whether the context is being disposed.
*/
private boolean isDisposing;
private Resource resource;
private final Map<EObject, Set<ViewModelContext>> childContexts = new LinkedHashMap<EObject, Set<ViewModelContext>>();
private final Map<ViewModelContext, VElement> childContextUsers = new LinkedHashMap<ViewModelContext, VElement>();
private ViewModelContext parentContext;
private final Set<ViewModelContextDisposeListener> disposeListeners = new LinkedHashSet<ViewModelContextDisposeListener>();
// TODO replace with eclipse context?!
private final Map<Class<?>, Object> serviceMap = new LinkedHashMap<Class<?>, Object>();
private final Map<ServiceReference<?>, Class<?>> usedOSGiServices = new LinkedHashMap<ServiceReference<?>, Class<?>>();
private ServiceListener serviceListener;
/**
* Instantiates a new view model context impl.
*
* @param view the view
* @param domainObject the domain object
*/
public ViewModelContextImpl(VElement view, EObject domainObject) {
this.view = view;
this.domainObject = domainObject;
instantiate();
}
/**
* Instantiates a new view model context impl.
*
* @param view the view
* @param domainObject the domain object
* @param parent The parent {@link ViewModelContext}
* @param parentVElement the parent {@link VElement}
*/
public ViewModelContextImpl(VElement view, EObject domainObject, ViewModelContext parent,
VElement parentVElement) {
this.view = view;
this.domainObject = domainObject;
parentContext = parent;
instantiate();
}
/**
* Instantiates a new view model context impl.
*
* @param view the view
* @param domainObject the domain object
* @param modelServices an array of services to use in the {@link ViewModelContext}
*/
public ViewModelContextImpl(VElement view, EObject domainObject, ViewModelService... modelServices) {
this.view = view;
this.domainObject = domainObject;
for (final ViewModelService vms : modelServices) {
viewServices.add(vms);
}
instantiate();
}
/**
* Instantiates a new view model context impl with a parent context.
*
* @param view the view
* @param domainObject the domain object
* @param modelServices an array of services to use in the {@link ViewModelContext}
* @param parent The parent {@link ViewModelContext}
* @param parentVElement The parent {@link VElement}
*/
public ViewModelContextImpl(VElement view, EObject domainObject, ViewModelContext parent,
VElement parentVElement,
ViewModelService... modelServices) {
this.view = view;
this.domainObject = domainObject;
parentContext = parent;
for (final ViewModelService vms : modelServices) {
viewServices.add(vms);
}
instantiate();
}
/**
* Resolve all domain model references for a given resolvable and a given domain model root.
*
* @param resolvable The EObject to resolve all {@link VDomainModelReference domain model references} of.
* @param domainModelRoot the domain model used for the resolving.
* @throws EMFFormsExpandingFailedException If the domain expansion fails.
*/
private void resolveDomainReferences(EObject resolvable, EObject domainModelRoot) {
// Get domain expander service
final EMFFormsDomainExpander domainExpander = getService(EMFFormsDomainExpander.class);
if (domainExpander == null) {
return;
}
expandAndInitDMR(domainModelRoot, domainExpander, resolvable);
// Iterate over all domain model references of the given EObject.
final TreeIterator<EObject> eAllContents = resolvable.eAllContents();
while (eAllContents.hasNext()) {
final EObject eObject = eAllContents.next();
expandAndInitDMR(domainModelRoot, domainExpander, eObject);
}
}
private void expandAndInitDMR(EObject domainModelRoot, final EMFFormsDomainExpander domainExpander,
final EObject eObject) {
if (VDomainModelReference.class.isInstance(eObject)
&& !VDomainModelReference.class.isInstance(eObject.eContainer())) {
final VDomainModelReference domainModelReference = VDomainModelReference.class.cast(eObject);
// FIXME remove
domainModelReference.init(domainModelRoot);
try {
domainExpander.prepareDomainObject(domainModelReference, domainModelRoot);
} catch (final EMFFormsExpandingFailedException ex) {
Activator.getInstance().getReportService().report(new AbstractReport(ex));
}
}
}
/**
* Instantiate.
*/
private void instantiate() {
addResourceIfNecessary();
resolveDomainReferences(getViewModel(), getDomainModel());
loadImmediateServices();
viewModelContentAdapter = new ViewModelContentAdapter();
if (parentContext == null) {
domainModelContentAdapter = new DomainModelContentAdapter();
domainObject.eAdapters().add(domainModelContentAdapter);
}
view.eAdapters().add(viewModelContentAdapter);
for (final ViewModelService viewService : viewServices) {
viewService.instantiate(this);
}
for (final EMFFormsContextListener contextListener : contextListeners) {
contextListener.contextInitialised();
}
}
private void loadImmediateServices() {
final Bundle bundle = FrameworkUtil.getBundle(getClass());
if (bundle != null) {
final BundleContext bundleContext = bundle.getBundleContext();
final ServiceReference<EMFFormsLegacyServicesManager> serviceReferenceLegacy = bundleContext
.getServiceReference(EMFFormsLegacyServicesManager.class);
if (serviceReferenceLegacy != null) {
final EMFFormsLegacyServicesManager legacyServicesFactory = bundleContext
.getService(serviceReferenceLegacy);
legacyServicesFactory.instantiate();
bundleContext.ungetService(serviceReferenceLegacy);
}
servicesManager = getService(EMFFormsViewServiceManager.class);
for (final Class<?> localImmediateService : servicesManager.getAllLocalImmediateServiceTypes()) {
final Optional<?> service = servicesManager.createLocalImmediateService(localImmediateService, this);
if (!service.isPresent()) {
// error case?
continue;
}
serviceMap.put(localImmediateService, service.get());
}
if (parentContext == null) {
for (final Class<?> globalImmediateService : servicesManager.getAllGlobalImmediateServiceTypes()) {
final Optional<?> service = servicesManager.createGlobalImmediateService(globalImmediateService,
this);
if (!service.isPresent()) {
// error case?
continue;
}
final Object serviceObject = service.get();
serviceMap.put(globalImmediateService, serviceObject);
}
}
serviceListener = new ServiceListener() {
@Override
public void serviceChanged(ServiceEvent event) {
if (event.getType() == ServiceEvent.UNREGISTERING &&
usedOSGiServices.containsKey(event.getServiceReference())) {
final Class<?> remove = usedOSGiServices.remove(event.getServiceReference());
serviceMap.remove(remove);
}
}
};
bundleContext.addServiceListener(serviceListener);
}
}
private void addResourceIfNecessary() {
if (domainObject.eResource() != null) {
return;
}
final EObject rootObject = EcoreUtil.getRootContainer(domainObject);
final ResourceSet rs = new ResourceSetImpl();
final ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(new AdapterFactory[] {
new ReflectiveItemProviderAdapterFactory(),
new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE) });
final AdapterFactoryEditingDomain domain = new AdapterFactoryEditingDomain(
adapterFactory,
new BasicCommandStack(), rs);
rs.eAdapters().add(new AdapterFactoryEditingDomain.EditingDomainProvider(domain));
resource = rs.createResource(URI.createURI("VIRTAUAL_URI")); //$NON-NLS-1$
if (resource != null) {
resource.getContents().add(rootObject);
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getControlsFor(org.eclipse.emf.ecore.EStructuralFeature.Setting)
* @deprecated
*/
@Deprecated
@Override
public Set<VControl> getControlsFor(Setting setting) {
return getService(EMFFormsSettingToControlMapper.class).getControlsFor(setting);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getControlsFor(org.eclipse.emf.ecp.common.spi.UniqueSetting)
* @deprecated
*/
@Deprecated
@Override
public Set<VElement> getControlsFor(UniqueSetting setting) {
return getService(EMFFormsSettingToControlMapper.class).getControlsFor(setting);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getViewModel()
*/
@Override
public VElement getViewModel() {
if (isDisposed) {
throw new IllegalStateException(THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED);
}
return view;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getDomainModel()
*/
@Override
public EObject getDomainModel() {
if (isDisposed) {
throw new IllegalStateException(THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED);
}
return domainObject;
}
/**
* Dispose.
*/
@Override
public void dispose() {
if (isDisposed) {
return;
}
isDisposing = true;
for (final ViewModelContextDisposeListener listener : disposeListeners) {
listener.contextDisposed(this);
}
if (resource != null) {
resource.getContents().remove(domainObject);
}
view.eAdapters().remove(viewModelContentAdapter);
domainObject.eAdapters().remove(domainModelContentAdapter);
viewModelChangeListener.clear();
domainModelChangeListener.clear();
for (final ViewModelService viewService : viewServices) {
viewService.dispose();
}
viewServices.clear();
for (final EMFFormsContextListener contextListener : contextListeners) {
contextListener.contextDispose();
}
final Set<ViewModelContext> toDispose = new LinkedHashSet<ViewModelContext>(childContextUsers.keySet());
for (final ViewModelContext vmc : toDispose) {
vmc.dispose();
}
childContextUsers.clear();
childContexts.clear();
releaseOSGiServices();
serviceMap.clear();
final Bundle bundle = FrameworkUtil.getBundle(getClass());
if (bundle != null) {
final BundleContext bundleContext = bundle.getBundleContext();
bundleContext.removeServiceListener(serviceListener);
serviceListener = null;
}
isDisposing = false;
isDisposed = true;
}
private void releaseOSGiServices() {
final Bundle bundle = FrameworkUtil.getBundle(getClass());
if (bundle != null) {
final BundleContext bundleContext = bundle.getBundleContext();
for (final ServiceReference<?> serviceReference : usedOSGiServices.keySet()) {
bundleContext.ungetService(serviceReference);
}
}
usedOSGiServices.clear();
}
@Override
public void registerViewChangeListener(ModelChangeListener modelChangeListener) {
if (isDisposed) {
throw new IllegalStateException(THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED);
}
if (modelChangeListener == null) {
throw new IllegalArgumentException(MODEL_CHANGE_LISTENER_MUST_NOT_BE_NULL);
}
viewModelChangeListener.add(modelChangeListener);
}
@Override
public void unregisterViewChangeListener(ModelChangeListener modelChangeListener) {
// if (isDisposed) {
// throw new IllegalStateException("The ViewModelContext was already disposed.");
// }
viewModelChangeListener.remove(modelChangeListener);
}
@Override
public void registerDomainChangeListener(ModelChangeListener modelChangeListener) {
if (isDisposed) {
throw new IllegalStateException(THE_VIEW_MODEL_CONTEXT_WAS_ALREADY_DISPOSED);
}
if (modelChangeListener == null) {
throw new IllegalArgumentException(MODEL_CHANGE_LISTENER_MUST_NOT_BE_NULL);
}
if (parentContext == null) {
// TODO performance
// needed to make sure, all data operations are done before any validation etc provided by services happens
if (VDomainModelReference.class.isInstance(modelChangeListener)) {
domainModelChangeListener.add(0, modelChangeListener);
} else {
// TODO hack for https://bugs.eclipse.org/bugs/show_bug.cgi?id=460158
int positionToInsert = -1;
for (int i = 0; i < domainModelChangeListener.size(); i++) {
final ModelChangeListener listener = domainModelChangeListener.get(i);
if (modelChangeListener.getClass().isInstance(listener)) {
positionToInsert = i;
break;
}
}
if (positionToInsert == -1) {
domainModelChangeListener.add(modelChangeListener);
} else {
domainModelChangeListener.add(positionToInsert, modelChangeListener);
}
}
} else {
parentContext.registerDomainChangeListener(modelChangeListener);
}
}
@Override
public void unregisterDomainChangeListener(ModelChangeListener modelChangeListener) {
// if (isDisposed) {
// throw new IllegalStateException("The ViewModelContext was already disposed.");
// }
if (parentContext == null) {
domainModelChangeListener.remove(modelChangeListener);
} else {
parentContext.unregisterDomainChangeListener(modelChangeListener);
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#hasService(java.lang.Class)
*/
@Override
public <T> boolean hasService(Class<T> serviceType) {
for (final ViewModelService service : viewServices) {
if (serviceType.isInstance(service)) {
return true;
}
}
if (parentContext != null) {
return parentContext.hasService(serviceType);
}
return false;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getService(java.lang.Class)
*/
@Override
@SuppressWarnings("unchecked")
public <T> T getService(Class<T> serviceType) {
// legacy stuff
for (final ViewModelService service : viewServices) {
if (serviceType.isInstance(service)) {
return (T) service;
}
}
if (serviceMap.containsKey(serviceType)) {
return (T) serviceMap.get(serviceType);
} else if (servicesManager != null) {
final Optional<T> lazyService = servicesManager.createLocalLazyService(serviceType, this);
if (lazyService.isPresent()) {
final T t = lazyService.get();
serviceMap.put(serviceType, t);
return t;
}
}
if (servicesManager != null && parentContext == null) {
final Optional<T> lazyService = servicesManager.createGlobalLazyService(serviceType, this);
if (lazyService.isPresent()) {
final T t = lazyService.get();
serviceMap.put(serviceType, t);
return t;
}
}
if (parentContext != null) {
return parentContext.getService(serviceType);
}
final Bundle bundle = FrameworkUtil.getBundle(getClass());
if (bundle != null) {
final BundleContext bundleContext = bundle.getBundleContext();
final ServiceReference<T> serviceReference = bundleContext.getServiceReference(serviceType);
if (serviceReference != null) {
usedOSGiServices.put(serviceReference, serviceType);
final T service = bundleContext.getService(serviceReference);
serviceMap.put(serviceType, service);
return service;
}
}
report(new ViewModelServiceNotAvailableReport(serviceType));
return null;
}
private void report(AbstractReport report) {
final Activator activator = Activator.getInstance();
if (activator != null) {
final ReportService reportService = activator.getReportService();
if (reportService != null) {
reportService.report(report);
}
}
}
/**
* The content adapter for the view model.
*/
private class ViewModelContentAdapter extends EContentAdapter {
@Override
public void notifyChanged(Notification notification) {
super.notifyChanged(notification);
// do not notify while being disposed
if (isDisposing) {
return;
}
if (isDisposed) {
return;
}
if (notification.isTouch()) {
return;
}
final ModelChangeNotification modelChangeNotification = new ModelChangeNotification(notification);
for (final ModelChangeListener modelChangeListener : viewModelChangeListener) {
modelChangeListener.notifyChange(modelChangeNotification);
}
}
@Override
protected void addAdapter(Notifier notifier) {
super.addAdapter(notifier);
// do not notify while being disposed
if (isDisposing || isDisposed) {
return;
}
for (final ModelChangeListener modelChangeListener : viewModelChangeListener) {
if (ModelChangeAddRemoveListener.class.isInstance(modelChangeListener)) {
ModelChangeAddRemoveListener.class.cast(modelChangeListener).notifyAdd(notifier);
}
}
}
@Override
protected void removeAdapter(Notifier notifier) {
super.removeAdapter(notifier);
// do not notify while being disposed
if (isDisposing) {
return;
}
if (VElement.class.isInstance(notifier)) {
VElement.class.cast(notifier).setDiagnostic(null);
}
for (final ModelChangeListener modelChangeListener : viewModelChangeListener) {
if (ModelChangeAddRemoveListener.class.isInstance(modelChangeListener)) {
ModelChangeAddRemoveListener.class.cast(modelChangeListener).notifyRemove(notifier);
}
}
}
}
/**
* The content adapter for the domain model.
*/
private class DomainModelContentAdapter extends EContentAdapter {
@Override
public void notifyChanged(Notification notification) {
super.notifyChanged(notification);
// do not notify while being disposed
if (isDisposing) {
return;
}
final ModelChangeNotification modelChangeNotification = new ModelChangeNotification(notification);
for (final ModelChangeListener modelChangeListener : domainModelChangeListener) {
modelChangeListener.notifyChange(modelChangeNotification);
}
}
@Override
protected void addAdapter(Notifier notifier) {
super.addAdapter(notifier);
// do not notify while being disposed
if (isDisposing) {
return;
}
for (final ModelChangeListener modelChangeListener : domainModelChangeListener) {
if (ModelChangeAddRemoveListener.class.isInstance(modelChangeListener)) {
ModelChangeAddRemoveListener.class.cast(modelChangeListener).notifyAdd(notifier);
}
}
}
@Override
protected void removeAdapter(Notifier notifier) {
super.removeAdapter(notifier);
// do not notify while being disposed
if (isDisposing) {
return;
}
for (final ModelChangeListener modelChangeListener : domainModelChangeListener) {
if (ModelChangeAddRemoveListener.class.isInstance(modelChangeListener)) {
ModelChangeAddRemoveListener.class.cast(modelChangeListener).notifyRemove(notifier);
}
}
}
}
private final Set<Object> users = new LinkedHashSet<Object>();
private EMFFormsViewServiceManager servicesManager;
/**
* Inner method for registering context users (not {@link ViewModelService}).
*
* @param user the user of the context
*/
@Override
public void addContextUser(Object user) {
users.add(user);
}
/**
* Inner method for unregistering the context user.
*
* @param user the user of the context
*/
@Override
public void removeContextUser(Object user) {
users.remove(user);
// Every renderer is registered here, as it needs to know when the view model changes (rules, validations, etc).
// If no listener is left, we can dispose the context
// if (users.isEmpty() || users.size() == 1 && parentContext != null && users.contains(parentContext)) {
if (users.isEmpty()) {
dispose();
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#getContextValue(java.lang.String)
*/
@Override
public Object getContextValue(String key) {
if (parentContext != null) {
return parentContext.getContextValue(key);
}
return keyObjectMap.get(key);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#putContextValue(java.lang.String, java.lang.Object)
*/
@Override
public void putContextValue(String key, Object value) {
if (parentContext != null) {
parentContext.putContextValue(key, value);
return;
}
keyObjectMap.put(key, value);
}
private void addChildContext(VElement vElement, EObject eObject, ViewModelContext childContext) {
if (!childContexts.containsKey(eObject)) {
childContexts.put(eObject, new LinkedHashSet<ViewModelContext>());
}
childContexts.get(eObject).add(childContext);
childContextUsers.put(childContext, vElement);
for (final EMFFormsContextListener contextListener : contextListeners) {
contextListener.childContextAdded(vElement, childContext);
}
// notify all global View model services
for (final ViewModelService viewModelService : viewServices) {
if (GlobalViewModelService.class.isInstance(viewModelService)) {
GlobalViewModelService.class.cast(viewModelService).childViewModelContextAdded(childContext);
}
}
}
private void removeChildContext(EObject eObject) {
final Set<ViewModelContext> removedContexts = childContexts.remove(eObject);
if (removedContexts != null) {
for (final ViewModelContext removedContext : removedContexts) {
childContextUsers.remove(removedContext);
}
}
}
@Override
public ViewModelContext getChildContext(final EObject eObject, VElement parent, VView vView,
ViewModelService... viewModelServices) {
final Set<ViewModelContext> contexts = childContexts.get(eObject);
if (contexts != null) {
for (final ViewModelContext context : contexts) {
// TODO change to use bidirectional map
if (childContextUsers.get(context).equals(parent)) {
return context;
}
}
}
// no context found -> create a new one
final ViewModelContext childContext = new ViewModelContextImpl(vView, eObject, this, parent, viewModelServices);
childContext.registerDisposeListener(new ViewModelContextDisposeListener() {
@Override
public void contextDisposed(ViewModelContext viewModelContext) {
removeChildContext(eObject);
for (final EMFFormsContextListener contextListener : contextListeners) {
contextListener.childContextDisposed(childContext);
}
}
});
addChildContext(parent, eObject, childContext);
return childContext;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.context.ViewModelContext#registerDisposeListener(org.eclipse.emf.ecp.view.spi.context.ViewModelContextDisposeListener)
*/
@Override
public void registerDisposeListener(ViewModelContextDisposeListener listener) {
disposeListeners.add(listener);
}
@Override
public void registerEMFFormsContextListener(EMFFormsContextListener contextListener) {
contextListeners.add(contextListener);
}
@Override
public void unregisterEMFFormsContextListener(EMFFormsContextListener contextListener) {
contextListeners.remove(contextListener);
}
}