/*
* Copyright (c) 2008, 2009 Borland Software Corporation
*
* 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:
* Tatiana Fesenko (Borland) - initial API and implementation
*/
package org.eclipse.uml2.diagram.common.sheet.chooser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.UIPlugin;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.uml2.diagram.common.Messages;
import org.eclipse.uml2.diagram.common.providers.ImageUtils;
public class ElementTreeChooser implements ElementChooserPage {
private final AdapterFactory myItemProvidersAdapterFactory;
private TreeViewer myTreeViewer;
private URI mySelectedModelElementURI;
private final TransactionalEditingDomain myEditingDomain;
private final EObject mySourceObject;
private final EStructuralFeature myFeature;
private MainRoot myRoot;
public ElementTreeChooser(AdapterFactory itemProvidersAdapterFactory, EObject sourceObject, EStructuralFeature feature, TransactionalEditingDomain editingDomain) {
myItemProvidersAdapterFactory = itemProvidersAdapterFactory;
mySourceObject = sourceObject;
myFeature = feature;
myEditingDomain = editingDomain;
}
public Control createControl(Composite parent) {
Composite composite = createModelBrowser(parent);
myRoot = new MainRoot(mySourceObject);
myTreeViewer.setInput(myRoot);
myTreeViewer.addFilter(new UMLFileFilter());
if (myFeature instanceof EReference) {
// false -- for now always ignore template-related containments, see
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=261691#c3
myTreeViewer.addFilter(new UMLContainmentFilter((EReference) myFeature, false));
}
myTreeViewer.expandToLevel(myRoot.getCurrentResourceRoot(), 1);
myTreeViewer.expandToLevel(myRoot.getLoadedResourcesRoot(), 1);
new MenuBuilder(myTreeViewer).attachMenu();
return composite;
}
public List<?> getSelection() {
return ((IStructuredSelection) myTreeViewer.getSelection()).toList();
}
public void setSelection(List<?> selection) {
if (selection == null || selection.isEmpty()) {
myTreeViewer.setSelection(null);
} else {
ArrayList<TreePath> paths = new ArrayList<TreePath>(selection.size());
for (Object next : selection) {
TreePath path = getTreePath(next);
if (path != null) {
paths.add(path);
}
}
myTreeViewer.setSelection(new StructuredSelection(paths), true);
}
}
private Composite createModelBrowser(Composite composite) {
myTreeViewer = new TreeViewer(composite, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
myTreeViewer.setContentProvider(new ModelElementsTreeContentProvider(myItemProvidersAdapterFactory, myEditingDomain.getResourceSet()));
myTreeViewer.setLabelProvider(new ModelElementsTreeLabelProvider());
GridData layoutData = new GridData(GridData.FILL_BOTH);
layoutData.heightHint = 300;
layoutData.widthHint = 300;
Tree result = myTreeViewer.getTree();
result.setLayout(new GridLayout());
result.setLayoutData(layoutData);
return result;
}
public URI getSelectedModelElementURI() {
return mySelectedModelElementURI;
}
public void dispose() {
for (Resource resource : myEditingDomain.getResourceSet().getResources()) {
resource.unload();
}
myEditingDomain.dispose();
}
private TreePath getTreePath(Object element) {
if (element == null) {
return null;
}
if (false == element instanceof EObject) {
return new TreePath(new Object[] { element });
}
List<Object> segments = buildPathSegments((EObject) element);
Collections.reverse(segments);
TreePath path = new TreePath(segments.toArray(new Object[segments.size()]));
return path;
}
private List<Object> buildPathSegments(EObject element) {
List<Object> segments = new ArrayList<Object>();
EObject container = element;
while (container != null) {
segments.add(container);
container = container.eContainer();
}
if (isInCurrentResource(element)) {
segments.add(myRoot.getCurrentResourceRoot());
segments.add(myRoot);
return segments;
}
if (inInLoadedResource(element)) {
segments.add(element.eResource());
segments.add(myRoot.getLoadedResourcesRoot());
segments.add(myRoot);
return segments;
}
Path path = new Path(element.eResource().getURI().toFileString());
IResource resource = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
while (resource != null) {
segments.add(resource);
resource = resource.getParent();
}
segments.add(myRoot.getWorkspaceRoot());
segments.add(myRoot);
return segments;
}
private boolean inInLoadedResource(EObject element) {
return mySourceObject.eResource().getResourceSet().getResources().contains(element.eResource());
}
private boolean isInCurrentResource(EObject element) {
return mySourceObject.eResource().equals(element.eResource());
}
private static class ModelElementsTreeContentProvider implements ITreeContentProvider {
private static final Object[] NOTHING = new Object[0];
private static final ITreeContentProvider myWorkbenchContentProvider = new WorkbenchContentProvider();
private final AdapterFactoryContentProvider myAdapterFctoryContentProvier;
private final ResourceSet myResourceSet;
public ModelElementsTreeContentProvider(AdapterFactory itemProvidersAdapterFactory, ResourceSet resourceSet) {
myAdapterFctoryContentProvier = new AdapterFactoryContentProvider(itemProvidersAdapterFactory);
myResourceSet = resourceSet;
}
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof TreeRoot) {
parentElement = ((TreeRoot) parentElement).getObject();
}
Object[] result = myWorkbenchContentProvider.getChildren(parentElement);
if (result != null && result.length > 0) {
return result;
}
if (parentElement instanceof ResourceSet) {
EList<Resource> resources = ((ResourceSet) parentElement).getResources();
return resources.toArray(new Resource[resources.size()]);
}
if (parentElement instanceof IFile) {
IFile modelFile = (IFile) parentElement;
IPath resourcePath = modelFile.getFullPath();
try {
Resource modelResource = myResourceSet.getResource(URI.createPlatformResourceURI(resourcePath.toString(), true), true);
return myAdapterFctoryContentProvier.getChildren(modelResource);
} catch (WrappedException e) {
e.printStackTrace();
//
}
return NOTHING;
}
return myAdapterFctoryContentProvier.getChildren(parentElement);
}
public Object getParent(Object element) {
Object parent = myWorkbenchContentProvider.getParent(element);
if (parent != null) {
return parent;
}
if (false == element instanceof EObject) {
return null;
}
EObject eObject = (EObject) element;
if (eObject.eContainer() == null && eObject.eResource().getURI().isFile()) {
String path = eObject.eResource().getURI().path();
return ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(new Path(path));
}
return myAdapterFctoryContentProvier.getParent(eObject);
}
public boolean hasChildren(Object element) {
if (element instanceof TreeRoot) {
element = ((TreeRoot) element).getObject();
}
if (element instanceof IFile) {
return isValidModelFile((IFile) element);
}
return myWorkbenchContentProvider.hasChildren(element) || myAdapterFctoryContentProvier.hasChildren(element);
}
private boolean isValidModelFile(IFile file) {
String fileExtension = file.getFullPath().getFileExtension();
return "uml".equals(fileExtension); //$NON-NLS-1$
}
public Object[] getElements(Object inputElement) {
MainRoot root = (MainRoot) inputElement;
return new Object[] { root.getCurrentResourceRoot(), root.getLoadedResourcesRoot(), root.getWorkspaceRoot() };
}
public void dispose() {
myAdapterFctoryContentProvier.dispose();
myWorkbenchContentProvider.dispose();
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
myWorkbenchContentProvider.inputChanged(viewer, oldInput, newInput);
myAdapterFctoryContentProvier.inputChanged(viewer, oldInput, newInput);
}
}
private interface TreeRoot {
Object getObject();
String getLabel();
}
class MainRoot implements TreeRoot {
private EObject object;
private TreeRoot myCurrentResourceRoot;
private TreeRoot myLoadedResourcesRoot;
private TreeRoot myWorkspaceRoot;
MainRoot(EObject object) {
this.object = object;
}
public EObject getObject() {
return object;
}
public String getLabel() {
return null;
}
public TreeRoot getCurrentResourceRoot() {
if (myCurrentResourceRoot == null) {
myCurrentResourceRoot = new CurrentResource(object);
}
return myCurrentResourceRoot;
}
public TreeRoot getLoadedResourcesRoot() {
if (myLoadedResourcesRoot == null) {
myLoadedResourcesRoot = new LoadedResources(object);
}
return myLoadedResourcesRoot;
}
public TreeRoot getWorkspaceRoot() {
if (myWorkspaceRoot == null) {
myWorkspaceRoot = new Workspace(object);
}
return myWorkspaceRoot;
}
private class CurrentResource implements TreeRoot {
private Resource object;
CurrentResource(EObject object) {
this.object = object.eResource();
}
public Object getObject() {
return object;
}
public String getLabel() {
return Messages.ElementTreeChooser_label_current_resources;
}
}
private class LoadedResources implements TreeRoot {
private ResourceSet object;
LoadedResources(EObject object) {
this.object = object.eResource().getResourceSet();
}
public Object getObject() {
return object;
}
public String getLabel() {
return Messages.ElementTreeChooser_label_loaded_resources;
}
}
private class Workspace implements TreeRoot {
private IWorkspaceRoot object;
Workspace(EObject object) {
this.object = ResourcesPlugin.getWorkspace().getRoot();
}
public Object getObject() {
return object;
}
public String getLabel() {
return Messages.ElementTreeChooser_label_workspace;
}
}
}
private class ModelElementsTreeLabelProvider implements ILabelProvider {
private WorkbenchLabelProvider myWorkbenchLabelProvider = new WorkbenchLabelProvider();
private AdapterFactoryLabelProvider myAdapterFactoryLabelProvider = new AdapterFactoryLabelProvider(myItemProvidersAdapterFactory);
public Image getImage(Object element) {
if (element instanceof TreeRoot) {
element = ((TreeRoot) element).getObject();
}
Image result = myWorkbenchLabelProvider.getImage(element);
return result != null ? result : myAdapterFactoryLabelProvider.getImage(element);
}
public String getText(Object element) {
if (element instanceof TreeRoot) {
return ((TreeRoot) element).getLabel();
}
String result = myWorkbenchLabelProvider.getText(element);
return result != null && result.length() > 0 ? result : myAdapterFactoryLabelProvider.getText(element);
}
public void addListener(ILabelProviderListener listener) {
myWorkbenchLabelProvider.addListener(listener);
myAdapterFactoryLabelProvider.addListener(listener);
}
public void dispose() {
myWorkbenchLabelProvider.dispose();
myAdapterFactoryLabelProvider.dispose();
}
public boolean isLabelProperty(Object element, String property) {
return myWorkbenchLabelProvider.isLabelProperty(element, property) || myAdapterFactoryLabelProvider.isLabelProperty(element, property);
}
public void removeListener(ILabelProviderListener listener) {
myWorkbenchLabelProvider.removeListener(listener);
myAdapterFactoryLabelProvider.removeListener(listener);
}
}
public void addSelectionListener(ISelectionChangedListener l) {
myTreeViewer.addSelectionChangedListener(l);
}
public void addDoubleClickListener(IDoubleClickListener l) {
myTreeViewer.addDoubleClickListener(l);
}
private static class MenuBuilder implements IMenuListener {
private final TreeViewer myViewer;
private final ExpandNodeAction myExpandNodeChildrenAction;
private final CollapseAllAction myCollapseAllChildrenAction;
private static final ImageDescriptor IMG_COLLAPSE_ALL = ImageUtils.getImageDescriptor(ImageUtils.IMG_COLLAPSE_ALL);
public MenuBuilder(final TreeViewer viewer) {
myViewer = viewer;
myCollapseAllChildrenAction = new CollapseAllAction(viewer);
myCollapseAllChildrenAction.setImageDescriptor(IMG_COLLAPSE_ALL);
myCollapseAllChildrenAction.setText(Messages.ElementTreeChooser_action_collapse_all);
myExpandNodeChildrenAction = new ExpandNodeAction(viewer);
myExpandNodeChildrenAction.setText(Messages.ElementTreeChooser_action_expand_node);
}
public void attachMenu() {
MenuManager menuManager = new MenuManager();
menuManager.addMenuListener(this);
menuManager.add(myCollapseAllChildrenAction);
menuManager.add(new Separator());
menuManager.add(myExpandNodeChildrenAction);
myViewer.getTree().setMenu(menuManager.createContextMenu(myViewer.getTree()));
}
public void removeMenu() {
myViewer.getTree().setMenu(null);
}
public void menuAboutToShow(IMenuManager manager) {
myCollapseAllChildrenAction.update();
myExpandNodeChildrenAction.update();
}
}
private static class ExpandNodeAction extends Action {
private List<Object> mySelection;
private final TreeViewer myViewer;
ExpandNodeAction(TreeViewer viewer) {
myViewer = viewer;
}
@Override
public void run() {
Object firstSelected = mySelection.isEmpty() ? null : mySelection.get(0);
if (firstSelected == null) {
return;
}
myViewer.expandToLevel(firstSelected, AbstractTreeViewer.ALL_LEVELS);
}
@SuppressWarnings("unchecked")
public void update() {
mySelection = ((IStructuredSelection) myViewer.getSelection()).toList();
}
}
private static class CollapseAllAction extends Action {
private final TreeViewer myViewer;
CollapseAllAction(TreeViewer viewer) {
myViewer = viewer;
}
@Override
public void run() {
myViewer.collapseAll();
}
public void update() {
}
}
}