| /***************************************************************************** |
| * Copyright (c) 2017 Christian W. Damus 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: |
| * Christian W. Damus - Initial API and implementation |
| * |
| *****************************************************************************/ |
| |
| package org.eclipse.papyrus.aas.ui.widgets; |
| |
| 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.EAttribute; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.papyrus.aas.AASPackage; |
| import org.eclipse.papyrus.aas.Endpoint; |
| import org.eclipse.papyrus.aas.Property; |
| import org.eclipse.uml2.uml.Type; |
| import org.eclipse.uml2.uml.TypedElement; |
| import org.eclipse.uml2.uml.UMLPackage; |
| import org.eclipse.uml2.uml.ValueSpecification; |
| |
| /** |
| * An adapter for multiplicity elements that maintains itself on the bounds |
| * values and provides call-backs for them. |
| */ |
| public class MultiplicityElementAdapter extends AdapterImpl { |
| |
| public MultiplicityElementAdapter() { |
| super(); |
| } |
| |
| @Override |
| public void setTarget(Notifier newTarget) { |
| // I only actually track the multiplicity element |
| if ((newTarget == null) || getTargetType().isInstance(newTarget)) { |
| super.setTarget(newTarget); |
| |
| if (newTarget instanceof org.eclipse.papyrus.aas.Property) { |
| handleNewTarget(); |
| } |
| } |
| } |
| |
| void handleNewTarget() { |
| Property newTarget = getTarget(); |
| |
| if (newTarget.getEndPoint() != null) { |
| adapt(newTarget.getEndPoint()); |
| } |
| |
| } |
| |
| Class<? extends Property> getTargetType() { |
| return Property.class; |
| } |
| |
| @Override |
| public void unsetTarget(Notifier oldTarget) { |
| if (oldTarget == getTarget()) { |
| handleOldTarget(); |
| } |
| |
| super.unsetTarget(oldTarget); |
| } |
| |
| void handleOldTarget() { |
| Property oldTarget = getTarget(); |
| |
| if (oldTarget.getEndPoint() != null) { |
| unadapt(oldTarget.getEndPoint()); |
| } |
| |
| } |
| |
| @Override |
| public Property getTarget() { |
| return (Property) super.getTarget(); |
| } |
| |
| public void adapt(Notifier notifier) { |
| if (!notifier.eAdapters().contains(this)) { |
| notifier.eAdapters().add(this); |
| } |
| } |
| |
| public void unadapt(Notifier notifier) { |
| notifier.eAdapters().remove(this); |
| } |
| |
| @Override |
| public void notifyChanged(Notification notification) { |
| // We are only interested in changes to scalar features, and they |
| // only support SET, UNSET, and RESOLVE notifications |
| switch (notification.getEventType()) { |
| case Notification.ADD: |
| case Notification.ADD_MANY: |
| case Notification.REMOVE: |
| case Notification.REMOVE_MANY: |
| // Except for OpaqueExpression bodies, which are lists |
| if (notification.getFeature() != UMLPackage.Literals.OPAQUE_EXPRESSION__BODY) { |
| break; |
| } |
| // Fall through |
| case Notification.SET: |
| case Notification.UNSET: |
| if (notification.isTouch()) { |
| break; |
| } |
| // Fall through |
| case Notification.RESOLVE: |
| Property target = getTarget(); |
| Object notifier = notification.getNotifier(); |
| Object feature = notification.getFeature(); |
| |
| if (notifier == target) { |
| switch (notification.getFeatureID(Property.class)) { |
| case AASPackage.SUBMODEL_ELEMENT__END_POINT: |
| Endpoint oldValue = (Endpoint) notification.getOldValue(); |
| Endpoint newValue = (Endpoint) notification.getNewValue(); |
| |
| adaptChange(oldValue, newValue); |
| |
| |
| default: |
| handleOtherTargetNotification(notification); |
| break; |
| } |
| } else if (!handleDetailNotification(notifier, feature, notification)) { |
| // We shouldn't be adapting this object |
| unadapt((Notifier) notifier); |
| } |
| |
| break; |
| } |
| |
| } |
| |
| final void adaptChange(Notifier oldNotifier, Notifier newNotifier) { |
| if (oldNotifier != null) { |
| // stop listening to this one |
| unadapt(oldNotifier); |
| } |
| if (newNotifier != null) { |
| // start listening to this one |
| adapt(newNotifier); |
| } |
| } |
| |
| void handleOtherTargetNotification(Notification notification) { |
| // Pass |
| } |
| |
| boolean handleDetailNotification(Object notifier, Object feature, Notification notification) { |
| boolean result = false; |
| |
| if ((notifier instanceof Endpoint) |
| && (getTarget() != null) |
| && (((Endpoint) notifier).eContainer() == getTarget())) { |
| |
| result = true; |
| |
| if (feature instanceof EAttribute) { |
| Endpoint value = (Endpoint) notifier; |
| if (value == getTarget().getEndPoint()) { |
| handleLowerValueChanged(value, (EAttribute) feature, notification); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Handles replacement of the lower value specification. By default, just calls |
| * {@link #handleMultiplicityChanged(Notification)}. |
| * |
| * @param oldLowerValue |
| * the previous lower value specification (could be {@code null}) |
| * @param newLowerValue |
| * the new lower value specification (could be {@code null}) |
| * @param notification |
| * the notification describing the change |
| * |
| * @see #handleMultiplicityChanged(Notification) |
| */ |
| protected void handleLowerValueReplaced(Endpoint oldLowerValue, ValueSpecification newLowerValue, Notification notification) { |
| handleMultiplicityChanged(notification); |
| } |
| |
| /** |
| * Handles replacement of the upper value specification. By default, just calls |
| * {@link #handleMultiplicityChanged(Notification)}. |
| * |
| * @param oldUpperValue |
| * the previous upper value specification (could be {@code null}) |
| * @param newUpperValue |
| * the new upper value specification (could be {@code null}) |
| * @param notification |
| * the notification describing the change |
| * |
| * @see #handleMultiplicityChanged(Notification) |
| */ |
| protected void handleUpperValueReplaced(ValueSpecification oldUpperValue, ValueSpecification newUpperValue, Notification notification) { |
| handleMultiplicityChanged(notification); |
| } |
| |
| /** |
| * May be overridden by subclasses to handle the simple and common case of just needing |
| * to react to any change at all in the multiplicity. |
| * |
| * @param notification |
| * the notification describing the change |
| */ |
| protected void handleMultiplicityChanged(Notification notification) { |
| // Pass |
| } |
| |
| /** |
| * Handles a change in the lower value specification. By default, just calls |
| * {@link #handleMultiplicityChanged(Notification)}. |
| * |
| * @param lowerValue |
| * the lower value specification that changed (not {@code null}) |
| * @param feature |
| * the attribute of the lower value that changed (not {@code null}) |
| * @param notification |
| * the notification describing the change |
| * |
| * @see #handleMultiplicityChanged(Notification) |
| */ |
| protected void handleLowerValueChanged(Endpoint lowerValue, EAttribute feature, Notification notification) { |
| handleMultiplicityChanged(notification); |
| } |
| |
| /** |
| * Handles a change in the upper value specification. By default, just calls |
| * {@link #handleMultiplicityChanged(Notification)}. |
| * |
| * @param upperValue |
| * the upper value specification that changed (not {@code null}) |
| * @param feature |
| * the attribute of the upper value that changed (not {@code null}) |
| * @param notification |
| * the notification describing the change |
| * |
| * @see #handleMultiplicityChanged(Notification) |
| */ |
| |
| |
| // |
| // Nested types |
| // |
| |
| /** |
| * A specialization of the {@link MultiplicityElementAdapter} for {@link Property} |
| * instances that provides hooks for changes in the property's {@link TypedElement#getType() type}. |
| */ |
| public static class ForProperty extends MultiplicityElementAdapter { |
| public ForProperty() { |
| super(); |
| } |
| |
| @Override |
| Class<? extends Property> getTargetType() { |
| return Property.class; |
| } |
| |
| @Override |
| public Property getTarget() { |
| return super.getTarget(); |
| } |
| |
| @Override |
| void handleNewTarget() { |
| super.handleNewTarget(); |
| |
| Property newTarget = getTarget(); |
| if (newTarget.getEndPoint() != null) { |
| adapt(newTarget.getEndPoint()); |
| } |
| } |
| |
| @Override |
| void handleOldTarget() { |
| Property oldTarget = getTarget(); |
| if (oldTarget.getEndPoint() != null) { |
| unadapt(oldTarget.getEndPoint()); |
| } |
| |
| super.handleOldTarget(); |
| } |
| |
| @Override |
| void handleOtherTargetNotification(Notification notification) { |
| switch (notification.getFeatureID(Property.class)) { |
| case UMLPackage.PROPERTY__TYPE: |
| Type oldType = (Type) notification.getOldValue(); |
| Type newType = (Type) notification.getNewValue(); |
| |
| adaptChange(oldType, newType); |
| |
| handleTypeReplaced(oldType, newType, notification); |
| break; |
| } |
| } |
| |
| @Override |
| boolean handleDetailNotification(Object notifier, Object feature, Notification notification) { |
| boolean result = false; |
| |
| if ((notifier instanceof Type) |
| && (feature instanceof EStructuralFeature) |
| && (getTarget().getEndPoint() == notifier)) { |
| |
| result = true; |
| |
| handleTypeChanged((Type) notifier, (EStructuralFeature) feature, notification); |
| } |
| |
| return result || super.handleDetailNotification(notifier, feature, notification); |
| } |
| |
| /** |
| * Handles replacement of the property's type. By default, just calls |
| * {@link #handleTypeChanged(Notification)}. |
| * |
| * @param oldType |
| * the previous type (could be {@code null}) |
| * @param newType |
| * the new type (could be {@code null}) |
| * @param notification |
| * the notification describing the change |
| * |
| * @see #handleTypeChanged(Notification) |
| */ |
| protected void handleTypeReplaced(Type oldType, Type newType, Notification notification) { |
| handleTypeChanged(notification); |
| } |
| |
| /** |
| * May be overridden by subclasses to handle the simple and common case of just needing |
| * to react to any change at all in the property's type. By default, forwards the |
| * {@code notification} to the {@link #handleMultiplicityChanged(Notification)} method. |
| * |
| * @param notification |
| * the notification describing the change |
| */ |
| protected void handleTypeChanged(Notification notification) { |
| handleMultiplicityChanged(notification); |
| } |
| |
| /** |
| * Handles a change in the property's type. By default, just calls |
| * {@link #handleTypeChanged(Notification)}. |
| * |
| * @param type |
| * the type that changed (not {@code null}) |
| * @param feature |
| * the feature of the type that changed (not {@code null}) |
| * @param notification |
| * the notification describing the change |
| * |
| * @see #handleTypeChanged(Notification) |
| */ |
| protected void handleTypeChanged(Type type, EStructuralFeature feature, Notification notification) { |
| handleTypeChanged(notification); |
| } |
| } |
| } |