blob: 644f2a7084f77a94e73fee8996d462806a4d89a9 [file] [log] [blame]
/**
* 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);
}
}
}