Package org.apache.xerces.dom

Source Code of org.apache.xerces.dom.RangeImpl

/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 The Apache Software Foundation.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment: 
*       "This product includes software developed by the
*        Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*4dorse or promote products derived from this
*    software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    nor may "Apache" appear in their name, without prior written
*    permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.apache.org.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.xerces.dom;

import org.w3c.dom.DOMException;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.CharacterData;
import org.apache.xerces.dom.DOMExceptionImpl;
import org.apache.xerces.dom.DocumentImpl;
import org.w3c.dom.range.*;
import java.util.Vector;
import java.util.Enumeration;

/** The RangeImpl class implements the org.w3c.dom.range.Range interface.
<p> Please see the API documentation for the interface classes 
*  and use the interfaces in your client programs.
*/
public class RangeImpl  implements Range {
   
    //
    // Constants
    //
   

    // Where to collapse to.
    static final int START = 0;
    static final int AFTER = 1;
    static final int BEFORE = -1;
   
    //
    // Data
    //
   
    DocumentImpl fDocument;
    Node fStartContainer;
    Node fEndContainer;
    int fStartOffset;
    int fEndOffset;
    boolean fIsCollapsed;  
    boolean fDetach = false;
    Node fInsertNode = null;
    Node fDeleteNode = null;
    Node fSplitNode = null;
   
   
    /** The constructor. Clients must use DocumentRange.createRange(),
     *  because it registers the Range with the document, so it can
     *  be fixed-up.
     */
    public RangeImpl(DocumentImpl document) {
        fDocument = document;
        fStartContainer = document;
        fEndContainer = document;
        fStartOffset = 0;
        fEndOffset = 0;
        fDetach = false;
    }
   
    public Node getStartContainer() {
        return fStartContainer;
    }
   
    public int getStartOffset() {
        return fStartOffset;
    }
   
    public Node getEndContainer() {
        return fEndContainer;
    }
    public int getEndOffset() {
        return fEndOffset;
    }
   
    public boolean getCollapsed() {
        return (fStartContainer == fEndContainer
             && fStartOffset == fEndOffset);
    }
   
    public Node getCommonAncestorContainer(){
        Vector startV = new Vector();
        Node node;
        for (node=fStartContainer; node != null;
             node=node.getParentNode())
        {
            startV.addElement(node);
        }
        Vector endV = new Vector();
        for (node=fEndContainer; node != null;
             node=node.getParentNode())
        {
            endV.addElement(node);
        }
        int s = startV.size()-1;
        int e = endV.size()-1;
        Object result = null;
        while (s>=0 && e>=0) {
            if (startV.elementAt(s) == endV.elementAt(e)) {
                result = startV.elementAt(s);
            } else {
                break;
            }
            --s;
            --e;
        }
        return (Node)result;
    }
   
   
    public void setStart(Node refNode, int offset)
                         throws RangeException, DOMException
    {
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
        }
        if ( !isAncestorTypeValid(refNode)) {
        throw new RangeExceptionImpl(
          RangeException.INVALID_NODE_TYPE_ERR,
      "DOM012 Invalid node type");
        }
       
        checkIndex(refNode, offset);
       
