| /* |
| * Copyright (c) 2015 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 |
| */ |
| package org.eclipse.emf.cdo.explorer.ui.checkouts; |
| |
| import org.eclipse.emf.cdo.CDOElement; |
| import org.eclipse.emf.cdo.common.branch.CDOBranch; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; |
| import org.eclipse.emf.cdo.eresource.CDOResource; |
| import org.eclipse.emf.cdo.eresource.CDOResourceFolder; |
| import org.eclipse.emf.cdo.eresource.CDOResourceNode; |
| import org.eclipse.emf.cdo.explorer.CDOExplorerUtil; |
| import org.eclipse.emf.cdo.explorer.checkouts.CDOCheckout; |
| import org.eclipse.emf.cdo.explorer.checkouts.CDOCheckout.ObjectType; |
| import org.eclipse.emf.cdo.explorer.repositories.CDORepository; |
| import org.eclipse.emf.cdo.explorer.ui.bundle.OM; |
| import org.eclipse.emf.cdo.explorer.ui.checkouts.actions.CompareWithActionProvider; |
| import org.eclipse.emf.cdo.explorer.ui.checkouts.actions.MergeFromActionProvider; |
| import org.eclipse.emf.cdo.explorer.ui.checkouts.actions.ReplaceWithActionProvider; |
| import org.eclipse.emf.cdo.explorer.ui.checkouts.actions.SwitchToActionProvider; |
| import org.eclipse.emf.cdo.session.CDOSession; |
| import org.eclipse.emf.cdo.transaction.CDOTransaction; |
| import org.eclipse.emf.cdo.util.CDOUtil; |
| |
| import org.eclipse.net4j.util.AdapterUtil; |
| import org.eclipse.net4j.util.ObjectUtil; |
| |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jface.util.LocalSelectionTransfer; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.DropTargetEvent; |
| import org.eclipse.swt.dnd.FileTransfer; |
| import org.eclipse.swt.dnd.TransferData; |
| import org.eclipse.ui.navigator.CommonDropAdapter; |
| import org.eclipse.ui.navigator.CommonDropAdapterAssistant; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class CDOCheckoutDropAdapterAssistant extends CommonDropAdapterAssistant |
| { |
| private static final EObject[] NO_OBJECTS = {}; |
| |
| public CDOCheckoutDropAdapterAssistant() |
| { |
| } |
| |
| @Override |
| public boolean isSupportedType(TransferData transferType) |
| { |
| return super.isSupportedType(transferType) || FileTransfer.getInstance().isSupportedType(transferType); |
| } |
| |
| @Override |
| public IStatus validateDrop(Object target, int dropOperation, TransferData transferType) |
| { |
| CDOBranchPoint branchPoint = getSelectedBranchPoint(target, transferType); |
| if (branchPoint != null) |
| { |
| return Status.OK_STATUS; |
| } |
| |
| if (dropOperation != DND.DROP_LINK) |
| { |
| Operation<?> operation = Operation.getFor(target, transferType); |
| if (operation != null && operation.canDrop()) |
| { |
| if (dropOperation == DND.DROP_MOVE && !operation.canMove()) |
| { |
| getCommonDropAdapter().overrideOperation(DND.DROP_COPY); |
| } |
| |
| return Status.OK_STATUS; |
| } |
| } |
| |
| return Status.CANCEL_STATUS; |
| } |
| |
| @Override |
| public IStatus handleDrop(CommonDropAdapter dropAdapter, DropTargetEvent dropTargetEvent, Object target) |
| { |
| if (target == null || dropTargetEvent.data == null) |
| { |
| return Status.CANCEL_STATUS; |
| } |
| |
| TransferData transferType = dropAdapter.getCurrentTransfer(); |
| int dropOperation = dropAdapter.getCurrentOperation(); |
| |
| CDOBranchPoint branchPoint = getSelectedBranchPoint(target, transferType); |
| if (branchPoint != null) |
| { |
| CDOCheckout checkout = (CDOCheckout)target; |
| if (dropOperation == DND.DROP_MOVE) |
| { |
| if (checkout.isOnline()) |
| { |
| SwitchToActionProvider.switchTo(checkout, branchPoint); |
| } |
| else |
| { |
| ReplaceWithActionProvider.replaceWith(checkout, branchPoint); |
| } |
| } |
| else if (dropOperation == DND.DROP_COPY) |
| { |
| MergeFromActionProvider.mergeFrom(checkout, branchPoint); |
| } |
| else if (dropOperation == DND.DROP_LINK) |
| { |
| CompareWithActionProvider.compareWith(checkout, branchPoint); |
| } |
| |
| return Status.OK_STATUS; |
| } |
| |
| Operation<?> operation = Operation.getFor(target, transferType); |
| if (operation != null) |
| { |
| operation.drop(dropOperation == DND.DROP_COPY); |
| return Status.OK_STATUS; |
| } |
| |
| return Status.CANCEL_STATUS; |
| } |
| |
| private static CDOBranchPoint getSelectedBranchPoint(Object target, TransferData transferType) |
| { |
| // Drag within Eclipse? |
| if (LocalSelectionTransfer.getTransfer().isSupportedType(transferType)) |
| { |
| ISelection selection = LocalSelectionTransfer.getTransfer().getSelection(); |
| if (target instanceof CDOCheckout && selection instanceof IStructuredSelection) |
| { |
| CDOCheckout checkout = (CDOCheckout)target; |
| if (checkout.isOpen()) |
| { |
| IStructuredSelection ssel = (IStructuredSelection)selection; |
| |
| if (ssel.size() == 1) |
| { |
| Object element = ssel.getFirstElement(); |
| if (element instanceof CDORepository) |
| { |
| CDORepository repository = (CDORepository)element; |
| if (repository.isConnected()) |
| { |
| element = repository.getSession().getBranchManager().getMainBranch(); |
| } |
| } |
| |
| if (element instanceof CDOCheckout) |
| { |
| CDOCheckout otherCheckout = (CDOCheckout)element; |
| CDOBranchPoint branchPoint = checkout.getBranchPoint(otherCheckout); |
| if (branchPoint != null) |
| { |
| element = branchPoint; |
| } |
| } |
| |
| if (element instanceof CDOBranch) |
| { |
| CDOBranch branch = (CDOBranch)element; |
| element = branch.getHead(); |
| } |
| |
| if (element instanceof CDOBranchPoint) |
| { |
| CDOBranchPoint branchPoint = (CDOBranchPoint)element; |
| CDOSession session = CDOUtil.getSession(branchPoint); |
| if (session == checkout.getView().getSession()) |
| { |
| if (checkout.isReadOnly() || branchPoint.getTimeStamp() == CDOBranchPoint.UNSPECIFIED_DATE) |
| { |
| return branchPoint; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| private static EObject[] getSelectedObjects() |
| { |
| ISelection selection = LocalSelectionTransfer.getTransfer().getSelection(); |
| if (selection instanceof IStructuredSelection) |
| { |
| return getSelectedObjects((IStructuredSelection)selection); |
| } |
| |
| return NO_OBJECTS; |
| } |
| |
| private static EObject[] getSelectedObjects(IStructuredSelection selection) |
| { |
| try |
| { |
| List<EObject> selectedObjects = new ArrayList<EObject>(); |
| ObjectType firstObjectType = null; |
| |
| for (Iterator<?> it = selection.iterator(); it.hasNext();) |
| { |
| Object object = it.next(); |
| |
| if (object instanceof CDOElement) |
| { |
| CDOElement element = (CDOElement)object; |
| for (Object child : element.getChildren()) |
| { |
| firstObjectType = addSelectedObject(selectedObjects, firstObjectType, child); |
| } |
| } |
| else |
| { |
| firstObjectType = addSelectedObject(selectedObjects, firstObjectType, object); |
| } |
| } |
| |
| return selectedObjects.toArray(new EObject[selectedObjects.size()]); |
| } |
| catch (RuntimeException ex) |
| { |
| return NO_OBJECTS; |
| } |
| } |
| |
| private static ObjectType addSelectedObject(List<EObject> selectedObjects, ObjectType firstObjectType, Object object) |
| { |
| EObject eObject = AdapterUtil.adapt(object, EObject.class); |
| if (eObject != null) |
| { |
| ObjectType objectType = ObjectType.valueFor(eObject); |
| if (objectType == null || objectType == ObjectType.Root) |
| { |
| throw new RuntimeException(); |
| } |
| |
| if (firstObjectType == null) |
| { |
| firstObjectType = objectType; |
| } |
| else |
| { |
| boolean firstIsObject = firstObjectType == ObjectType.Object; |
| boolean isObject = objectType == ObjectType.Object; |
| if (firstIsObject != isObject) |
| { |
| throw new RuntimeException(); |
| } |
| } |
| |
| selectedObjects.add(eObject); |
| } |
| |
| return firstObjectType; |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static abstract class Operation<T extends EObject> |
| { |
| private final EObject[] objects; |
| |
| private final T target; |
| |
| public Operation(EObject[] objects, T target) |
| { |
| this.objects = objects; |
| this.target = target; |
| } |
| |
| public final EObject[] getObjects() |
| { |
| return objects; |
| } |
| |
| public final T getTarget() |
| { |
| return target; |
| } |
| |
| public boolean canDrop() |
| { |
| T target = getTarget(); |
| EObject[] objects = getObjects(); |
| |
| CDOCheckout targetCheckout = CDOExplorerUtil.getCheckout(target); |
| for (EObject object : objects) |
| { |
| CDOCheckout checkout = CDOExplorerUtil.getCheckout(object); |
| if (checkout != targetCheckout) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| public boolean canMove() |
| { |
| T target = getTarget(); |
| EObject[] objects = getObjects(); |
| |
| for (EObject object : objects) |
| { |
| if (object == target || CDOExplorerUtil.getParent(object) == target) |
| { |
| return false; |
| } |
| } |
| |
| LinkedList<Object> path = CDOExplorerUtil.getPath(target); |
| if (path == null) |
| { |
| return false; |
| } |
| |
| Set<Object> targetPath = new HashSet<Object>(path); |
| |
| for (EObject object : objects) |
| { |
| if (targetPath.contains(object)) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| public final void drop(final boolean copy) |
| { |
| final String title = (copy ? "Copy " : "Move ") |
| + (ObjectType.valueFor(objects[0]) == ObjectType.Object ? "objects" : "resource nodes"); |
| |
| new Job(title) |
| { |
| @Override |
| protected IStatus run(IProgressMonitor monitor) |
| { |
| monitor.beginTask(title, 111 + objects.length); |
| |
| CDOCheckout checkout = CDOExplorerUtil.getCheckout(objects[0]); |
| CDOTransaction transaction = checkout.openTransaction(); |
| monitor.worked(1); |
| |
| try |
| { |
| EcoreUtil.Copier copier = null; |
| if (copy) |
| { |
| copier = new EcoreUtil.Copier(); |
| } |
| |
| List<EObject> transactionalObjects = new ArrayList<EObject>(); |
| for (int i = 0; i < objects.length; i++) |
| { |
| EObject object = objects[i]; |
| EObject transactionalObject = transaction.getObject(object); |
| |
| if (copier != null) |
| { |
| transactionalObject = copier.copy(transactionalObject); |
| } |
| else |
| { |
| EcoreUtil.remove(transactionalObject); |
| } |
| |
| transactionalObjects.add(transactionalObject); |
| monitor.worked(1); |
| } |
| |
| if (copier != null) |
| { |
| copier.copyReferences(); |
| monitor.worked(1); |
| } |
| |
| T transactionalTarget = transaction.getObject(target); |
| |
| insert(transactionalObjects, transactionalTarget, copy, new SubProgressMonitor(monitor, 10)); |
| transaction.commit(new SubProgressMonitor(monitor, 100)); |
| } |
| catch (Exception ex) |
| { |
| OM.LOG.error(ex); |
| return new Status(IStatus.ERROR, OM.BUNDLE_ID, "The operation did not complete sucessfully.", ex); |
| } |
| finally |
| { |
| monitor.done(); |
| transaction.close(); |
| } |
| |
| return Status.OK_STATUS; |
| } |
| }.schedule(); |
| } |
| |
| protected abstract void insert(List<? extends EObject> objects, T target, boolean copy, IProgressMonitor monitor); |
| |
| @SuppressWarnings("unchecked") |
| public static <T extends EObject> Operation<T> getFor(Object target, TransferData transferType) |
| { |
| // Drag within Eclipse? |
| if (LocalSelectionTransfer.getTransfer().isSupportedType(transferType)) |
| { |
| EObject[] selectedObjects = getSelectedObjects(); |
| if (selectedObjects.length != 0) |
| { |
| ObjectType objectType = ObjectType.valueFor(selectedObjects[0]); |
| if (objectType == ObjectType.Object) |
| { |
| if (target instanceof CDOResource) |
| { |
| CDOResource resource = (CDOResource)target; |
| if (!resource.isRoot()) |
| { |
| return (Operation<T>)new ObjectToResource(selectedObjects, resource); |
| } |
| } |
| |
| if (target instanceof EObject && !(target instanceof CDOResourceNode)) |
| { |
| return (Operation<T>)new ObjectToObject(selectedObjects, (EObject)target); |
| } |
| |
| if (target instanceof CDOCheckout) |
| { |
| CDOCheckout checkout = (CDOCheckout)target; |
| EObject rootObject = checkout.getRootObject(); |
| if (rootObject instanceof CDOResource) |
| { |
| return (Operation<T>)new ObjectToResource(selectedObjects, (CDOResource)rootObject); |
| } |
| |
| if (!(rootObject instanceof CDOResourceNode)) |
| { |
| return (Operation<T>)new ObjectToObject(selectedObjects, rootObject); |
| } |
| } |
| } |
| else |
| { |
| if (target instanceof CDOCheckout) |
| { |
| CDOCheckout checkout = (CDOCheckout)target; |
| EObject rootObject = checkout.getRootObject(); |
| if (rootObject instanceof CDOResourceFolder) |
| { |
| return (Operation<T>)new ResourceNodeToFolder(selectedObjects, (CDOResourceFolder)rootObject); |
| } |
| |
| if (rootObject instanceof CDOResource) |
| { |
| CDOResource resource = (CDOResource)rootObject; |
| if (resource.isRoot()) |
| { |
| return (Operation<T>)new ResourceNodeToRootResource(selectedObjects, resource); |
| } |
| } |
| } |
| |
| if (target instanceof CDOResourceFolder) |
| { |
| return (Operation<T>)new ResourceNodeToFolder(selectedObjects, (CDOResourceFolder)target); |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| private static void setUniqueName(CDOResourceNode resourceNode, EList<? extends EObject> targetContents, |
| boolean copy) |
| { |
| boolean nameConflict = false; |
| String resourceName = resourceNode.getName(); |
| Set<String> names = new HashSet<String>(); |
| |
| for (Object existingChild : targetContents) |
| { |
| if (existingChild != resourceNode) |
| { |
| String name = ((CDOResourceNode)existingChild).getName(); |
| if (ObjectUtil.equals(name, resourceName)) |
| { |
| nameConflict = true; |
| } |
| |
| names.add(name); |
| } |
| } |
| |
| if (nameConflict) |
| { |
| String renamed = resourceName + (copy ? "-copy" : "-renamed"); |
| for (int i = 0; i < Integer.MAX_VALUE; i++) |
| { |
| String name = renamed; |
| if (i != 0) |
| { |
| name += i; |
| } |
| |
| if (!names.contains(name)) |
| { |
| resourceNode.setName(name); |
| return; |
| } |
| } |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static final class ObjectToResource extends Operation<CDOResource> |
| { |
| public ObjectToResource(EObject[] objects, CDOResource target) |
| { |
| super(objects, target); |
| } |
| |
| @Override |
| protected void insert(List<? extends EObject> objects, CDOResource target, boolean copy, IProgressMonitor monitor) |
| { |
| target.getContents().addAll(objects); |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static final class ObjectToObject extends Operation<EObject> |
| { |
| public ObjectToObject(EObject[] objects, EObject target) |
| { |
| super(objects, target); |
| } |
| |
| @Override |
| public boolean canDrop() |
| { |
| if (!super.canDrop()) |
| { |
| return false; |
| } |
| |
| final EObject target = getTarget(); |
| final EObject[] objects = getObjects(); |
| |
| class FeatureUsage |
| { |
| private final int upperBound; |
| |
| private int size; |
| |
| public FeatureUsage(EReference feature) |
| { |
| upperBound = feature.getUpperBound(); |
| |
| if (feature.isMany()) |
| { |
| @SuppressWarnings("unchecked") |
| List<EObject> list = (List<EObject>)target.eGet(feature); |
| size = list.size(); |
| } |
| else |
| { |
| if (feature.isUnsettable()) |
| { |
| if (target.eIsSet(feature)) |
| { |
| size = 1; |
| } |
| } |
| else |
| { |
| Object value = target.eGet(feature); |
| if (value != null) |
| { |
| size = 1; |
| } |
| } |
| } |
| } |
| |
| public boolean use() |
| { |
| return ++size <= upperBound || upperBound == -1; |
| } |
| } |
| |
| Map<EReference, FeatureUsage> usages = new HashMap<EReference, FeatureUsage>(); |
| for (EReference feature : target.eClass().getEAllContainments()) |
| { |
| usages.put(feature, new FeatureUsage(feature)); |
| } |
| |
| for (EObject object : objects) |
| { |
| EReference feature = getTargetFeature(object, target); |
| if (feature == null) |
| { |
| return false; |
| } |
| |
| FeatureUsage usage = usages.get(feature); |
| if (usage == null || !usage.use()) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| @Override |
| protected void insert(List<? extends EObject> objects, EObject target, boolean copy, IProgressMonitor monitor) |
| { |
| for (EObject object : objects) |
| { |
| EReference feature = getTargetFeature(object, target); |
| if (feature.isMany()) |
| { |
| @SuppressWarnings("unchecked") |
| List<EObject> list = (List<EObject>)target.eGet(feature); |
| list.add(object); |
| } |
| else |
| { |
| } |
| } |
| } |
| |
| private EReference getTargetFeature(EObject object, EObject target) |
| { |
| EClass eClass = object.eClass(); |
| for (EReference feature : target.eClass().getEAllContainments()) |
| { |
| EClass referenceType = feature.getEReferenceType(); |
| if (referenceType.isSuperTypeOf(eClass)) |
| { |
| return feature; |
| } |
| } |
| |
| return null; |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static final class ResourceNodeToRootResource extends Operation<CDOResource> |
| { |
| public ResourceNodeToRootResource(EObject[] objects, CDOResource target) |
| { |
| super(objects, target); |
| } |
| |
| /** |
| * TODO Consolidate with {@link ResourceNodeToFolder#insert(List, CDOResourceFolder, IProgressMonitor)}. |
| */ |
| @Override |
| protected void insert(List<? extends EObject> objects, CDOResource target, boolean copy, IProgressMonitor monitor) |
| { |
| EList<EObject> contents = target.getContents(); |
| for (EObject object : objects) |
| { |
| CDOResourceNode resourceNode = (CDOResourceNode)object; |
| setUniqueName(resourceNode, contents, copy); |
| contents.add(resourceNode); |
| } |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static final class ResourceNodeToFolder extends Operation<CDOResourceFolder> |
| { |
| public ResourceNodeToFolder(EObject[] objects, CDOResourceFolder target) |
| { |
| super(objects, target); |
| } |
| |
| @Override |
| protected void insert(List<? extends EObject> objects, CDOResourceFolder target, boolean copy, |
| IProgressMonitor monitor) |
| { |
| EList<CDOResourceNode> nodes = target.getNodes(); |
| for (EObject object : objects) |
| { |
| CDOResourceNode resourceNode = (CDOResourceNode)object; |
| setUniqueName(resourceNode, nodes, copy); |
| nodes.add(resourceNode); |
| } |
| } |
| } |
| } |
| } |