/******************************************************************************* * Copyright (c) 2001, 2010 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: * IBM Corporation - initial API and implementation * Jens Lukowski/Innoopract - initial renaming/restructuring * *******************************************************************************/ package org.eclipse.wst.xml.ui.internal.dnd; import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Vector; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Path; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.widgets.Shell; import org.eclipse.wst.common.ui.internal.dnd.DefaultDragAndDropCommand; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.eclipse.wst.xml.ui.internal.Logger; import org.eclipse.wst.xml.ui.internal.XMLUIMessages; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.Element; import org.w3c.dom.Node; public class DragNodeCommand extends DefaultDragAndDropCommand { private List fSelections; private TreeViewer fTreeViewer; public DragNodeCommand(Object target, float location, int operations, int operation, Collection sources, TreeViewer treeViewer) { super(target, location, operations, operation, sources); fTreeViewer = treeViewer; fSelections = new ArrayList(); } private void beginModelChange(Node node, boolean batchUpdate) { IStructuredModel structuredModel = getStructuredModel(node); if (structuredModel != null) { String undoDesc = new String(); if (getOperation() == DND.DROP_MOVE) { undoDesc = XMLUIMessages.DragNodeCommand_0; } else if (getOperation() == DND.DROP_COPY) { undoDesc = XMLUIMessages.DragNodeCommand_1; } structuredModel.beginRecording(this, undoDesc); if (batchUpdate) { // structuredModel.aboutToChangeModel(); } } } public boolean canExecute() { return executeHelper(true); } private boolean doModify(Node source, Node parentNode, Node refChild, boolean testOnly) { boolean result = false; if (source.getNodeType() == Node.ATTRIBUTE_NODE) { Attr sourceAttribute = (Attr) source; Element sourceAttributeOwnerElement = sourceAttribute.getOwnerElement(); if ((parentNode.getNodeType() == Node.ELEMENT_NODE) && (sourceAttributeOwnerElement != parentNode)) { result = true; if (!testOnly) { try { if (getOperation() == DND.DROP_MOVE) { Element targetElement = (Element) parentNode; sourceAttributeOwnerElement.removeAttributeNode(sourceAttribute); targetElement.getAttributes().setNamedItem(sourceAttribute); fSelections.add(sourceAttribute); } else if (getOperation() == DND.DROP_COPY) { Attr cloneAttribute = (Attr) sourceAttribute.cloneNode(false); Element targetElement = (Element) parentNode; targetElement.getAttributes().setNamedItem(cloneAttribute); fSelections.add(cloneAttribute); } } catch (Exception e) { Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e); } } } } else { if (((parentNode.getNodeType() == Node.ELEMENT_NODE) || (parentNode.getNodeType() == Node.DOCUMENT_NODE)) && !(refChild instanceof Attr)) { result = true; if (!testOnly) { try { if (isAncestor(source, parentNode)) { // System.out.println("can not perform this drag drop // operation.... todo... pop up dialog"); } else { // defect 221055 this test is required or else the // node will // be removed from the tree and the insert will fail if (source != refChild) { if (getOperation() == DND.DROP_MOVE) { Node oldParent = source.getParentNode(); Node oldSibling = source.getNextSibling(); oldParent.removeChild(source); try { parentNode.insertBefore(source, refChild); } catch (DOMException e) { // bug151692 - if unable to move node to new location, reinsert back to old location oldParent.insertBefore(source, oldSibling); } fSelections.add(source); } else if (getOperation() == DND.DROP_COPY) { Node nodeClone = source.cloneNode(true); parentNode.insertBefore(nodeClone, refChild); fSelections.add(nodeClone); } } } } catch (Exception e) { Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e); } } } } return result; } private void endModelChange(Node node, boolean batchUpdate) { IStructuredModel structuredModel = getStructuredModel(node); if (structuredModel != null) { structuredModel.endRecording(this); if (batchUpdate) { // structuredModel.changedModel(); } } } public void execute() { executeHelper(false); // Make our selection if the treeViewer != null if (fTreeViewer != null) { StructuredSelection structuredSelection = new StructuredSelection(fSelections); fTreeViewer.setSelection(structuredSelection); } } private boolean executeHelper(boolean testOnly) { boolean result = true; if (target instanceof Node) { Node targetNode = (Node) target; if (!testOnly && fTreeViewer != null && !validateEdit(getStructuredModel(targetNode), fTreeViewer.getControl().getShell())) return false; Node parentNode = getParentForDropPosition(targetNode); Node refChild = getRefChild(targetNode); Vector sourcesList = new Vector(); sourcesList.addAll(sources); removeMemberDescendants(sourcesList); boolean performBatchUpdate = sourcesList.size() > 5; if (!testOnly) { beginModelChange(targetNode, performBatchUpdate); } for (Iterator i = sourcesList.iterator(); i.hasNext();) { Object source = i.next(); if (source instanceof Node) { if (!((refChild == null) && (targetNode instanceof Attr)) && (parentNode != null)) { result = doModify((Node) source, parentNode, refChild, testOnly); } else { result = false; } if (!result) { break; } } } if (!testOnly) { endModelChange(targetNode, performBatchUpdate); } } else { result = false; } return result; } /** * Checks that the resource backing the model is writeable utilizing <code>validateEdit</code> * on a given <tt>IWorkspace</tt>. * * @param model the model to be checked * @param context the shell context for which <code>validateEdit</code> will be run * @return boolean result of checking <code>validateEdit</code>. If the resource is unwriteable, <code>status.isOK()</code> * will return true; otherwise, false. */ private boolean validateEdit(IStructuredModel model, Shell context) { if (model != null && model.getBaseLocation() != null) { IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(model.getBaseLocation())); return !file.isAccessible() || ResourcesPlugin.getWorkspace().validateEdit(new IFile[] {file}, context).isOK(); } return false; //$NON-NLS-1$ } public int getFeedback() { int result = DND.FEEDBACK_SELECT; if (location > 0.75) { result = DND.FEEDBACK_INSERT_AFTER; } else if (location < 0.25) { result = DND.FEEDBACK_INSERT_BEFORE; } return result; } private Node getParentForDropPosition(Node node) { Node result = null; int feedback = getFeedback(); if (feedback == DND.FEEDBACK_SELECT) { result = node; } else { result = getParentOrOwner(node); } return result; } private Node getParentOrOwner(Node node) { return (node.getNodeType() == Node.ATTRIBUTE_NODE) ? ((Attr) node).getOwnerElement() : node.getParentNode(); } private Node getRefChild(Node node) { Node result = null; int feedback = getFeedback(); if (feedback == DND.FEEDBACK_INSERT_BEFORE) { result = node; } else if (feedback == DND.FEEDBACK_INSERT_AFTER) { result = node.getNextSibling(); } return result; } private IStructuredModel getStructuredModel(Node node) { IStructuredModel result = null; if (node instanceof IDOMNode) { result = ((IDOMNode) node).getModel(); } return result; } // returns true if a is an ancestore of b // private boolean isAncestor(Node a, Node b) { boolean result = false; for (Node parent = b; parent != null; parent = parent.getParentNode()) { if (parent == a) { result = true; break; } } return result; } /** * This method removes members of the list that have ancestors that are * also members of the list. */ private void removeMemberDescendants(List list) { Hashtable table = new Hashtable(); for (Iterator i = list.iterator(); i.hasNext();) { Object node = i.next(); table.put(node, node); } for (int i = list.size() - 1; i >= 0; i--) { Object node = list.get(i); if (node instanceof Node){ for (Node parent = getParentOrOwner((Node)node); parent != null; parent = getParentOrOwner(parent)) { if (table.get(parent) != null) { list.remove(i); break; } } } } } }