| /* |
| * |
| * Copyright (c) 2011, 2016 - 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 |
| * Loetz GmbH&Co.KG |
| * |
| */ |
| package org.eclipse.osbp.vaadin.emf.data; |
| |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.emf.common.notify.AdapterFactory; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.edit.domain.EditingDomain; |
| import org.eclipse.emf.edit.provider.IChangeNotifier; |
| import org.eclipse.emf.edit.provider.IItemLabelProvider; |
| import org.eclipse.emf.edit.provider.INotifyChangedListener; |
| import org.eclipse.emf.edit.provider.ITreeItemContentProvider; |
| import org.eclipse.osbp.runtime.web.vaadin.common.resource.IResourceProvider; |
| import org.eclipse.osbp.vaadin.emf.api.IModelingContext; |
| import org.eclipse.osbp.vaadin.emf.views.UiSync; |
| |
| import com.vaadin.data.Item; |
| import com.vaadin.data.util.HierarchicalContainer; |
| import com.vaadin.server.Resource; |
| |
| // TODO: Auto-generated Javadoc |
| /** |
| * The base container to provide eObject hierarchy and their values based on |
| * emf.edit. It registers a change notifier on the model to refresh items on the |
| * fly. |
| * <p> |
| * ATTENTION: This container MUST be disposed! |
| */ |
| @SuppressWarnings("serial") |
| public class EmfModelTreeContainer extends HierarchicalContainer { |
| |
| /** The Constant ICON. */ |
| private static final String ICON = "icon"; |
| |
| /** The Constant LABEL. */ |
| private static final String LABEL = "label"; |
| |
| /** The root element. */ |
| private EObject rootElement; |
| |
| /** The editing domain. */ |
| private final EditingDomain editingDomain; |
| |
| /** The adapter factory. */ |
| private final AdapterFactory adapterFactory; |
| |
| /** The ui sync. */ |
| private final UiSync uiSync; |
| |
| /** The resolved. */ |
| private Set<Object> resolved = new HashSet<Object>(); |
| |
| /** The resource provider. */ |
| private IResourceProvider resourceProvider; |
| |
| /** The change listener. */ |
| private INotifyChangedListener changeListener; |
| |
| /** |
| * Instantiates a new emf model tree container. |
| * |
| * @param modelingContext |
| * the modeling context |
| * @param resourceProvider |
| * the resource provider |
| * @param uiSync |
| * the ui sync |
| */ |
| public EmfModelTreeContainer(IModelingContext modelingContext, |
| IResourceProvider resourceProvider, UiSync uiSync) { |
| this.resourceProvider = resourceProvider; |
| this.editingDomain = modelingContext.getEditingDomain(); |
| this.adapterFactory = modelingContext.getAdapterFactory(); |
| this.uiSync = uiSync; |
| |
| addContainerProperty(LABEL, String.class, ""); |
| addContainerProperty(ICON, Resource.class, null); |
| |
| IChangeNotifier notifier = (IChangeNotifier) modelingContext |
| .getAdapterFactory(); |
| notifier.addListener(getModelChangeListener()); |
| } |
| |
| /** |
| * Gets the root element. |
| * |
| * @return the root element |
| */ |
| public EObject getRootElement() { |
| return rootElement; |
| } |
| |
| /** |
| * Sets the root element. |
| * |
| * @param rootElement |
| * the new root element |
| */ |
| public void setRootElement(EObject rootElement) { |
| if (this.rootElement == rootElement) { |
| return; |
| } |
| |
| removeAllItems(); |
| resolved.clear(); |
| |
| this.rootElement = rootElement; |
| |
| loadRootElements(); |
| } |
| |
| /** |
| * Load root elements. |
| */ |
| private void loadRootElements() { |
| if (rootElement != null) { |
| doAddItem(null, rootElement, -1); |
| resolveChildren(rootElement); |
| } |
| } |
| |
| /** |
| * Resolve children. |
| * |
| * @param itemId |
| * the item id |
| */ |
| public void resolveChildren(Object itemId) { |
| // if children are allowed and they have not been resolved so far |
| if (areChildrenAllowed(itemId) |
| && (getChildren(itemId) == null || getChildren(itemId) |
| .isEmpty())) { |
| resolved.add(itemId); |
| |
| ITreeItemContentProvider parentTreeProvider = (ITreeItemContentProvider) adapterFactory |
| .adapt(itemId, ITreeItemContentProvider.class); |
| |
| // resolve children |
| for (Object newChild : parentTreeProvider.getChildren(itemId)) { |
| doAddItem(itemId, (EObject) newChild, -1); |
| } |
| } |
| } |
| |
| /** |
| * Refreshes the given node. |
| * |
| * @param itemId |
| * the item id |
| */ |
| public void refreshNode(Object itemId) { |
| for (Object child : new ArrayList<>(getChildren(itemId))) { |
| removeItemRecursively(child); |
| } |
| resolved.remove(itemId); |
| resolveChildren(itemId); |
| } |
| |
| /** |
| * Adds a new item. |
| * |
| * @param parent |
| * the parent |
| * @param newChild |
| * the new child |
| * @param index |
| * pass -1 for last index |
| */ |
| @SuppressWarnings("unchecked") |
| private void doAddItem(Object parent, EObject newChild, int index) { |
| if (newChild == null || (parent != null && !containsId(parent))) { |
| return; |
| } |
| Item item; |
| if (index >= 0) { |
| item = addItemAt(index, newChild); |
| } else { |
| item = addItem(newChild); |
| } |
| if(item == null) { |
| // TODO handle same EObject instances |
| return; |
| } |
| IItemLabelProvider labelProvider = (IItemLabelProvider) adapterFactory |
| .adapt(newChild, IItemLabelProvider.class); |
| item.getItemProperty(LABEL).setValue(labelProvider.getText(newChild)); |
| |
| // parse the platform uri |
| URL url = ((URL) labelProvider.getImage(newChild)); |
| item.getItemProperty(ICON).setValue( |
| resourceProvider.getResource(url.toString())); |
| |
| // calculate children allowed |
| // ITreeItemContentProvider treeProvider = (ITreeItemContentProvider) |
| // adapterFactory |
| // .adapt(newChild, ITreeItemContentProvider.class); |
| // setChildrenAllowed(newChild, treeProvider.hasChildren(newChild)); |
| |
| // if the newChild is not root and the container is available in the |
| // tree |
| if (newChild.eContainer() != null && containsId(newChild.eContainer())) { |
| setParent(newChild, newChild.eContainer()); |
| // the new parent may have children again |
| // setChildrenAllowed(newChild.eContainer(), true); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see com.vaadin.data.util.HierarchicalContainer#hasChildren(java.lang.Object) |
| */ |
| @Override |
| public boolean hasChildren(Object itemId) { |
| boolean result = super.hasChildren(itemId); |
| if (!result) { |
| // calculate children allowed |
| ITreeItemContentProvider treeProvider = (ITreeItemContentProvider) adapterFactory |
| .adapt(itemId, ITreeItemContentProvider.class); |
| result = treeProvider.hasChildren(itemId); |
| } |
| return result; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.vaadin.data.util.HierarchicalContainer#areChildrenAllowed(java.lang.Object) |
| */ |
| @Override |
| public boolean areChildrenAllowed(Object itemId) { |
| // calculate children allowed |
| ITreeItemContentProvider treeProvider = (ITreeItemContentProvider) adapterFactory |
| .adapt(itemId, ITreeItemContentProvider.class); |
| return treeProvider.hasChildren(itemId); |
| } |
| |
| /** |
| * Gets the model change listener. |
| * |
| * @return the model change listener |
| */ |
| private INotifyChangedListener getModelChangeListener() { |
| this.changeListener = e -> handleNotification(e); |
| return changeListener; |
| } |
| |
| /** |
| * Refreshes the item with the given eObject. |
| * |
| * @param notification |
| * the notification |
| */ |
| protected void handleNotification(Notification notification) { |
| if (notification.isTouch()) { |
| return; |
| } |
| |
| switch (notification.getEventType()) { |
| case Notification.ADD: |
| uiSync.sync(new Runnable() { |
| @Override |
| public void run() { |
| if (notification.getNewValue() instanceof EObject) { |
| EObject addItem = (EObject) notification.getNewValue(); |
| doAddItem(addItem.eContainer(), addItem, |
| notification.getPosition()); |
| } |
| } |
| }); |
| break; |
| case Notification.REMOVE: |
| uiSync.sync(new Runnable() { |
| @Override |
| public void run() { |
| if (notification.getOldValue() instanceof EObject) { |
| EObject removeItem = (EObject) notification |
| .getOldValue(); |
| removeItemRecursively(removeItem); |
| } |
| } |
| }); |
| break; |
| case Notification.MOVE: |
| EObject moveItem = (EObject) notification.getNewValue(); |
| EStructuralFeature feature = moveItem.eContainingFeature(); |
| if (feature.isMany()) { |
| EObject container = moveItem.eContainer(); |
| // if the parent is not visible, then leave |
| if (!containsId(container)) { |
| return; |
| } |
| uiSync.sync(new Runnable() { |
| @SuppressWarnings("unchecked") |
| @Override |
| public void run() { |
| List<EObject> eObjects = (List<EObject>) container |
| .eGet(feature); |
| EObject previousSibling = null; |
| if (notification.getPosition() > 0) { |
| previousSibling = eObjects.get(notification |
| .getPosition() - 1); |
| moveAfterSibling(moveItem, previousSibling); |
| } |
| } |
| }); |
| } |
| break; |
| case Notification.SET: |
| // update the property with the changed value |
| uiSync.sync(new Runnable() { |
| @Override |
| public void run() { |
| EObject notifier = (EObject) notification.getNotifier(); |
| if (notification.getNewValue() instanceof EObject) { |
| EObject itemId = (EObject) notification.getNewValue(); |
| // the containment reference was set |
| if (itemId.eContainingFeature() == notification |
| .getFeature()) { |
| removeItem(itemId); |
| doAddItem(itemId.eContainer(), itemId, -1); |
| } else { |
| refreshLabel(notifier); |
| } |
| } else { |
| refreshLabel(notifier); |
| } |
| } |
| |
| @SuppressWarnings({ "unchecked" }) |
| private void refreshLabel(EObject notifier) { |
| Item item = getItem(notifier); |
| if (item != null) { |
| IItemLabelProvider labelProvider = (IItemLabelProvider) adapterFactory |
| .adapt(notifier, IItemLabelProvider.class); |
| item.getItemProperty(LABEL).setValue( |
| labelProvider.getText(notifier)); |
| } |
| } |
| }); |
| break; |
| case Notification.ADD_MANY: |
| uiSync.sync(new Runnable() { |
| @SuppressWarnings("unchecked") |
| @Override |
| public void run() { |
| if (notification.getOldValue() instanceof Collection) { |
| Collection<Object> elements = (Collection<Object>) notification |
| .getNewValue(); |
| for (Object element : elements) { |
| EObject addItem = (EObject) element; |
| doAddItem(addItem.eContainer(), addItem, |
| notification.getPosition()); |
| } |
| } |
| } |
| }); |
| break; |
| case Notification.REMOVE_MANY: |
| uiSync.sync(new Runnable() { |
| @SuppressWarnings("unchecked") |
| @Override |
| public void run() { |
| if (notification.getOldValue() instanceof Collection) { |
| Collection<Object> elements = (Collection<Object>) notification |
| .getOldValue(); |
| for (Object element : elements) { |
| EObject removeItem = (EObject) element; |
| removeItemRecursively(removeItem); |
| } |
| } |
| } |
| }); |
| break; |
| } |
| } |
| |
| /** |
| * This container MUST be disposed!. |
| */ |
| public void dispose() { |
| IChangeNotifier notifier = (IChangeNotifier) adapterFactory; |
| notifier.removeListener(changeListener); |
| } |
| } |