/*******************************************************************************
* Copyright (c) 2012, 2014 Wind River Systems, Inc. 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.te.tcf.ui.navigator.dnd;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.tcf.protocol.IPeer;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.te.runtime.callback.Callback;
import org.eclipse.tcf.te.runtime.persistence.interfaces.IPersistableNodeProperties;
import org.eclipse.tcf.te.runtime.persistence.interfaces.IURIPersistenceService;
import org.eclipse.tcf.te.runtime.services.ServiceManager;
import org.eclipse.tcf.te.tcf.core.peers.Peer;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
import org.eclipse.tcf.te.tcf.locator.interfaces.services.IPeerModelLookupService;
import org.eclipse.tcf.te.tcf.locator.interfaces.services.IPeerModelRefreshService;
import org.eclipse.tcf.te.tcf.locator.model.ModelManager;
import org.eclipse.tcf.te.ui.views.Managers;
import org.eclipse.tcf.te.ui.views.interfaces.ICategory;
import org.eclipse.tcf.te.ui.views.interfaces.IRoot;
import org.eclipse.tcf.te.ui.views.interfaces.IUIConstants;
import org.eclipse.tcf.te.ui.views.interfaces.categories.ICategorizable;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.navigator.CommonDropAdapter;
import org.eclipse.ui.navigator.CommonNavigator;
/**
* Common DND operation implementations.
*/
public class CommonDnD {
/**
* If the current selection is draggable.
*
* @param selection The currently selected nodes.
* @return true if it is draggable.
*/
public static boolean isDraggable(IStructuredSelection selection) {
if (selection.isEmpty()) {
return false;
}
Object[] objects = selection.toArray();
for (Object object : objects) {
if (!isDraggableObject(object)) {
return false;
}
}
return true;
}
/**
* If the specified object is a draggable element.
*
* @param object The object to be dragged.
* @return true if it is draggable.
*/
private static boolean isDraggableObject(Object object) {
return object instanceof IPeerNode;
}
/**
* Perform the drop operation over dragged selection.
*
* @param dropAdapter The common drop adapter.
* @param target The target Object to be moved to.
* @param operations The current DnD operation.
* @param selection The local selection being dropped.
* @return true if the dropping is successful.
*/
public static boolean dropLocalSelection(CommonDropAdapter dropAdapter, Object target, int operations, IStructuredSelection selection) {
boolean result = false;
boolean refreshModel = false;
ICategory catToSelect = null;
Object elementToSelect = null;
Iterator<?> iterator = selection.iterator();
while (iterator.hasNext()) {
Object element = iterator.next();
if (!isDraggableObject(element)) {
continue;
}
ICategorizable categorizable = getCategorizable(element);
if (categorizable == null || !isDraggableObject(element)) {
continue;
}
ICategory[] parentCategories = getParentCategories(element, selection);
if (parentCategories.length == 0) {
continue;
}
for (ICategory parentCategory : parentCategories) {
if (target instanceof ICategory) {
ICategory category = (ICategory) target;
if (element instanceof IPeerNode && category.getId().equals(parentCategory.getId())) {
List<String> usedNames = getUsedNames();
Map<String,String> attrs = new HashMap<String,String>(((IPeerNode)element).getPeer().getAttributes());
attrs.put(IPeer.ATTR_ID, UUID.randomUUID().toString());
attrs.remove(IPersistableNodeProperties.PROPERTY_URI);
int i = 0;
String baseName = attrs.get(IPeer.ATTR_NAME);
baseName = baseName.replaceAll("\\s*\\([\\d*]\\)$", ""); //$NON-NLS-1$ //$NON-NLS-2$
String name = baseName + " (" + i + ")"; //$NON-NLS-1$ //$NON-NLS-2$
while (usedNames.contains(name.toUpperCase())) {
i++;
name = baseName + " (" + i + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
attrs.put(IPeer.ATTR_NAME, name);
IPeer newPeer = new Peer(attrs);
// Save the new peer
IURIPersistenceService persistenceService = ServiceManager.getInstance().getService(IURIPersistenceService.class);
if (persistenceService != null) {
try {
persistenceService.write(newPeer, null);
refreshModel = true;
if (catToSelect == null || elementToSelect == null) {
catToSelect = category;
elementToSelect = newPeer;
}
result = true;
}
catch (Exception e) {
}
}
}
else if (!Managers.getCategoryManager().isLinked(category.getId(), categorizable.getId()) &&
categorizable.isValid(ICategorizable.OPERATION.ADD, parentCategory, category)) {
Managers.getCategoryManager().add(category.getId(), categorizable.getId());
if (catToSelect == null || elementToSelect == null) {
catToSelect = category;
elementToSelect = element;
}
result = true;
}
}
else if (target instanceof IRoot) {
if (Managers.getCategoryManager().isLinked(parentCategory.getId(), categorizable.getId())) {
Managers.getCategoryManager().remove(parentCategory.getId(), categorizable.getId());
catToSelect = parentCategory;
result = true;
}
}
}
}
if (result) {
final CommonNavigator cNav = getCommonNavigator();
if (refreshModel) {
final ICategory finalCat = catToSelect;
final Object finalElement = elementToSelect;
final IPeer finalNewPeer = (elementToSelect instanceof IPeer) ? (IPeer)elementToSelect : null;
// Trigger a refresh of the model to read in the newly created static peer
final IPeerModelRefreshService service = ModelManager.getPeerModel().getService(IPeerModelRefreshService.class);
if (service != null) {
Runnable runnable = new Runnable() {
@Override
public void run() {
service.refresh(new Callback() {
@Override
protected void internalDone(Object caller, org.eclipse.core.runtime.IStatus status) {
IPeerNode peerNode = null;
if (finalNewPeer != null) {
IPeerModelLookupService service = ModelManager.getPeerModel().getService(IPeerModelLookupService.class);
if (service != null) {
peerNode = service.lkupPeerModelById(finalNewPeer.getID());
}
}
refresh(cNav, finalCat, peerNode != null ? peerNode : finalElement);
}
});
}
};
if (Protocol.isDispatchThread())
runnable.run();
else
Protocol.invokeLater(runnable);
}
else {
refresh(cNav, catToSelect, elementToSelect);
}
}
else {
refresh(cNav, catToSelect, elementToSelect);
}
}
return result;
}
protected static void refresh(final CommonNavigator cNav, final ICategory category, final Object element) {
Runnable runnable = new Runnable() {
@Override
public void run() {
cNav.getCommonViewer().refresh();
if (category != null) {
cNav.getCommonViewer().setSelection(new StructuredSelection(category), true);
cNav.getCommonViewer().expandToLevel(category, 1);
}
if (element != null)
cNav.getCommonViewer().setSelection(new TreeSelection(new TreePath(new Object[]{category, element})), true);
}
};
// Execute asynchronously
if (PlatformUI.isWorkbenchRunning()) {
PlatformUI.getWorkbench().getDisplay().asyncExec(runnable);
}
}
protected static CommonNavigator getCommonNavigator() {
final AtomicReference<IViewPart> viewPart = new AtomicReference<IViewPart>();
// Create the runnable
Runnable runnable = new Runnable() {
@Override
public void run() {
// Check the active workbench window and active page instances
if (PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null && PlatformUI
.getWorkbench().getActiveWorkbenchWindow().getActivePage() != null) {
// show the view
try {
viewPart.set(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(IUIConstants.ID_EXPLORER));
}
catch (Exception e) {
}
}
}
};
// Execute asynchronously
if (PlatformUI.isWorkbenchRunning()) {
PlatformUI.getWorkbench().getDisplay().syncExec(runnable);
}
return viewPart.get() instanceof CommonNavigator ? (CommonNavigator)viewPart.get() : null;
}
/**
* Validate dropping when the elements being dragged are local selection.
*
* @param dropAdapter The common drop adapter.
* @param target The target object.
* @param operation The DnD operation.
* @param transferType The transfered data type.
*
* @return true if it is valid for dropping.
*/
public static boolean validateLocalSelectionDrop(CommonDropAdapter dropAdapter, Object target, int operation, TransferData transferType) {
int overrideOperation = -1;
boolean valid = false;
LocalSelectionTransfer transfer = LocalSelectionTransfer.getTransfer();
IStructuredSelection selection = (IStructuredSelection) transfer.getSelection();
boolean allow = true;
boolean link = false;
boolean copy = false;
Iterator<?> iterator = selection.iterator();
while (allow && iterator.hasNext()) {
Object element = iterator.next();
if (!isDraggableObject(element)) {
allow = false;
break;
}
ICategorizable categorizable = getCategorizable(element);
if (categorizable == null || !isDraggableObject(element)) {
allow = false;
break;
}
ICategory[] parentCategories = getParentCategories(element, selection);
if (parentCategories.length == 0) {
allow = false;
}
for (ICategory parentCategory : parentCategories) {
if (target instanceof ICategory) {
ICategory category = (ICategory) target;
if (!link && element instanceof IPeerNode && category.getId().equals(parentCategory.getId())) {
overrideOperation = DND.DROP_COPY;
copy = true;
}
else if (!copy && !Managers.getCategoryManager().isLinked(category.getId(), categorizable.getId()) &&
categorizable.isValid(ICategorizable.OPERATION.ADD, parentCategory, category)) {
overrideOperation = DND.DROP_LINK;
link = true;
}
else {
allow = false;
break;
}
}
else if (target instanceof IRoot) {
overrideOperation = DND.DROP_DEFAULT;
if (!Managers.getCategoryManager().isLinked(parentCategory.getId(), categorizable.getId())) {
allow = false;
break;
}
}
}
}
valid = allow;
if (dropAdapter != null) {
if (!valid) {
dropAdapter.overrideOperation(DND.DROP_NONE);
}
else if (overrideOperation != -1) {
dropAdapter.overrideOperation(overrideOperation);
}
else {
dropAdapter.overrideOperation(operation);
}
}
return valid;
}
protected static ICategory[] getParentCategories(Object element, ISelection selection) {
List<ICategory> candidates = new ArrayList<ICategory>();
// To determine the parent category, we have to look at the tree path
TreePath[] pathes = selection instanceof TreeSelection ? ((TreeSelection)selection).getPathsFor(element) : null;
if (pathes != null && pathes.length > 0) {
for (TreePath path : pathes) {
TreePath parentPath = path.getParentPath();
while (parentPath != null) {
if (parentPath.getLastSegment() instanceof ICategory) {
if (!candidates.contains(parentPath.getLastSegment())) {
candidates.add((ICategory)parentPath.getLastSegment());
break;
}
}
parentPath = parentPath.getParentPath();
}
}
}
return candidates.toArray(new ICategory[candidates.size()]);
}
protected static ICategorizable getCategorizable(Object element) {
ICategorizable categorizable = element instanceof IAdaptable ? (ICategorizable)((IAdaptable)element).getAdapter(ICategorizable.class) : null;
if (categorizable == null) categorizable = (ICategorizable)Platform.getAdapterManager().getAdapter(element, ICategorizable.class);
return categorizable;
}
protected static List<String> getUsedNames() {
final List<String> usedNames = new ArrayList<String>();
Runnable runnable = new Runnable() {
@Override
public void run() {
// Get all peer model objects
IPeerNode[] peers = ModelManager.getPeerModel().getPeerNodes();
// Loop them and find the ones which are of our handled types
for (IPeerNode peerNode : peers) {
String name = peerNode.getPeer().getName();
Assert.isNotNull(name);
if (!"".equals(name) && !usedNames.contains(name)) { //$NON-NLS-1$
usedNames.add(name.trim().toUpperCase());
}
}
}
};
if (Protocol.isDispatchThread())
runnable.run();
else
Protocol.invokeAndWait(runnable);
return usedNames;
}
}