| /******************************************************************************* |
| * Copyright (c) 2006 Sybase, 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: |
| * Sybase, Inc. - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.pagedesigner.commands.range; |
| |
| import java.util.Stack; |
| import java.util.Vector; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.gef.GraphicalViewer; |
| import org.eclipse.gef.dnd.TemplateTransfer; |
| import org.eclipse.jst.pagedesigner.IHTMLConstants; |
| import org.eclipse.jst.pagedesigner.css2.CSSUtil; |
| import org.eclipse.jst.pagedesigner.css2.ICSSStyle; |
| import org.eclipse.jst.pagedesigner.dom.DOMRange; |
| import org.eclipse.jst.pagedesigner.dom.EditHelper; |
| import org.eclipse.jst.pagedesigner.dom.EditModelQuery; |
| import org.eclipse.jst.pagedesigner.dom.EditValidateUtil; |
| import org.eclipse.jst.pagedesigner.dom.IDOMPosition; |
| import org.eclipse.jst.pagedesigner.utils.DOMUtil; |
| import org.eclipse.swt.dnd.Clipboard; |
| import org.eclipse.swt.dnd.TextTransfer; |
| import org.eclipse.swt.dnd.Transfer; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.Text; |
| |
| /** |
| * @author mengbo |
| */ |
| public abstract class DesignEdit { |
| |
| private Stack _selections; |
| |
| private DOMRange _range; |
| |
| private GraphicalViewer _viewer; |
| |
| private IDOMPosition _operationPosition; |
| |
| private final Document _document; |
| |
| private Stack _processedResult; |
| |
| /** |
| * @param range |
| * @param viewer |
| */ |
| public DesignEdit(DOMRange range, GraphicalViewer viewer) { |
| setRange(range); |
| _viewer = viewer; |
| _operationPosition = getRange().getStartPosition(); |
| _document = ((IDOMNode) _operationPosition.getContainerNode()) |
| .getModel().getDocument(); |
| } |
| |
| |
| /** |
| * @return the target document |
| */ |
| protected final Document getDocument() { |
| return _document; |
| } |
| |
| /** |
| * @return the result |
| */ |
| protected abstract boolean operate(); |
| |
| /** |
| * @param node |
| * @return the text |
| */ |
| protected abstract Text processText(WorkNode node); |
| |
| /** |
| * @param node |
| * @return the node |
| */ |
| protected abstract Node processNode(WorkNode node); |
| |
| /** |
| * @param node |
| * @return the node |
| */ |
| protected abstract Node processContainer(WorkNode node); |
| |
| /** |
| * @return the dom range |
| */ |
| public DOMRange getRange() { |
| return _range; |
| } |
| |
| void setRange(DOMRange range) { |
| range = EditHelper.normal(range); |
| IDOMPosition start = EditHelper.ensureDOMPosition(range |
| .getStartPosition()); |
| IDOMPosition end = EditHelper.ensureDOMPosition(range.getEndPosition()); |
| _range = new DOMRange(start, end); |
| EditValidateUtil.validRange(range); |
| } |
| |
| /** |
| * @return the clipboard |
| */ |
| protected Clipboard getClipboard() { |
| return new Clipboard(_viewer.getControl().getDisplay()); |
| } |
| |
| /** |
| * @return the position |
| */ |
| public IDOMPosition getOperationPosition() { |
| // try |
| // { |
| // Assert.isTrue(_operationPosition != null && |
| // _operationPosition.getContainerNode() != null && |
| // _operationPosition.getOffset() > -1); |
| // if (_operationPosition.isText()) |
| // { |
| // int length = ((Text) |
| // _operationPosition.getContainerNode()).getLength(); |
| // Assert.isTrue(_operationPosition.getOffset() >= 0 && |
| // _operationPosition.getOffset() <= length); |
| // } |
| // } |
| // catch (Exception e) |
| // { |
| // // "Error", "Error in operation location move" |
| // PDPlugin.getAlerts().confirm("Alert.DesignEdit.opLocationValidTitle", |
| // "Alert.DesignEdit.opLocationValidMessage"); //$NON-NLS-1$ |
| // //$NON-NLS-2$ |
| // } |
| |
| return _operationPosition; |
| } |
| |
| /** |
| * @param position |
| */ |
| protected void setOperationPosition(IDOMPosition position) { |
| if (!EditValidateUtil.validPosition(position)) { |
| return; |
| } |
| position = EditHelper.ensureDOMPosition(position); |
| _operationPosition = position; |
| } |
| |
| /** |
| * @return the result of performing the edit |
| */ |
| public boolean perform() { |
| boolean result = false; |
| |
| result = operate(); |
| return result; |
| } |
| |
| /** |
| * @return Returns the _viewer. |
| */ |
| public GraphicalViewer getViewer() { |
| return _viewer; |
| } |
| |
| private Stack collectNodes() { |
| Node node; |
| Stack result = new Stack(); |
| IDOMPosition start = getRange().getStartPosition(), end = getRange() |
| .getEndPosition(); |
| int pos[] = new int[] { EditModelQuery.getIndexedRegionLocation(start), |
| EditModelQuery.getIndexedRegionLocation(end), }; |
| if (!EditModelQuery.isSame(start, end)) { |
| Node ancestor = EditModelQuery.getInstance().getCommonAncestor( |
| start, end); |
| WorkNode rootWorkNode = new WorkNode(ancestor, pos[0], pos[1]); |
| rootWorkNode.setRoot(true); |
| result.push(rootWorkNode); |
| try { |
| // Loop all the children of the ancestor, and and the result |
| // will be collected |
| if (EditModelQuery.isText(ancestor)) { |
| Stack temp = new Stack(); |
| EditHelper.getInstance().collectNodes(ancestor, pos[0], |
| pos[1], ancestor, temp); |
| WorkNode wNode = (WorkNode) temp.remove(0); |
| wNode.setParent(rootWorkNode); |
| result.push(wNode); |
| } else { |
| node = ancestor.getFirstChild(); |
| Stack temp = new Stack(); |
| while (node != null) { |
| EditHelper.getInstance().collectNodes(node, pos[0], |
| pos[1], ancestor, temp); |
| while (temp.size() > 0) { |
| WorkNode wNode = (WorkNode) temp.remove(0); |
| if (wNode.getNode().getParentNode() == ancestor) { |
| wNode.setParent(rootWorkNode); |
| } |
| result.push(wNode); |
| } |
| node = node.getNextSibling(); |
| } |
| } |
| } catch (Exception e) { |
| result.clear(); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * @return Returns the result. |
| */ |
| public Stack getSelections() { |
| if (_selections == null) { |
| _selections = collectNodes(); |
| } |
| return _selections; |
| } |
| |
| /** |
| * @return the result stack |
| */ |
| public Stack getProcessedResult() { |
| if (_processedResult == null) { |
| _processedResult = new Stack(); |
| WorkNode rootNode = getRootWorkNode(); |
| if (rootNode != null) { |
| processNodes(rootNode, _processedResult); |
| } |
| } |
| return _processedResult; |
| } |
| |
| /** |
| * @return the root work node |
| */ |
| protected final WorkNode getRootWorkNode() { |
| WorkNode result = null; |
| if (getSelections().size() > 0) { |
| WorkNode node = (WorkNode) getSelections().get(0); |
| while (node.getParent() != null) { |
| node = node.getParent(); |
| } |
| result = node; |
| Assert.isTrue(node.isRoot()); |
| } |
| return result; |
| } |
| |
| /** |
| * @param node |
| * @param result |
| * @return true if node |
| */ |
| private final boolean processText(WorkNode node, Stack result) { |
| boolean done = false; |
| if (EditModelQuery.isText(node.getNode())) { |
| Node text = processText(node); |
| if (text != null) { |
| result.add(text); |
| } |
| getSelections().remove(node); |
| done = true; |
| } |
| return done; |
| } |
| |
| /** |
| * @param node |
| * @param result |
| */ |
| private final void processContainer(WorkNode node, Stack result) { |
| processContainer(node); |
| getSelections().remove(node); |
| } |
| |
| /** |
| * @param node |
| * @param result |
| * @return true if done |
| */ |
| private final boolean processChildren(WorkNode node, Stack result) { |
| boolean done = false; |
| if (getFirstSelectedChild(node) != null) { |
| Stack myResult = new Stack(); |
| { |
| WorkNode child = null; |
| while ((child = getFirstSelectedChild(node)) != null) { |
| { |
| processNodes(child, myResult); |
| } |
| } |
| Node newParent = processContainer(node); |
| newParent = toBeParent(newParent, myResult); |
| result.push(newParent); |
| } |
| getSelections().remove(node); |
| done = true; |
| } |
| return done; |
| } |
| |
| /** |
| * @param node |
| * @param result |
| * @return true if done |
| */ |
| private final boolean processChildren1(WorkNode node, Stack result) { |
| boolean done = false; |
| if (node.getNode().hasChildNodes()) { |
| Stack myResult = new Stack(); |
| { |
| Node childNode = node.getNode().getFirstChild(); |
| Node next = null; |
| while (childNode != null) { |
| next = childNode.getNextSibling(); |
| int x1 = EditModelQuery.getNodeStartIndex(childNode) - 1; |
| int x2 = EditModelQuery.getNodeEndIndex(childNode) + 1; |
| processNodes(new WorkNode(childNode, x1, x2), myResult); |
| childNode = next; |
| } |
| Node newParent = processContainer(node); |
| newParent = toBeParent(newParent, myResult); |
| result.push(newParent); |
| } |
| getSelections().remove(node); |
| done = true; |
| } |
| return done; |
| } |
| |
| /** |
| * Process the nodes that are selected, the result is a collection of nodes |
| * that either are clones or the nodes cuted. |
| * |
| * @param node |
| * @param result |
| */ |
| protected final void processNodes(WorkNode node, Stack result) { |
| WorkNode child = null; |
| if (node.isRoot()) { |
| while ((child = getFirstSelectedChild(node)) != null) { |
| processNodes(child, result); |
| } |
| } else { |
| if (node.isWholeSelected() |
| || // |
| (!EditModelQuery.isText(node.getNode()) && EditModelQuery |
| .getInstance().isSingleRegionNode(node.getNode())) |
| || // |
| EditModelQuery.isWidget(node.getNode())) { |
| Node temp = processNode(node); |
| if (temp != null) { |
| result.push(temp); |
| getSelections().remove(node); |
| } else { |
| if (!processText(node, result)) { |
| if (!processChildren1(node, result)) { |
| processContainer(node, result); |
| } |
| } |
| } |
| } else { |
| if (!processText(node, result)) { |
| if (!processChildren(node, result)) { |
| processContainer(node, result); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * @param result |
| */ |
| protected void setClipboard(Stack result) { |
| Node[] nodes = (Node[]) result.toArray(new Node[result.size()]); |
| StringBuffer sb = new StringBuffer(); |
| for (int i = 0, size = nodes.length; i < size; i++) { |
| DOMUtil.nodeToString(nodes[i], sb); |
| } |
| getClipboard().setContents( |
| new Object[] { result, sb.toString() }, |
| new Transfer[] { TemplateTransfer.getInstance(), |
| TextTransfer.getInstance() }); |
| } |
| |
| private Node toBeParent(Node parent, Stack children) { |
| while (children.size() > 0) { |
| parent.appendChild((Node) children.remove(0)); |
| } |
| return parent; |
| } |
| |
| private WorkNode getFirstSelectedChild(WorkNode node) { |
| for (int i = 0, n = getSelections().size(); i < n; i++) { |
| WorkNode wNode = (WorkNode) getSelections().get(i); |
| if (wNode.getParent() == node) { |
| return wNode; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @param rootNode |
| * @param result |
| * @return the node |
| */ |
| Node collectStyleNodes(Node rootNode, Vector result) { |
| Element element = null; |
| if (rootNode instanceof Element) { |
| element = (Element) rootNode; |
| } else if (rootNode.getParentNode() != null) { |
| element = (Element) rootNode.getParentNode(); |
| } |
| ICSSStyle style = CSSUtil.getCSSStyle(element); |
| |
| Node node = EditModelQuery.getDocumentNode(rootNode).createElement( |
| "span"); |
| for (int i = 0, n = result.size(); i < n; i++) { |
| node.appendChild((Node) result.elementAt(i)); |
| } |
| ((Element) node).setAttribute(IHTMLConstants.ATTR_STYLE, CSSUtil |
| .resolveCSSStyle(style)); |
| result.removeAllElements(); |
| result.add(node); |
| return node; |
| } |
| |
| /** |
| * @param rootNode |
| * @param result |
| * @return the node |
| */ |
| protected final Node collectOtherStyles(Node rootNode, Vector result) { |
| Node cur = rootNode, prev = null, appendPoint = null; |
| if (EditValidateUtil.validNode(rootNode)) { |
| while (!EditModelQuery.isDocument(cur)) { |
| if (!EditValidateUtil.validNode(cur)) { |
| return null; |
| } |
| String name = cur.getNodeName() != null ? cur.getNodeName() |
| .toLowerCase() : ""; |
| if (EditModelQuery.HTML_STYLE_NODES.contains(name)) { |
| if (prev != null) { |
| Node newone = cur.cloneNode(false); |
| newone.appendChild(prev); |
| prev = newone; |
| } else { |
| prev = cur.cloneNode(false); |
| appendPoint = prev; |
| } |
| } |
| cur = cur.getParentNode(); |
| } |
| if (appendPoint != null) { |
| for (int i = 0, n = result.size(); i < n; i++) { |
| appendPoint.appendChild((Node) result.elementAt(i)); |
| } |
| result.removeAllElements(); |
| result.add(prev); |
| } |
| } |
| return prev; |
| } |
| } |