/******************************************************************************* * Copyright (c) 2008-2011 Chair for Applied Software Engineering, * Technische Universitaet Muenchen. * 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: * naughton * emueller * koegel ******************************************************************************/ package org.eclipse.emf.emfstore.internal.common.model.impl; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.ResourceImpl; import org.eclipse.emf.ecore.xmi.XMIResource; import org.eclipse.emf.emfstore.common.ESSafeRunnable; import org.eclipse.emf.emfstore.common.ESSafeRunner; import org.eclipse.emf.emfstore.internal.common.model.IdEObjectCollection; import org.eclipse.emf.emfstore.internal.common.model.NotifiableIdEObjectCollection; import org.eclipse.emf.emfstore.internal.common.model.util.EObjectChangeNotifier; import org.eclipse.emf.emfstore.internal.common.model.util.IdEObjectCollectionChangeObserver; import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil; /** * A collection that is able of maintaining a list of {@link IdEObjectCollectionChangeObserver}. * * @author koegel * @author naughton * @author emueller */ public abstract class NotifiableIdEObjectCollectionImpl extends IdEObjectCollectionImpl implements NotifiableIdEObjectCollection { // observer related attributes private boolean isNotifiying; private List<IdEObjectCollectionChangeObserver> observers; private Set<IdEObjectCollectionChangeObserver> exceptionThrowingObservers; private Set<IdEObjectCollectionChangeObserver> observersToRemove; private Set<IdEObjectCollectionChangeObserver> undetachableObservers; private Set<IdEObjectCollectionChangeObserver> observersToAttach; private EObjectChangeNotifier changeNotifier; /** * Constructor. */ protected NotifiableIdEObjectCollectionImpl() { super(); initObservers(); } /** * Constructor. Adds the contents of the given {@link XMIResource} as model * elements to the collection. If the {@link XMIResource} also has XMI IDs * assigned to the {@link EObject}s it contains, they will be used for * creating the model element IDs within the project, if not, the model element IDs will get created on * the fly. * * An {@link EObjectChangeNotifier} is also attached to the given {@link XMIResource}. * * @param xmiResource * a {@link XMIResource} * @throws IOException * if the given {@link XMIResource} could not be loaded */ public NotifiableIdEObjectCollectionImpl(XMIResource xmiResource) throws IOException { super(xmiResource); initObservers(); changeNotifier = new EObjectChangeNotifier(this, xmiResource); } /** * Initializes all observers. */ private void initObservers() { observers = new ArrayList<IdEObjectCollectionChangeObserver>(); observersToRemove = new LinkedHashSet<IdEObjectCollectionChangeObserver>(); exceptionThrowingObservers = new LinkedHashSet<IdEObjectCollectionChangeObserver>(); undetachableObservers = new LinkedHashSet<IdEObjectCollectionChangeObserver>(); observersToAttach = new LinkedHashSet<IdEObjectCollectionChangeObserver>(); } /** * * {@inheritDoc} * * @see org.eclipse.emf.emfstore.internal.common.model.NotifiableIdEObjectCollection#modelElementAdded(org.eclipse.emf.emfstore.internal.common.model.IdEObjectCollection, * org.eclipse.emf.ecore.EObject) */ public void modelElementAdded(final IdEObjectCollection collection, final EObject eObject) { addToResource(eObject); addModelElementAndChildrenToCache(eObject); final EObjectChangeObserverNotificationCommand command = new EObjectChangeObserverNotificationCommand() { public void run(IdEObjectCollectionChangeObserver projectChangeObserver) { projectChangeObserver.modelElementAdded(collection, eObject); } }; notifyIdEObjectCollectionChangeObservers(command); } private void addToResource(EObject eObject) { final Resource resource = eObject.eResource(); if (resource != null && resource != eResource()) { resource.getContents().remove(eObject); } } /** * Add a new cut element. * * @param eObject The new cut element. */ public abstract void addCutElement(EObject eObject); /** * Notifies all collection change observers. * * @param command * the notification command */ protected synchronized void notifyIdEObjectCollectionChangeObservers( final EObjectChangeObserverNotificationCommand command) { isNotifiying = true; for (final IdEObjectCollectionChangeObserver changeObserver : observers) { final ESSafeRunnable code = new ESSafeRunnable() { public void run() { command.run(changeObserver); } public void handleException(Throwable exception) { if (exceptionThrowingObservers.contains(changeObserver)) { if (!undetachableObservers.contains(changeObserver)) { observersToRemove.add(changeObserver); ModelUtil.logException( Messages.NotifiableIdEObjectCollectionImpl_Exception_Detach + changeObserver.getClass().getName(), exception); } else { ModelUtil.logException( Messages.NotifiableIdEObjectCollectionImpl_Exception_NoDetach + changeObserver.getClass().getName(), exception); } } else { exceptionThrowingObservers.add(changeObserver); ModelUtil.logWarning(Messages.NotifiableIdEObjectCollectionImpl_Exception + changeObserver.getClass().getName(), exception); } } }; ESSafeRunner.run(code); } isNotifiying = false; for (final IdEObjectCollectionChangeObserver observer : observersToRemove) { removeIdEObjectCollectionChangeObserver(observer); } for (final IdEObjectCollectionChangeObserver observer : observersToAttach) { addIdEObjectCollectionChangeObserver(observer); } observersToRemove.clear(); } @Override public void initMapping() { if (isCacheInitialized()) { return; } super.initMapping(); if (changeNotifier == null) { changeNotifier = new EObjectChangeNotifier(this, this); } } /** * * {@inheritDoc} * * @see org.eclipse.emf.emfstore.internal.common.model.impl.IdEObjectCollectionImpl#initMapping(java.util.Map, * java.util.Map) */ @Override public void initMapping(Map<EObject, String> eObjectToIdMap, Map<String, EObject> idToEObjectMap) { super.initMapping(eObjectToIdMap, idToEObjectMap); if (changeNotifier == null) { changeNotifier = new EObjectChangeNotifier(this, this); } } /** * * {@inheritDoc} * * @see org.eclipse.emf.emfstore.internal.common.model.util.IdEObjectCollectionChangeObserver#notify(org.eclipse.emf.common.notify.Notification, * org.eclipse.emf.emfstore.internal.common.model.IdEObjectCollection, org.eclipse.emf.ecore.EObject) */ public void notify(final Notification notification, final IdEObjectCollection project, final EObject modelElement) { final EObjectChangeObserverNotificationCommand command = new EObjectChangeObserverNotificationCommand() { public void run(IdEObjectCollectionChangeObserver projectChangeObserver) { projectChangeObserver.notify(notification, project, modelElement); } }; final Resource resource = modelElement.eResource(); if (resource != null && resource instanceof ResourceImpl) { final ResourceImpl resourceImpl = (ResourceImpl) resource; if (resourceImpl.isLoading()) { return; } } notifyIdEObjectCollectionChangeObservers(command); } /** * * {@inheritDoc} * * @see org.eclipse.emf.emfstore.internal.common.model.NotifiableIdEObjectCollection#addIdEObjectCollectionChangeObserver(org.eclipse.emf.emfstore.internal.common.model.util.IdEObjectCollectionChangeObserver) */ public synchronized void addIdEObjectCollectionChangeObserver( IdEObjectCollectionChangeObserver eObjectChangeObserver) { initMapping(); if (isNotifiying) { observersToAttach.add(eObjectChangeObserver); return; } observers.add(eObjectChangeObserver); } /** * * {@inheritDoc} * * @see org.eclipse.emf.emfstore.internal.common.model.NotifiableIdEObjectCollection#removeIdEObjectCollectionChangeObserver(org.eclipse.emf.emfstore.internal.common.model.util.IdEObjectCollectionChangeObserver) */ public synchronized void removeIdEObjectCollectionChangeObserver( IdEObjectCollectionChangeObserver projectChangeObserver) { if (isNotifiying) { observersToRemove.add(projectChangeObserver); return; } observers.remove(projectChangeObserver); exceptionThrowingObservers.remove(projectChangeObserver); undetachableObservers.remove(projectChangeObserver); } /** * Make a project change observer undetachable. * * @param observer * the observer */ public void setUndetachable(IdEObjectCollectionChangeObserver observer) { undetachableObservers.add(observer); } /** * * {@inheritDoc} * * @see org.eclipse.emf.emfstore.internal.common.model.NotifiableIdEObjectCollection#modelElementRemoved(org.eclipse.emf.emfstore.internal.common.model.IdEObjectCollection, * org.eclipse.emf.ecore.EObject) */ public void modelElementRemoved(final IdEObjectCollection projectImpl, final EObject modelElement) { removeModelElementAndChildrenFromCache(modelElement); final EObjectChangeObserverNotificationCommand command = new EObjectChangeObserverNotificationCommand() { public void run(IdEObjectCollectionChangeObserver projectChangeObserver) { projectChangeObserver.modelElementRemoved(projectImpl, modelElement); } }; notifyIdEObjectCollectionChangeObservers(command); } /** * Copies the current collection. * * @param <T> * the actual collection type to be copied * * @return the copied collection */ @Override public <T extends IdEObjectCollection> T copy() { if (changeNotifier != null) { changeNotifier.disableNotifications(true); } final T result = super.copy(); if (changeNotifier != null) { changeNotifier.disableNotifications(false); } return result; } /** * * {@inheritDoc} * * @see org.eclipse.emf.emfstore.internal.common.model.util.IdEObjectCollectionChangeObserver#collectionDeleted(org.eclipse.emf.emfstore.internal.common.model.IdEObjectCollection) */ public void collectionDeleted(IdEObjectCollection collection) { } /** * {@inheritDoc} * * @see org.eclipse.emf.emfstore.internal.common.model.NotifiableIdEObjectCollection#getChangeNotifier() */ public EObjectChangeNotifier getChangeNotifier() { return changeNotifier; } }