Source Code Cross Referenced for RangeImpl.java in  » Web-Server » Rimfaxe-Web-Server » org » apache » xerces » dom » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Web Server » Rimfaxe Web Server » org.apache.xerces.dom 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * The Apache Software License, Version 1.1
0003:         *
0004:         *
0005:         * Copyright (c) 1999 The Apache Software Foundation.  All rights 
0006:         * reserved.
0007:         *
0008:         * Redistribution and use in source and binary forms, with or without
0009:         * modification, are permitted provided that the following conditions
0010:         * are met:
0011:         *
0012:         * 1. Redistributions of source code must retain the above copyright
0013:         *    notice, this list of conditions and the following disclaimer. 
0014:         *
0015:         * 2. Redistributions in binary form must reproduce the above copyright
0016:         *    notice, this list of conditions and the following disclaimer in
0017:         *    the documentation and/or other materials provided with the
0018:         *    distribution.
0019:         *
0020:         * 3. The end-user documentation included with the redistribution,
0021:         *    if any, must include the following acknowledgment:  
0022:         *       "This product includes software developed by the
0023:         *        Apache Software Foundation (http://www.apache.org/)."
0024:         *    Alternately, this acknowledgment may appear in the software itself,
0025:         *    if and wherever such third-party acknowledgments normally appear.
0026:         *4dorse or promote products derived from this
0027:         *    software without prior written permission. For written 
0028:         *    permission, please contact apache@apache.org.
0029:         *
0030:         * 5. Products derived from this software may not be called "Apache",
0031:         *    nor may "Apache" appear in their name, without prior written
0032:         *    permission of the Apache Software Foundation.
0033:         *
0034:         * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0035:         * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0036:         * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0037:         * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0038:         * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0039:         * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0040:         * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0041:         * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0042:         * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0043:         * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0044:         * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0045:         * SUCH DAMAGE.
0046:         * ====================================================================
0047:         *
0048:         * This software consists of voluntary contributions made by many
0049:         * individuals on behalf of the Apache Software Foundation and was
0050:         * originally based on software copyright (c) 1999, International
0051:         * Business Machines, Inc., http://www.apache.org.  For more
0052:         * information on the Apache Software Foundation, please see
0053:         * <http://www.apache.org/>.
0054:         */
0055:
0056:        package org.apache.xerces.dom;
0057:
0058:        import java.util.Vector;
0059:
0060:        import org.w3c.dom.DOMException;
0061:        import org.w3c.dom.DocumentFragment;
0062:        import org.w3c.dom.Document;
0063:        import org.w3c.dom.Node;
0064:        import org.w3c.dom.CharacterData;
0065:        import org.w3c.dom.ranges.Range;
0066:        import org.w3c.dom.ranges.RangeException;
0067:
0068:        /** The RangeImpl class implements the org.w3c.dom.range.Range interface.
0069:         *  <p> Please see the API documentation for the interface classes  
0070:         *  and use the interfaces in your client programs.
0071:         */
0072:        public class RangeImpl implements  Range {
0073:
0074:            //
0075:            // Constants
0076:            //
0077:
0078:            //
0079:            // Data
0080:            //
0081:
0082:            DocumentImpl fDocument;
0083:            Node fStartContainer;
0084:            Node fEndContainer;
0085:            int fStartOffset;
0086:            int fEndOffset;
0087:            boolean fIsCollapsed;
0088:            boolean fDetach = false;
0089:            Node fInsertNode = null;
0090:            Node fDeleteNode = null;
0091:            Node fSplitNode = null;
0092:
0093:            /** The constructor. Clients must use DocumentRange.createRange(),
0094:             *  because it registers the Range with the document, so it can 
0095:             *  be fixed-up.
0096:             */
0097:            public RangeImpl(DocumentImpl document) {
0098:                fDocument = document;
0099:                fStartContainer = document;
0100:                fEndContainer = document;
0101:                fStartOffset = 0;
0102:                fEndOffset = 0;
0103:                fDetach = false;
0104:            }
0105:
0106:            public Node getStartContainer() {
0107:                return fStartContainer;
0108:            }
0109:
0110:            public int getStartOffset() {
0111:                return fStartOffset;
0112:            }
0113:
0114:            public Node getEndContainer() {
0115:                return fEndContainer;
0116:            }
0117:
0118:            public int getEndOffset() {
0119:                return fEndOffset;
0120:            }
0121:
0122:            public boolean getCollapsed() {
0123:                return (fStartContainer == fEndContainer && fStartOffset == fEndOffset);
0124:            }
0125:
0126:            public Node getCommonAncestorContainer() {
0127:                Vector startV = new Vector();
0128:                Node node;
0129:                for (node = fStartContainer; node != null; node = node
0130:                        .getParentNode()) {
0131:                    startV.addElement(node);
0132:                }
0133:                Vector endV = new Vector();
0134:                for (node = fEndContainer; node != null; node = node
0135:                        .getParentNode()) {
0136:                    endV.addElement(node);
0137:                }
0138:                int s = startV.size() - 1;
0139:                int e = endV.size() - 1;
0140:                Object result = null;
0141:                while (s >= 0 && e >= 0) {
0142:                    if (startV.elementAt(s) == endV.elementAt(e)) {
0143:                        result = startV.elementAt(s);
0144:                    } else {
0145:                        break;
0146:                    }
0147:                    --s;
0148:                    --e;
0149:                }
0150:                return (Node) result;
0151:            }
0152:
0153:            public void setStart(Node refNode, int offset)
0154:                    throws RangeException, DOMException {
0155:                if (fDetach) {
0156:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0157:                            "DOM011 Invalid state");
0158:                }
0159:                if (!isLegalContainer(refNode)) {
0160:                    throw new RangeExceptionImpl(
0161:                            RangeException.INVALID_NODE_TYPE_ERR,
0162:                            "DOM012 Invalid node type");
0163:                }
0164:
0165:                checkIndex(refNode, offset);
0166:
0167:                fStartContainer = refNode;
0168:                fStartOffset = offset;
0169:            }
0170:
0171:            public void setEnd(Node refNode, int offset) throws RangeException,
0172:                    DOMException {
0173:                if (fDetach) {
0174:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0175:                            "DOM011 Invalid state");
0176:                }
0177:                if (!isLegalContainer(refNode)) {
0178:                    throw new RangeExceptionImpl(
0179:                            RangeException.INVALID_NODE_TYPE_ERR,
0180:                            "DOM012 Invalid node type");
0181:                }
0182:
0183:                checkIndex(refNode, offset);
0184:
0185:                fEndContainer = refNode;
0186:                fEndOffset = offset;
0187:            }
0188:
0189:            public void setStartBefore(Node refNode) throws RangeException {
0190:                if (fDetach) {
0191:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0192:                            "DOM011 Invalid state");
0193:                }
0194:                if (!hasLegalRootContainer(refNode)
0195:                        || !isLegalContainedNode(refNode)) {
0196:                    throw new RangeExceptionImpl(
0197:                            RangeException.INVALID_NODE_TYPE_ERR,
0198:                            "DOM012 Invalid node type");
0199:                }
0200:                fStartContainer = refNode.getParentNode();
0201:                int i = 0;
0202:                for (Node n = refNode; n != null; n = n.getPreviousSibling()) {
0203:                    i++;
0204:                }
0205:                fStartOffset = i - 1;
0206:            }
0207:
0208:            public void setStartAfter(Node refNode) throws RangeException {
0209:                if (fDetach) {
0210:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0211:                            "DOM011 Invalid state");
0212:                }
0213:                if (!hasLegalRootContainer(refNode)
0214:                        || !isLegalContainedNode(refNode)) {
0215:                    throw new RangeExceptionImpl(
0216:                            RangeException.INVALID_NODE_TYPE_ERR,
0217:                            "DOM012 Invalid node type");
0218:                }
0219:                fStartContainer = refNode.getParentNode();
0220:                int i = 0;
0221:                for (Node n = refNode; n != null; n = n.getPreviousSibling()) {
0222:                    i++;
0223:                }
0224:                fStartOffset = i;
0225:            }
0226:
0227:            public void setEndBefore(Node refNode) throws RangeException {
0228:                if (fDetach) {
0229:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0230:                            "DOM011 Invalid state");
0231:                }
0232:                if (!hasLegalRootContainer(refNode)
0233:                        || !isLegalContainedNode(refNode)) {
0234:                    throw new RangeExceptionImpl(
0235:                            RangeException.INVALID_NODE_TYPE_ERR,
0236:                            "DOM012 Invalid node type");
0237:                }
0238:                fEndContainer = refNode.getParentNode();
0239:                int i = 0;
0240:                for (Node n = refNode; n != null; n = n.getPreviousSibling()) {
0241:                    i++;
0242:                }
0243:                fEndOffset = i - 1;
0244:            }
0245:
0246:            public void setEndAfter(Node refNode) throws RangeException {
0247:                if (fDetach) {
0248:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0249:                            "DOM011 Invalid state");
0250:                }
0251:                if (!hasLegalRootContainer(refNode)
0252:                        || !isLegalContainedNode(refNode)) {
0253:                    throw new RangeExceptionImpl(
0254:                            RangeException.INVALID_NODE_TYPE_ERR,
0255:                            "DOM012 Invalid node type");
0256:                }
0257:                fEndContainer = refNode.getParentNode();
0258:                int i = 0;
0259:                for (Node n = refNode; n != null; n = n.getPreviousSibling()) {
0260:                    i++;
0261:                }
0262:                fEndOffset = i;
0263:            }
0264:
0265:            public void collapse(boolean toStart) {
0266:
0267:                if (fDetach) {
0268:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0269:                            "DOM011 Invalid state");
0270:                }
0271:
0272:                if (toStart) {
0273:                    fEndContainer = fStartContainer;
0274:                    fEndOffset = fStartOffset;
0275:                } else {
0276:                    fStartContainer = fEndContainer;
0277:                    fStartOffset = fEndOffset;
0278:                }
0279:            }
0280:
0281:            public void selectNode(Node refNode) throws RangeException {
0282:                if (fDetach) {
0283:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0284:                            "DOM011 Invalid state");
0285:                }
0286:                if (!isLegalContainer(refNode.getParentNode())
0287:                        || !isLegalContainedNode(refNode)) {
0288:                    throw new RangeExceptionImpl(
0289:                            RangeException.INVALID_NODE_TYPE_ERR,
0290:                            "DOM012 Invalid node type");
0291:                }
0292:                Node parent = refNode.getParentNode();
0293:                if (parent != null) // REVIST: what to do if it IS null?
0294:                {
0295:                    fStartContainer = parent;
0296:                    fEndContainer = parent;
0297:                    int i = 0;
0298:                    for (Node n = refNode; n != null; n = n
0299:                            .getPreviousSibling()) {
0300:                        i++;
0301:                    }
0302:                    fStartOffset = i - 1;
0303:                    fEndOffset = fStartOffset + 1;
0304:                }
0305:            }
0306:
0307:            public void selectNodeContents(Node refNode) throws RangeException {
0308:                if (fDetach) {
0309:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0310:                            "DOM011 Invalid state");
0311:                }
0312:                if (!isLegalContainer(refNode)) {
0313:                    throw new RangeExceptionImpl(
0314:                            RangeException.INVALID_NODE_TYPE_ERR,
0315:                            "DOM012 Invalid node type");
0316:                }
0317:                fStartContainer = refNode;
0318:                fEndContainer = refNode;
0319:                Node first = refNode.getFirstChild();
0320:                fStartOffset = 0;
0321:                if (first == null) {
0322:                    fEndOffset = 0;
0323:                } else {
0324:                    int i = 0;
0325:                    for (Node n = first; n != null; n = n.getNextSibling()) {
0326:                        i++;
0327:                    }
0328:                    fEndOffset = i;
0329:                }
0330:
0331:            }
0332:
0333:            public short compareBoundaryPoints(short how, Range sourceRange)
0334:                    throws DOMException {
0335:                if (fDetach) {
0336:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0337:                            "DOM011 Invalid state");
0338:                }
0339:
0340:                Node endPointA;
0341:                Node endPointB;
0342:                int offsetA;
0343:                int offsetB;
0344:
0345:                if (how == START_TO_START) {
0346:                    endPointA = sourceRange.getStartContainer();
0347:                    endPointB = fStartContainer;
0348:                    offsetA = sourceRange.getStartOffset();
0349:                    offsetB = fStartOffset;
0350:                } else if (how == START_TO_END) {
0351:                    endPointA = sourceRange.getStartContainer();
0352:                    endPointB = fEndContainer;
0353:                    offsetA = sourceRange.getStartOffset();
0354:                    offsetB = fEndOffset;
0355:                } else if (how == END_TO_START) {
0356:                    endPointA = sourceRange.getEndContainer();
0357:                    endPointB = fStartContainer;
0358:                    offsetA = sourceRange.getEndOffset();
0359:                    offsetB = fStartOffset;
0360:                } else {
0361:                    endPointA = sourceRange.getEndContainer();
0362:                    endPointB = fEndContainer;
0363:                    offsetA = sourceRange.getEndOffset();
0364:                    offsetB = fEndOffset;
0365:                }
0366:
0367:                // The DOM Spec outlines four cases that need to be tested
0368:                // to compare two range boundary points:
0369:                //   case 1: same container
0370:                //   case 2: Child C of container A is ancestor of B
0371:                //   case 3: Child C of container B is ancestor of A
0372:                //   case 4: preorder traversal of context tree.
0373:
0374:                // case 1: same container
0375:                if (endPointA == endPointB) {
0376:                    if (offsetA < offsetB)
0377:                        return 1;
0378:                    if (offsetA == offsetB)
0379:                        return 0;
0380:                    return -1;
0381:                }
0382:                // case 2: Child C of container A is ancestor of B
0383:                // This can be quickly tested by walking the parent chain of B
0384:                for (Node c = endPointB, p = c.getParentNode(); p != null; c = p, p = p
0385:                        .getParentNode()) {
0386:                    if (p == endPointA) {
0387:                        int index = indexOf(c, endPointA);
0388:                        if (offsetA <= index)
0389:                            return 1;
0390:                        return -1;
0391:                    }
0392:                }
0393:
0394:                // case 3: Child C of container B is ancestor of A
0395:                // This can be quickly tested by walking the parent chain of A
0396:                for (Node c = endPointA, p = c.getParentNode(); p != null; c = p, p = p
0397:                        .getParentNode()) {
0398:                    if (p == endPointB) {
0399:                        int index = indexOf(c, endPointB);
0400:                        if (index < offsetB)
0401:                            return 1;
0402:                        return -1;
0403:                    }
0404:                }
0405:
0406:                // case 4: preorder traversal of context tree.
0407:                // Instead of literally walking the context tree in pre-order,
0408:                // we use relative node depth walking which is usually faster
0409:
0410:                int depthDiff = 0;
0411:                for (Node n = endPointA; n != null; n = n.getParentNode())
0412:                    depthDiff++;
0413:                for (Node n = endPointB; n != null; n = n.getParentNode())
0414:                    depthDiff--;
0415:                while (depthDiff > 0) {
0416:                    endPointA = endPointA.getParentNode();
0417:                    depthDiff--;
0418:                }
0419:                while (depthDiff < 0) {
0420:                    endPointB = endPointB.getParentNode();
0421:                    depthDiff++;
0422:                }
0423:                for (Node pA = endPointA.getParentNode(), pB = endPointB
0424:                        .getParentNode(); pA != pB; pA = pA.getParentNode(), pB = pB
0425:                        .getParentNode()) {
0426:                    endPointA = pA;
0427:                    endPointB = pB;
0428:                }
0429:                for (Node n = endPointA.getNextSibling(); n != null; n = n
0430:                        .getNextSibling()) {
0431:                    if (n == endPointB) {
0432:                        return 1;
0433:                    }
0434:                }
0435:                return -1;
0436:            }
0437:
0438:            public void deleteContents() throws DOMException {
0439:                traverseContents(DELETE_CONTENTS);
0440:            }
0441:
0442:            public DocumentFragment extractContents() throws DOMException {
0443:                return traverseContents(EXTRACT_CONTENTS);
0444:            }
0445:
0446:            public DocumentFragment cloneContents() throws DOMException {
0447:                return traverseContents(CLONE_CONTENTS);
0448:            }
0449:
0450:            public void insertNode(Node newNode) throws DOMException,
0451:                    RangeException {
0452:                if (newNode == null)
0453:                    return; //throw exception?
0454:
0455:                if (fDetach) {
0456:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0457:                            "DOM011 Invalid state");
0458:                }
0459:                if (fDocument != newNode.getOwnerDocument()) {
0460:                    throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
0461:                            "DOM004 Wrong document");
0462:                }
0463:
0464:                int type = newNode.getNodeType();
0465:                if (type == Node.ATTRIBUTE_NODE || type == Node.ENTITY_NODE
0466:                        || type == Node.NOTATION_NODE
0467:                        || type == Node.DOCUMENT_NODE) {
0468:                    throw new RangeExceptionImpl(
0469:                            RangeException.INVALID_NODE_TYPE_ERR,
0470:                            "DOM012 Invalid node type");
0471:                }
0472:                Node cloneCurrent;
0473:                Node current;
0474:                int currentChildren = 0;
0475:
0476:                //boolean MULTIPLE_MODE = false;
0477:                if (fStartContainer.getNodeType() == Node.TEXT_NODE) {
0478:
0479:                    Node parent = fStartContainer.getParentNode();
0480:                    currentChildren = parent.getChildNodes().getLength(); //holds number of kids before insertion
0481:                    // split text node: results is 3 nodes..
0482:                    cloneCurrent = fStartContainer.cloneNode(false);
0483:                    ((TextImpl) cloneCurrent)
0484:                            .setNodeValueInternal((cloneCurrent.getNodeValue())
0485:                                    .substring(fStartOffset));
0486:                    ((TextImpl) fStartContainer)
0487:                            .setNodeValueInternal((fStartContainer
0488:                                    .getNodeValue()).substring(0, fStartOffset));
0489:                    Node next = fStartContainer.getNextSibling();
0490:                    if (next != null) {
0491:                        if (parent != null) {
0492:                            parent.insertBefore(newNode, next);
0493:                            parent.insertBefore(cloneCurrent, next);
0494:                        }
0495:                    } else {
0496:                        if (parent != null) {
0497:                            parent.appendChild(newNode);
0498:                            parent.appendChild(cloneCurrent);
0499:                        }
0500:                    }
0501:                    //update ranges after the insertion
0502:                    if (fEndContainer == fStartContainer) {
0503:                        fEndContainer = cloneCurrent; //endContainer is the new Node created
0504:                        fEndOffset -= fStartOffset;
0505:                    } else if (fEndContainer == parent) { //endContainer was not a text Node.
0506:                        //endOffset + = number_of_children_added
0507:                        fEndOffset += (parent.getChildNodes().getLength() - currentChildren);
0508:                    }
0509:
0510:                    // signal other Ranges to update their start/end containers/offsets
0511:                    signalSplitData(fStartContainer, cloneCurrent, fStartOffset);
0512:
0513:                } else { // ! TEXT_NODE
0514:                    if (fEndContainer == fStartContainer) //need to remember number of kids
0515:                        currentChildren = fEndContainer.getChildNodes()
0516:                                .getLength();
0517:
0518:                    current = fStartContainer.getFirstChild();
0519:                    int i = 0;
0520:                    for (i = 0; i < fStartOffset && current != null; i++) {
0521:                        current = current.getNextSibling();
0522:                    }
0523:                    if (current != null) {
0524:                        fStartContainer.insertBefore(newNode, current);
0525:                    } else {
0526:                        fStartContainer.appendChild(newNode);
0527:                    }
0528:                    //update fEndOffset. ex:<body><p/></body>. Range(start;end): body,0; body,1
0529:                    // insert <h1>: <body></h1><p/></body>. Range(start;end): body,0; body,2
0530:                    if (fEndContainer == fStartContainer) { //update fEndOffset
0531:                        fEndOffset += (fEndContainer.getChildNodes()
0532:                                .getLength() - currentChildren);
0533:                    }
0534:
0535:                }
0536:            }
0537:
0538:            public void surroundContents(Node newParent) throws DOMException,
0539:                    RangeException {
0540:                if (newParent == null)
0541:                    return;
0542:
0543:                if (fDetach) {
0544:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0545:                            "DOM011 Invalid state");
0546:                }
0547:                int type = newParent.getNodeType();
0548:                if (type == Node.ATTRIBUTE_NODE || type == Node.ENTITY_NODE
0549:                        || type == Node.NOTATION_NODE
0550:                        || type == Node.DOCUMENT_TYPE_NODE
0551:                        || type == Node.DOCUMENT_NODE
0552:                        || type == Node.DOCUMENT_FRAGMENT_NODE) {
0553:                    throw new RangeExceptionImpl(
0554:                            RangeException.INVALID_NODE_TYPE_ERR,
0555:                            "DOM012 Invalid node type");
0556:                }
0557:
0558:                Node root = getCommonAncestorContainer();
0559:
0560:                Node realStart = fStartContainer;
0561:                Node realEnd = fEndContainer;
0562:                if (fStartContainer.getNodeType() == Node.TEXT_NODE) {
0563:                    realStart = fStartContainer.getParentNode();
0564:                }
0565:                if (fEndContainer.getNodeType() == Node.TEXT_NODE) {
0566:                    realEnd = fEndContainer.getParentNode();
0567:                }
0568:
0569:                if (realStart != realEnd) {
0570:                    throw new RangeExceptionImpl(
0571:                            RangeException.BAD_BOUNDARYPOINTS_ERR,
0572:                            "DOM013 Bad boundary points");
0573:                }
0574:
0575:                DocumentFragment frag = extractContents();
0576:                insertNode(newParent);
0577:                newParent.appendChild(frag);
0578:                selectNode(newParent);
0579:            }
0580:
0581:            public Range cloneRange() {
0582:                if (fDetach) {
0583:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0584:                            "DOM011 Invalid state");
0585:                }
0586:
0587:                Range range = fDocument.createRange();
0588:                range.setStart(fStartContainer, fStartOffset);
0589:                range.setEnd(fEndContainer, fEndOffset);
0590:                return range;
0591:            }
0592:
0593:            public String toString() {
0594:                if (fDetach) {
0595:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0596:                            "DOM011 Invalid state");
0597:                }
0598:
0599:                Node node = fStartContainer;
0600:                Node stopNode = fEndContainer;
0601:                StringBuffer sb = new StringBuffer();
0602:                if (fStartContainer.getNodeType() == Node.TEXT_NODE
0603:                        || fStartContainer.getNodeType() == Node.CDATA_SECTION_NODE) {
0604:                    if (fStartContainer == fEndContainer) {
0605:                        sb.append(fStartContainer.getNodeValue().substring(
0606:                                fStartOffset, fEndOffset));
0607:                        return sb.toString();
0608:                    }
0609:                    sb.append(fStartContainer.getNodeValue().substring(
0610:                            fStartOffset));
0611:                    node = nextNode(node, true); //fEndContainer!=fStartContainer
0612:
0613:                } else { //fStartContainer is not a TextNode
0614:                    node = node.getFirstChild();
0615:                    if (fStartOffset > 0) { //find a first node within a range, specified by fStartOffset
0616:                        int counter = 0;
0617:                        while (counter < fStartOffset && node != null) {
0618:                            node = node.getNextSibling();
0619:                            counter++;
0620:                        }
0621:                    }
0622:                    if (node == null) {
0623:                        node = nextNode(fStartContainer, false);
0624:                    }
0625:                }
0626:                if (fEndContainer.getNodeType() != Node.TEXT_NODE
0627:                        && fEndContainer.getNodeType() != Node.CDATA_SECTION_NODE) {
0628:                    int i = fEndOffset;
0629:                    stopNode = fEndContainer.getFirstChild();
0630:                    while (i > 0 && stopNode != null) {
0631:                        --i;
0632:                        stopNode = stopNode.getNextSibling();
0633:                    }
0634:                    if (stopNode == null)
0635:                        stopNode = nextNode(fEndContainer, false);
0636:                }
0637:                while (node != stopNode) { //look into all kids of the Range
0638:                    if (node == null)
0639:                        break;
0640:                    if (node.getNodeType() == Node.TEXT_NODE
0641:                            || node.getNodeType() == Node.CDATA_SECTION_NODE) {
0642:                        sb.append(node.getNodeValue());
0643:                    }
0644:
0645:                    node = nextNode(node, true);
0646:                }
0647:
0648:                if (fEndContainer.getNodeType() == Node.TEXT_NODE
0649:                        || fEndContainer.getNodeType() == Node.CDATA_SECTION_NODE) {
0650:                    sb.append(fEndContainer.getNodeValue().substring(0,
0651:                            fEndOffset));
0652:                }
0653:                return sb.toString();
0654:            }
0655:
0656:            public void detach() {
0657:                fDetach = true;
0658:                fDocument.removeRange(this );
0659:            }
0660:
0661:            // 
0662:            // Mutation functions
0663:            //
0664:
0665:            /** Signal other Ranges to update their start/end 
0666:             *  containers/offsets. The data has already been split
0667:             *  into the two Nodes.
0668:             */
0669:            void signalSplitData(Node node, Node newNode, int offset) {
0670:                fSplitNode = node;
0671:                // notify document
0672:                fDocument.splitData(node, newNode, offset);
0673:                fSplitNode = null;
0674:            }
0675:
0676:            /** Fix up this Range if another Range has split a Text Node
0677:             *  into 2 Nodes.
0678:             */
0679:            void receiveSplitData(Node node, Node newNode, int offset) {
0680:                if (node == null || newNode == null)
0681:                    return;
0682:                if (fSplitNode == node)
0683:                    return;
0684:
0685:                if (node == fStartContainer
0686:                        && fStartContainer.getNodeType() == Node.TEXT_NODE) {
0687:                    if (fStartOffset > offset) {
0688:                        fStartOffset = fStartOffset - offset;
0689:                        fStartContainer = newNode;
0690:                    }
0691:                }
0692:                if (node == fEndContainer
0693:                        && fEndContainer.getNodeType() == Node.TEXT_NODE) {
0694:                    if (fEndOffset > offset) {
0695:                        fEndOffset = fEndOffset - offset;
0696:                        fEndContainer = newNode;
0697:                    }
0698:                }
0699:
0700:            }
0701:
0702:            /** This function inserts text into a Node and invokes
0703:             *  a method to fix-up all other Ranges.
0704:             */
0705:            void deleteData(CharacterData node, int offset, int count) {
0706:                fDeleteNode = node;
0707:                node.deleteData(offset, count);
0708:                fDeleteNode = null;
0709:            }
0710:
0711:            /** This function is called from DOM.
0712:             *  The  text has already beeen inserted.
0713:             *  Fix-up any offsets.
0714:             */
0715:            void receiveDeletedText(Node node, int offset, int count) {
0716:                if (node == null)
0717:                    return;
0718:                if (fDeleteNode == node)
0719:                    return;
0720:                if (node == fStartContainer
0721:                        && fStartContainer.getNodeType() == Node.TEXT_NODE) {
0722:                    if (fStartOffset > offset + count) {
0723:                        fStartOffset = offset
0724:                                + (fStartOffset - (offset + count));
0725:                    } else if (fStartOffset > offset) {
0726:                        fStartOffset = offset;
0727:                    }
0728:                }
0729:                if (node == fEndContainer
0730:                        && fEndContainer.getNodeType() == Node.TEXT_NODE) {
0731:                    if (fEndOffset > offset + count) {
0732:                        fEndOffset = offset + (fEndOffset - (offset + count));
0733:                    } else if (fEndOffset > offset) {
0734:                        fEndOffset = offset;
0735:                    }
0736:                }
0737:
0738:            }
0739:
0740:            /** This function inserts text into a Node and invokes
0741:             *  a method to fix-up all other Ranges.
0742:             */
0743:            void insertData(CharacterData node, int index, String insert) {
0744:                fInsertNode = node;
0745:                node.insertData(index, insert);
0746:                fInsertNode = null;
0747:            }
0748:
0749:            /** This function is called from DOM.
0750:             *  The  text has already beeen inserted.
0751:             *  Fix-up any offsets.
0752:             */
0753:            void receiveInsertedText(Node node, int index, int len) {
0754:                if (node == null)
0755:                    return;
0756:                if (fInsertNode == node)
0757:                    return;
0758:                if (node == fStartContainer
0759:                        && fStartContainer.getNodeType() == Node.TEXT_NODE) {
0760:                    if (index < fStartOffset) {
0761:                        fStartOffset = fStartOffset + len;
0762:                    }
0763:                }
0764:                if (node == fEndContainer
0765:                        && fEndContainer.getNodeType() == Node.TEXT_NODE) {
0766:                    if (index < fEndOffset) {
0767:                        fEndOffset = fEndOffset + len;
0768:                    }
0769:                }
0770:
0771:            }
0772:
0773:            /** This function is called from DOM.
0774:             *  The  text has already beeen replaced.
0775:             *  Fix-up any offsets.
0776:             */
0777:            void receiveReplacedText(Node node) {
0778:                if (node == null)
0779:                    return;
0780:                if (node == fStartContainer
0781:                        && fStartContainer.getNodeType() == Node.TEXT_NODE) {
0782:                    fStartOffset = 0;
0783:                }
0784:                if (node == fEndContainer
0785:                        && fEndContainer.getNodeType() == Node.TEXT_NODE) {
0786:                    fEndOffset = 0;
0787:                }
0788:
0789:            }
0790:
0791:            /** This function is called from the DOM.
0792:             *  This node has already been inserted into the DOM.
0793:             *  Fix-up any offsets.
0794:             */
0795:            public void insertedNodeFromDOM(Node node) {
0796:                if (node == null)
0797:                    return;
0798:                if (fInsertNode == node)
0799:                    return;
0800:
0801:                Node parent = node.getParentNode();
0802:
0803:                if (parent == fStartContainer) {
0804:                    int index = indexOf(node, fStartContainer);
0805:                    if (index < fStartOffset) {
0806:                        fStartOffset++;
0807:                    }
0808:                }
0809:
0810:                if (parent == fEndContainer) {
0811:                    int index = indexOf(node, fEndContainer);
0812:                    if (index < fEndOffset) {
0813:                        fEndOffset++;
0814:                    }
0815:                }
0816:
0817:            }
0818:
0819:            /** This function is called within Range 
0820:             *  instead of Node.removeChild,
0821:             *  so that the range can remember that it is actively
0822:             *  removing this child.
0823:             */
0824:
0825:            Node fRemoveChild = null;
0826:
0827:            Node removeChild(Node parent, Node child) {
0828:                fRemoveChild = child;
0829:                Node n = parent.removeChild(child);
0830:                fRemoveChild = null;
0831:                return n;
0832:            }
0833:
0834:            /** This function must be called by the DOM _BEFORE_
0835:             *  a node is deleted, because at that time it is
0836:             *  connected in the DOM tree, which we depend on.
0837:             */
0838:            void removeNode(Node node) {
0839:                if (node == null)
0840:                    return;
0841:                if (fRemoveChild == node)
0842:                    return;
0843:
0844:                Node parent = node.getParentNode();
0845:
0846:                if (parent == fStartContainer) {
0847:                    int index = indexOf(node, fStartContainer);
0848:                    if (index < fStartOffset) {
0849:                        fStartOffset--;
0850:                    }
0851:                }
0852:
0853:                if (parent == fEndContainer) {
0854:                    int index = indexOf(node, fEndContainer);
0855:                    if (index < fEndOffset) {
0856:                        fEndOffset--;
0857:                    }
0858:                }
0859:                //startContainer or endContainer or both is/are the ancestor(s) of the Node to be deleted
0860:                if (parent != fStartContainer || parent != fEndContainer) {
0861:                    if (isAncestorOf(node, fStartContainer)) {
0862:                        fStartContainer = parent;
0863:                        fStartOffset = indexOf(node, parent);
0864:                    }
0865:                    if (isAncestorOf(node, fEndContainer)) {
0866:                        fEndContainer = parent;
0867:                        fEndOffset = indexOf(node, parent);
0868:                    }
0869:                }
0870:
0871:            }
0872:
0873:            //
0874:            // Utility functions.
0875:            //
0876:
0877:            // parameters for traverseContents(int)
0878:            //REVIST: use boolean, since there are only 2 now...
0879:            static final int EXTRACT_CONTENTS = 1;
0880:            static final int CLONE_CONTENTS = 2;
0881:            static final int DELETE_CONTENTS = 3;
0882:
0883:            /**
0884:             * This is the master routine invoked to visit the nodes
0885:             * selected by this range.  For each such node, different
0886:             * actions are taken depending on the value of the
0887:             * <code>how</code> argument.
0888:             * 
0889:             * @param how    Specifies what type of traversal is being
0890:             *               requested (extract, clone, or delete).
0891:             *               Legal values for this argument are:
0892:             * 
0893:             *               <ol>
0894:             *               <li><code>EXTRACT_CONTENTS</code> - will produce
0895:             *               a document fragment containing the range's content.
0896:             *               Partially selected nodes are copied, but fully
0897:             *               selected nodes are moved.
0898:             *               
0899:             *               <li><code>CLONE_CONTENTS</code> - will leave the
0900:             *               context tree of the range undisturbed, but sill
0901:             *               produced cloned content in a document fragment
0902:             *               
0903:             *               <li><code>DELETE_CONTENTS</code> - will delete from
0904:             *               the context tree of the range, all fully selected
0905:             *               nodes.
0906:             *               </ol>
0907:             * 
0908:             * @return Returns a document fragment containing any
0909:             *         copied or extracted nodes.  If the <code>how</code>
0910:             *         parameter was <code>DELETE_CONTENTS</code>, the
0911:             *         return value is null.
0912:             */
0913:            private DocumentFragment traverseContents(int how)
0914:                    throws DOMException {
0915:                if (fStartContainer == null || fEndContainer == null) {
0916:                    return null; // REVIST: Throw exception?
0917:                }
0918:
0919:                //Check for a detached range.
0920:                if (fDetach) {
0921:                    throw new DOMException(DOMException.INVALID_STATE_ERR,
0922:                            "DOM011 Invalid state");
0923:                }
0924:
0925:                /*
0926:                  Traversal is accomplished by first determining the
0927:                  relationship between the endpoints of the range.
0928:                  For each of four significant relationships, we will
0929:                  delegate the traversal call to a method that 
0930:                  can make appropriate assumptions.
0931:                 */
0932:
0933:                // case 1: same container
0934:                if (fStartContainer == fEndContainer)
0935:                    return traverseSameContainer(how);
0936:
0937:                // case 2: Child C of start container is ancestor of end container 
0938:                // This can be quickly tested by walking the parent chain of 
0939:                // end container
0940:                int endContainerDepth = 0;
0941:                for (Node c = fEndContainer, p = c.getParentNode(); p != null; c = p, p = p
0942:                        .getParentNode()) {
0943:                    if (p == fStartContainer)
0944:                        return traverseCommonStartContainer(c, how);
0945:                    ++endContainerDepth;
0946:                }
0947:
0948:                // case 3: Child C of container B is ancestor of A
0949:                // This can be quickly tested by walking the parent chain of A
0950:                int startContainerDepth = 0;
0951:                for (Node c = fStartContainer, p = c.getParentNode(); p != null; c = p, p = p
0952:                        .getParentNode()) {
0953:                    if (p == fEndContainer)
0954:                        return traverseCommonEndContainer(c, how);
0955:                    ++startContainerDepth;
0956:                }
0957:
0958:                // case 4: There is a common ancestor container.  Find the
0959:                // ancestor siblings that are children of that container.
0960:                int depthDiff = startContainerDepth - endContainerDepth;
0961:
0962:                Node startNode = fStartContainer;
0963:                while (depthDiff > 0) {
0964:                    startNode = startNode.getParentNode();
0965:                    depthDiff--;
0966:                }
0967:
0968:                Node endNode = fEndContainer;
0969:                while (depthDiff < 0) {
0970:                    endNode = endNode.getParentNode();
0971:                    depthDiff++;
0972:                }
0973:
0974:                // ascend the ancestor hierarchy until we have a common parent.
0975:                for (Node sp = startNode.getParentNode(), ep = endNode
0976:                        .getParentNode(); sp != ep; sp = sp.getParentNode(), ep = ep
0977:                        .getParentNode()) {
0978:                    startNode = sp;
0979:                    endNode = ep;
0980:                }
0981:                return traverseCommonAncestors(startNode, endNode, how);
0982:            }
0983:
0984:            /**
0985:             * Visits the nodes selected by this range when we know
0986:             * a-priori that the start and end containers are the same.
0987:             * This method is invoked by the generic <code>traverse</code>
0988:             * method.
0989:             * 
0990:             * @param how    Specifies what type of traversal is being
0991:             *               requested (extract, clone, or delete).
0992:             *               Legal values for this argument are:
0993:             *               
0994:             *               <ol>
0995:             *               <li><code>EXTRACT_CONTENTS</code> - will produce
0996:             *               a document fragment containing the range's content.
0997:             *               Partially selected nodes are copied, but fully
0998:             *               selected nodes are moved.
0999:             *               
1000:             *               <li><code>CLONE_CONTENTS</code> - will leave the
1001:             *               context tree of the range undisturbed, but sill
1002:             *               produced cloned content in a document fragment
1003:             *               
1004:             *               <li><code>DELETE_CONTENTS</code> - will delete from
1005:             *               the context tree of the range, all fully selected
1006:             *               nodes.
1007:             *               </ol>
1008:             * 
1009:             * @return Returns a document fragment containing any
1010:             *         copied or extracted nodes.  If the <code>how</code>
1011:             *         parameter was <code>DELETE_CONTENTS</code>, the
1012:             *         return value is null.
1013:             */
1014:            private DocumentFragment traverseSameContainer(int how) {
1015:                DocumentFragment frag = null;
1016:                if (how != DELETE_CONTENTS)
1017:                    frag = fDocument.createDocumentFragment();
1018:
1019:                // If selection is empty, just return the fragment
1020:                if (fStartOffset == fEndOffset)
1021:                    return frag;
1022:
1023:                // Text node needs special case handling
1024:                if (fStartContainer.getNodeType() == Node.TEXT_NODE) {
1025:                    // get the substring
1026:                    String s = fStartContainer.getNodeValue();
1027:                    String sub = s.substring(fStartOffset, fEndOffset);
1028:
1029:                    // set the original text node to its new value
1030:                    if (how != CLONE_CONTENTS) {
1031:                        fStartContainer.setNodeValue(s.substring(0,
1032:                                fStartOffset)
1033:                                + s.substring(fEndOffset));
1034:
1035:                        // Nothing is partially selected, so collapse to start point
1036:                        collapse(true);
1037:                    }
1038:                    if (how == DELETE_CONTENTS)
1039:                        return null;
1040:                    frag.appendChild(fDocument.createTextNode(sub));
1041:                    return frag;
1042:                }
1043:
1044:                // Copy nodes between the start/end offsets.
1045:                Node n = getSelectedNode(fStartContainer, fStartOffset);
1046:                int cnt = fEndOffset - fStartOffset;
1047:                while (cnt > 0) {
1048:                    Node sibling = n.getNextSibling();
1049:                    Node xferNode = traverseFullySelected(n, how);
1050:                    if (frag != null)
1051:                        frag.appendChild(xferNode);
1052:                    --cnt;
1053:                    n = sibling;
1054:                }
1055:
1056:                // Nothing is partially selected, so collapse to start point
1057:                if (how != CLONE_CONTENTS)
1058:                    collapse(true);
1059:                return frag;
1060:            }
1061:
1062:            /**
1063:             * Visits the nodes selected by this range when we know
1064:             * a-priori that the start and end containers are not the
1065:             * same, but the start container is an ancestor of the
1066:             * end container. This method is invoked by the generic 
1067:             * <code>traverse</code> method.
1068:             * 
1069:             * @param endAncestor
1070:             *               The ancestor of the end container that is a direct child
1071:             *               of the start container.
1072:             * 
1073:             * @param how    Specifies what type of traversal is being
1074:             *               requested (extract, clone, or delete).
1075:             *               Legal values for this argument are:
1076:             *               
1077:             *               <ol>
1078:             *               <li><code>EXTRACT_CONTENTS</code> - will produce
1079:             *               a document fragment containing the range's content.
1080:             *               Partially selected nodes are copied, but fully
1081:             *               selected nodes are moved.
1082:             *               
1083:             *               <li><code>CLONE_CONTENTS</code> - will leave the
1084:             *               context tree of the range undisturbed, but sill
1085:             *               produced cloned content in a document fragment
1086:             *               
1087:             *               <li><code>DELETE_CONTENTS</code> - will delete from
1088:             *               the context tree of the range, all fully selected
1089:             *               nodes.
1090:             *               </ol>
1091:             * 
1092:             * @return Returns a document fragment containing any
1093:             *         copied or extracted nodes.  If the <code>how</code>
1094:             *         parameter was <code>DELETE_CONTENTS</code>, the
1095:             *         return value is null.
1096:             */
1097:            private DocumentFragment traverseCommonStartContainer(
1098:                    Node endAncestor, int how) {
1099:                DocumentFragment frag = null;
1100:                if (how != DELETE_CONTENTS)
1101:                    frag = fDocument.createDocumentFragment();
1102:                Node n = traverseRightBoundary(endAncestor, how);
1103:                if (frag != null)
1104:                    frag.appendChild(n);
1105:
1106:                int endIdx = indexOf(endAncestor, fStartContainer);
1107:                int cnt = endIdx - fStartOffset;
1108:                if (cnt <= 0) {
1109:                    // Collapse to just before the endAncestor, which 
1110:                    // is partially selected.
1111:                    if (how != CLONE_CONTENTS) {
1112:                        setEndBefore(endAncestor);
1113:                        collapse(false);
1114:                    }
1115:                    return frag;
1116:                }
1117:
1118:                n = endAncestor.getPreviousSibling();
1119:                while (cnt > 0) {
1120:                    Node sibling = n.getPreviousSibling();
1121:                    Node xferNode = traverseFullySelected(n, how);
1122:                    if (frag != null)
1123:                        frag.insertBefore(xferNode, frag.getFirstChild());
1124:                    --cnt;
1125:                    n = sibling;
1126:                }
1127:                // Collapse to just before the endAncestor, which 
1128:                // is partially selected.
1129:                if (how != CLONE_CONTENTS) {
1130:                    setEndBefore(endAncestor);
1131:                    collapse(false);
1132:                }
1133:                return frag;
1134:            }
1135:
1136:            /**
1137:             * Visits the nodes selected by this range when we know
1138:             * a-priori that the start and end containers are not the
1139:             * same, but the end container is an ancestor of the
1140:             * start container. This method is invoked by the generic
1141:             * <code>traverse</code> method.
1142:             * 
1143:             * @param startAncestor
1144:             *               The ancestor of the start container that is a direct
1145:             *               child of the end container.
1146:             * 
1147:             * @param how    Specifies what type of traversal is being
1148:             *               requested (extract, clone, or delete).
1149:             *               Legal values for this argument are:
1150:             *               
1151:             *               <ol>
1152:             *               <li><code>EXTRACT_CONTENTS</code> - will produce
1153:             *               a document fragment containing the range's content.
1154:             *               Partially selected nodes are copied, but fully
1155:             *               selected nodes are moved.
1156:             *               
1157:             *               <li><code>CLONE_CONTENTS</code> - will leave the
1158:             *               context tree of the range undisturbed, but sill
1159:             *               produced cloned content in a document fragment
1160:             *               
1161:             *               <li><code>DELETE_CONTENTS</code> - will delete from
1162:             *               the context tree of the range, all fully selected
1163:             *               nodes.
1164:             *               </ol>
1165:             * 
1166:             * @return Returns a document fragment containing any
1167:             *         copied or extracted nodes.  If the <code>how</code>
1168:             *         parameter was <code>DELETE_CONTENTS</code>, the
1169:             *         return value is null.
1170:             */
1171:            private DocumentFragment traverseCommonEndContainer(
1172:                    Node startAncestor, int how) {
1173:                DocumentFragment frag = null;
1174:                if (how != DELETE_CONTENTS)
1175:                    frag = fDocument.createDocumentFragment();
1176:                Node n = traverseLeftBoundary(startAncestor, how);
1177:                if (frag != null)
1178:                    frag.appendChild(n);
1179:                int startIdx = indexOf(startAncestor, fEndContainer);
1180:                ++startIdx; // Because we already traversed it....
1181:
1182:                int cnt = fEndOffset - startIdx;
1183:                n = startAncestor.getNextSibling();
1184:                while (cnt > 0) {
1185:                    Node sibling = n.getNextSibling();
1186:                    Node xferNode = traverseFullySelected(n, how);
1187:                    if (frag != null)
1188:                        frag.appendChild(xferNode);
1189:                    --cnt;
1190:                    n = sibling;
1191:                }
1192:
1193:                if (how != CLONE_CONTENTS) {
1194:                    setStartAfter(startAncestor);
1195:                    collapse(true);
1196:                }
1197:
1198:                return frag;
1199:            }
1200:
1201:            /**
1202:             * Visits the nodes selected by this range when we know
1203:             * a-priori that the start and end containers are not
1204:             * the same, and we also know that neither the start
1205:             * nor end container is an ancestor of the other.
1206:             * This method is invoked by
1207:             * the generic <code>traverse</code> method.
1208:             * 
1209:             * @param startAncestor
1210:             *               Given a common ancestor of the start and end containers,
1211:             *               this parameter is the ancestor (or self) of the start
1212:             *               container that is a direct child of the common ancestor.
1213:             * 
1214:             * @param endAncestor
1215:             *               Given a common ancestor of the start and end containers,
1216:             *               this parameter is the ancestor (or self) of the end
1217:             *               container that is a direct child of the common ancestor.
1218:             * 
1219:             * @param how    Specifies what type of traversal is being
1220:             *               requested (extract, clone, or delete).
1221:             *               Legal values for this argument are:
1222:             *               
1223:             *               <ol>
1224:             *               <li><code>EXTRACT_CONTENTS</code> - will produce
1225:             *               a document fragment containing the range's content.
1226:             *               Partially selected nodes are copied, but fully
1227:             *               selected nodes are moved.
1228:             *               
1229:             *               <li><code>CLONE_CONTENTS</code> - will leave the
1230:             *               context tree of the range undisturbed, but sill
1231:             *               produced cloned content in a document fragment
1232:             *               
1233:             *               <li><code>DELETE_CONTENTS</code> - will delete from
1234:             *               the context tree of the range, all fully selected
1235:             *               nodes.
1236:             *               </ol>
1237:             * 
1238:             * @return Returns a document fragment containing any
1239:             *         copied or extracted nodes.  If the <code>how</code>
1240:             *         parameter was <code>DELETE_CONTENTS</code>, the
1241:             *         return value is null.
1242:             */
1243:            private DocumentFragment traverseCommonAncestors(
1244:                    Node startAncestor, Node endAncestor, int how) {
1245:                DocumentFragment frag = null;
1246:                if (how != DELETE_CONTENTS)
1247:                    frag = fDocument.createDocumentFragment();
1248:
1249:                Node n = traverseLeftBoundary(startAncestor, how);
1250:                if (frag != null)
1251:                    frag.appendChild(n);
1252:
1253:                Node commonParent = startAncestor.getParentNode();
1254:                int startOffset = indexOf(startAncestor, commonParent);
1255:                int endOffset = indexOf(endAncestor, commonParent);
1256:                ++startOffset;
1257:
1258:                int cnt = endOffset - startOffset;
1259:                Node sibling = startAncestor.getNextSibling();
1260:
1261:                while (cnt > 0) {
1262:                    Node nextSibling = sibling.getNextSibling();
1263:                    n = traverseFullySelected(sibling, how);
1264:                    if (frag != null)
1265:                        frag.appendChild(n);
1266:                    sibling = nextSibling;
1267:                    --cnt;
1268:                }
1269:
1270:                n = traverseRightBoundary(endAncestor, how);
1271:                if (frag != null)
1272:                    frag.appendChild(n);
1273:
1274:                if (how != CLONE_CONTENTS) {
1275:                    setStartAfter(startAncestor);
1276:                    collapse(true);
1277:                }
1278:                return frag;
1279:            }
1280:
1281:            /**
1282:             * Traverses the "right boundary" of this range and
1283:             * operates on each "boundary node" according to the
1284:             * <code>how</code> parameter.  It is a-priori assumed
1285:             * by this method that the right boundary does
1286:             * not contain the range's start container.
1287:             * <p>
1288:             * A "right boundary" is best visualized by thinking
1289:             * of a sample tree:<pre>
1290:             *                 A
1291:             *                /|\
1292:             *               / | \
1293:             *              /  |  \
1294:             *             B   C   D
1295:             *            /|\     /|\
1296:             *           E F G   H I J
1297:             * </pre>
1298:             * Imagine first a range that begins between the
1299:             * "E" and "F" nodes and ends between the
1300:             * "I" and "J" nodes.  The start container is
1301:             * "B" and the end container is "D".  Given this setup,
1302:             * the following applies:
1303:             * <p>
1304:             * Partially Selected Nodes: B, D<br>
1305:             * Fully Selected Nodes: F, G, C, H, I
1306:             * <p>
1307:             * The "right boundary" is the highest subtree node
1308:             * that contains the ending container.  The root of
1309:             * this subtree is always partially selected.
1310:             * <p>
1311:             * In this example, the nodes that are traversed
1312:             * as "right boundary" nodes are: H, I, and D.
1313:             * 
1314:             * @param root   The node that is the root of the "right boundary" subtree.
1315:             * 
1316:             * @param how    Specifies what type of traversal is being
1317:             *               requested (extract, clone, or delete).
1318:             *               Legal values for this argument are:
1319:             *               
1320:             *               <ol>
1321:             *               <li><code>EXTRACT_CONTENTS</code> - will produce
1322:             *               a node containing the boundaries content.
1323:             *               Partially selected nodes are copied, but fully
1324:             *               selected nodes are moved.
1325:             *               
1326:             *               <li><code>CLONE_CONTENTS</code> - will leave the
1327:             *               context tree of the range undisturbed, but will
1328:             *               produced cloned content.
1329:             *               
1330:             *               <li><code>DELETE_CONTENTS</code> - will delete from
1331:             *               the context tree of the range, all fully selected
1332:             *               nodes within the boundary.
1333:             *               </ol>
1334:             * 
1335:             * @return Returns a node that is the result of visiting nodes.
1336:             *         If the traversal operation is
1337:             *         <code>DELETE_CONTENTS</code> the return value is null.
1338:             */
1339:            private Node traverseRightBoundary(Node root, int how) {
1340:                Node next = getSelectedNode(fEndContainer, fEndOffset - 1);
1341:                boolean isFullySelected = (next != fEndContainer);
1342:
1343:                if (next == root)
1344:                    return traverseNode(next, isFullySelected, false, how);
1345:
1346:                Node parent = next.getParentNode();
1347:                Node clonedParent = traverseNode(parent, false, false, how);
1348:
1349:                while (parent != null) {
1350:                    while (next != null) {
1351:                        Node prevSibling = next.getPreviousSibling();
1352:                        Node clonedChild = traverseNode(next, isFullySelected,
1353:                                false, how);
1354:                        if (how != DELETE_CONTENTS) {
1355:                            clonedParent.insertBefore(clonedChild, clonedParent
1356:                                    .getFirstChild());
1357:                        }
1358:                        isFullySelected = true;
1359:                        next = prevSibling;
1360:                    }
1361:                    if (parent == root)
1362:                        return clonedParent;
1363:
1364:                    next = parent.getPreviousSibling();
1365:                    parent = parent.getParentNode();
1366:                    Node clonedGrandParent = traverseNode(parent, false, false,
1367:                            how);
1368:                    if (how != DELETE_CONTENTS)
1369:                        clonedGrandParent.appendChild(clonedParent);
1370:                    clonedParent = clonedGrandParent;
1371:
1372:                }
1373:
1374:                // should never occur
1375:                return null;
1376:            }
1377:
1378:            /**
1379:             * Traverses the "left boundary" of this range and
1380:             * operates on each "boundary node" according to the
1381:             * <code>how</code> parameter.  It is a-priori assumed
1382:             * by this method that the left boundary does
1383:             * not contain the range's end container.
1384:             * <p>
1385:             * A "left boundary" is best visualized by thinking
1386:             * of a sample tree:<pre>
1387:             * 
1388:             *                 A
1389:             *                /|\
1390:             *               / | \
1391:             *              /  |  \
1392:             *             B   C   D
1393:             *            /|\     /|\
1394:             *           E F G   H I J
1395:             * </pre>
1396:             * Imagine first a range that begins between the
1397:             * "E" and "F" nodes and ends between the
1398:             * "I" and "J" nodes.  The start container is
1399:             * "B" and the end container is "D".  Given this setup,
1400:             * the following applies:
1401:             * <p>
1402:             * Partially Selected Nodes: B, D<br>
1403:             * Fully Selected Nodes: F, G, C, H, I
1404:             * <p>
1405:             * The "left boundary" is the highest subtree node
1406:             * that contains the starting container.  The root of
1407:             * this subtree is always partially selected.
1408:             * <p>
1409:             * In this example, the nodes that are traversed
1410:             * as "left boundary" nodes are: F, G, and B.
1411:             * 
1412:             * @param root   The node that is the root of the "left boundary" subtree.
1413:             * 
1414:             * @param how    Specifies what type of traversal is being
1415:             *               requested (extract, clone, or delete).
1416:             *               Legal values for this argument are:
1417:             *               
1418:             *               <ol>
1419:             *               <li><code>EXTRACT_CONTENTS</code> - will produce
1420:             *               a node containing the boundaries content.
1421:             *               Partially selected nodes are copied, but fully
1422:             *               selected nodes are moved.
1423:             *               
1424:             *               <li><code>CLONE_CONTENTS</code> - will leave the
1425:             *               context tree of the range undisturbed, but will
1426:             *               produced cloned content.
1427:             *               
1428:             *               <li><code>DELETE_CONTENTS</code> - will delete from
1429:             *               the context tree of the range, all fully selected
1430:             *               nodes within the boundary.
1431:             *               </ol>
1432:             * 
1433:             * @return Returns a node that is the result of visiting nodes.
1434:             *         If the traversal operation is
1435:             *         <code>DELETE_CONTENTS</code> the return value is null.
1436:             */
1437:            private Node traverseLeftBoundary(Node root, int how) {
1438:                Node next = getSelectedNode(getStartContainer(),
1439:                        getStartOffset());
1440:                boolean isFullySelected = (next != getStartContainer());
1441:
1442:                if (next == root)
1443:                    return traverseNode(next, isFullySelected, true, how);
1444:
1445:                Node parent = next.getParentNode();
1446:                Node clonedParent = traverseNode(parent, false, true, how);
1447:
1448:                while (parent != null) {
1449:                    while (next != null) {
1450:                        Node nextSibling = next.getNextSibling();
1451:                        Node clonedChild = traverseNode(next, isFullySelected,
1452:                                true, how);
1453:                        if (how != DELETE_CONTENTS)
1454:                            clonedParent.appendChild(clonedChild);
1455:                        isFullySelected = true;
1456:                        next = nextSibling;
1457:                    }
1458:                    if (parent == root)
1459:                        return clonedParent;
1460:
1461:                    next = parent.getNextSibling();
1462:                    parent = parent.getParentNode();
1463:                    Node clonedGrandParent = traverseNode(parent, false, true,
1464:                            how);
1465:                    if (how != DELETE_CONTENTS)
1466:                        clonedGrandParent.appendChild(clonedParent);
1467:                    clonedParent = clonedGrandParent;
1468:
1469:                }
1470:
1471:                // should never occur
1472:                return null;
1473:
1474:            }
1475:
1476:            /**
1477:             * Utility method for traversing a single node.
1478:             * Does not properly handle a text node containing both the
1479:             * start and end offsets.  Such nodes should
1480:             * have been previously detected and been routed to traverseTextNode.
1481:             * 
1482:             * @param n      The node to be traversed.
1483:             * 
1484:             * @param isFullySelected
1485:             *               Set to true if the node is fully selected.  Should be 
1486:             *               false otherwise.
1487:             *               Note that although the DOM 2 specification says that a 
1488:             *               text node that is boththe start and end container is not
1489:             *               selected, we treat it here as if it were partially 
1490:             *               selected.
1491:             * 
1492:             * @param isLeft Is true if we are traversing the node as part of navigating
1493:             *               the "left boundary" of the range.  If this value is false,
1494:             *               it implies we are navigating the "right boundary" of the
1495:             *               range.
1496:             * 
1497:             * @param how    Specifies what type of traversal is being
1498:             *               requested (extract, clone, or delete).
1499:             *               Legal values for this argument are:
1500:             *               
1501:             *               <ol>
1502:             *               <li><code>EXTRACT_CONTENTS</code> - will simply
1503:             *               return the original node.
1504:             *               
1505:             *               <li><code>CLONE_CONTENTS</code> - will leave the
1506:             *               context tree of the range undisturbed, but will
1507:             *               return a cloned node.
1508:             *               
1509:             *               <li><code>DELETE_CONTENTS</code> - will delete the
1510:             *               node from it's parent, but will return null.
1511:             *               </ol>
1512:             * 
1513:             * @return Returns a node that is the result of visiting the node.
1514:             *         If the traversal operation is
1515:             *         <code>DELETE_CONTENTS</code> the return value is null.
1516:             */
1517:            private Node traverseNode(Node n, boolean isFullySelected,
1518:                    boolean isLeft, int how) {
1519:                if (isFullySelected)
1520:                    return traverseFullySelected(n, how);
1521:                if (n.getNodeType() == Node.TEXT_NODE)
1522:                    return traverseTextNode(n, isLeft, how);
1523:                return traversePartiallySelected(n, how);
1524:            }
1525:
1526:            /**
1527:             * Utility method for traversing a single node when
1528:             * we know a-priori that the node if fully
1529:             * selected.
1530:             * 
1531:             * @param n      The node to be traversed.
1532:             * 
1533:             * @param how    Specifies what type of traversal is being
1534:             *               requested (extract, clone, or delete).
1535:             *               Legal values for this argument are:
1536:             *               
1537:             *               <ol>
1538:             *               <li><code>EXTRACT_CONTENTS</code> - will simply
1539:             *               return the original node.
1540:             *               
1541:             *               <li><code>CLONE_CONTENTS</code> - will leave the
1542:             *               context tree of the range undisturbed, but will
1543:             *               return a cloned node.
1544:             *               
1545:             *               <li><code>DELETE_CONTENTS</code> - will delete the
1546:             *               node from it's parent, but will return null.
1547:             *               </ol>
1548:             * 
1549:             * @return Returns a node that is the result of visiting the node.
1550:             *         If the traversal operation is
1551:             *         <code>DELETE_CONTENTS</code> the return value is null.
1552:             */
1553:            private Node traverseFullySelected(Node n, int how) {
1554:                switch (how) {
1555:                case CLONE_CONTENTS:
1556:                    return n.cloneNode(true);
1557:                case EXTRACT_CONTENTS:
1558:                    if (n.getNodeType() == Node.DOCUMENT_TYPE_NODE) {
1559:                        // TBD: This should be a HIERARCHY_REQUEST_ERR
1560:                        throw new RangeExceptionImpl(
1561:                                RangeException.INVALID_NODE_TYPE_ERR,
1562:                                "DOM012 Invalid node type");
1563:                    }
1564:                    return n;
1565:                case DELETE_CONTENTS:
1566:                    n.getParentNode().removeChild(n);
1567:                    return null;
1568:                }
1569:                return null;
1570:            }
1571:
1572:            /**
1573:             * Utility method for traversing a single node when
1574:             * we know a-priori that the node if partially
1575:             * selected and is not a text node.
1576:             * 
1577:             * @param n      The node to be traversed.
1578:             * 
1579:             * @param how    Specifies what type of traversal is being
1580:             *               requested (extract, clone, or delete).
1581:             *               Legal values for this argument are:
1582:             *               
1583:             *               <ol>
1584:             *               <li><code>EXTRACT_CONTENTS</code> - will simply
1585:             *               return the original node.
1586:             *               
1587:             *               <li><code>CLONE_CONTENTS</code> - will leave the
1588:             *               context tree of the range undisturbed, but will
1589:             *               return a cloned node.
1590:             *               
1591:             *               <li><code>DELETE_CONTENTS</code> - will delete the
1592:             *               node from it's parent, but will return null.
1593:             *               </ol>
1594:             * 
1595:             * @return Returns a node that is the result of visiting the node.
1596:             *         If the traversal operation is
1597:             *         <code>DELETE_CONTENTS</code> the return value is null.
1598:             */
1599:            private Node traversePartiallySelected(Node n, int how) {
1600:                switch (how) {
1601:                case DELETE_CONTENTS:
1602:                    return null;
1603:                case CLONE_CONTENTS:
1604:                case EXTRACT_CONTENTS:
1605:                    return n.cloneNode(false);
1606:                }
1607:                return null;
1608:            }
1609:
1610:            /**
1611:             * Utility method for traversing a text node that we know
1612:             * a-priori to be on a left or right boundary of the range.
1613:             * This method does not properly handle text nodes that contain
1614:             * both the start and end points of the range.
1615:             * 
1616:             * @param n      The node to be traversed.
1617:             * 
1618:             * @param isLeft Is true if we are traversing the node as part of navigating
1619:             *               the "left boundary" of the range.  If this value is false,
1620:             *               it implies we are navigating the "right boundary" of the
1621:             *               range.
1622:             * 
1623:             * @param how    Specifies what type of traversal is being
1624:             *               requested (extract, clone, or delete).
1625:             *               Legal values for this argument are:
1626:             *               
1627:             *               <ol>
1628:             *               <li><code>EXTRACT_CONTENTS</code> - will simply
1629:             *               return the original node.
1630:             *               
1631:             *               <li><code>CLONE_CONTENTS</code> - will leave the
1632:             *               context tree of the range undisturbed, but will
1633:             *               return a cloned node.
1634:             *               
1635:             *               <li><code>DELETE_CONTENTS</code> - will delete the
1636:             *               node from it's parent, but will return null.
1637:             *               </ol>
1638:             * 
1639:             * @return Returns a node that is the result of visiting the node.
1640:             *         If the traversal operation is
1641:             *         <code>DELETE_CONTENTS</code> the return value is null.
1642:             */
1643:            private Node traverseTextNode(Node n, boolean isLeft, int how) {
1644:                String txtValue = n.getNodeValue();
1645:                String newNodeValue;
1646:                String oldNodeValue;
1647:
1648:                if (isLeft) {
1649:                    int offset = getStartOffset();
1650:                    newNodeValue = txtValue.substring(offset);
1651:                    oldNodeValue = txtValue.substring(0, offset);
1652:                } else {
1653:                    int offset = getEndOffset();
1654:                    newNodeValue = txtValue.substring(0, offset);
1655:                    oldNodeValue = txtValue.substring(offset);
1656:                }
1657:
1658:                if (how != CLONE_CONTENTS)
1659:                    n.setNodeValue(oldNodeValue);
1660:                if (how == DELETE_CONTENTS)
1661:                    return null;
1662:                Node newNode = n.cloneNode(false);
1663:                newNode.setNodeValue(newNodeValue);
1664:                return newNode;
1665:            }
1666:
1667:            void checkIndex(Node refNode, int offset) throws DOMException {
1668:                if (offset < 0) {
1669:                    throw new DOMException(DOMException.INDEX_SIZE_ERR,
1670:                            "DOM004 Index out of bounds");
1671:                }
1672:
1673:                int type = refNode.getNodeType();
1674:
1675:                // If the node contains text, ensure that the
1676:                // offset of the range is <= to the length of the text
1677:                if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE
1678:                        || type == Node.COMMENT_NODE
1679:                        || type == Node.PROCESSING_INSTRUCTION_NODE) {
1680:                    if (offset > refNode.getNodeValue().length()) {
1681:                        throw new DOMException(DOMException.INDEX_SIZE_ERR,
1682:                                "DOM004 Index out of bounds");
1683:                    }
1684:                } else {
1685:                    // Since the node is not text, ensure that the offset
1686:                    // is valid with respect to the number of child nodes
1687:                    if (offset > refNode.getChildNodes().getLength()) {
1688:                        throw new DOMException(DOMException.INDEX_SIZE_ERR,
1689:                                "DOM004 Index out of bounds");
1690:                    }
1691:                }
1692:            }
1693:
1694:            /**
1695:             * Given a node, calculate what the Range's root container
1696:             * for that node would be.
1697:             */
1698:            private Node getRootContainer(Node node) {
1699:                if (node == null)
1700:                    return null;
1701:
1702:                while (node.getParentNode() != null)
1703:                    node = node.getParentNode();
1704:                return node;
1705:            }
1706:
1707:            /**
1708:             * Returns true IFF the given node can serve as a container
1709:             * for a range's boundary points.
1710:             */
1711:            private boolean isLegalContainer(Node node) {
1712:                if (node == null)
1713:                    return false;
1714:
1715:                while (node != null) {
1716:                    switch (node.getNodeType()) {
1717:                    case Node.ENTITY_NODE:
1718:                    case Node.NOTATION_NODE:
1719:                    case Node.DOCUMENT_TYPE_NODE:
1720:                        return false;
1721:                    }
1722:                    node = node.getParentNode();
1723:                }
1724:
1725:                return true;
1726:            }
1727:
1728:            /**
1729:             * Finds the root container for the given node and determines
1730:             * if that root container is legal with respect to the
1731:             * DOM 2 specification.  At present, that means the root
1732:             * container must be either an attribute, a document,
1733:             * or a document fragment.
1734:             */
1735:            private boolean hasLegalRootContainer(Node node) {
1736:                if (node == null)
1737:                    return false;
1738:
1739:                Node rootContainer = getRootContainer(node);
1740:                switch (rootContainer.getNodeType()) {
1741:                case Node.ATTRIBUTE_NODE:
1742:                case Node.DOCUMENT_NODE:
1743:                case Node.DOCUMENT_FRAGMENT_NODE:
1744:                    return true;
1745:                }
1746:                return false;
1747:            }
1748:
1749:            /**
1750:             * Returns true IFF the given node can be contained by
1751:             * a range.
1752:             */
1753:            private boolean isLegalContainedNode(Node node) {
1754:                if (node == null)
1755:                    return false;
1756:                switch (node.getNodeType()) {
1757:                case Node.DOCUMENT_NODE:
1758:                case Node.DOCUMENT_FRAGMENT_NODE:
1759:                case Node.ATTRIBUTE_NODE:
1760:                case Node.ENTITY_NODE:
1761:                case Node.NOTATION_NODE:
1762:                    return false;
1763:                }
1764:                return true;
1765:            }
1766:
1767:            Node nextNode(Node node, boolean visitChildren) {
1768:
1769:                if (node == null)
1770:                    return null;
1771:
1772:                Node result;
1773:                if (visitChildren) {
1774:                    result = node.getFirstChild();
1775:                    if (result != null) {
1776:                        return result;
1777:                    }
1778:                }
1779:
1780:                // if hasSibling, return sibling
1781:                result = node.getNextSibling();
1782:                if (result != null) {
1783:                    return result;
1784:                }
1785:
1786:                // return parent's 1st sibling.
1787:                Node parent = node.getParentNode();
1788:                while (parent != null && parent != fDocument) {
1789:                    result = parent.getNextSibling();
1790:                    if (result != null) {
1791:                        return result;
1792:                    } else {
1793:                        parent = parent.getParentNode();
1794:                    }
1795:
1796:                } // while (parent != null && parent != fRoot) {
1797:
1798:                // end of list, return null
1799:                return null;
1800:            }
1801:
1802:            /** is a an ancestor of b ? */
1803:            boolean isAncestorOf(Node a, Node b) {
1804:                for (Node node = b; node != null; node = node.getParentNode()) {
1805:                    if (node == a)
1806:                        return true;
1807:                }
1808:                return false;
1809:            }
1810:
1811:            /** what is the index of the child in the parent */
1812:            int indexOf(Node child, Node parent) {
1813:                if (child.getParentNode() != parent)
1814:                    return -1;
1815:                int i = 0;
1816:                for (Node node = parent.getFirstChild(); node != child; node = node
1817:                        .getNextSibling()) {
1818:                    i++;
1819:                }
1820:                return i;
1821:            }
1822:
1823:            /**
1824:             * Utility method to retrieve a child node by index.  This method
1825:             * assumes the caller is trying to find out which node is 
1826:             * selected by the given index.  Note that if the index is
1827:             * greater than the number of children, this implies that the
1828:             * first node selected is the parent node itself.
1829:             * 
1830:             * @param container A container node
1831:             * 
1832:             * @param offset    An offset within the container for which a selected node should
1833:             *                  be computed.  If the offset is less than zero, or if the offset
1834:             *                  is greater than the number of children, the container is returned.
1835:             * 
1836:             * @return Returns either a child node of the container or the
1837:             *         container itself.
1838:             */
1839:            private Node getSelectedNode(Node container, int offset) {
1840:                if (container.getNodeType() == Node.TEXT_NODE)
1841:                    return container;
1842:
1843:                // This case is an important convenience for 
1844:                // traverseRightBoundary()
1845:                if (offset < 0)
1846:                    return container;
1847:
1848:                Node child = container.getFirstChild();
1849:                while (child != null && offset > 0) {
1850:                    --offset;
1851:                    child = child.getNextSibling();
1852:                }
1853:                if (child != null)
1854:                    return child;
1855:                return container;
1856:            }
1857:
1858:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.