        fStartContainer = refNode;
        fStartOffset = offset;
    }
   
    public void setEnd(Node refNode, int offset)
                       throws RangeException, DOMException
    {
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
        }
        if ( !isAncestorTypeValid(refNode)) {
        throw new RangeExceptionImpl(
          RangeException.INVALID_NODE_TYPE_ERR,
      "DOM012 Invalid node type");
        }
       
        checkIndex(refNode, offset);
       
        fEndContainer = refNode;
        fEndOffset = offset;
    }
    public void setStartBefore(Node refNode)
        throws RangeException
    {
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
        }
        if ( !isAncestorTypeValid(refNode)) {
        throw new RangeExceptionImpl(
          RangeException.INVALID_NODE_TYPE_ERR,
      "DOM012 Invalid node type");
        }
        fStartContainer = refNode.getParentNode();
        int i = 0;
        for (Node n = refNode; n!=null; n = n.getPreviousSibling()) {
            i++;
        }
        fStartOffset = i-1;
    }
    public void setStartAfter(Node refNode)
        throws RangeException
    {
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
        }
        if ( !isAncestorTypeValid(refNode)) {
        throw new RangeExceptionImpl(
          RangeException.INVALID_NODE_TYPE_ERR,
      "DOM012 Invalid node type");
        }
        fStartContainer = refNode.getParentNode();
        int i = 0;
        for (Node n = refNode; n!=null; n = n.getPreviousSibling()) {
            i++;
        }
        fStartOffset = i;
    }
    public void setEndBefore(Node refNode)
        throws RangeException
    {
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
        }
        if ( !isAncestorTypeValid(refNode)) {
        throw new RangeExceptionImpl(
          RangeException.INVALID_NODE_TYPE_ERR,
      "DOM012 Invalid node type");
        }
        fEndContainer = refNode.getParentNode();
        int i = 0;
        for (Node n = refNode; n!=null; n = n.getPreviousSibling()) {
            i++;
        }
        fEndOffset = i-1;
    }
                                           
    public void setEndAfter(Node refNode)
        throws RangeException
    {
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
        }
        if ( !isAncestorTypeValid(refNode)) {
        throw new RangeExceptionImpl(
          RangeException.INVALID_NODE_TYPE_ERR,
      "DOM012 Invalid node type");
        }
        fEndContainer = refNode.getParentNode();
        int i = 0;
        for (Node n = refNode; n!=null; n = n.getPreviousSibling()) {
            i++;
        }
        fEndOffset = i;
    }
    public void collapse(boolean toStart) {
       
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
        }
       
        if (toStart) {
            fEndContainer = fStartContainer;
            fEndOffset = fStartOffset;
        } else {
            fStartContainer = fEndContainer;
            fStartOffset = fEndOffset;
        }
    }
   
    public void selectNode(Node refNode)
        throws RangeException
    {
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
        }
        if ( !isAncestorTypeValid(refNode)) {
        throw new RangeExceptionImpl(
          RangeException.INVALID_NODE_TYPE_ERR,
      "DOM012 Invalid node type");
        }
        Node parent = refNode.getParentNode();
        if (parent != null ) // REVIST: what to do if it IS null?
        {
            fStartContainer = parent;
            fEndContainer = parent;
            int i = 0;
            for (Node n = refNode; n!=null; n = n.getPreviousSibling()) {
                i++;
            }
            fStartOffset = i-1;
            fEndOffset = fStartOffset+1;
        }
    }
       
    public void selectNodeContents(Node refNode)
        throws RangeException
    {
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
        }
        if ( !isAncestorTypeValid(refNode)) {
        throw new RangeExceptionImpl(
          RangeException.INVALID_NODE_TYPE_ERR,
      "DOM012 Invalid node type");
        }
        fStartContainer = refNode;
        fEndContainer = refNode;
        Node first = refNode.getFirstChild();
        fStartOffset = 0;
        if (first == null) {
            fEndOffset = 0;
        } else {
            int i = 0;
            for (Node n = first; n!=null; n = n.getNextSibling()) {
                i++;
            }
            fEndOffset = i;
        }
       
    }

    public short compareBoundaryPoints(short how, Range sourceRange)
        throws DOMException
    {
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
      }
      
        Node endPointA;
        Node endPointB;
        int offsetA;
        int offsetB;
       
        if (how == START_TO_START) {
            endPointA = sourceRange.getStartContainer();
            endPointB = fStartContainer;
            offsetA = sourceRange.getStartOffset();
            offsetB = fStartOffset;
        } else
        if (how == START_TO_END) {
            endPointA = sourceRange.getStartContainer();
            endPointB = fEndContainer;
            offsetA = sourceRange.getStartOffset();
            offsetB = fEndOffset;
        } else
        if (how == END_TO_START) {
            endPointA = sourceRange.getEndContainer();
            endPointB = fStartContainer;
            offsetA = sourceRange.getEndOffset();
            offsetB = fStartOffset;
        } else {
            endPointA = sourceRange.getEndContainer();
            endPointB = fEndContainer;
            offsetA = sourceRange.getEndOffset();
            offsetB = fEndOffset;
        }
       
        // case 1: same container
        if (endPointA == endPointB) {
            if (offsetA < offsetB) return -1;
            if (offsetA == offsetB) return 0;
            return 1;
        }
        // case 2: Child C of container A is ancestor of B
        for (Node node=endPointA.getFirstChild(); node != null; node=node.getNextSibling()) {
            if (isAncestorOf(node, endPointB)) {
                int index = indexOf(node, endPointA);
                if (offsetA <  index) return -1;
                if (offsetA == index) return 0;
                return 1;
            }
        }
        // case 3: Child C of container B is ancestor of A
        for (Node node=endPointB.getFirstChild(); node != null; node=node.getNextSibling()) {
            if (isAncestorOf(node, endPointA)) {
                int index = indexOf(node, endPointB);
                if (offsetB <  index) return -1;
                if (offsetB == index) return 0;
                return 1;
            }
        }
        // case 4: preorder traversal of context tree.
        Node ancestor = getCommonAncestorContainer();
        Node current = ancestor;
               
        do {
            if (current == endPointA) return -1;
            if (current == endPointB) return 1;
            current = nextNode(current, true);// REVIST this...
        }
        while (current!=null && current!=ancestor);
       
        // REVISIT: this should never happen?!?!?!?
        return -2;
    }
   
    public void deleteContents()
        throws DOMException
    {
        Node current = fStartContainer;
        Node parent = null;
        Node next;
        boolean deleteCurrent = false;
        Node root = getCommonAncestorContainer();
       
        // if same container, simplify case
        if (fStartContainer == fEndContainer) {
            if (fStartOffset == fEndOffset) { // eg collapsed
                return;
            }
            if (fStartContainer.getNodeType() == Node.TEXT_NODE) {
                String value = fStartContainer.getNodeValue();
                //REVIST: This section should probably throw an exception!
                // This should NEVER happen, if the Range is always valid.
                int realStart = fStartOffset;
                int realEnd = fEndOffset;
                if (fStartOffset > value.length()) realStart = value.length()-1;
                if (fEndOffset > value.length()) realEnd = value.length()-1;
               
                deleteData((CharacterData)fStartContainer, realStart, realEnd-realStart);
               
                } else {
                current = fStartContainer.getFirstChild();
                int i = 0;
                for(i = 0; i < fStartOffset && current != null; i++) {
                    current=current.getNextSibling();
                }
                for(i = 0; i < fEndOffset-fStartOffset && current != null; i++) {
                    Node newCurrent=current.getNextSibling();
                    removeChild(fStartContainer, current);
                    current = newCurrent;
                }
            }
            collapse(true);
            return;
        }
       
        Node partialNode = null;
        int partialInt = START;
       
        Node startRoot = null;
        // initialize current for startContainer.
        if (current.getNodeType() == Node.TEXT_NODE) {
            deleteData((CharacterData)current, fStartOffset,
                current.getNodeValue().length()-fStartOffset);
        } else {
            current = current.getFirstChild();
            for (int i = 0 ; i < fStartOffset && current != null; i++){
                current = current.getNextSibling();
            }
            if (current==null) {
                current = fStartContainer;
            } else
            if (current != fStartContainer)
            {
                deleteCurrent = true;
            }
        }
       
       
        // traverse up from the startContainer...
        // current starts as the node to delete;
        while (current != root && current != null) {
           
            parent = current.getParentNode();
            if (parent == root) {
                if (startRoot == null)
                    startRoot = current;
            } else {
                if (partialNode==null) {
                    partialNode = parent;
                    partialInt = AFTER;
                }
            }

           
            if (parent != root) {
                next = current.getNextSibling();
                Node nextnext;
                while (next != null) {
                    nextnext = next.getNextSibling();
                    //parent.removeChild(next);
                    removeChild(parent, next);
                    next = nextnext;
                }
            }
           
            if (deleteCurrent) {
                //parent.removeChild(current);
                removeChild(parent, current);
                deleteCurrent = false;
            }
            current = parent;
        }
       
        Node endRoot = null;
        // initialize current for endContainer.
        current = fEndContainer;
        if (current.getNodeType() == Node.TEXT_NODE) {
            deleteData((CharacterData)current, 0, fEndOffset);
        } else {
   
            if (fEndOffset == 0) { // "before"
                current = fEndContainer;
            }
            else {
                current = current.getFirstChild();
                for(int i = 1; i < fEndOffset && current != null; i++) {
                    current=current.getNextSibling();
                }
                if (current==null) { // REVIST: index-out-of-range what to do?
                    current = fEndContainer.getLastChild();
                } else
                if (current != fStartContainer) {
                    deleteCurrent = true;
                }
               
            }
        }
       
        // traverse up from the endContainer...
        while (current != root && current != null) {
           
            parent = current.getParentNode();
            if (parent == root) {
                if (endRoot == null)
                    endRoot = current;
            } else {
                if (partialNode==null) {
                    partialNode = parent;
                    partialInt = BEFORE;
                }
            }
      
            if (parent != root && parent != null) {
                next = current.getPreviousSibling();
                Node nextnext;
                while (next != null) {
                    nextnext = next.getPreviousSibling();
                    removeChild(parent, next);
                    next = nextnext;
                }
            }
           
            if (deleteCurrent) {
                removeChild(parent, current);
                deleteCurrent = false;
            }
            current = parent;
        }
       
        //if (endRoot == null || startRoot == null) return; //REVIST
        current = endRoot.getPreviousSibling();
        Node prev = null;
        while (current != null && current != startRoot ) {
            prev = current.getPreviousSibling();
            parent = current.getParentNode();
            if (parent != null) {
                removeChild(parent, current);
            }
            current = prev;
        }
       
        if (partialNode == null) {
            collapse(true);
        } else
        if (partialInt == AFTER) {
            setStartAfter(partialNode);
            setEndAfter(partialNode);
        }
        else if (partialInt == BEFORE) {
            setStartBefore(partialNode);
            setEndBefore(partialNode);
        }       
    }
       
    public DocumentFragment extractContents()
        throws DOMException
    {
        return traverseContents(EXTRACT_CONTENTS);
    }
       
    public DocumentFragment cloneContents()
        throws DOMException
    {
        return traverseContents(CLONE_CONTENTS);
    }
   
    public void insertNode(Node newNode)
        throws DOMException, RangeException
    {
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
      }
        int type = newNode.getNodeType();
        if (type == Node.ATTRIBUTE_NODE
            || type == Node.ENTITY_NODE
            || type == Node.NOTATION_NODE
            || type == Node.DOCUMENT_NODE
            || type == Node.DOCUMENT_FRAGMENT_NODE)
        {
        throw new RangeExceptionImpl(
          RangeException.INVALID_NODE_TYPE_ERR,
      "DOM012 Invalid node type");
        }
        if (newNode == null) return; //throw exception?
        Node cloneCurrent;
        Node current;
        boolean MULTIPLE_MODE = false;
        if (fStartContainer.getNodeType() == Node.TEXT_NODE) {
            if (newNode.getNodeType()!= Node.TEXT_NODE) { // result is 3 text nodes...
                cloneCurrent = fStartContainer.cloneNode(false);
                ((TextImpl)cloneCurrent).setNodeValueInternal(
                    (cloneCurrent.getNodeValue()).substring(fStartOffset));
                ((TextImpl)fStartContainer).setNodeValueInternal(
                    (fStartContainer.getNodeValue()).substring(0,fStartOffset));
                Node next = fStartContainer.getNextSibling();
                if (next != null) {
                    Node parent = fStartContainer.getParentNode();
                    if (parent !=  null) {
                        parent.insertBefore(newNode, next);
                        parent.insertBefore(cloneCurrent, next);
                    }
                } else {
                    Node parent = fStartContainer.getParentNode();
                    if (parent != null) {
                        parent.appendChild(newNode);
                        parent.appendChild(cloneCurrent);
                    }
                }
                // signal other Ranges to update their start/end containers/offsets
                signalSplitData(fStartContainer, cloneCurrent, fStartOffset);
               
            } else { // result is 1 text node.
                String value = fStartContainer.getNodeValue();
                String newValue = newNode.getNodeValue();
                insertData( (CharacterData)fStartContainer, fStartOffset, newValue);
            }
        } else { // ! TEXT_NODE
            current = fStartContainer.getFirstChild();
            int i = 0;
            for(i = 0; i < fStartOffset && current != null; i++) {
                current=current.getNextSibling();
            }
            if (current != null) {
                fStartContainer.insertBefore(newNode, current);
            } else {
                fStartContainer.appendChild(newNode);
            }
        }               
       
    }
   
    public void surroundContents(Node newParent)
        throws DOMException, RangeException
    {
        if (newParent==null) return;
       
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
      }
        int type = newParent.getNodeType();
        if (type == Node.ATTRIBUTE_NODE
            || type == Node.ENTITY_NODE
            || type == Node.NOTATION_NODE
            || type == Node.DOCUMENT_TYPE_NODE
            || type == Node.DOCUMENT_NODE
            || type == Node.DOCUMENT_FRAGMENT_NODE)
        {
        throw new RangeExceptionImpl(
          RangeException.INVALID_NODE_TYPE_ERR,
      "DOM012 Invalid node type");
        }
       
        Node root = getCommonAncestorContainer();
       
        Node realStart = fStartContainer;
        Node realEnd = fEndContainer;
        if (fStartContainer.getNodeType() == Node.TEXT_NODE) {
            realStart = fStartContainer.getParentNode();
        }
        if (fEndContainer.getNodeType() == Node.TEXT_NODE) {
            realEnd = fEndContainer.getParentNode();
        }
           
        if (realStart != realEnd) {
             throw new RangeExceptionImpl(
        RangeException.BAD_BOUNDARYPOINTS_ERR,
        "DOM013 Bad boundary points");
        }

      DocumentFragment frag = extractContents();
      insertNode(newParent);
      newParent.appendChild(frag);
      selectNode(newParent);
    }
       
    public Range cloneRange(){
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
      }
       
        Range range = fDocument.createRange();
        range.setStart(fStartContainer, fStartOffset);
        range.setEnd(fEndContainer, fEndOffset);
        return range;
    }
   
    public String toString(){
      if( fDetach) {
        throw new DOMExceptionImpl(
          DOMException.INVALID_STATE_ERR,
      "DOM011 Invalid state");
      }
     
      Node node = fStartContainer;
      StringBuffer sb = new StringBuffer();
      if (fStartContainer.getNodeType() == Node.TEXT_NODE
       || fStartContainer.getNodeType() == Node.CDATA_SECTION_NODE
      ) {
          if (fStartContainer == fEndContainer) {
              sb.append(fStartContainer.getNodeValue().substring(fStartOffset, fEndOffset));
              return sb.toString();
          } else {
              sb.append(fStartContainer.getNodeValue().substring(fStartOffset));
          }
      }
      while (node != fEndContainer) {
          node = nextNode(node, true);
          if (node == null) break;
          if (node.getNodeType() == Node.TEXT_NODE
          ||  node.getNodeType() == Node.CDATA_SECTION_NODE
          ) {
              sb.append(node.getNodeValue());
          }
      }
      if (fEndContainer.getNodeType() == Node.TEXT_NODE
       || fEndContainer.getNodeType() == Node.CDATA_SECTION_NODE) {
          sb.append(fEndContainer.getNodeValue().substring(0,fEndOffset));
      }
      return sb.toString();
    }
   
    public void detach() {
        fDetach = true;
        fDocument.removeRange(this);
    }
   
    //
    // Mutation functions
    //
   
    /** Signal other Ranges to update their start/end
     *  containers/offsets. The data has already been
     *  into the two Nodes.
     */
    void signalSplitData(Node node, Node newNode, int offset) {
        fSplitNode = node;
        // fix-up other Ranges
        Enumeration ranges = fDocument.getRanges();
        if (ranges != null) {
            while ( ranges.hasMoreElements()) {
                ((RangeImpl)ranges.nextElement()).receiveSplitData(node, newNode, offset);
            }
        }
        fSplitNode = null;
    }
   
    /** Fix up this Range if another Range has split a Text Node
     *  into 2 Nodes.
     */
    void receiveSplitData(Node node, Node newNode, int offset) {
        if (node == null || newNode == null) return;
        if (fSplitNode == node) return;
       
        if (node == fStartContainer
        && fStartContainer.getNodeType() == Node.TEXT_NODE) {
            if (fStartOffset > offset) {
                fStartOffset = fStartOffset - offset;
                fStartContainer = newNode;
            }
        }
        if (node == fEndContainer
        && fEndContainer.getNodeType() == Node.TEXT_NODE) {
            if (fEndOffset > offset) {
                fEndOffset = fEndOffset-offset;
                fEndContainer = newNode;
            }
        }
       
    }
  
    /** This function inserts text into a Node and invokes
     *  a method to fix-up all other Ranges.
     */
    void deleteData(CharacterData node, int offset, int count) {
        fDeleteNode = node;
        node.deleteData( offset,  count);
        fDeleteNode = null;
    }
   
   
    /** This function is called from DOM.
     *  The  text has already beeen inserted.
     *  Fix-up any offsets.
     */
    void receiveDeletedText(Node node, int offset, int count) {
        if (node == null) return;
        if (fDeleteNode == node) return;
        if (node == fStartContainer
        && fStartContainer.getNodeType() == Node.TEXT_NODE) {
            if (fStartOffset > offset+count) {
                fStartOffset = offset+(fStartOffset-(offset+count));
            } else
            if (fStartOffset > offset) {
                fStartOffset = offset;
           
        }
        if (node == fEndContainer
        && fEndContainer.getNodeType() == Node.TEXT_NODE) {
            if (fEndOffset > offset+count) {
                fEndOffset = offset+(fEndOffset-(offset+count));
            } else
            if (fEndOffset > offset) {
                fEndOffset = offset;
           
        }
       
    }
  
    /** This function inserts text into a Node and invokes
     *  a method to fix-up all other Ranges.
     */
    void insertData(CharacterData node, int index, String insert) {
        fInsertNode = node;
        node.insertData( index,  insert);
        fInsertNode = null;
    }
   
   
    /** This function is called from DOM.
     *  The  text has already beeen inserted.
     *  Fix-up any offsets.
     */
    void receiveInsertedText(Node node, int index, int len) {
        if (node == null) return;
        if (fInsertNode == node) return;
        if (node == fStartContainer
        && fStartContainer.getNodeType() == Node.TEXT_NODE) {
            if (index < fStartOffset) {
                fStartOffset = fStartOffset+len;
            }
        }
        if (node == fEndContainer
        && fEndContainer.getNodeType() == Node.TEXT_NODE) {
            if (index < fEndOffset) {
                fEndOffset = fEndOffset+len;
            }
        }
       
    }
  
    /** This function is called from DOM.
     *  The  text has already beeen replaced.
     *  Fix-up any offsets.
     */
    void receiveReplacedText(Node node) {
        if (node == null) return;
        if (node == fStartContainer
        && fStartContainer.getNodeType() == Node.TEXT_NODE) {
            fStartOffset = 0;
        }
        if (node == fEndContainer
        && fEndContainer.getNodeType() == Node.TEXT_NODE) {
            fEndOffset = 0;
        }
       
    }
   
    /** This function is called from the DOM.
     *  This node has already been inserted into the DOM.
     *  Fix-up any offsets.
     */
    public void insertedNodeFromDOM(Node node) {
        if (node == null) return;
        if (fInsertNode == node) return;
       
        Node parent = node.getParentNode();
       
        if (parent == fStartContainer) {
            int index = indexOf(node, fStartContainer);
            if (index < fStartOffset) {
                fStartOffset++;
            }
        }
       
        if (parent == fEndContainer) {
            int index = indexOf(node, fEndContainer);
            if (index < fEndOffset) {
                fEndOffset++;
            }
        }
       
    }
   
    /** This function is called within Range
     *  instead of Node.removeChild,
     *  so that the range can remember that it is actively
     *  removing this child.
     */
    
    Node fRemoveChild = null;
    Node removeChild(Node parent, Node child) {
        fRemoveChild = child;
        Node n = parent.removeChild(child);
        fRemoveChild = null;
        return n;
    }
   
    /** This function must be called by the DOM _BEFORE_
     *  a node is deleted, because at that time it is
     *  connected in the DOM tree, which we depend on.
     */
    void removeNode(Node node) {
        if (node == null) return;
        if (fRemoveChild == node) return;
       
        Node parent = node.getParentNode();
       
        if (parent == fStartContainer) {
            int index = indexOf(node, fStartContainer);
            if (index <= fStartOffset) {
                fStartOffset--;
            }
        }
       
        if (parent == fEndContainer) {
            int index = indexOf(node, fEndContainer);
            if (index < fEndOffset) {
                fEndOffset--;
            }
        }
  
        if (parent != fStartContainer
        &&  parent != fEndContainer) {
            if (isAncestorOf(node, fStartContainer)) {
                fStartContainer = parent;
                fStartOffset = indexOf( node, parent)-1;
            }  
            if (isAncestorOf(node, fEndContainer)) {
                fEndContainer = parent;
                fEndOffset = indexOf( node, parent)-1;
            }
        }
       
    }
       
    //
    // Utility functions.
    //
   
    // parameters for traverseContents(int)
    //REVIST: use boolean, since there are only 2 now...
    static final int EXTRACT_CONTENTS = 1;
    static final int CLONE_CONTENTS = 2;
   
    /** This is the master traversal function which is used by
     *  both extractContents and cloneContents().
     */
    DocumentFragment traverseContents(int traversalType)
        throws DOMException
    {
        if (fStartContainer == null || fEndContainer == null) {
            return null; // REVIST: Throw exception?
        }
       
        DocumentFragment frag = fDocument.createDocumentFragment();
       
       
        Node current = fStartContainer;
        Node cloneCurrent = null;
        Node cloneParent = null;
        Node partialNode = null;
        int partialInt = START;
       
        Vector d = new Vector();
       
        // if same container, simplify case
        if (fStartContainer == fEndContainer) {
            if (fStartOffset == fEndOffset) { // eg collapsed
                return frag; // REVIST: what is correct re spec?
            }
            if (fStartContainer.getNodeType() == Node.TEXT_NODE) {
                cloneCurrent = fStartContainer.cloneNode(false);
                cloneCurrent.setNodeValue(
                (cloneCurrent.getNodeValue()).substring(fStartOffset, fEndOffset));
                if (traversalType == EXTRACT_CONTENTS) {
                    deleteData((CharacterData)current, fStartOffset, fEndOffset-fStartOffset);
                }
                frag.appendChild(cloneCurrent);
            } else {
                current = current.getFirstChild();
                int i = 0;
                for(i = 0; i < fStartOffset && current != null; i++) {
                    current=current.getNextSibling();
                }
                int n = fEndOffset-fStartOffset;
                for(i = 0; i < n && current != null ;i++) {
                    Node newCurrent=current.getNextSibling();
               
                    if (traversalType == CLONE_CONTENTS) {
                        cloneCurrent = current.cloneNode(true);
                        frag.appendChild(cloneCurrent);
                    } else
                    if (traversalType == EXTRACT_CONTENTS) {
                        frag.appendChild(current);
                    }
                    current = newCurrent;
                }
            }
            return frag;
        }
       
        //***** END SIMPLE CASE ****
  
        Node root = getCommonAncestorContainer();
        Node parent = null;
       
        // go up the start tree...
        current = fStartContainer;
       
        //REVIST: Always clone TEXT_NODE's?
        if (current.getNodeType() == Node.TEXT_NODE) {
            cloneCurrent = current.cloneNode(false);
            cloneCurrent.setNodeValue(
                (cloneCurrent.getNodeValue()).substring(fStartOffset));
            if (traversalType == EXTRACT_CONTENTS) {
                deleteData((CharacterData)current, fStartOffset,
                    current.getNodeValue().length()-fStartOffset);
            }
        } else {
            current = current.getFirstChild();
            for(int i = 0; i < fStartOffset && current != null; i++) {
                current=current.getNextSibling();
            }
            // current is now at the offset.
            if (current==null) { //"after"
                current = fStartContainer;
            }
       
            if (traversalType == CLONE_CONTENTS) {
                cloneCurrent = current.cloneNode(true);
            } else
            if (traversalType == EXTRACT_CONTENTS ) {
                cloneCurrent = current;
            }
        }
       
        Node startRoot = null;
        parent = null;
       
        // going up in a direct line from boundary point
        // through parents to the common ancestor,
        // all these nodes are partially selected, and must
        // be cloned.
        while (current != root) {
            parent = current.getParentNode();

            if (parent == root) {
                cloneParent = frag;
                startRoot = current;
            } else {
                if (parent == null) System.out.println("parent==null: current="+current);
                cloneParent = parent.cloneNode(false);
                if (partialNode==null && parent != root) {
                    partialNode = parent;
                    partialInt = AFTER;
                }
               
            }
           
            // The children to thr "right" of the "ancestor hierarchy"
            // are "fully-selected".
            Node next = null;
           
            //increment to the next sibling BEFORE doing the appendChild
            current = current.getNextSibling();
           
            //do this appendChild after the increment above.
            cloneParent.appendChild(cloneCurrent);
                    
            while (current != null) {
                next = current.getNextSibling();
                if (current != null && parent != root) {
                    if (traversalType == CLONE_CONTENTS) {
                        cloneCurrent = current.cloneNode(true);
                        cloneParent.appendChild(cloneCurrent);
                    } else
                    if (traversalType == EXTRACT_CONTENTS) {
                        cloneParent.appendChild(current);
                    }
                }
                current = next;
            }
           
            current = parent;
            cloneCurrent = cloneParent;
        }
       
        // go up the end tree...
        Node endRoot = null;
        current = fEndContainer;
       
        if (current.getNodeType() == Node.TEXT_NODE) {
            cloneCurrent = current.cloneNode(false);
            cloneCurrent.setNodeValue(
                (cloneCurrent.getNodeValue()).substring(0,fEndOffset));
            if (traversalType == EXTRACT_CONTENTS) {
                deleteData((CharacterData)current, 0, fEndOffset);
            }
        } else {
            if (fEndOffset == 0) { // "before"
                current = fEndContainer;
            }
            else {
                current = current.getFirstChild();
                for(int i = 1; i < fEndOffset && current != null; i++) {
                    current=current.getNextSibling();
                }
                if (current==null) { // REVIST: index-out-of-range what to do?
                    current = fEndContainer.getLastChild();
                }
            }
            if (traversalType == CLONE_CONTENTS) {
                cloneCurrent = current.cloneNode(true);
            } else
            if (traversalType == EXTRACT_CONTENTS ) {
                cloneCurrent = current;
            }
        }
               
        while (current != root && current != null) {
            parent = current.getParentNode();
            if (parent == root) {
                cloneParent = frag;
                endRoot = current;
            } else {
                cloneParent = parent.cloneNode(false);
                if (partialNode==null && parent != root) {
                    partialNode = parent;
                    partialInt = BEFORE;
                }
            }
           
            Node holdCurrent = current;
               
            current = parent.getFirstChild();
           
            cloneParent.appendChild(cloneCurrent);
         
            Node next = null;
            while (current != holdCurrent && current != null) {
                next = current.getNextSibling();
                // The leftmost children are fully-selected
                // and are removed, and appended, not cloned.
                if (current != null && parent != root) {
                    if (traversalType == CLONE_CONTENTS) {
                        cloneCurrent = current.cloneNode(true);
                        cloneParent.appendChild(cloneCurrent);
                    } else
                    if (traversalType == EXTRACT_CONTENTS) {
                        //cloneCurrent = current;
                        cloneParent.appendChild(current);
                    }
                }
                current = next;
            }
           
            current = parent;
            cloneCurrent = cloneParent;
       
        }
       
        d.removeAllElements();
       
        // traverse the "fully-selected" middle...
        Node clonedPrevious = frag.getLastChild();
        current = endRoot.getPreviousSibling();
        Node prev = null;
        while (current != startRoot && current != null) {
            prev = current.getPreviousSibling();
           
            if (traversalType == CLONE_CONTENTS) {
                cloneCurrent = current.cloneNode(true);
            } else
            if (traversalType == EXTRACT_CONTENTS) {
                cloneCurrent = current;
            }
                       
            frag.insertBefore(cloneCurrent, clonedPrevious);
           
            current = prev;
            clonedPrevious = cloneCurrent;
        }

        // collapse the range...
        if (traversalType == EXTRACT_CONTENTS ) {
            if (partialNode == null) {
                collapse(true);
            } else
            if (partialInt == AFTER) {
                setStartAfter(partialNode);
                setEndAfter(partialNode);
            }
            else if (partialInt == BEFORE) {
                setStartBefore(partialNode);
                setEndBefore(partialNode);
            }         
        }
       
        return frag;
           
    }
   
    void checkIndex(Node refNode, int offset) throws DOMException
    {
        if (offset < 0) {
        throw new DOMExceptionImpl(
          DOMException.INDEX_SIZE_ERR,
      "DOM004 Index out of bounds");
      }

        int type = refNode.getNodeType();
       
        if((type == Node.TEXT_NODE
        || type == Node.CDATA_SECTION_NODE
        || type == Node.COMMENT_NODE
        || type == Node.PROCESSING_INSTRUCTION_NODE)
        && offset > refNode.getNodeValue().length()){
        throw new DOMExceptionImpl(
          DOMException.INDEX_SIZE_ERR,
      "DOM004 Index out of bounds");
        }
       
        Node child = refNode.getFirstChild();
        int i = 1;
        for (; child != null; i++) {
            child = child.getNextSibling();
        }
        if (i > offset) {
        throw new DOMExceptionImpl(
          DOMException.INDEX_SIZE_ERR,
      "DOM004 Index out of bounds");
        }
           
    }
                                       
    boolean isAncestorTypeValid(Node node) {
        for (Node n = node; n!=null; n = n.getParentNode()) {
            int type = n.getNodeType();
            if (type == Node.ATTRIBUTE_NODE
             || type == Node.ENTITY_NODE
             || type == Node.NOTATION_NODE
             || type == Node.DOCUMENT_TYPE_NODE)
             return false;
        }
        return true;
    }
   
    Node nextNode(Node node, boolean visitChildren) {
           
        if (node == null) return null;

        Node result;
        if (visitChildren) {
            result = node.getFirstChild();
            if (result != null) {
                return result;
            }
        }
           
        // if hasSibling, return sibling
        result = node.getNextSibling();
        if (result != null) {
            return result;
        }
       
               
        // return parent's 1st sibling.
        Node parent = node.getParentNode();
        while (parent != null
               && parent != fDocument
                ) {
            result = parent.getNextSibling();
            if (result != null) {
                return result;
            } else {
                parent = parent.getParentNode();
            }
                           
        } // while (parent != null && parent != fRoot) {
       
        // end of list, return null
        return null;           
    }
   
    /** is a an ancestor of b ? */
    boolean isAncestorOf(Node a, Node b) {
        for (Node node=b; node != null; node=node.getParentNode()) {
            if (node == a) return true;
        }
        return false;
    }

    /** what is the index of the child in the parent */
    int indexOf(Node child, Node parent) {
        Node node;
        int i = 0;
        if (child.getParentNode() != parent) return -1;
        for(node = child; node!= null; node=node.getPreviousSibling()) {
            i++;
        }
        return i;
    }

}
TOP

Related Classes of org.apache.xerces.dom.RangeImpl

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.