blob: ae67831f33249a570543465b2ec740f8c62762cc [file] [log] [blame]
/*******************************************************************************
* 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.utils;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.gef.EditPart;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jst.jsf.core.internal.tld.CMUtil;
import org.eclipse.jst.pagedesigner.dom.DOMPosition;
import org.eclipse.jst.pagedesigner.dom.DOMPositionHelper;
import org.eclipse.jst.pagedesigner.dom.DOMRefPosition;
import org.eclipse.jst.pagedesigner.dom.DOMRefPosition2;
import org.eclipse.jst.pagedesigner.dom.DOMUtil;
import org.eclipse.jst.pagedesigner.dom.EditValidateUtil;
import org.eclipse.jst.pagedesigner.dom.IDOMPosition;
import org.eclipse.jst.pagedesigner.parts.NodeEditPart;
import org.eclipse.jst.pagedesigner.viewer.DesignPosition;
import org.eclipse.jst.pagedesigner.viewer.DesignRange;
import org.eclipse.jst.pagedesigner.viewer.IHTMLGraphicalViewer;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
/**
* @author mengbo
* @version 1.5
*/
public class SelectionHelper {
/**
* convert the text selection to a Node. Will use the start offset of the
* text selection.
*
* @param model
* @param textSel
* @return
*/
public static Node toNode(IStructuredModel model, ITextSelection textSel) {
// FIXME: currently always normalize to a single node. should also
// consider change into DesignRange
// on text selection, find the appropriate Node
Object inode = model.getIndexedRegion(textSel.getOffset());
if (inode instanceof Node) {
return (Node) inode;
}
return null;
}
/**
* convert a structured selection of NodeEditPart or Node into the first
* node.
*
* @param selection
* @return
*/
public static Node toNode(IStructuredSelection selection) {
if (selection.isEmpty()) {
return null;
}
Object first = selection.getFirstElement();
if (first instanceof Node) {
return (Node) first;
} else if (first instanceof NodeEditPart) {
return ((NodeEditPart) first).getIDOMNode();
} else {
return null;
}
}
/**
* convert a DesignRange into a single node.
*
* @param range
* @return
*/
public static Node toNode(DesignRange range) {
if (range.isValid()) {
Node node1 = range.getStartPosition().getContainerNode();
Node node2 = range.getEndPosition().getContainerNode();
return DOMUtil.findCommonAncester(node1, node2);
}
return null;
}
/**
* @param model
* @param region
* if null, then will calculate it using offset.
* @param offset
* offset in source.
* @return
*/
public static IDOMPosition toDOMPosition(IDOMModel model,
IndexedRegion region, int offset) {
if (region == null) {
region = model.getIndexedRegion(offset);
}
if (region == null && offset > 0) {
// in case this is at end of file.
offset = offset - 1;
region = model.getIndexedRegion(offset);
if (region != null) {
if (region.getEndOffset() >= offset + 1) {
offset += 1; // restore offset.
}
}
}
if (region == null) {
return new DOMPosition(model.getDocument(), 0);
}
IDOMNode node = (IDOMNode) region;
int start = node.getStartOffset();
if (offset <= start) {
return new DOMRefPosition(node, false);
}
int end = node.getEndOffset();
if (offset >= end) {
return new DOMRefPosition(node, true);
}
if (node instanceof CharacterData) {
String data = ((CharacterData) node).getData();
String source = node.getSource();
if (data.equals(source)) {
return new DOMPosition(node, offset - start);
}
IStructuredDocumentRegion r = node
.getFirstStructuredDocumentRegion();
int countedData = 0;
// TODO: dead? int offsetInNode = offset - start;
while (r != null) {
if (DOMRegionContext.XML_CHAR_REFERENCE.equals(r.getType())
|| DOMRegionContext.XML_ENTITY_REFERENCE.equals(r
.getType())) {
countedData += 1; // FIXME: what if the entity reference's
// corresponding data is more than 1
// char?
// where can we get that information?
if (r.getEnd() >= offset) {
return new DOMPosition(node, countedData);
}
} else {
if (r.getEnd() >= offset) {
return new DOMPosition(node, countedData + offset
- r.getStart());
}
countedData += r.getLength();
}
r = r.getNext();
}
return new DOMRefPosition(node, true);
} else if (node instanceof Element) {
CMElementDeclaration cm = CMUtil
.getElementDeclaration((Element) node);
if (cm != null && cm.getContentType() == CMElementDeclaration.EMPTY) {
// this node can't have children.
return new DOMRefPosition(node, true);
}
IStructuredDocumentRegion startRegion = node
.getStartStructuredDocumentRegion();
if (startRegion == null) {
return new DOMRefPosition(node, true);
}
int startRegionEnd = node.getStartStructuredDocumentRegion()
.getEnd();
if (offset <= startRegionEnd) {
// it is in the start tag region. So put position at first
// child position.
return new DOMRefPosition2(node, false);
}
return new DOMRefPosition2(node, true);
} else {
return new DOMRefPosition(node, true);
}
// XXX: the implementation in EditModelQuery seemed to be very complex.
// Need revisit that
// and refactor the implementation to this class later. (lium)
}
/**
* Give a text selection with offset and length, convert it into a Designer
* selection (IStrucuturedSelection of editpart or DesignerRange). If the
* text selection include just a single element node, we'll create a
* IStructuredSelection, otherwise we'll create a DesignerRange.
*
* @param graphicViewer
* @param offset
* @param length
*/
public static ISelection convertToDesignerSelection(
IHTMLGraphicalViewer graphicViewer, int offset, int length) {
IDOMModel model = graphicViewer.getModel();
IndexedRegion region1 = model.getIndexedRegion(offset);
IndexedRegion region2 = model.getIndexedRegion(offset + length);
IDOMNode node1 = (IDOMNode) region1;
if (node1 == null) {
IDOMPosition endOfDoc = new DOMRefPosition2(model.getDocument(),
true);
DesignPosition p = DOMPositionHelper.toDesignPosition(endOfDoc);
return new DesignRange(p, p);
}
if ((region1 == region2 || node1.getEndOffset() == offset + length)
&& !(node1 instanceof Text)) {
// ok, we selected a single node.
EditPart part = (EditPart) node1.getAdapterFor(EditPart.class);
if (part != null) {
return new StructuredSelection(part);
}
}
// when we reach here, we'll create a DesignerRange
IDOMPosition position1 = toDOMPosition(model, region1, offset);
IDOMPosition position2 = (length == 0 ? position1 : toDOMPosition(
model, region2, offset + length));
if (position1 == null || position2 == null) {
return new DesignRange(null, null);
}
DesignPosition p1 = DOMPositionHelper.toDesignPosition(position1);
DesignPosition p2 = (length == 0 ? p1 : DOMPositionHelper
.toDesignPosition(position2));
if (p1 == null || p2 == null) {
return new DesignRange(null, null);
}
return new DesignRange(p1, p2);
}
/**
* convert a IDOMPosition into index in the source.
*
* @param p
* @return
*/
private static int getIndexedRegionLocation(IDOMPosition p) {
if (!EditValidateUtil.validPosition(p)) {
return 0;
}
IDOMNode parent = (IDOMNode) p.getContainerNode();
if (p.isText()) {
String text = ((CharacterData) parent).getData();
String source = parent.getSource();
if (text.length() == source.length()) {
// no entity reference.
return parent.getStartOffset() + p.getOffset();
}
// CR404708. Need to handle entity reference in the text.
int offset = p.getOffset();
int counted = 0;
IStructuredDocumentRegion r = parent
.getFirstStructuredDocumentRegion();
while (r != null && counted < offset) {
if (DOMRegionContext.XML_CHAR_REFERENCE.equals(r.getType())
|| DOMRegionContext.XML_ENTITY_REFERENCE.equals(r
.getType())) {
counted++;
if (counted >= offset) {
return r.getEndOffset();
}
} else {
int length = r.getLength();
if (counted + length >= offset) {
return r.getStartOffset() + offset - counted;
}
counted += length;
}
r = r.getNext();
}
return parent.getStartOffset() + p.getOffset();
}
IDOMNode previous = (IDOMNode) p.getPreviousSiblingNode();
if (previous != null) {
return previous.getEndOffset();
}
IDOMNode next = (IDOMNode) p.getNextSiblingNode();
if (next != null) {
return next.getStartOffset();
}
IStructuredDocumentRegion r = parent
.getStartStructuredDocumentRegion();
if (r != null) {
return r.getEnd();
}
// r == null normally means the parent is the document node.
return parent.getEndOffset();
}
/**
* convert design selection of structured selection of NodeEditPart into
* structured selection of Node
*
* @param sel
* @return
*/
public static IStructuredSelection convertFromDesignSelection(
IStructuredSelection sel) {
List list = sel.toList();
if (list != null) {
List result = new ArrayList(list.size());
for (int i = 0, size = list.size(); i < size; i++) {
NodeEditPart part = (NodeEditPart) list.get(i);
result.add(part.getIDOMNode());
}
return new StructuredSelection(result);
}
return new StructuredSelection();
}
/**
*
* @param selection
* selection from designer, could be IStructuredSelection of
* NodeEditPart, or DesignRange.
* @return
*/
public static ITextSelection convertFromDesignSelection(DesignRange range) {
if (range.isValid()) {
IDOMPosition start = DOMPositionHelper.toDOMPosition(range
.getStartPosition());
IDOMPosition end = DOMPositionHelper.toDOMPosition(range
.getEndPosition());
// We should not encounter invalid position.
if (EditValidateUtil.validPosition(start)
&& EditValidateUtil.validPosition(end)) {
int offset = getIndexedRegionLocation(start);
int endoffset = getIndexedRegionLocation(end);
if (offset > endoffset) {
int temp = offset;
offset = endoffset;
endoffset = temp;
}
return new TextSelection(offset, endoffset - offset);
}
}
return new TextSelection(0, 0);
}
public static ITextSelection convertFromDesignSelectionToTextSelection(
ISelection selection) {
if (selection instanceof IStructuredSelection) {
IStructuredSelection nodes = convertFromDesignSelection((IStructuredSelection) selection);
IDOMNode node = (IDOMNode) nodes.getFirstElement();
if (node != null && node.getNodeType() != Node.DOCUMENT_NODE) {
return new TextSelection(node.getStartOffset(), node
.getEndOffset()
- node.getStartOffset());
}
} else if (selection instanceof DesignRange) {
return convertFromDesignSelection((DesignRange) selection);
}
return new TextSelection(0, 0);
}
}