blob: e5088cbe7f1dca1b32304748d632c894a1aae761 [file] [log] [blame]
/*
* Copyright (c) 2008 Borland Software Corporation.
* 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:
* Artem Tikhomirov (Borland) - initial API and implementation
*/
package org.eclipse.gmf.graphdef.editor.sheet;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
/**
* Helper adapter that tracks specific feature of the notifier(s) it is
* attached to (with regular <code>objOfInterest.eAdapters().add(attachAdapter)</code> call)
* and attaches/detaches provided adapter when feature value changes (set/unset, add/remove)
*
* @author artem
*/
public final class AttachAdapter extends AdapterImpl {
private final Adapter[] myAdapters;
private final EReference myRef; // may add support for multiple references later
private final ChangeTracker myTracker;
/**
* Plain attaching/detaching adapter, with no additional change tracker.
* @see #AttachAdapter(EReference, ChangeTracker, Adapter...)
*/
public AttachAdapter(EReference reference, Adapter... attachAdapters) {
this(reference, null, attachAdapters);
}
/**
* @param reference - feature to track, non-null, adapter's target expected to conform feature's container type
* @param tracker - notified on feature change in addition to attached/detached adapters, may be null.
* If specified, gets notification *after* adapters were attached/detached.
* @param attachAdapters - copy by reference, do not change initial array
*/
public AttachAdapter(EReference reference, ChangeTracker tracker, Adapter... attachAdapters) {
assert attachAdapters != null;
assert !Arrays.asList(attachAdapters).contains(null);
assert reference != null;
myAdapters = attachAdapters;
myRef = reference;
myTracker = tracker;
}
//
// API ends
//
@Override
public void setTarget(Notifier newTarget) {
if (newTarget instanceof EObject) {
final EObject eTarget = (EObject) newTarget;
if (checkTarget(eTarget) && eTarget.eIsSet(myRef)) {
Object value = eTarget.eGet(myRef, false);
if (myRef.isMany() && value instanceof List) {
for (Object o : (List<?>) value) {
if (o instanceof EObject) {
attachTo((EObject) o);
}
}
} else if (value instanceof EObject) {
attachTo((EObject) value);
}
}
}
super.setTarget(newTarget);
}
@Override
public void unsetTarget(Notifier oldTarget) {
if (oldTarget instanceof EObject) {
final EObject eTarget = (EObject) oldTarget;
if (checkTarget(eTarget) && eTarget.eIsSet(myRef)) {
Object value = eTarget.eGet(myRef, false);
if (myRef.isMany() && value instanceof List) {
for (Object o : (List<?>) value) {
if (o instanceof EObject) {
detachFrom((EObject) o);
}
}
} else if (value instanceof EObject) {
detachFrom((EObject) value);
}
}
}
super.unsetTarget(oldTarget);
}
@Override
public void notifyChanged(Notification msg) {
if (msg.isTouch()) {
return;
}
if (!myRef.equals(msg.getFeature())) {
return;
}
switch (msg.getEventType()) {
case Notification.ADD :
// XXX since we do know it's a EReference, can't we just cast?
if (msg.getNewValue() instanceof EObject) {
attachTo((EObject) msg.getNewValue());
} else {
System.out.println("AttachAdapter.notifyChanged() - EReference.add(nonEObject)!!!"); //FIXME remove debug printout
}
break;
case Notification.ADD_MANY :
if (msg.getNewValue() instanceof Collection) {
for (Object o : (Collection<?>) msg.getNewValue()) {
if (o instanceof EObject) {
attachTo((EObject) o);
}
}
}
break;
case Notification.REMOVE :
if (msg.getOldValue() instanceof EObject) {
detachFrom((EObject) msg.getOldValue());
}
break;
case Notification.REMOVE_MANY :
if (msg.getOldValue() instanceof Collection) {
for (Object o : (Collection<?>) msg.getOldValue()) {
if (o instanceof EObject) {
detachFrom((EObject) o);
}
}
}
case Notification.SET :
if (msg.getOldValue() instanceof EObject) {
detachFrom((EObject) msg.getOldValue());
}
if (msg.getNewValue() instanceof EObject) {
attachTo((EObject) msg.getNewValue());
}
break;
case Notification.UNSET :
// XXX not sure whether unset gives oldValue or newValue
// EContentAdapter handles UNSET in a different way - FIXME!!!
if (msg.getOldValue() instanceof EObject) {
detachFrom((EObject) msg.getOldValue());
}
break;
}
// dispatch to tracker afterwards, so that respective adapters are already attached/detached
if (myTracker != null) {
myTracker.modelChanged(msg);
}
}
// API note: next two methods may become protected, if needed
private void attachTo(EObject obj) {
for (int i = 0; i < myAdapters.length; i++) {
obj.eAdapters().add(myAdapters[i]);
}
}
private void detachFrom(EObject obj) {
for (int i = myAdapters.length - 1; i >= 0; i--) {
obj.eAdapters().remove(myAdapters[i]);
}
}
private boolean checkTarget(EObject eTarget) {
return myRef.getEContainingClass().isSuperTypeOf(eTarget.eClass());
}
}