Source Code Cross Referenced for RangeImpl.java in  » XML » xerces-2_9_1 » 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 » XML » xerces 2_9_1 » org.apache.xerces.dom 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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