blob: 62ee9a697523a42aeeaf9edbf1bd8208780b9558 [file] [log] [blame]
/*
* Copyright (c) 2009-2013 Eike Stepper (Berlin, Germany) 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:
* Eike Stepper - initial API and implementation
* Victor Roldan Betancort - maintenance
*/
package org.eclipse.emf.cdo.ui.ide;
import org.eclipse.emf.cdo.CDOObject;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
import org.eclipse.emf.cdo.common.branch.CDOBranchCreatedEvent;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.internal.ui.actions.RemoveResourceActionDelegate;
import org.eclipse.emf.cdo.team.IRepositoryManager;
import org.eclipse.emf.cdo.team.IRepositoryProject;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
import org.eclipse.emf.cdo.transaction.CDOTransactionCommentator;
import org.eclipse.emf.cdo.ui.CDOEditorInput;
import org.eclipse.emf.cdo.ui.CDOEditorUtil;
import org.eclipse.emf.cdo.ui.CDOEventHandler;
import org.eclipse.emf.cdo.ui.ide.CommonNavigatorUtils.MessageType;
import org.eclipse.emf.cdo.ui.ide.Node.BranchNode;
import org.eclipse.emf.cdo.ui.ide.Node.PackagesNode;
import org.eclipse.emf.cdo.ui.ide.Node.ResourcesNode;
import org.eclipse.emf.cdo.ui.ide.Node.SessionsNode;
import org.eclipse.emf.cdo.ui.internal.ide.bundle.OM;
import org.eclipse.emf.cdo.ui.internal.ide.messages.Messages;
import org.eclipse.emf.cdo.util.CDOUtil;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.cdo.view.CDOViewInvalidationEvent;
import org.eclipse.emf.cdo.view.CDOViewTargetChangedEvent;
import org.eclipse.emf.internal.cdo.view.CDOStateMachine2;
import org.eclipse.net4j.util.container.ContainerEventAdapter;
import org.eclipse.net4j.util.container.IContainer;
import org.eclipse.net4j.util.event.IEvent;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.net4j.util.ui.StructuredContentProvider;
import org.eclipse.net4j.util.ui.UIUtil;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.edit.EMFEditPlugin;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory.Descriptor.Registry;
import org.eclipse.emf.edit.provider.ITreeItemContentProvider;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emf.spi.cdo.InternalCDOObject;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* {@link org.eclipse.jface.viewers.ITreeContentProvider ITreeContentProvider} implementation for the CDO Team
* integration. Capable of providing containment information for an {@link org.eclipse.core.resources.IProject project}
* synchronized with a repository. Understands containment information of the {@link org.eclipse.emf.cdo.ui.ide.Node
* node} abstraction.
*
* @author Eike Stepper
*/
public class RepositoryContentProvider extends StructuredContentProvider<IWorkspaceRoot> implements
ITreeContentProvider
{
private static final Object[] EMPTY = {};
private ComposedAdapterFactory adapterFactory;
private Map<IRepositoryProject, RepositoryInfo> infos = new HashMap<IRepositoryProject, RepositoryInfo>();
private Map<IRepositoryProject, RepositoryCDOEventHandler> eventHandlers = new HashMap<IRepositoryProject, RepositoryCDOEventHandler>();
private boolean sessionsNodeHidden;
private boolean packagesNodeHidden;
private boolean resourcesNodeHidden;
private IListener repositoryManagerListener = new ContainerEventAdapter<IRepositoryProject>()
{
@Override
protected void onAdded(IContainer<IRepositoryProject> container, IRepositoryProject element)
{
refreshViewer(element);
}
@Override
protected void onRemoved(IContainer<IRepositoryProject> container, IRepositoryProject element)
{
refreshViewer(element);
}
private void refreshViewer(IRepositoryProject element)
{
getViewer().refresh(element.getProject());
}
};
public RepositoryContentProvider()
{
adapterFactory = createAdapterFactory();
IRepositoryManager.INSTANCE.addListener(repositoryManagerListener);
}
@Override
public void dispose()
{
IRepositoryManager.INSTANCE.removeListener(repositoryManagerListener);
adapterFactory.dispose();
super.dispose();
}
public boolean isSessionsNodeHidden()
{
return sessionsNodeHidden;
}
public void setSessionsNodeHidden(boolean sessionNodesHidden)
{
sessionsNodeHidden = sessionNodesHidden;
}
public boolean isPackagesNodeHidden()
{
return packagesNodeHidden;
}
public void setPackagesNodeHidden(boolean packageNodesHidden)
{
packagesNodeHidden = packageNodesHidden;
}
public boolean isResourcesNodeHidden()
{
return resourcesNodeHidden;
}
public void setResourcesNodeHidden(boolean resourceNodesHidden)
{
resourcesNodeHidden = resourceNodesHidden;
}
public Object[] getChildren(Object parentElement)
{
try
{
return doGetChildren(parentElement);
}
catch (Exception e)
{
OM.LOG.error(e);
return CommonNavigatorUtils.createMessageProviderChild(Messages.getString("RepositoryContentProvider_0"), //$NON-NLS-1$
MessageType.ERROR);
}
}
private Object[] doGetChildren(Object parentElement)
{
if (parentElement instanceof IProject)
{
IProject project = (IProject)parentElement;
IRepositoryProject repositoryProject = IRepositoryManager.INSTANCE.getElement(project);
if (repositoryProject != null)
{
return getChildren(repositoryProject);
}
}
if (parentElement instanceof Node)
{
Node node = (Node)parentElement;
return node.getChildren();
}
if (parentElement instanceof Notifier)
{
Notifier notifier = (Notifier)parentElement;
ITreeItemContentProvider adapter = (ITreeItemContentProvider)adapterFactory.adapt(notifier,
ITreeItemContentProvider.class);
if (adapter != null)
{
return adapter.getChildren(notifier).toArray();
}
}
return EMPTY;
}
public Object[] getElements(Object parentElement)
{
return getChildren(parentElement);
}
public Object getParent(Object element)
{
if (element instanceof Node)
{
Node node = (Node)element;
return node.getParent();
}
if (element instanceof Notifier)
{
Notifier notifier = (Notifier)element;
ITreeItemContentProvider adapter = (ITreeItemContentProvider)adapterFactory.adapt(notifier,
ITreeItemContentProvider.class);
if (adapter != null)
{
return adapter.getParent(notifier);
}
}
return null;
}
private Object[] getChildren(IRepositoryProject repositoryProject)
{
List<Object> children = new ArrayList<Object>();
RepositoryInfo info = getRepositoryInfo(repositoryProject);
children.add(info.getMainBranch());
// First try virtual parent nodes
if (!isPackagesNodeHidden())
{
children.add(info.getPackages());
}
if (!isResourcesNodeHidden())
{
children.add(info.getResources());
}
if (!isSessionsNodeHidden())
{
children.add(info.getSessions());
}
// Then try flattened sub nodes
if (isPackagesNodeHidden())
{
addChildren(children, info.getPackages());
}
if (isResourcesNodeHidden())
{
addChildren(children, info.getResources());
}
if (isSessionsNodeHidden())
{
addChildren(children, info.getSessions());
}
return children.toArray(new Object[children.size()]);
}
private void addChildren(List<Object> result, Node node)
{
Object[] children = node.getChildren();
for (Object child : children)
{
result.add(child);
}
}
private RepositoryInfo getRepositoryInfo(IRepositoryProject repositoryProject)
{
RepositoryInfo info = infos.get(repositoryProject);
if (info == null)
{
info = new RepositoryInfo(repositoryProject);
infos.put(repositoryProject, info);
wireUpViewerRefresher(repositoryProject, info);
prepareViewerEventHandlers();
// TODO Get rid of info mappings that are no longer needed (lifecycle or weakref)
}
return info;
}
private void prepareViewerEventHandlers()
{
// Mouse double-click
getViewer().addDoubleClickListener(new MouseListener());
// Keyboard actions
getViewer().getControl().addKeyListener(new CDONavigatorKeyListener());
}
private void wireUpViewerRefresher(IRepositoryProject repositoryProject, RepositoryInfo info)
{
// Handles invalidated objects
eventHandlers.put(repositoryProject, new RepositoryCDOEventHandler(info.getResources(),
repositoryProject.getView(), (TreeViewer)getViewer()));
repositoryProject.getView().getBranch().getBranchManager().addListener(new IListener()
{
public void notifyEvent(IEvent event)
{
if (event instanceof CDOBranchCreatedEvent)
{
refreshViewer(true);
}
}
});
repositoryProject.getView().addListener(new IListener()
{
public void notifyEvent(IEvent event)
{
if (event instanceof CDOViewInvalidationEvent || event instanceof CDOViewTargetChangedEvent)
{
refreshViewer(true);
}
}
});
}
public static ComposedAdapterFactory createAdapterFactory()
{
Registry registry = EMFEditPlugin.getComposedAdapterFactoryDescriptorRegistry();
ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(registry);
adapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());
return adapterFactory;
}
/**
* @author Victor Roldan Betancort
*/
private final class RepositoryCDOEventHandler extends CDOEventHandler
{
private Node resourcesNode;
public RepositoryCDOEventHandler(Node resourcesNode, CDOView view, TreeViewer treeViewer)
{
super(view, treeViewer);
this.resourcesNode = resourcesNode;
}
@Override
protected void objectInvalidated(InternalCDOObject cdoObject)
{
if (CDOUtil.isLegacyObject(cdoObject))
{
CDOStateMachine2.INSTANCE.read(cdoObject);
}
if (cdoObject instanceof CDOResource)
{
if (((CDOResource)cdoObject).isRoot())
{
refreshViewer(true);
return;
}
}
refreshElement(cdoObject, true);
}
@Override
protected void viewInvalidated(Set<? extends CDOObject> dirtyObjects)
{
// Necessary when the parent of the dirtyObject is ResourcesNode
// (since viewer.getInput() is IWorkspaceRoot)
for (CDOObject cdoObject : dirtyObjects)
{
if (cdoObject instanceof CDOResource)
{
if (((CDOResource)cdoObject).isRoot())
{
refreshElement(resourcesNode, true);
return;
}
}
}
super.viewInvalidated(dirtyObjects);
}
@Override
protected void viewConflict(final CDOObject conflictingObject, boolean firstConflict)
{
refreshElement(conflictingObject, true);
}
@Override
protected void viewClosed()
{
// TODO what should we do here? CDOObjects become disconnected, exceptions could arise everywhere
// Temporary closing the project.
try
{
// View gets on shutdown, but we shouldn't close the project
if (PlatformUI.isWorkbenchRunning())
{
resourcesNode.getRepositoryProject().getProject().close(new NullProgressMonitor());
}
}
catch (CoreException ex)
{
OM.LOG.error(ex);
}
}
@Override
protected void viewDirtyStateChanged()
{
}
}
/**
* @author Eike Stepper
*/
private static final class RepositoryInfo
{
private BranchNode mainBranch;
private PackagesNode packages;
private ResourcesNode resources;
private SessionsNode sessions;
public RepositoryInfo(IRepositoryProject repositoryProject)
{
CDOBranch main = repositoryProject.getView().getSession().getBranchManager().getMainBranch();
mainBranch = new BranchNode(repositoryProject, main);
packages = new PackagesNode(repositoryProject);
resources = new ResourcesNode(repositoryProject);
sessions = new SessionsNode(repositoryProject);
}
public BranchNode getMainBranch()
{
return mainBranch;
}
public PackagesNode getPackages()
{
return packages;
}
public ResourcesNode getResources()
{
return resources;
}
public SessionsNode getSessions()
{
return sessions;
}
}
/**
* @author Victor Roldan Betancort
*/
private static final class MouseListener implements IDoubleClickListener
{
public MouseListener()
{
}
public void doubleClick(DoubleClickEvent event)
{
Object selection = UIUtil.getElement(event.getSelection());
if (selection instanceof CDOResource)
{
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
CDOTransaction transaction = ((CDOResource)selection).cdoView().getSession().openTransaction();
new CDOTransactionCommentator(transaction);
try
{
CDOEditorInput editorInput = CDOEditorUtil.createCDOEditorInput(transaction,
((CDOResource)selection).getPath(), true);
page.openEditor(editorInput, CDOEditorUtil.getEditorID());
}
catch (PartInitException ex)
{
OM.LOG.error(ex);
}
}
if (selection instanceof IAdaptable)
{
Runnable runnable = (Runnable)((IAdaptable)selection).getAdapter(Runnable.class);
if (runnable != null)
{
UIUtil.getDisplay().asyncExec(runnable);
}
}
}
}
public boolean hasChildren(Object parentElement)
{
Object[] children = getChildren(parentElement);
return children != null && children.length != 0;
}
/**
* @author Victor Roldan Betancort
*/
private final class CDONavigatorKeyListener extends KeyAdapter
{
public CDONavigatorKeyListener()
{
}
@Override
public void keyPressed(KeyEvent e)
{
if (e.keyCode == SWT.DEL)
{
RemoveResourceActionDelegate action = new RemoveResourceActionDelegate();
action.selectionChanged(null, getViewer().getSelection());
action.run(null);
}
}
}
}