/******************************************************************************* * 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.dom; import java.util.List; import org.eclipse.core.runtime.Assert; import org.eclipse.gef.EditPart; import org.eclipse.jst.pagedesigner.parts.NodeEditPart; import org.eclipse.jst.pagedesigner.parts.TextEditPart; import org.eclipse.jst.pagedesigner.utils.HTMLUtil; import org.eclipse.jst.pagedesigner.viewer.DesignPosition; import org.eclipse.jst.pagedesigner.viewer.DesignRefPosition; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.w3c.dom.Node; import org.w3c.dom.Text; /** * @author mengbo */ public class DOMPositionHelper { /** * @param position * @return the design position */ public static DesignPosition toDesignRefPosition(DOMRefPosition position) { Node node = position.getReferenceNode(); do { IDOMNode container = (IDOMNode) node.getParentNode(); EditPart part = (EditPart) container.getAdapterFor(EditPart.class); if (part != null) { // XXX: what if the node has not corresponding part? EditPart child = DOMPositionHelper.findEditPart(part, node); if (child != null) { return new DesignRefPosition(child, position.isForward()); } return DesignPosition.INVALID; } node = node.getParentNode(); } while (true); } /** * * @param position * if it is null, then will return null * @return null if position is null or invalid. */ public static DesignPosition toDesignPosition(IDOMPosition position) { if (position == null) { return null; } if (position instanceof DOMRefPosition) { return toDesignRefPosition((DOMRefPosition) position); } do { IDOMNode container = (IDOMNode) position.getContainerNode(); EditPart part = (EditPart) container.getAdapterFor(EditPart.class); if (part != null) { if (container instanceof Text) { String textData = ((Text) container).getData(); String displayData = ((TextEditPart) part).getTextData(); return new DesignPosition(part, textDataOffsetToDisplayOffset(textData, displayData, position.getOffset())); } Node pre = position.getPreviousSiblingNode(); while (pre != null) { int index = findChildEditPartIndex(part, pre); if (index != -1) { return new DesignPosition(part, index + 1); } pre = pre.getPreviousSibling(); } return new DesignPosition(part, 0); } position = new DOMRefPosition(position.getContainerNode(), false); } while (true); } /** * Here is the position is not currect, currently it will returns invalid * pos. * * @param position * @return the design position */ public static DesignPosition toDesignPosition1(IDOMPosition position) { if (position instanceof DOMRefPosition) { return toDesignRefPosition((DOMRefPosition) position); } do { IDOMNode container = (IDOMNode) position.getContainerNode(); EditPart part = (EditPart) container.getAdapterFor(EditPart.class); if (part != null) { if (container instanceof Text) { String textData = ((Text) container).getData(); String displayData = ((TextEditPart) part).getTextData(); return new DesignPosition(part, textDataOffsetToDisplayOffset(textData, displayData, position.getOffset())); } Node pre = position.getPreviousSiblingNode(); while (pre != null) { int index = findChildEditPartIndex(part, pre); if (index != -1) { return new DesignPosition(part, index + 1); } pre = pre.getPreviousSibling(); } return new DesignPosition(part, 0); } return DesignPosition.INVALID; } while (true); } static int findChildEditPartIndex(EditPart parent, Node node) { List children = parent.getChildren(); for (int i = 0; i < children.size(); i++) { if (((EditPart) children.get(i)).getModel() == node) { return i; } } return -1; } static EditPart findEditPart(EditPart parent, Node node) { List children = parent.getChildren(); EditPart part; for (int i = 0; i < children.size(); i++) { if ((part = (EditPart) children.get(i)).getModel() == node) { return part; } } return null; } /** * convert a DesignPosition into DOMPosition. * * @param position * @return the dom position */ public static IDOMPosition toDOMRefPosition(DesignRefPosition position) { // ok, it is not text. EditPart sibling = position.getRefPart(); if (sibling != null) { return new DOMRefPosition((Node) sibling.getModel(), position .caretIsAtRight()); } // should never happens Assert.isTrue(false); return null; } /** * convert a DesignPosition into DOMPosition. * * @param position * @return the dom position */ public static IDOMPosition toDOMPosition(DesignPosition position) { if (!EditValidateUtil.validPosition(position)) { return null; } else if (position instanceof DesignRefPosition) { return toDOMRefPosition((DesignRefPosition) position); } EditPart part = position.getContainerPart(); if (part instanceof TextEditPart) { Text text = (Text) ((TextEditPart) part).getIDOMNode(); int offset = position.getOffset(); if (offset == 0) { return new DOMPosition(text, 0); } String displayData = ((TextEditPart) part).getTextData(); String nodeData = text.getData(); if (offset >= displayData.length()) { // point to end of the text node. return new DOMPosition(text, nodeData.length()); } // we need to calculate it out. int index = displayOffsetToTextDataOffset(displayData, nodeData, offset); return new DOMPosition(text, index); } // ok, it is not text. EditPart sibling = position.getSiblingEditPart(true); if (sibling instanceof NodeEditPart) { return new DOMRefPosition(((NodeEditPart) sibling).getDOMNode(), false); } sibling = position.getSiblingEditPart(false); if (sibling instanceof NodeEditPart) { return new DOMRefPosition(((NodeEditPart) sibling).getDOMNode(), true); } // no previous sibling, no next sibling, the parent node must be // empty return new DOMPosition(((NodeEditPart) part).getDOMNode(), 0); } /** * if "position" is inside a text node, then split the text node and return * a new IDOMPosition semantically equal to the position in the two * splitted text. If the "position" is not a text position, then no action * will be taken and will return the original position. * * @param position * @return IDOMPosition */ public static IDOMPosition splitText(IDOMPosition position) { Node container = position.getContainerNode(); if (container instanceof Text) { int offset = position.getOffset(); if (offset <= 0) { // at beginning of text node. no need to split return new DOMRefPosition(container, false); } String textData = ((Text) container).getData(); if (offset >= textData.length()) { // at end of text node. no need to split return new DOMRefPosition(container, true); } // ok, we need split ((Text) container).splitText(offset); return new DOMRefPosition(container, true); } return position; } /** * Remove all the content in the range. And return the new position. * * @param range * @return the dom position */ public static IDOMPosition removeRange(DOMRange range) { boolean ordered = range.isOrdered(); // IDOMPosition start = ordered ? range.getStartPosition() : range // .getEndPosition(); IDOMPosition end = ordered ? range.getEndPosition() : range .getStartPosition(); // FIXME: Not DONE: return end; } /** * try to merge the position in adjacent text node (if it is not already in) * * @param position * @return the dom position */ public static IDOMPosition mergeIntoText(IDOMPosition position) { if (position.getContainerNode() instanceof Text) return position; Node pre = position.getPreviousSiblingNode(); if (pre instanceof Text) { return new DOMPosition(pre, ((Text) pre).getData().length()); } Node after = position.getNextSiblingNode(); if (after instanceof Text) { return new DOMPosition(after, 0); } return position; } /** * @param displayData * @param nodeData * @param offset * @return the offset */ // FIXME: this method is still buggy public static int displayOffsetToTextDataOffset(String displayData, String nodeData, int offset) { char[] display = displayData.toCharArray(); if (offset >= display.length) { // offset is already at end return nodeData.length(); } char[] node = nodeData.toCharArray(); int nodeDataLength = node.length; int displayIndex = 0; int nodeIndex = 0; while (displayIndex < offset && nodeIndex < nodeDataLength) { if (display[displayIndex] == node[nodeIndex]) { displayIndex++; nodeIndex++; continue; } if (HTMLUtil.isHTMLWhitespace(node[nodeIndex])) { if (HTMLUtil.isHTMLWhitespace(display[displayIndex])) { displayIndex++; nodeIndex++; } else { nodeIndex++; } continue; } // should not happen! displayIndex++; nodeIndex++; } if (nodeIndex >= nodeDataLength) return nodeDataLength; // else means displayIndex == offset // since we already checked that offset < displayLength, so we can get // the next char if (display[offset] != ' ') { // we may need to skip whitespaces after nodeIndex while (nodeIndex < nodeDataLength && HTMLUtil.isHTMLWhitespace(node[nodeIndex])) { nodeIndex++; } } return nodeIndex; } /** * @param nodeData * @param displayData * @param offset * @return the offset */ // FIXME: this method is still buggy public static int textDataOffsetToDisplayOffset(String nodeData, String displayData, int offset) { if (offset >= nodeData.length()) { return displayData.length(); } char[] node = nodeData.toCharArray(); char[] display = displayData.toCharArray(); int displayIndex = 0; int nodeIndex = 0; int displayDataLength = display.length; while (nodeIndex < offset && displayIndex < displayDataLength) { if (display[displayIndex] == node[nodeIndex]) { displayIndex++; nodeIndex++; continue; } if (HTMLUtil.isHTMLWhitespace(node[nodeIndex])) { if (HTMLUtil.isHTMLWhitespace(display[displayIndex])) { displayIndex++; nodeIndex++; } else { nodeIndex++; } continue; } // should not happen! displayIndex++; nodeIndex++; } return displayIndex; } /** * Convert a IDOMPosition to IDOMRefPosition. If can't convert to * IDOMRefPosition, will return the original one. * * @param position * @return IDOMPosition */ public static IDOMPosition toDOMRefPosition(IDOMPosition position) { if (position.isText()) { return position; // can't convert Text node. } if (position instanceof IDOMRefPosition) { return position; } if (position.getNextSiblingNode() != null) { return new DOMRefPosition(position.getNextSiblingNode(), false); } if (position.getPreviousSiblingNode() != null) { return new DOMRefPosition(position.getPreviousSiblingNode(), true); } return new DOMRefPosition2(position.getContainerNode(), true); } }