| /** |
| * Copyright (c) 2011, 2015 - Lunifera GmbH (Gross Enzersdorf, Austria), Loetz GmbH&Co.KG (69115 Heidelberg, Germany) |
| * 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: |
| * Florian Pirchner - Initial implementation |
| */ |
| package org.eclipse.osbp.ecview.core.common.editpart.emf; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.UUID; |
| |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.osbp.ecview.core.common.context.IViewContext; |
| import org.eclipse.osbp.ecview.core.common.editpart.DelegatingEditPartManager; |
| import org.eclipse.osbp.ecview.core.common.editpart.IElementEditpart; |
| import org.eclipse.osbp.ecview.core.common.editpart.IElementEditpartProvider; |
| import org.eclipse.osbp.ecview.core.common.editpart.IEmbeddableEditpart; |
| import org.eclipse.osbp.ecview.core.common.editpart.IViewEditpart; |
| import org.eclipse.osbp.ecview.core.common.model.core.CoreModelPackage; |
| import org.eclipse.osbp.ecview.core.common.model.core.YElement; |
| import org.eclipse.osbp.ecview.core.common.model.core.YEmbeddable; |
| import org.eclipse.osbp.runtime.common.dispose.IDisposable; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| // TODO: Auto-generated Javadoc |
| /** |
| * The implementation of the main edit part. <br> |
| * The whole ui is based on an ui model which is built by emf. And each edit |
| * part is based on an EObject (ui model element) that is given in the |
| * initialize method and contains all information required to render the ui |
| * element. The edit part is just the controller that renders the ui but the |
| * informations how to render is given by the EObject. |
| * <p> |
| * For general, edit parts have 3 different kind of methods. <br> |
| * <h3>API</h3> API are public methods that are declared by the interfaces. They |
| * can be used to create the ui based on the edit parts like |
| * <p> |
| * <code> |
| * layoutEditpart.addElement(element) |
| * </code> |
| * <p> |
| * Internally these methods do not handle the calls directly, but will delegate |
| * to the underlying EMF model. What means, that the implementation of the |
| * public methods do not create ui, but just changes the EObject. |
| * |
| * <h3>EMF adpater</h3> |
| * Since each edit part inherits {@link Adapter} and observes all the |
| * notifications from the assigned element, the changes made by the public API |
| * methods will notify the {@link #notifyChanged(Notification)} method that |
| * delegates the call to the handleModel_... methods.<br> |
| * |
| * <h3>Internal methods</h3> The handleModel_... methods interpret the model |
| * change and invoke the internal... methods. These are responsible to make the |
| * changes to internal state of the edit part. |
| * |
| * <h3>Advantage</h3> The advantage of that pattern is to provide a very |
| * flexible API. The ui can be built up by the edit parts, but it can be also |
| * defined based on the underlying emf model. |
| * |
| * <h3>Presenters</h3> The presenter used to render the ui by a special ui-kit |
| * may use databinding. Therefore they can bind the emf model directly to become |
| * notified of changes to the ui model. But they can also bind the edit parts |
| * properties. But attention! Binding an edit part can only notify about edit |
| * part related changes. For instance, if a new edit part was prepared based on |
| * a model change. All returned lists for <code>"List get[Element]()"</code> |
| * should return an unmodifiable bindable list representation like a |
| * UnmodifiableObservableList. |
| * <p> |
| * |
| * @param <M> |
| * the generic type |
| */ |
| public abstract class ElementEditpart<M extends YElement> extends AdapterImpl |
| implements IElementEditpart, IElementEditpartProvider { |
| |
| /** The Constant LOGGER. */ |
| private static final Logger LOGGER = LoggerFactory |
| .getLogger(ElementEditpart.class); |
| |
| /** The disposed. */ |
| private boolean disposed; |
| |
| /** The dispose listeners. */ |
| private List<IDisposable.Listener> disposeListeners; |
| |
| /** The model. */ |
| private M model; |
| |
| /** The id. */ |
| private String id; |
| |
| /** The disposing. */ |
| private boolean disposing; |
| |
| protected IViewContext viewContext; |
| |
| /** |
| * Returns the edit part for the given model yElement. |
| * |
| * @param <A> |
| * An instance of {@link IElementEditpart} |
| * @param yElement |
| * the model element |
| * @return editpart |
| */ |
| public static <A extends IElementEditpart> A findEditPartFor( |
| YElement yElement) { |
| return EditpartManager.findEditPartFor(yElement); |
| } |
| |
| /** |
| * Returns an existing edit part or creates a new one. |
| * |
| * @param <A> |
| * An instance of {@link IElementEditpart} |
| * @param context |
| * the context to be used to create the editpart |
| * @param yElement |
| * the model element |
| * @return editpart |
| */ |
| public static <A extends IElementEditpart> A getEditpart( |
| IViewContext context, YElement yElement) { |
| if (yElement == null) { |
| return null; |
| } |
| |
| A editPart = findEditPartFor(yElement); |
| if (editPart != null) { |
| return editPart; |
| } |
| return DelegatingEditPartManager.getInstance().getEditpart(context, |
| yElement); |
| } |
| |
| public IViewContext getContext() { |
| return viewContext; |
| } |
| |
| /** |
| * Returns the view context for the given embeddable. |
| * |
| * @param yEmbeddable |
| * the y embeddable |
| * @return the view context |
| */ |
| public static IViewContext getViewContext(YEmbeddable yEmbeddable) { |
| IViewEditpart viewEditpart = getViewEditpart(yEmbeddable); |
| return viewEditpart != null ? viewEditpart.getContext() : null; |
| } |
| |
| /** |
| * Returns the view editpart for the given embeddable. Or <code>null</code>. |
| * |
| * @param yEmbeddable |
| * the y embeddable |
| * @return the view editpart |
| */ |
| public static IViewEditpart getViewEditpart(YEmbeddable yEmbeddable) { |
| IEmbeddableEditpart editpart = findEditPartFor(yEmbeddable); |
| return editpart != null ? editpart.getView() : null; |
| } |
| |
| /** |
| * Returns the view context for the given embeddable. |
| * |
| * @param eObject |
| * the context |
| * @return the view context |
| */ |
| public static IViewContext getViewContext(EObject eObject) { |
| if (eObject == null) { |
| return null; |
| } |
| |
| if (eObject instanceof YElement) { |
| IElementEditpart editpart = findEditPartFor((YElement) eObject); |
| if (editpart != null && editpart.getContext() != null) { |
| return editpart.getContext(); |
| } |
| } |
| |
| // // otherwise iterate up the hierarchy to find a view context |
| // if (context instanceof YEmbeddable) { |
| // IViewEditpart viewEditpart = getViewEditpart((YEmbeddable) context); |
| // return viewEditpart != null ? viewEditpart.getContext() : null; |
| // } else if (context instanceof YView) { |
| // IViewEditpart viewEditpart = getEditpart((YView) context); |
| // return viewEditpart != null ? viewEditpart.getContext() : null; |
| // } else { |
| EObject parent = eObject.eContainer(); |
| return getViewContext(parent); |
| // } |
| } |
| |
| /** |
| * The default constructor. |
| */ |
| protected ElementEditpart() { |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String getId() { |
| checkDisposed(); |
| |
| return id; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public M getModel() { |
| // do not check disposed here. The model is required for lifecycle |
| // events directly after disposing stuff. |
| return model; |
| } |
| |
| /** |
| * Is called to initialize this edit part with the given model. The editpart |
| * will be built based on the given model. |
| * |
| * @param model |
| * the model element |
| */ |
| public void initialize(IViewContext context, M model) { |
| checkDisposed(); |
| |
| if (this.model != null) { |
| LOGGER.error("Editparts must only be initialized once!"); |
| throw new IllegalStateException( |
| "Editparts must only be initialized once!"); |
| } |
| |
| this.viewContext = context; |
| |
| checkAdapter(model); |
| |
| this.model = model; |
| |
| id = model.getId(); |
| if (id == null || id.equals("")) { |
| id = UUID.randomUUID().toString(); |
| model.setId(id); |
| } |
| |
| registerAdapter(this); |
| |
| viewContext = getViewContext(model); |
| } |
| |
| protected void checkAdapter(M model) { |
| for (Adapter adapter : castEObject(model).eAdapters()) { |
| if (adapter instanceof IElementEditpartProvider) { |
| LOGGER.error("For a modelelement instance only one editpart can be created!"); |
| throw new RuntimeException( |
| "For a modelelement instance only one editpart can be created!"); |
| } |
| } |
| } |
| |
| /** |
| * Casts element to eObject. |
| * |
| * @param element |
| * the element |
| * @return the e object |
| */ |
| protected EObject castEObject(M element) { |
| return (EObject) element; |
| } |
| |
| /** |
| * Implementation of {@link IElementEditpartProvider} and returns |
| * <code>this</code> in that special case. |
| * |
| * {@inheritDoc} |
| */ |
| @Override |
| public IElementEditpart getEditpart() { |
| checkDisposed(); |
| |
| return this; |
| } |
| |
| /** |
| * Registers an adapter at the model element. |
| * |
| * @param adapter |
| * The adapter to be added to the model element |
| */ |
| protected void registerAdapter(Adapter adapter) { |
| checkDisposed(); |
| |
| EObject eObject = castEObject(model); |
| if (!eObject.eAdapters().contains(adapter)) { |
| eObject.eAdapters().add(adapter); |
| } |
| } |
| |
| /** |
| * Unregisters an adapter at the model element. |
| * |
| * @param adapter |
| * The adapter to be removed from the model element |
| */ |
| protected void unregisterAdapter(Adapter adapter) { |
| castEObject(model).eAdapters().remove(adapter); |
| } |
| |
| /** |
| * Is called by the emf model element, if state changed. |
| * |
| * @param notification |
| * notification to be processed |
| */ |
| public void notifyChanged(Notification notification) { |
| int featureId = notification.getFeatureID(YElement.class); |
| switch (notification.getEventType()) { |
| case Notification.ADD: |
| handleModelAdd(featureId, notification); |
| break; |
| case Notification.ADD_MANY: |
| handleModelAddMany(featureId, notification); |
| break; |
| case Notification.REMOVE: |
| handleModelRemove(featureId, notification); |
| break; |
| case Notification.REMOVE_MANY: |
| handleModelRemoveMany(featureId, notification); |
| break; |
| case Notification.MOVE: |
| handleModelMove(featureId, notification); |
| break; |
| case Notification.SET: |
| handleModelSet(featureId, notification); |
| break; |
| default: |
| |
| } |
| } |
| |
| /** |
| * Is called from {@link #notifyChanged(Notification)} for the |
| * add-many-event to be handled by subclasses. |
| * |
| * @param featureId |
| * The featureId the notication belongs to |
| * @param notification |
| * The notfication |
| */ |
| protected void handleModelRemoveMany(int featureId, |
| Notification notification) { |
| checkDisposed(); |
| } |
| |
| /** |
| * Is called from {@link #notifyChanged(Notification)} for the |
| * remove-many-event to be handled by subclasses. |
| * |
| * @param featureId |
| * The featureId the notication belongs to |
| * @param notification |
| * The notfication |
| */ |
| protected void handleModelAddMany(int featureId, Notification notification) { |
| checkDisposed(); |
| } |
| |
| /** |
| * Is called from {@link #notifyChanged(Notification)} for the add-event to |
| * be handled by subclasses. |
| * |
| * @param featureId |
| * The featureId the notication belongs to |
| * @param notification |
| * The notfication |
| */ |
| protected void handleModelAdd(int featureId, Notification notification) { |
| checkDisposed(); |
| } |
| |
| /** |
| * Is called from {@link #notifyChanged(Notification)} for the remove-event |
| * to be handled by subclasses. |
| * |
| * @param featureId |
| * The featureId the notication belongs to |
| * @param notification |
| * The notfication |
| */ |
| protected void handleModelRemove(int featureId, Notification notification) { |
| checkDisposed(); |
| } |
| |
| /** |
| * Is called from {@link #notifyChanged(Notification)} for the move-event to |
| * be handled by subclasses. |
| * |
| * @param featureId |
| * The featureId the notication belongs to |
| * @param notification |
| * The notfication |
| */ |
| protected void handleModelMove(int featureId, Notification notification) { |
| checkDisposed(); |
| } |
| |
| /** |
| * Is called from {@link #notifyChanged(Notification)} for the set-event to |
| * be handled by subclasses. |
| * |
| * @param featureId |
| * The featureId the notication belongs to |
| * @param notification |
| * The notfication |
| */ |
| protected void handleModelSet(int featureId, Notification notification) { |
| checkDisposed(); |
| |
| if (featureId == CoreModelPackage.YELEMENT__ID) { |
| throw new IllegalArgumentException("The id must never be changed!"); |
| } |
| } |
| |
| /** |
| * Returns true, of the UI element contains a tag with the given tagName. |
| * |
| * @param tag |
| * the tag |
| * @return true, if successful |
| */ |
| public boolean containsTag(String tag) { |
| return getModel().getTags().contains(tag); |
| } |
| |
| /** |
| * Returns true, of the UI element contains a property with the given key. |
| * |
| * @param key |
| * the key |
| * @return true, if successful |
| */ |
| public boolean containsProperty(String key) { |
| return getModel().getProperties().contains(key); |
| } |
| |
| /** |
| * Returns the property value from the UI element with the given key. |
| * |
| * @param key |
| * the key |
| * @return the property value |
| */ |
| public String getPropertyValue(String key) { |
| return getModel().getProperties().get(key); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean isDisposed() { |
| return disposed; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean isDisposing() { |
| return disposing; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void dispose() { |
| try { |
| disposing = true; |
| if (!isDisposed()) { |
| internalDispose(); |
| } |
| } finally { |
| disposing = false; |
| } |
| } |
| |
| /** |
| * Is called to dispose all this instance. |
| */ |
| protected void internalDispose() { |
| try { |
| // unregisters the observer from observing the model |
| unregisterAdapter(this); |
| |
| // first call the dispose listener and the set disposed=true |
| notifyDisposeListeners(); |
| |
| viewContext = null; |
| } finally { |
| disposed = true; |
| } |
| } |
| |
| /** |
| * Subclasses may override. |
| * <p> |
| * Default implementation of request dispose directly invokes #dispose() |
| */ |
| @Override |
| public void requestDispose() { |
| dispose(); |
| } |
| |
| /** |
| * Checks whether the element is disposed. Throws a DisposeException is the |
| * element is disposed. |
| * |
| * @throws DisposeException |
| * the dispose exception |
| */ |
| protected void checkDisposed() { |
| IDisposable.DisposableUtil.checkDisposed(this); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.osbp.runtime.common.dispose.IDisposable#addDisposeListener |
| * (org.eclipse.osbp.runtime.common.dispose.IDisposable.Listener) |
| */ |
| @Override |
| public void addDisposeListener(Listener listener) { |
| checkDisposed(); |
| |
| if (listener == null) { |
| return; |
| } |
| |
| if (disposeListeners == null) { |
| disposeListeners = new ArrayList<IDisposable.Listener>(); |
| } |
| |
| if (!disposeListeners.contains(listener)) { |
| disposeListeners.add(listener); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.osbp.runtime.common.dispose.IDisposable#removeDisposeListener |
| * (org.eclipse.osbp.runtime.common.dispose.IDisposable.Listener) |
| */ |
| @Override |
| public void removeDisposeListener(Listener listener) { |
| checkDisposed(); |
| |
| if (listener == null || disposeListeners == null) { |
| return; |
| } |
| |
| disposeListeners.remove(listener); |
| } |
| |
| /** |
| * Notifies all listeners about the disposal of that elemenyElement. |
| */ |
| protected void notifyDisposeListeners() { |
| if (disposeListeners == null) { |
| return; |
| } |
| |
| for (IDisposable.Listener listener : disposeListeners |
| .toArray(new IDisposable.Listener[disposeListeners.size()])) { |
| listener.notifyDisposed(this); |
| } |
| } |
| } |