/******************************************************************************* * Copyright (c) 2007, 2013 IBM Corporation 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 - Pawel Piech - Initial Implementation - Drag/Drop to Expressions View (Bug 184057), Integration with non-standard debug models (Bug 209883) * IBM Corporation - further implementation and documentation * Wind River Systems - integration with non-standard debug models *******************************************************************************/ package org.eclipse.debug.internal.ui.views.expression; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IExpressionManager; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.core.model.IExpression; import org.eclipse.debug.core.model.IVariable; import org.eclipse.debug.core.model.IWatchExpression; import org.eclipse.debug.internal.core.ExpressionManager; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; import org.eclipse.debug.internal.ui.views.variables.IndexedVariablePartition; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter; import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter2; import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapterExtension; import org.eclipse.jface.util.LocalSelectionTransfer; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ViewerDropAdapter; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.TransferData; import org.eclipse.ui.IWorkbenchPartSite; /** * Drop Adapter allowing expressions, variables and text to be dropped in the Expression View. * When IVariables or text is dropped new watch expressions are created at the drop location. * When IExpressions are dropped, they are moved to the drop location * * @see org.eclipse.debug.internal.ui.views.variables.VariablesDragAdapter * @see ExpressionManager * @since 3.4 */ public class ExpressionDropAdapter extends ViewerDropAdapter { private IWorkbenchPartSite fSite; private TransferData fCurrentTransferType = null; private boolean fInsertBefore; private int fDropType; private static final int DROP_TYPE_DEFAULT = 0; private static final int DROP_TYPE_VARIABLE = 1; private static final int DROP_TYPE_EXPRESSION = 2; private static final int DROP_TYPE_WATCH_ADAPTABLE_ELEMENT = 3; /** * Constructor takes the viewer this drop adapter applies to. * @param viewer the viewer to add drop to */ protected ExpressionDropAdapter(IWorkbenchPartSite site, TreeModelViewer viewer) { super(viewer); fSite = site; setFeedbackEnabled(true); setSelectionFeedbackEnabled(false); setScrollExpandEnabled(false); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ViewerDropAdapter#dragEnter(org.eclipse.swt.dnd.DropTargetEvent) */ @Override public void dragEnter(DropTargetEvent event) { fDropType = DROP_TYPE_DEFAULT; event.detail = DND.DROP_NONE; for (int i = 0; i < event.dataTypes.length; i++) { if (LocalSelectionTransfer.getTransfer().isSupportedType(event.dataTypes[i])) { if (isExpressionDrop()){ event.currentDataType = event.dataTypes[i]; event.detail = DND.DROP_MOVE; fDropType = DROP_TYPE_EXPRESSION; break; } else if (isVariableDrop()){ event.currentDataType = event.dataTypes[i]; event.detail = DND.DROP_COPY; fDropType = DROP_TYPE_VARIABLE; break; } else if (isWatchAdaptableElementDrop()){ event.currentDataType = event.dataTypes[i]; event.detail = DND.DROP_COPY; fDropType = DROP_TYPE_WATCH_ADAPTABLE_ELEMENT; break; } } else if (TextTransfer.getInstance().isSupportedType(event.dataTypes[i])) { event.currentDataType = event.dataTypes[i]; event.detail = DND.DROP_COPY; fDropType = DROP_TYPE_DEFAULT; break; } } super.dragEnter(event); } /** * @return whether the selection transfer contains only IExpressions */ private boolean isExpressionDrop() { IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection(); Iterator<?> iterator = selection.iterator(); while (iterator.hasNext()) { Object element = iterator.next(); if (getTargetExpression(element) == null){ return false; } } return true; } /** * @return whether the selection transfer contains only IVariables */ private boolean isVariableDrop() { IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection(); Iterator<?> iterator = selection.iterator(); while (iterator.hasNext()) { Object element = iterator.next(); if (!(element instanceof IVariable)){ return false; } } return true; } /** * @return whether the selection transfer contains only objects adaptable * to IWatchExpressionFactoryAdapter2 */ private boolean isWatchAdaptableElementDrop() { IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection(); Iterator<?> iterator = selection.iterator(); while (iterator.hasNext()) { Object element = iterator.next(); if (!(element instanceof IAdaptable && ((IAdaptable)element).getAdapter(IWatchExpressionFactoryAdapter2.class) != null)) { return false; } } return true; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ViewerDropAdapter#dragOver(org.eclipse.swt.dnd.DropTargetEvent) */ @Override public void dragOver(DropTargetEvent event) { super.dragOver(event); // Allow scrolling (but not expansion) event.feedback |= DND.FEEDBACK_SCROLL; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ViewerDropAdapter#validateDrop(java.lang.Object, int, org.eclipse.swt.dnd.TransferData) */ @Override public boolean validateDrop(Object target, int operation, TransferData transferType) { if (LocalSelectionTransfer.getTransfer().isSupportedType(transferType)) { if (fDropType == DROP_TYPE_EXPRESSION){ return validateExpressionDrop(target); } else if (fDropType == DROP_TYPE_VARIABLE){ return validateVariableDrop(target); } else if (fDropType == DROP_TYPE_WATCH_ADAPTABLE_ELEMENT){ return validateWatchAdaptableDrop(target); } } else if (TextTransfer.getInstance().isSupportedType(transferType)) { return validateTextDrop(target); } return false; } /** * Validates if an IExpression drop is valid by checking if the target * is an IExpression. * @param target target of the drop * @return whether the drop is valid */ private boolean validateExpressionDrop(Object target){ return target instanceof IExpression || ((target instanceof IAdaptable) && ((IAdaptable)target).getAdapter(IExpression.class) != null); } private IExpression getTargetExpression(Object target) { if (target instanceof IExpression) { return (IExpression)target; } else if (target instanceof IAdaptable) { return ((IAdaptable)target).getAdapter(IExpression.class); } return null; } /** * Validates if the drop is valid by validating the local selection transfer * to ensure that a watch expression can be created for each contained IVariable. * @param target target of the drop * @return whether the drop is valid */ private boolean validateVariableDrop(Object target) { // Target must be null or an IExpression, you cannot add a new watch expression inside another if (target != null && getTargetExpression(target) == null) { return false; } IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection(); int enabled = 0; int size = -1; if (selection != null) { size = selection.size(); IExpressionManager manager = DebugPlugin.getDefault().getExpressionManager(); Iterator<?> iterator = selection.iterator(); while (iterator.hasNext()) { Object element = iterator.next(); if (element instanceof IVariable){ IVariable variable = (IVariable) element; if (variable instanceof IndexedVariablePartition) { break; } else if (manager.hasWatchExpressionDelegate(variable.getModelIdentifier()) && isFactoryEnabled(variable)) { enabled++; } else { break; } } } } return enabled == size; } /** * Validates if the drop is valid by validating the local selection transfer * to ensure that a watch expression can be created for each contained element. * @param target target of the drop * @return whether the drop is valid */ private boolean validateWatchAdaptableDrop(Object target) { // Target must be null or an IExpression, you cannot add a new watch expression inside another if (target != null && getTargetExpression(target) == null) { return false; } IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection(); int enabled = 0; int size = -1; if (selection != null) { size = selection.size(); Iterator<?> iterator = selection.iterator(); while (iterator.hasNext()) { Object element = iterator.next(); if (isFactory2Enabled(element)) { enabled++; } else { break; } } } return enabled == size; } /** * Validates if the drop is valid by validating the drop location. * Only valid if the target is <code>null</code> or an <code>IExpression</code>. * You cannot add a new watch expression inside another. * @param target target of the drop * @return whether the drop is valid */ private boolean validateTextDrop(Object target){ return target == null || getTargetExpression(target) != null; } /** * Returns whether the factory adapter for the given variable is currently enabled. * * @param variable the variable to ask for the adapter * @return whether the factory is enabled */ private boolean isFactoryEnabled(IVariable variable) { IWatchExpressionFactoryAdapter factory = getFactory(variable); if (factory instanceof IWatchExpressionFactoryAdapterExtension) { IWatchExpressionFactoryAdapterExtension ext = (IWatchExpressionFactoryAdapterExtension) factory; return ext.canCreateWatchExpression(variable); } return true; } /** * Returns whether the factory adapter for the given element is currently enabled. * * @param element the element to ask for the adapter * @return whether the factory is enabled */ private boolean isFactory2Enabled(Object element) { IWatchExpressionFactoryAdapter2 factory = getFactory2(element); if (factory != null) { return factory.canCreateWatchExpression(element); } return false; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ViewerDropAdapter#drop(org.eclipse.swt.dnd.DropTargetEvent) */ @Override public void drop(DropTargetEvent event) { fCurrentTransferType = event.currentDataType; // Unless insert after is explicitly set, insert before fInsertBefore = getCurrentLocation() != LOCATION_AFTER; super.drop(event); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ViewerDropAdapter#performDrop(java.lang.Object) */ @Override public boolean performDrop(Object data) { if (LocalSelectionTransfer.getTransfer().isSupportedType(fCurrentTransferType)) { IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection(); if (fDropType == DROP_TYPE_EXPRESSION){ return performExpressionDrop(selection); } else if (fDropType == DROP_TYPE_VARIABLE || fDropType == DROP_TYPE_WATCH_ADAPTABLE_ELEMENT){ return performVariableOrWatchAdaptableDrop(selection); } } else if (TextTransfer.getInstance().isSupportedType(fCurrentTransferType)) { if (data != null) { return performTextDrop((String)data); } } return false; } /** * Performs the drop when the selection is a collection of IExpressions. * Moves the given expressions from their original locations to the * location of the current target. * @param selection the dragged selection * @return whether the drop could be completed */ private boolean performExpressionDrop(IStructuredSelection selection) { IExpression targetExpression = getTargetExpression(getCurrentTarget()); if (targetExpression != null){ IExpression[] expressions = new IExpression[selection.size()]; Object[] selectionElements = selection.toArray(); for (int i = 0; i < selectionElements.length; i++) { expressions[i] = getTargetExpression(selectionElements[i]); } IExpressionManager manager = DebugPlugin.getDefault().getExpressionManager(); if (manager instanceof ExpressionManager){ ((ExpressionManager)manager).moveExpressions(expressions, targetExpression, fInsertBefore); } return true; } return false; } /** * If the dragged data is a structured selection, get any IVariables in it * and create expressions for each of them. Insert the created expressions * at the currently selected target or add them to the end of the collection * if no target is selected. * * @param selection Structured selection containing IVariables * @return whether the drop was successful */ private boolean performVariableOrWatchAdaptableDrop(IStructuredSelection selection) { List<IExpression> expressions = new ArrayList<IExpression>(selection.size()); for (Iterator<?> itr = selection.iterator(); itr.hasNext();) { Object element = itr.next(); String expressionText = createExpressionString(element); if (expressionText != null){ IExpression expression = createExpression(expressionText); if (expression != null){ expressions.add(expression); } else { DebugUIPlugin.log(new Status(IStatus.ERROR,DebugUIPlugin.getUniqueIdentifier(),"Drop failed. Watch expression could not be created for the text " + expressionText)); //$NON-NLS-1$ return false; } } else { return false; } } if (expressions.size() == selection.size()){ IExpressionManager manager = DebugPlugin.getDefault().getExpressionManager(); if (manager instanceof ExpressionManager){ IExpression targetExpression = getTargetExpression(getCurrentTarget()); if (targetExpression != null){ ((ExpressionManager)manager).insertExpressions(expressions.toArray(new IExpression[expressions.size()]), targetExpression, fInsertBefore); } else { ((ExpressionManager)manager).addExpressions(expressions.toArray(new IExpression[expressions.size()])); } return true; } } return false; } /** * Performs the drop when text was dragged. Creates a new watch expression from * the text. Inserts the expression at the currently selected target or adds it * to the end of the collection if no target is selected. * * @param text string to use to create the expression * @return whether the drop was successful */ private boolean performTextDrop(String text){ IExpression expression = createExpression(text); if (expression != null){ IExpressionManager manager = DebugPlugin.getDefault().getExpressionManager(); if (manager instanceof ExpressionManager){ IExpression targetExpression = getTargetExpression(getCurrentTarget()); if (targetExpression != null){ ((ExpressionManager)manager).insertExpressions(new IExpression[]{expression}, targetExpression, fInsertBefore); } else { ((ExpressionManager)manager).addExpression(expression); } return true; } } DebugUIPlugin.log(new Status(IStatus.ERROR,DebugUIPlugin.getUniqueIdentifier(),"Drop failed. Watch expression could not be created for the text " + text)); //$NON-NLS-1$ return false; } /** * Creates a new watch expression from an IVariable using the watch expression factory * adapter for that variable. * * @param variable the variable to use to create the watch expression * @return the string to be used to create expression, return <code>null</code> * if no expression is to be created */ private String createExpressionString(Object element) { try { if (element instanceof IVariable) { IVariable variable = (IVariable)element; IWatchExpressionFactoryAdapter factory = getFactory(variable); String exp = variable.getName(); if (factory != null) { //if a factory exists, use it to create expression, //otherwise just use variable name exp = factory.createWatchExpression(variable); } return exp; } else { IWatchExpressionFactoryAdapter2 factory2 = getFactory2(element); if (factory2 != null) { return factory2.createWatchExpression(element); } } } catch (CoreException e) { DebugUIPlugin.log(e.getStatus()); } return null; } /** * Creates a new watch expression from a string using the default expression manager. * * @param exp the string to use to create the expression */ private IExpression createExpression(String exp) { IWatchExpression expression = DebugPlugin.getDefault().getExpressionManager().newWatchExpression(exp); IAdaptable object = DebugUITools.getPartDebugContext(fSite); IDebugElement context = null; if (object instanceof IDebugElement) { context = (IDebugElement) object; } else if (object instanceof ILaunch) { context = ((ILaunch) object).getDebugTarget(); } expression.setExpressionContext(context); return expression; } /** * Returns the factory adapter for the given variable or <code>null</code> if none. * * @param variable * @return factory or <code>null</code> */ private IWatchExpressionFactoryAdapter getFactory(IVariable variable) { return variable.getAdapter(IWatchExpressionFactoryAdapter.class); } /** * Returns the factory adapter for the given element or <code>null</code> if none. * * @param element * @return factory or <code>null</code> */ private IWatchExpressionFactoryAdapter2 getFactory2(Object element) { if (element instanceof IAdaptable) { return ((IAdaptable)element).getAdapter(IWatchExpressionFactoryAdapter2.class); } return null; } }