/** * <copyright> * * Copyright (c) 2009 BestSolution.at 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: * Tom Schindl <tom.schindl@bestsolution.at> - port to EMF in 262160 * </copyright> * * $Id: EMFPropertyListener.java,v 1.7 2011/02/08 22:28:34 tschindl Exp $ */ package org.eclipse.emf.databinding.internal; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.eclipse.core.databinding.observable.Diffs; import org.eclipse.core.databinding.observable.list.ListDiff; import org.eclipse.core.databinding.observable.list.ListDiffEntry; import org.eclipse.core.databinding.observable.map.MapDiff; import org.eclipse.core.databinding.observable.set.SetDiff; import org.eclipse.core.databinding.property.INativePropertyListener; import org.eclipse.core.databinding.property.IProperty; import org.eclipse.core.databinding.property.ISimplePropertyListener; import org.eclipse.core.databinding.property.SimplePropertyEvent; 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.common.util.EMap; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; /** * <p><b>PROVISIONAL:</b> This API is subject to arbitrary change, including renaming or removal.</p> * * @since 2.5 */ public abstract class EMFPropertyListener extends AdapterImpl implements INativePropertyListener { public void addTo(Object source) { if (source != null) ((EObject)source).eAdapters().add(this); } public void removeFrom(Object source) { if (source != null) ((EObject)source).eAdapters().remove(this); } @Override public abstract void notifyChanged(Notification msg); /** * @return the listener */ protected abstract ISimplePropertyListener getListener(); /** * @return the feature */ protected abstract EStructuralFeature getFeature(); /** * @return the owner property */ protected abstract IProperty getOwner(); /** * */ public abstract static class EMFListPropertyListener extends EMFPropertyListener { @Override public void notifyChanged(Notification msg) { if (getFeature() == msg.getFeature() && !msg.isTouch()) { final ListDiff diff; switch (msg.getEventType()) { case Notification.ADD: { diff = Diffs.createListDiff(Diffs.createListDiffEntry(msg.getPosition(), true, msg.getNewValue())); break; } case Notification.ADD_MANY: { Collection< ? > newValues = (Collection< ? >)msg.getNewValue(); ListDiffEntry[] listDiffEntries = new ListDiffEntry [newValues.size()]; int position = msg.getPosition(); int index = 0; for (Object newValue : newValues) { listDiffEntries[index++] = Diffs.createListDiffEntry(position++, true, newValue); } diff = Diffs.createListDiff(listDiffEntries); break; } case Notification.REMOVE: { diff = Diffs.createListDiff(Diffs.createListDiffEntry(msg.getPosition(), false, msg.getOldValue())); break; } case Notification.REMOVE_MANY: { Collection< ? > oldValues = (Collection< ? >)msg.getOldValue(); ListDiffEntry[] listDiffEntries = new ListDiffEntry [oldValues.size()]; int[] positions = (int[])msg.getNewValue(); if (positions == null) { int index = 0; for (Object oldValue : oldValues) { listDiffEntries[index] = Diffs.createListDiffEntry(0, false, oldValue); ++index; } } else { int index = 0; for (Object oldValue : oldValues) { listDiffEntries[index] = Diffs.createListDiffEntry(positions[index] - index, false, oldValue); ++index; } } diff = Diffs.createListDiff(listDiffEntries); break; } case Notification.SET: case Notification.RESOLVE: { ListDiffEntry[] listDiffEntries = new ListDiffEntry [2]; int pos = msg.getPosition(); // Looks like a single valued feature if (pos == -1) { pos = 0; } listDiffEntries[0] = Diffs.createListDiffEntry(pos, false, msg.getOldValue()); listDiffEntries[1] = Diffs.createListDiffEntry(pos, true, msg.getNewValue()); diff = Diffs.createListDiff(listDiffEntries); break; } case Notification.MOVE: { Object movedValue = msg.getNewValue(); ListDiffEntry[] listDiffEntries = new ListDiffEntry [2]; listDiffEntries[0] = Diffs.createListDiffEntry((Integer)msg.getOldValue(), false, movedValue); listDiffEntries[1] = Diffs.createListDiffEntry(msg.getPosition(), true, movedValue); diff = Diffs.createListDiff(listDiffEntries); break; } case Notification.UNSET: { // This just represents going back to the unset state, but // that doesn't affect the contents of the list. // return; } default: { throw new RuntimeException("unhandled case"); } } getListener().handleEvent((new SimplePropertyEvent(SimplePropertyEvent.CHANGE, msg.getNotifier(), getOwner(), diff))); } } } /** * */ public abstract static class EMFSetPropertyListener extends EMFPropertyListener { @Override public void notifyChanged(Notification msg) { if (getFeature() == msg.getFeature() && !msg.isTouch()) { final SetDiff diff; switch (msg.getEventType()) { case Notification.ADD: { diff = Diffs.createSetDiff(Collections.singleton(msg.getNewValue()), Collections.emptySet()); break; } case Notification.ADD_MANY: { Collection< ? > newValues = (Collection< ? >)msg.getNewValue(); diff = Diffs.createSetDiff(new HashSet<Object>(newValues), Collections.emptySet()); break; } case Notification.REMOVE: { diff = Diffs.createSetDiff(Collections.emptySet(), Collections.singleton(msg.getOldValue())); break; } case Notification.REMOVE_MANY: { Collection< ? > oldValues = (Collection< ? >)msg.getOldValue(); diff = Diffs.createSetDiff(Collections.emptySet(), new HashSet<Object>(oldValues)); break; } case Notification.SET: case Notification.RESOLVE: { diff = Diffs.createSetDiff(Collections.singleton(msg.getNewValue()), Collections.singleton(msg.getOldValue())); break; } case Notification.MOVE: case Notification.UNSET: { // This just represents going back to the unset state, but // that doesn't affect the contents of the list. // return; } default: { throw new RuntimeException("unhandled case"); } } getListener().handleEvent((new SimplePropertyEvent(SimplePropertyEvent.CHANGE, msg.getNotifier(), getOwner(), diff))); } } } /** * */ public abstract static class EMFMapPropertyListener extends EMFPropertyListener { @Override public void addTo(Object source) { super.addTo(source); if (source != null) { EMap<?, ?> map = (EMap<?, ?>) ((EObject) source).eGet(getFeature()); for (Map.Entry<?, ?> entry : map) { ((Notifier) entry).eAdapters().add(this); } } } @Override public void removeFrom(Object source) { if (source != null) { EMap<?, ?> map = (EMap<?, ?>) ((EObject) source).eGet(getFeature()); for (Map.Entry<?, ?> entry : map) { ((Notifier) entry).eAdapters().remove(this); } } super.removeFrom(source); } @Override public void notifyChanged(Notification msg) { if (msg.isTouch()) return; if (getFeature() == msg.getFeature()) { final MapDiff diff; switch (msg.getEventType()) { case Notification.ADD: { Map.Entry<?, ?> entry = (Map.Entry<?, ?>)msg.getNewValue(); ((EObject) entry).eAdapters().add(this); diff = Diffs.createMapDiffSingleAdd(entry.getKey(), entry.getValue()); break; } case Notification.ADD_MANY: { @SuppressWarnings("unchecked") Collection<Map.Entry<?, ?>> newEntries = (Collection<Map.Entry<?, ?>>)msg.getNewValue(); Map<Object, Object> newValues = new HashMap<Object, Object>(); for (Map.Entry<?, ?> entry : newEntries) { ((EObject) entry).eAdapters().add(this); newValues.put(entry.getKey(), entry.getValue()); } diff = Diffs.createMapDiff(newValues.keySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptyMap(), newValues); break; } case Notification.REMOVE: { Map.Entry<?, ?> entry = (Map.Entry<?, ?>)msg.getOldValue(); ((EObject) entry).eAdapters().remove(this); diff = Diffs.createMapDiffSingleRemove(entry.getKey(), entry.getValue()); break; } case Notification.REMOVE_MANY: { @SuppressWarnings("unchecked") Collection<Map.Entry<?, ?>> oldEntries = (Collection<Map.Entry<?, ?>>)msg.getOldValue(); Map<Object, Object> oldValues = new HashMap<Object, Object>(); for (Map.Entry<?, ?> entry : oldEntries) { ((EObject) entry).eAdapters().remove(this); oldValues.put(entry.getKey(), entry.getValue()); } diff = Diffs.createMapDiff(Collections.emptySet(), oldValues.keySet(), Collections.emptySet(), oldValues, Collections.emptyMap()); break; } case Notification.SET: case Notification.RESOLVE: { Map.Entry<?, ?> oldEntry = (Map.Entry<?, ?>)msg.getOldValue(); ((EObject) oldEntry).eAdapters().remove(this); Map<Object, Object> oldValues = new HashMap<Object, Object>(); oldValues.put(oldEntry.getKey(), oldEntry.getValue()); Map.Entry<?, ?> newEntry = (Map.Entry<?, ?>)msg.getNewValue(); ((EObject) newEntry).eAdapters().add(this); Map<Object, Object> newValues = new HashMap<Object, Object>(); newValues.put(newEntry.getKey(), newEntry.getValue()); diff = Diffs.createMapDiff(newValues.keySet(), oldValues.keySet(), Collections.emptySet(), oldValues, newValues); break; } case Notification.MOVE: case Notification.UNSET: { // This just represents going back to the unset state, but // that doesn't affect the contents of the list. // return; } default: { throw new RuntimeException("unhandled case"); } } getListener().handleEvent((new SimplePropertyEvent(SimplePropertyEvent.CHANGE, msg.getNotifier(), getOwner(), diff))); } else if (((EClass) getFeature().getEType()).getEStructuralFeature("value") == msg.getFeature()) { Object key = ((Map.Entry<?, ?>) msg.getNotifier()).getKey(); MapDiff diff = Diffs.createMapDiffSingleChange(key, msg.getOldValue(), msg.getNewValue()); getListener().handleEvent(new SimplePropertyEvent(SimplePropertyEvent.CHANGE, msg.getNotifier(), getOwner(), diff)); } } } /** * */ public abstract static class EMFValuePropertyListener extends EMFPropertyListener { @Override public void notifyChanged(Notification msg) { if (getFeature() == msg.getFeature() && !msg.isTouch()) { getListener().handleEvent( new SimplePropertyEvent(SimplePropertyEvent.CHANGE, msg.getNotifier(), getOwner(), Diffs.createValueDiff( msg.getOldValue(), msg.getNewValue()))); } } } }