/*******************************************************************************
* Copyright (c) 2006-2013 The RCP Company 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:
* The RCP Company - initial API and implementation
*******************************************************************************/
package com.rcpcompany.uibindings.internal.utils;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import com.rcpcompany.uibindings.IDisposable;
/**
* Support class for {@link ISupportListener label providers} used to handle updates of the labels.
* <p>
* This version only supports EMF based models.
*
* @author Tonny Madsen, The RCP Company
*/
public class LabelProviderEventSupport implements IDisposable {
/**
* This label provider of this event support object.
*/
protected final ISupportListener myProvider;
/**
* Constructs and returns a new event support object for the specified label provider.
*
* @param provider the label provider
*/
public LabelProviderEventSupport(ISupportListener provider) {
myProvider = provider;
}
/**
* Disposes this support object by disposing of all the monitored objects.
*/
@Override
public void dispose() {
while (myObjects.values().size() > 0) {
myObjects.values().iterator().next().dispose();
}
};
public interface ISupportListener {
void objectsChanged(Object[] elements);
}
/**
* Adds a new object to this event support object that monitors changes to the specified
* {@link EObject} with the specified chain of features.
*
* @param object the object
* @param features the chain of features
*/
public void addObject(EObject object, EStructuralFeature[] features) {
final MonitoredObject o = myObjects.get(object);
if (o == null) {
new MonitoredObject(object, features);
}
}
/**
* Removes a previously added object.
*
* @param object the object to remove
*/
public void removeObject(EObject object) {
final MonitoredObject o = myObjects.get(object);
if (o != null) {
o.dispose();
}
}
/**
* Map with all monitored objects.
*/
protected Map<EObject, MonitoredObject> myObjects = new HashMap<EObject, MonitoredObject>();
/**
* Private class that represents the a single key value in the map.
* <p>
* Keeps an array with all the intermediate {@link EObject EObjects} that matches the
* myChainReferences array.
* <p>
* An adapter (<code>this</code>) is added to all of these objects. Changes are perceived to be
* seldom, so the complete set of adapters are re-hooked with all changes in the surveyed
* objects.
*/
private class MonitoredObject extends AdapterImpl implements IDisposable {
/**
* Array with all features.
*/
protected final EStructuralFeature[] myReferencesChain;
/**
* Array of the objects in the feature chain.
*/
protected final EObject[] myObjectChain;
/**
* The base object of this key...
*/
private final EObject myObject;
/**
* Constructs and returns a new monitored object.
*
* @param object the object to monitor
* @param features the features to monitor
*/
private MonitoredObject(EObject object, EStructuralFeature[] features) {
myObject = object;
final int l = features.length;
// Some sanity checks:
EClass prevEClass = myObject.eClass();
for (int i = 0; i < l - 1; i++) {
Assert.isTrue(features[i] instanceof EReference, "Intermidiate features must before references");
final EReference ref = (EReference) features[i];
if (prevEClass != null) {
Assert.isTrue(ref.getEContainingClass().isSuperTypeOf(prevEClass), "Reference "
+ ref.getEContainingClass().getName() + "." + ref.getName() + " not applicable for class "
+ prevEClass.getName());
}
prevEClass = ref.getEReferenceType();
}
final EStructuralFeature lastFeature = features[l - 1];
if (prevEClass != null) {
Assert.isTrue(lastFeature.getEContainingClass().isSuperTypeOf(prevEClass), "Feature "
+ lastFeature.getEContainingClass().getName() + "." + lastFeature.getName()
+ " not applicable for class " + prevEClass.getName());
}
myReferencesChain = new EStructuralFeature[l];
myObjectChain = new EObject[myReferencesChain.length];
System.arraycopy(features, 0, myReferencesChain, 0, l);
myObjects.put(myObject, this);
hookListeners();
}
@Override
public void dispose() {
unhookListeners();
myObjects.remove(myObject);
}
@Override
public void notifyChanged(Notification msg) {
if (msg.isTouch()) return;
for (int i = 0; i < myReferencesChain.length; i++) {
if (myObjectChain[i] == msg.getNotifier() && myReferencesChain[i] == msg.getFeature()) {
myProvider.objectsChanged(new Object[] { myObject });
unhookListeners();
hookListeners();
break;
}
}
}
public void hookListeners() {
myObjectChain[0] = myObject;
EObject next = myObjectChain[0];
if (next != null) {
next.eAdapters().add(this);
}
for (int i = 0; i < myReferencesChain.length - 1 && next != null; i++) {
next = (EObject) next.eGet(myReferencesChain[i]);
myObjectChain[i + 1] = next;
if (next != null) {
next.eAdapters().add(this);
}
}
}
public void unhookListeners() {
for (final EObject o : myObjectChain) {
if (o != null) {
o.eAdapters().remove(this);
}
}
}
}
}