Package org.eclipse.jst.pagedesigner.utils

Source Code of org.eclipse.jst.pagedesigner.utils.BodyHelper

/*******************************************************************************
* 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.jsf.core.internal.tld.CMUtil;
import org.eclipse.jst.jsf.core.internal.tld.ITLDConstants;
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.
  /**
   * Bit flag for empty text node
   */
  public static final int EMPTY_TEXT = 1;

  /**
   * Bit flag for comment node
   */
  public static final int COMMENT = 2;

  /**
   * Bit flag for HEAD node
   */
  public static final int HEADER = 3;

  /**
   *
   * @param child
   * @return boolean
   */
  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 IDOMNode
   */
  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 position
   * @return IDOMPosition
   */
  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;
      }
            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;
      }
            break;
    }

    // not find any body at same level as the insertion point.
    return position;
  }

  /**
   * The element type identified 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 uri
   * @param tag
   * @param position
   * @return IDOMPosition
   *
   */
  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 place 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);
    }
        // good, we find a body container and the new node should be header
        // of it.
        Node child = headerContainer.getFirstChild();

        // 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);
        }
        return new DOMPosition(parent, parent.getChildNodes()
            .getLength());
  }

  /**
   * Find the position to insert a header element into the specified parent.
   *
   * @param uri
   * @param tag
   * @param parent
   * @param ref
   */
  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;
  }

  /**
   * @param position
   * @param body
   * @param defaultPrefix
   * @return the new dom position based on the insert.  May return null if
   * insert fails.
   */
  public static IDOMPosition insertBody(IDOMPosition position, QName body,
      String defaultPrefix) {
    IBodyInfo bodyInfo = getBodyInfo((IDOMNode) position.getContainerNode());

    Node node = position.getContainerNode();
    final Node originalContainer = node;
    final 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
    }

    final String prefix = JSPUtil.getOrCreatePrefix(((IDOMNode) node).getModel(),
        body.getNamespaceURI(), defaultPrefix);
    final Element ele = ownerDoc.createElement((prefix == null ? "" //$NON-NLS-1$
        : (prefix + ":")) //$NON-NLS-1$
        + 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 = 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);
          }
        }
                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 true if tag is an element that should be moved in response to
   * body insert.
   */
  public static boolean shouldIgnoreAdjust(String uri, String tag) {
    // FIXME:
    return (ITLDConstants.URI_HTML.equalsIgnoreCase(uri) && "script" //$NON-NLS-1$
        .equalsIgnoreCase(tag))
        || (ITLDConstants.URI_JSP.equals(uri));
  }

  /**
   * @param node
   * @return the body info corresponding to node (should we use a node adapter?)
   */
  public static IBodyInfo getBodyInfo(IDOMNode node) {
    // TODO: in the future, when bodyinfo is no longer singleton, we'll use
    // adapter mechanism.
    return BodyInfo.getInstance();
  }
}
TOP

Related Classes of org.eclipse.jst.pagedesigner.utils.BodyHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.