diff options
Diffstat (limited to 'jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/utils/BodyHelper.java')
-rw-r--r-- | jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/utils/BodyHelper.java | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/utils/BodyHelper.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/utils/BodyHelper.java new file mode 100644 index 000000000..ba0cf0a65 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/utils/BodyHelper.java @@ -0,0 +1,327 @@ +/******************************************************************************* + * 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.Comparator; + +import javax.xml.namespace.QName; + +import org.eclipse.jst.pagedesigner.IJMTConstants; +import org.eclipse.jst.pagedesigner.adapters.IBodyInfo; +import org.eclipse.jst.pagedesigner.adapters.internal.BodyInfo; +import org.eclipse.jst.pagedesigner.dom.DOMPosition; +import org.eclipse.jst.pagedesigner.dom.DOMRefPosition; +import org.eclipse.jst.pagedesigner.dom.DOMRefPosition2; +import org.eclipse.jst.pagedesigner.dom.IDOMPosition; +import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; +import org.eclipse.wst.xml.core.internal.provisional.document.IDOMText; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * This class helps location insertion position to inside correct body or doc + * prefix. NOTE: this class only doing limited support on doc level position + * validation. Element specific position validation will be done in other + * places. + * + * @author mengbo + */ +public class BodyHelper { + // bit flags used for child skipping. + public static final int EMPTY_TEXT = 1; + + public static final int COMMENT = 2; + + public static final int HEADER = 3; + + /** + * + * @param child + * @return + */ + private static boolean isSkippableChild(Node parent, Node child, int flag) { + if ((flag & COMMENT) != 0 && child.getNodeType() == Node.COMMENT_NODE) + return true; + if ((flag & EMPTY_TEXT) != 0 && child instanceof IDOMText + && ((IDOMText) child).isElementContentWhitespace()) + return true; + + if ((flag & HEADER) != 0 && child.getNodeType() == Node.ELEMENT_NODE) { + String uri = CMUtil.getElementNamespaceURI((Element) child); + IBodyInfo parentInfo = getBodyInfo((IDOMNode) parent); + if (parentInfo != null + && parentInfo.isBodyHeader((IDOMNode) parent, uri, + ((Element) child).getLocalName())) + return true; + } + return false; + } + + /** + * check whether uri/tag should be header of any body container that is + * ancester of the start node. + * + * @param start + * @param uri + * @param tag + * @return + */ + public static IDOMNode findHeaderContainer(IDOMNode start, String uri, + String tag) { + while (start != null) { + IBodyInfo designInfo = getBodyInfo(start); + if (designInfo != null && designInfo.isBodyContainer(start)) { + if (designInfo.isBodyHeader(start, uri, tag)) + return start; + } + start = (IDOMNode) start.getParentNode(); + } + return null; + } + + /** + * find the closest body insertion point, to make it as deep as possible. + * (Move into as more body as possible) + * + * @param parent + * @param start + */ + public static IDOMPosition findBodyInsertLocation(IDOMPosition position) { + // forward first. + Node reference = position.getNextSiblingNode(); + Node container = position.getContainerNode(); + while (reference != null) { + IBodyInfo info = getBodyInfo((IDOMNode) reference); + if (info != null && info.isBodyContainer((IDOMNode) reference)) { + // good, we find a body! + position = new DOMPosition(reference, 0); + return findBodyInsertLocation(position); + } + if (isSkippableChild(container, reference, EMPTY_TEXT | COMMENT + | HEADER)) { + reference = reference.getNextSibling(); + continue; + } else + break; + } + + // backward + reference = position.getPreviousSiblingNode(); + while (reference != null) { + IBodyInfo info = getBodyInfo((IDOMNode) reference); + if (info != null && info.isBodyContainer((IDOMNode) reference)) { + // good, we find a body! + position = new DOMPosition(reference, reference.getChildNodes() + .getLength()); + return findBodyInsertLocation(position); + } + // XXX: not skip header here. So if there is some header with wrong + // location, we will respect user. + if (isSkippableChild(container, reference, EMPTY_TEXT | COMMENT)) { + reference = reference.getPreviousSibling(); + continue; + } else + break; + } + + // not find any body at same level as the insertion point. + return position; + } + + /** + * The element type identifiered by "uri" and "tag" is going to be inserted + * into the document. This method is used to adjust the insert position so + * it can be put into correct body or header section. + * + * @param parent + * @param reference + */ + public static IDOMPosition adjustInsertPosition(String uri, String tag, + IDOMPosition position) { + IDOMNode parent = (IDOMNode) position.getContainerNode(); + IBodyInfo designInfo = getBodyInfo(parent); + if (designInfo == null) { + return position; // should not happen. + } + + IDOMNode headerContainer = findHeaderContainer(parent, uri, tag); + + if (headerContainer == null) { + // the new node is not header. + if (shouldIgnoreAdjust(uri, tag)) { + return position; + } + + // new node is not body header. So should inside the inner most + // body. + if (!designInfo.isBodyContainer(parent)) { + return position; // it's parent is not body, so we suggest + // it's parent already correctly located, and respect user's + // choice. + } + + // ok, we are inside some body, but we don't know whether we are in + // the inner most body. + // try to find a body container at same level and see whether we can + // move into that body. + return findBodyInsertLocation(position); + } else { + // good, we find a body container and the new node should be header + // of it. + Node child = headerContainer.getFirstChild(); + Node refNode = position.getNextSiblingNode(); + // if parent is different from headerContainer, then + // child!=referenceHolder[0] will always be true + while (child != null) // && child != refNode) + { + Comparator comp = NodeLocationComparator.getInstance(); + // Currently the comparator deels with tags like taglib and + // loadbundle particularly, comparasion result 0 + // means it didn't compare the tags. + if (comp.compare(child, tag) < 0 + || (comp.compare(child, tag) == 0 && isSkippableChild( + headerContainer, child, COMMENT | EMPTY_TEXT + | HEADER))) { + child = child.getNextSibling(); + } else { + break; + } + } + if (child != null) { + return new DOMRefPosition(child, false); + } else { + return new DOMPosition(parent, parent.getChildNodes() + .getLength()); + } + // parentHolder[0] = headerContainer; + // referenceHolder[0] = child; + // return; + } + } + + /** + * Find the position to insert a header element into the specified parent. + * + * @param uri + * @param tag + * @param parent + */ + public static void findHeaderInsertPosition(String uri, String tag, + Node parent, Node[] ref) { + Node child = parent.getFirstChild(); + while (child != null) { + Comparator comp = NodeLocationComparator.getInstance(); + if (comp.compare(child, tag) < 0 + || (comp.compare(child, tag) == 0 && isSkippableChild( + parent, child, COMMENT | EMPTY_TEXT | HEADER))) { + child = child.getNextSibling(); + } else { + break; + } + } + ref[0] = child; + return; + } + + public static IDOMPosition insertBody(IDOMPosition position, QName body, + String defaultPrefix) { + IBodyInfo bodyInfo = getBodyInfo((IDOMNode) position.getContainerNode()); + + Node node = position.getContainerNode(); + Node originalContainer = node; + Node nextSibling = position.getNextSiblingNode(); + + // create the body element first. + Document ownerDoc; + if (node instanceof Document) { + ownerDoc = (Document) node; + } else { + ownerDoc = node.getOwnerDocument(); + } + if (ownerDoc == null) { + return null; // should not happen + } + + String prefix = JSPUtil.getOrCreatePrefix(((IDOMNode) node).getModel(), + body.getNamespaceURI(), defaultPrefix); + Element ele = ownerDoc.createElement((prefix == null ? "" + : (prefix + ":")) + + body.getLocalPart()); + + // need to find out the insertion point + while (node instanceof IDOMNode) { + if (bodyInfo.isBodyContainer((IDOMNode) node)) { + // ok, node is a body container. + // we could create the new node as child of node and move all + // node's none header children + // as children of the new node. + + NodeList nl = node.getChildNodes(); + ArrayList list = new ArrayList(); + for (int i = 0; i < nl.getLength(); i++) { + Node child = (Node) nl.item(i); + if (isSkippableChild(node, child, HEADER | COMMENT + | EMPTY_TEXT)) { + continue; + } + list.add(nl.item(i)); + } + for (int i = 0; i < list.size(); i++) { + ele.appendChild((Node) list.get(i)); + } + node.appendChild(ele); + + if (node == originalContainer) { + if (nextSibling == null) { + return new DOMRefPosition2(ele, true); + } else if (nextSibling.getParentNode() == ele) { + // next sibling is not in header part + return new DOMRefPosition(nextSibling, false); + } else { + return new DOMPosition(ele, 0); + } + } else { + return position; + } + } + node = node.getParentNode(); + } + // should not happen, because document and documentfragment node will + // always be body node + // so if reach here, means the position is not in document. + return null; + } + + /** + * For certain special tags, do not following the "header"/"body" separation + * and can't fit into the relocation process. + * + * @param uri + * @param tag + * @return + */ + public static boolean shouldIgnoreAdjust(String uri, String tag) { + // FIXME: + return (IJMTConstants.URI_HTML.equals(uri) && "script" + .equalsIgnoreCase(tag)) + || (IJMTConstants.URI_JSP.equals(uri)); + } + + public static IBodyInfo getBodyInfo(IDOMNode node) { + // TODO: in the future, when bodyinfo is no longer singleton, we'll use + // adapter mechanism. + return BodyInfo.getInstance(); + } +} |