/******************************************************************************* * 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: ******************************************************************************/ package org.eclipse.emf.emfstore.common.model.impl; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; 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.model.IdEObjectCollection; import org.eclipse.emf.emfstore.common.model.ModelElementId; import org.eclipse.emf.emfstore.common.model.NotifiableIdEObjectCollection; import org.eclipse.emf.emfstore.common.model.util.EObjectChangeNotifier; import org.eclipse.emf.emfstore.common.model.util.IdEObjectCollectionChangeObserver; import org.eclipse.emf.emfstore.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 {@link ModelElementId}s within the project, if not, the {@link ModelElementId}s 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 HashSet<IdEObjectCollectionChangeObserver>(); exceptionThrowingObservers = new HashSet<IdEObjectCollectionChangeObserver>(); undetachableObservers = new HashSet<IdEObjectCollectionChangeObserver>(); observersToAttach = new HashSet<IdEObjectCollectionChangeObserver>(); } /** * {@inheritDoc} * * @see org.eclipse.emf.emfstore.common.model.util.ProjectChangeObserver#modelElementAdded(org.eclipse.emf.emfstore.common.model.Project, * org.eclipse.emf.ecore.EObject) */ public void modelElementAdded(final IdEObjectCollection project, final EObject eObject) { addModelElementAndChildrenToCache(eObject); EObjectChangeObserverNotificationCommand command = new EObjectChangeObserverNotificationCommand() { public void run(IdEObjectCollectionChangeObserver projectChangeObserver) { projectChangeObserver.modelElementAdded(project, eObject); } }; notifyIdEObjectCollectionChangeObservers(command); } protected synchronized void notifyIdEObjectCollectionChangeObservers( EObjectChangeObserverNotificationCommand command) { isNotifiying = true; for (IdEObjectCollectionChangeObserver changeObserver : this.observers) { try { command.run(changeObserver); // BEGIN SUPRESS CATCH EXCEPTION } catch (RuntimeException ex) { // END SUPRESS CATCH EXCEPTION if (exceptionThrowingObservers.contains(changeObserver)) { if (!undetachableObservers.contains(changeObserver)) { observersToRemove.add(changeObserver); ModelUtil.logException( "EObject Change Observer threw an exception again, it has been detached, UI may not update now: " + changeObserver.getClass().getName(), ex); } else { ModelUtil.logException( "EObject Change Observer threw an exception again, but it will not be detached." + changeObserver.getClass().getName(), ex); } } else { exceptionThrowingObservers.add(changeObserver); ModelUtil.logWarning("EObject Change Observer threw an exception: " + changeObserver.getClass().getName(), ex); } } } isNotifiying = false; for (IdEObjectCollectionChangeObserver observer : this.observersToRemove) { removeIdEObjectCollectionChangeObserver(observer); } for (IdEObjectCollectionChangeObserver observer : this.observersToAttach) { addIdEObjectCollectionChangeObserver(observer); } this.observersToRemove.clear(); } @Override public void initCaches() { super.initCaches(); if (changeNotifier == null) { changeNotifier = new EObjectChangeNotifier(this, this); } }; /** * {@inheritDoc} * * @see org.eclipse.emf.emfstore.common.model.Project#initCaches(java.util.Map, java.util.Map) */ @Override public void initCaches(Map<EObject, ModelElementId> eObjectToIdMap, Map<ModelElementId, EObject> idToEObjectMap) { super.initCaches(eObjectToIdMap, idToEObjectMap); changeNotifier = new EObjectChangeNotifier(this, this); } /** * {@inheritDoc} * * @see org.eclipse.emf.emfstore.common.model.util.ProjectChangeObserver#notify(org.eclipse.emf.common.notify.Notification, * org.eclipse.emf.emfstore.common.model.Project, org.eclipse.emf.ecore.EObject) */ public void notify(final Notification notification, final IdEObjectCollection project, final EObject modelElement) { EObjectChangeObserverNotificationCommand command = new EObjectChangeObserverNotificationCommand() { public void run(IdEObjectCollectionChangeObserver projectChangeObserver) { projectChangeObserver.notify(notification, project, modelElement); } }; Resource resource = modelElement.eResource(); if (resource != null && resource instanceof ResourceImpl) { ResourceImpl resourceImpl = (ResourceImpl) resource; if (resourceImpl.isLoading()) { return; } } notifyIdEObjectCollectionChangeObservers(command); } /** * {@inheritDoc} * * @see org.eclipse.emf.emfstore.common.model.Project#addIdEObjectCollectionChangeObserver(org.eclipse.emf.emfstore.common.model.util.ProjectChangeObserver) */ public synchronized void addIdEObjectCollectionChangeObserver( IdEObjectCollectionChangeObserver eObjectChangeObserver) { initCaches(); if (isNotifiying) { observersToAttach.add(eObjectChangeObserver); return; } this.observers.add(eObjectChangeObserver); } /** * {@inheritDoc} * * @see org.eclipse.emf.emfstore.common.model.Project#removeIdEObjectCollectionChangeObserver(org.eclipse.emf.emfstore.common.model.util.ProjectChangeObserver) */ public synchronized void removeIdEObjectCollectionChangeObserver( IdEObjectCollectionChangeObserver projectChangeObserver) { if (isNotifiying) { observersToRemove.add(projectChangeObserver); return; } this.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); } /** * Handle the removal of an element from the containment hierachy. * * @param projectImpl * the project * @param modelElement * the model element */ public void modelElementRemoved(final IdEObjectCollection projectImpl, final EObject modelElement) { removeModelElementAndChildrenFromCache(modelElement); EObjectChangeObserverNotificationCommand command = new EObjectChangeObserverNotificationCommand() { public void run(IdEObjectCollectionChangeObserver projectChangeObserver) { projectChangeObserver.modelElementRemoved(projectImpl, modelElement); } }; notifyIdEObjectCollectionChangeObservers(command); } /** * Copies the current project. * * @return the copied project */ @Override public <T extends IdEObjectCollection> T copy() { if (this.changeNotifier != null) { this.changeNotifier.disableNotifications(true); } T result = super.copy(); if (changeNotifier != null) { changeNotifier.disableNotifications(false); } return result; } /** * * {@inheritDoc} * * @see org.eclipse.emf.emfstore.common.model.util.IdEObjectCollectionChangeObserver#collectionDeleted(org.eclipse.emf.emfstore.common.model.IdEObjectCollection) */ public void collectionDeleted(IdEObjectCollection collection) { } public EObjectChangeNotifier getChangeNotifier() { return changeNotifier; } }