| /* |
| * 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()); |
| } |
| } |