PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/1.x/nfop/xerces-2_0_2/src/org/apache/xerces/dom/RangeImpl.java

#
Java | 1945 lines | 1142 code | 172 blank | 631 comment | 355 complexity | 1bfe8ae2c526e0839f52ea2542478c1c MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *4dorse or promote products derived from this
  27. * software without prior written permission. For written
  28. * permission, please contact apache@apache.org.
  29. *
  30. * 5. Products derived from this software may not be called "Apache",
  31. * nor may "Apache" appear in their name, without prior written
  32. * permission of the Apache Software Foundation.
  33. *
  34. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  35. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  36. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  37. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  38. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  39. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  40. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  41. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  42. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  43. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  44. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  45. * SUCH DAMAGE.
  46. * ====================================================================
  47. *
  48. * This software consists of voluntary contributions made by many
  49. * individuals on behalf of the Apache Software Foundation and was
  50. * originally based on software copyright (c) 1999, International
  51. * Business Machines, Inc., http://www.apache.org. For more
  52. * information on the Apache Software Foundation, please see
  53. * <http://www.apache.org/>.
  54. */
  55. package org.apache.xerces.dom;
  56. import java.util.Vector;
  57. import org.w3c.dom.DOMException;
  58. import org.w3c.dom.DocumentFragment;
  59. import org.w3c.dom.Document;
  60. import org.w3c.dom.Node;
  61. import org.w3c.dom.CharacterData;
  62. import org.w3c.dom.ranges.Range;
  63. import org.w3c.dom.ranges.RangeException;
  64. /** The RangeImpl class implements the org.w3c.dom.range.Range interface.
  65. * <p> Please see the API documentation for the interface classes
  66. * and use the interfaces in your client programs.
  67. */
  68. public class RangeImpl implements Range {
  69. //
  70. // Constants
  71. //
  72. //
  73. // Data
  74. //
  75. DocumentImpl fDocument;
  76. Node fStartContainer;
  77. Node fEndContainer;
  78. int fStartOffset;
  79. int fEndOffset;
  80. boolean fIsCollapsed;
  81. boolean fDetach = false;
  82. Node fInsertNode = null;
  83. Node fDeleteNode = null;
  84. Node fSplitNode = null;
  85. /** The constructor. Clients must use DocumentRange.createRange(),
  86. * because it registers the Range with the document, so it can
  87. * be fixed-up.
  88. */
  89. public RangeImpl(DocumentImpl document) {
  90. fDocument = document;
  91. fStartContainer = document;
  92. fEndContainer = document;
  93. fStartOffset = 0;
  94. fEndOffset = 0;
  95. fDetach = false;
  96. }
  97. public Node getStartContainer() {
  98. return fStartContainer;
  99. }
  100. public int getStartOffset() {
  101. return fStartOffset;
  102. }
  103. public Node getEndContainer() {
  104. return fEndContainer;
  105. }
  106. public int getEndOffset() {
  107. return fEndOffset;
  108. }
  109. public boolean getCollapsed() {
  110. return (fStartContainer == fEndContainer
  111. && fStartOffset == fEndOffset);
  112. }
  113. public Node getCommonAncestorContainer(){
  114. Vector startV = new Vector();
  115. Node node;
  116. for (node=fStartContainer; node != null;
  117. node=node.getParentNode())
  118. {
  119. startV.addElement(node);
  120. }
  121. Vector endV = new Vector();
  122. for (node=fEndContainer; node != null;
  123. node=node.getParentNode())
  124. {
  125. endV.addElement(node);
  126. }
  127. int s = startV.size()-1;
  128. int e = endV.size()-1;
  129. Object result = null;
  130. while (s>=0 && e>=0) {
  131. if (startV.elementAt(s) == endV.elementAt(e)) {
  132. result = startV.elementAt(s);
  133. } else {
  134. break;
  135. }
  136. --s;
  137. --e;
  138. }
  139. return (Node)result;
  140. }
  141. public void setStart(Node refNode, int offset)
  142. throws RangeException, DOMException
  143. {
  144. if( fDetach) {
  145. throw new DOMException(
  146. DOMException.INVALID_STATE_ERR,
  147. "DOM011 Invalid state");
  148. }
  149. if ( !isLegalContainer(refNode)) {
  150. throw new RangeExceptionImpl(
  151. RangeException.INVALID_NODE_TYPE_ERR,
  152. "DOM012 Invalid node type");
  153. }
  154. checkIndex(refNode, offset);
  155. fStartContainer = refNode;
  156. fStartOffset = offset;
  157. }
  158. public void setEnd(Node refNode, int offset)
  159. throws RangeException, DOMException
  160. {
  161. if( fDetach) {
  162. throw new DOMException(
  163. DOMException.INVALID_STATE_ERR,
  164. "DOM011 Invalid state");
  165. }
  166. if ( !isLegalContainer(refNode)) {
  167. throw new RangeExceptionImpl(
  168. RangeException.INVALID_NODE_TYPE_ERR,
  169. "DOM012 Invalid node type");
  170. }
  171. checkIndex(refNode, offset);
  172. fEndContainer = refNode;
  173. fEndOffset = offset;
  174. }
  175. public void setStartBefore(Node refNode)
  176. throws RangeException
  177. {
  178. if( fDetach) {
  179. throw new DOMException(
  180. DOMException.INVALID_STATE_ERR,
  181. "DOM011 Invalid state");
  182. }
  183. if ( !hasLegalRootContainer(refNode) ||
  184. !isLegalContainedNode(refNode) )
  185. {
  186. throw new RangeExceptionImpl(
  187. RangeException.INVALID_NODE_TYPE_ERR,
  188. "DOM012 Invalid node type");
  189. }
  190. fStartContainer = refNode.getParentNode();
  191. int i = 0;
  192. for (Node n = refNode; n!=null; n = n.getPreviousSibling()) {
  193. i++;
  194. }
  195. fStartOffset = i-1;
  196. }
  197. public void setStartAfter(Node refNode)
  198. throws RangeException
  199. {
  200. if( fDetach) {
  201. throw new DOMException(
  202. DOMException.INVALID_STATE_ERR,
  203. "DOM011 Invalid state");
  204. }
  205. if ( !hasLegalRootContainer(refNode) ||
  206. !isLegalContainedNode(refNode)) {
  207. throw new RangeExceptionImpl(
  208. RangeException.INVALID_NODE_TYPE_ERR,
  209. "DOM012 Invalid node type");
  210. }
  211. fStartContainer = refNode.getParentNode();
  212. int i = 0;
  213. for (Node n = refNode; n!=null; n = n.getPreviousSibling()) {
  214. i++;
  215. }
  216. fStartOffset = i;
  217. }
  218. public void setEndBefore(Node refNode)
  219. throws RangeException
  220. {
  221. if( fDetach) {
  222. throw new DOMException(
  223. DOMException.INVALID_STATE_ERR,
  224. "DOM011 Invalid state");
  225. }
  226. if ( !hasLegalRootContainer(refNode) ||
  227. !isLegalContainedNode(refNode)) {
  228. throw new RangeExceptionImpl(
  229. RangeException.INVALID_NODE_TYPE_ERR,
  230. "DOM012 Invalid node type");
  231. }
  232. fEndContainer = refNode.getParentNode();
  233. int i = 0;
  234. for (Node n = refNode; n!=null; n = n.getPreviousSibling()) {
  235. i++;
  236. }
  237. fEndOffset = i-1;
  238. }
  239. public void setEndAfter(Node refNode)
  240. throws RangeException
  241. {
  242. if( fDetach) {
  243. throw new DOMException(
  244. DOMException.INVALID_STATE_ERR,
  245. "DOM011 Invalid state");
  246. }
  247. if ( !hasLegalRootContainer(refNode) ||
  248. !isLegalContainedNode(refNode)) {
  249. throw new RangeExceptionImpl(
  250. RangeException.INVALID_NODE_TYPE_ERR,
  251. "DOM012 Invalid node type");
  252. }
  253. fEndContainer = refNode.getParentNode();
  254. int i = 0;
  255. for (Node n = refNode; n!=null; n = n.getPreviousSibling()) {
  256. i++;
  257. }
  258. fEndOffset = i;
  259. }
  260. public void collapse(boolean toStart) {
  261. if( fDetach) {
  262. throw new DOMException(
  263. DOMException.INVALID_STATE_ERR,
  264. "DOM011 Invalid state");
  265. }
  266. if (toStart) {
  267. fEndContainer = fStartContainer;
  268. fEndOffset = fStartOffset;
  269. } else {
  270. fStartContainer = fEndContainer;
  271. fStartOffset = fEndOffset;
  272. }
  273. }
  274. public void selectNode(Node refNode)
  275. throws RangeException
  276. {
  277. if( fDetach) {
  278. throw new DOMException(
  279. DOMException.INVALID_STATE_ERR,
  280. "DOM011 Invalid state");
  281. }
  282. if ( !isLegalContainer( refNode.getParentNode() ) ||
  283. !isLegalContainedNode( refNode ) ) {
  284. throw new RangeExceptionImpl(
  285. RangeException.INVALID_NODE_TYPE_ERR,
  286. "DOM012 Invalid node type");
  287. }
  288. Node parent = refNode.getParentNode();
  289. if (parent != null ) // REVIST: what to do if it IS null?
  290. {
  291. fStartContainer = parent;
  292. fEndContainer = parent;
  293. int i = 0;
  294. for (Node n = refNode; n!=null; n = n.getPreviousSibling()) {
  295. i++;
  296. }
  297. fStartOffset = i-1;
  298. fEndOffset = fStartOffset+1;
  299. }
  300. }
  301. public void selectNodeContents(Node refNode)
  302. throws RangeException
  303. {
  304. if( fDetach) {
  305. throw new DOMException(
  306. DOMException.INVALID_STATE_ERR,
  307. "DOM011 Invalid state");
  308. }
  309. if ( !isLegalContainer(refNode)) {
  310. throw new RangeExceptionImpl(
  311. RangeException.INVALID_NODE_TYPE_ERR,
  312. "DOM012 Invalid node type");
  313. }
  314. fStartContainer = refNode;
  315. fEndContainer = refNode;
  316. Node first = refNode.getFirstChild();
  317. fStartOffset = 0;
  318. if (first == null) {
  319. fEndOffset = 0;
  320. } else {
  321. int i = 0;
  322. for (Node n = first; n!=null; n = n.getNextSibling()) {
  323. i++;
  324. }
  325. fEndOffset = i;
  326. }
  327. }
  328. public short compareBoundaryPoints(short how, Range sourceRange)
  329. throws DOMException
  330. {
  331. if( fDetach) {
  332. throw new DOMException(
  333. DOMException.INVALID_STATE_ERR,
  334. "DOM011 Invalid state");
  335. }
  336. Node endPointA;
  337. Node endPointB;
  338. int offsetA;
  339. int offsetB;
  340. if (how == START_TO_START) {
  341. endPointA = sourceRange.getStartContainer();
  342. endPointB = fStartContainer;
  343. offsetA = sourceRange.getStartOffset();
  344. offsetB = fStartOffset;
  345. } else
  346. if (how == START_TO_END) {
  347. endPointA = sourceRange.getStartContainer();
  348. endPointB = fEndContainer;
  349. offsetA = sourceRange.getStartOffset();
  350. offsetB = fEndOffset;
  351. } else
  352. if (how == END_TO_START) {
  353. endPointA = sourceRange.getEndContainer();
  354. endPointB = fStartContainer;
  355. offsetA = sourceRange.getEndOffset();
  356. offsetB = fStartOffset;
  357. } else {
  358. endPointA = sourceRange.getEndContainer();
  359. endPointB = fEndContainer;
  360. offsetA = sourceRange.getEndOffset();
  361. offsetB = fEndOffset;
  362. }
  363. // The DOM Spec outlines four cases that need to be tested
  364. // to compare two range boundary points:
  365. // case 1: same container
  366. // case 2: Child C of container A is ancestor of B
  367. // case 3: Child C of container B is ancestor of A
  368. // case 4: preorder traversal of context tree.
  369. // case 1: same container
  370. if (endPointA == endPointB) {
  371. if (offsetA < offsetB) return 1;
  372. if (offsetA == offsetB) return 0;
  373. return -1;
  374. }
  375. // case 2: Child C of container A is ancestor of B
  376. // This can be quickly tested by walking the parent chain of B
  377. for ( Node c = endPointB, p = c.getParentNode();
  378. p != null;
  379. c = p, p = p.getParentNode())
  380. {
  381. if (p == endPointA) {
  382. int index = indexOf(c, endPointA);
  383. if (offsetA <= index) return 1;
  384. return -1;
  385. }
  386. }
  387. // case 3: Child C of container B is ancestor of A
  388. // This can be quickly tested by walking the parent chain of A
  389. for ( Node c = endPointA, p = c.getParentNode();
  390. p != null;
  391. c = p, p = p.getParentNode())
  392. {
  393. if (p == endPointB) {
  394. int index = indexOf(c, endPointB);
  395. if (index < offsetB) return 1;
  396. return -1;
  397. }
  398. }
  399. // case 4: preorder traversal of context tree.
  400. // Instead of literally walking the context tree in pre-order,
  401. // we use relative node depth walking which is usually faster
  402. int depthDiff = 0;
  403. for ( Node n = endPointA; n != null; n = n.getParentNode() )
  404. depthDiff++;
  405. for ( Node n = endPointB; n != null; n = n.getParentNode() )
  406. depthDiff--;
  407. while (depthDiff > 0) {
  408. endPointA = endPointA.getParentNode();
  409. depthDiff--;
  410. }
  411. while (depthDiff < 0) {
  412. endPointB = endPointB.getParentNode();
  413. depthDiff++;
  414. }
  415. for (Node pA = endPointA.getParentNode(),
  416. pB = endPointB.getParentNode();
  417. pA != pB;
  418. pA = pA.getParentNode(), pB = pB.getParentNode() )
  419. {
  420. endPointA = pA;
  421. endPointB = pB;
  422. }
  423. for ( Node n = endPointA.getNextSibling();
  424. n != null;
  425. n = n.getNextSibling() )
  426. {
  427. if (n == endPointB) {
  428. return 1;
  429. }
  430. }
  431. return -1;
  432. }
  433. public void deleteContents()
  434. throws DOMException
  435. {
  436. traverseContents(DELETE_CONTENTS);
  437. }
  438. public DocumentFragment extractContents()
  439. throws DOMException
  440. {
  441. return traverseContents(EXTRACT_CONTENTS);
  442. }
  443. public DocumentFragment cloneContents()
  444. throws DOMException
  445. {
  446. return traverseContents(CLONE_CONTENTS);
  447. }
  448. public void insertNode(Node newNode)
  449. throws DOMException, RangeException
  450. {
  451. if ( newNode == null ) return; //throw exception?
  452. if( fDetach) {
  453. throw new DOMException(
  454. DOMException.INVALID_STATE_ERR,
  455. "DOM011 Invalid state");
  456. }
  457. if ( fDocument != newNode.getOwnerDocument() ) {
  458. throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,"DOM004 Wrong document");
  459. }
  460. int type = newNode.getNodeType();
  461. if (type == Node.ATTRIBUTE_NODE
  462. || type == Node.ENTITY_NODE
  463. || type == Node.NOTATION_NODE
  464. || type == Node.DOCUMENT_NODE)
  465. {
  466. throw new RangeExceptionImpl(
  467. RangeException.INVALID_NODE_TYPE_ERR,
  468. "DOM012 Invalid node type");
  469. }
  470. Node cloneCurrent;
  471. Node current;
  472. int currentChildren = 0;
  473. //boolean MULTIPLE_MODE = false;
  474. if (fStartContainer.getNodeType() == Node.TEXT_NODE) {
  475. Node parent = fStartContainer.getParentNode();
  476. currentChildren = parent.getChildNodes().getLength(); //holds number of kids before insertion
  477. // split text node: results is 3 nodes..
  478. cloneCurrent = fStartContainer.cloneNode(false);
  479. ((TextImpl)cloneCurrent).setNodeValueInternal(
  480. (cloneCurrent.getNodeValue()).substring(fStartOffset));
  481. ((TextImpl)fStartContainer).setNodeValueInternal(
  482. (fStartContainer.getNodeValue()).substring(0,fStartOffset));
  483. Node next = fStartContainer.getNextSibling();
  484. if (next != null) {
  485. if (parent != null) {
  486. parent.insertBefore(newNode, next);
  487. parent.insertBefore(cloneCurrent, next);
  488. }
  489. } else {
  490. if (parent != null) {
  491. parent.appendChild(newNode);
  492. parent.appendChild(cloneCurrent);
  493. }
  494. }
  495. //update ranges after the insertion
  496. if ( fEndContainer == fStartContainer) {
  497. fEndContainer = cloneCurrent; //endContainer is the new Node created
  498. fEndOffset -= fStartOffset;
  499. }
  500. else if ( fEndContainer == parent ) { //endContainer was not a text Node.
  501. //endOffset + = number_of_children_added
  502. fEndOffset += (parent.getChildNodes().getLength() - currentChildren);
  503. }
  504. // signal other Ranges to update their start/end containers/offsets
  505. signalSplitData(fStartContainer, cloneCurrent, fStartOffset);
  506. } else { // ! TEXT_NODE
  507. if ( fEndContainer == fStartContainer ) //need to remember number of kids
  508. currentChildren= fEndContainer.getChildNodes().getLength();
  509. current = fStartContainer.getFirstChild();
  510. int i = 0;
  511. for(i = 0; i < fStartOffset && current != null; i++) {
  512. current=current.getNextSibling();
  513. }
  514. if (current != null) {
  515. fStartContainer.insertBefore(newNode, current);
  516. } else {
  517. fStartContainer.appendChild(newNode);
  518. }
  519. //update fEndOffset. ex:<body><p/></body>. Range(start;end): body,0; body,1
  520. // insert <h1>: <body></h1><p/></body>. Range(start;end): body,0; body,2
  521. if ( fEndContainer == fStartContainer ) { //update fEndOffset
  522. fEndOffset += (fEndContainer.getChildNodes().getLength() - currentChildren);
  523. }
  524. }
  525. }
  526. public void surroundContents(Node newParent)
  527. throws DOMException, RangeException
  528. {
  529. if (newParent==null) return;
  530. if( fDetach) {
  531. throw new DOMException(
  532. DOMException.INVALID_STATE_ERR,
  533. "DOM011 Invalid state");
  534. }
  535. int type = newParent.getNodeType();
  536. if (type == Node.ATTRIBUTE_NODE
  537. || type == Node.ENTITY_NODE
  538. || type == Node.NOTATION_NODE
  539. || type == Node.DOCUMENT_TYPE_NODE
  540. || type == Node.DOCUMENT_NODE
  541. || type == Node.DOCUMENT_FRAGMENT_NODE)
  542. {
  543. throw new RangeExceptionImpl(
  544. RangeException.INVALID_NODE_TYPE_ERR,
  545. "DOM012 Invalid node type");
  546. }
  547. Node root = getCommonAncestorContainer();
  548. Node realStart = fStartContainer;
  549. Node realEnd = fEndContainer;
  550. if (fStartContainer.getNodeType() == Node.TEXT_NODE) {
  551. realStart = fStartContainer.getParentNode();
  552. }
  553. if (fEndContainer.getNodeType() == Node.TEXT_NODE) {
  554. realEnd = fEndContainer.getParentNode();
  555. }
  556. if (realStart != realEnd) {
  557. throw new RangeExceptionImpl(
  558. RangeException.BAD_BOUNDARYPOINTS_ERR,
  559. "DOM013 Bad boundary points");
  560. }
  561. DocumentFragment frag = extractContents();
  562. insertNode(newParent);
  563. newParent.appendChild(frag);
  564. selectNode(newParent);
  565. }
  566. public Range cloneRange(){
  567. if( fDetach) {
  568. throw new DOMException(
  569. DOMException.INVALID_STATE_ERR,
  570. "DOM011 Invalid state");
  571. }
  572. Range range = fDocument.createRange();
  573. range.setStart(fStartContainer, fStartOffset);
  574. range.setEnd(fEndContainer, fEndOffset);
  575. return range;
  576. }
  577. public String toString(){
  578. if( fDetach) {
  579. throw new DOMException(
  580. DOMException.INVALID_STATE_ERR,
  581. "DOM011 Invalid state");
  582. }
  583. Node node = fStartContainer;
  584. Node stopNode = fEndContainer;
  585. StringBuffer sb = new StringBuffer();
  586. if (fStartContainer.getNodeType() == Node.TEXT_NODE
  587. || fStartContainer.getNodeType() == Node.CDATA_SECTION_NODE
  588. ) {
  589. if (fStartContainer == fEndContainer) {
  590. sb.append(fStartContainer.getNodeValue().substring(fStartOffset, fEndOffset));
  591. return sb.toString();
  592. }
  593. sb.append(fStartContainer.getNodeValue().substring(fStartOffset));
  594. node=nextNode (node,true); //fEndContainer!=fStartContainer
  595. }
  596. else { //fStartContainer is not a TextNode
  597. node=node.getFirstChild();
  598. if (fStartOffset>0) { //find a first node within a range, specified by fStartOffset
  599. int counter=0;
  600. while (counter<fStartOffset && node!=null) {
  601. node=node.getNextSibling();
  602. counter++;
  603. }
  604. }
  605. if (node == null) {
  606. node = nextNode(fStartContainer,false);
  607. }
  608. }
  609. if ( fEndContainer.getNodeType()!= Node.TEXT_NODE &&
  610. fEndContainer.getNodeType()!= Node.CDATA_SECTION_NODE ){
  611. int i=fEndOffset;
  612. stopNode = fEndContainer.getFirstChild();
  613. while( i>0 && stopNode!=null ){
  614. --i;
  615. stopNode = stopNode.getNextSibling();
  616. }
  617. if ( stopNode == null )
  618. stopNode = nextNode( fEndContainer, false );
  619. }
  620. while (node != stopNode) { //look into all kids of the Range
  621. if (node == null) break;
  622. if (node.getNodeType() == Node.TEXT_NODE
  623. || node.getNodeType() == Node.CDATA_SECTION_NODE) {
  624. sb.append(node.getNodeValue());
  625. }
  626. node = nextNode(node, true);
  627. }
  628. if (fEndContainer.getNodeType() == Node.TEXT_NODE
  629. || fEndContainer.getNodeType() == Node.CDATA_SECTION_NODE) {
  630. sb.append(fEndContainer.getNodeValue().substring(0,fEndOffset));
  631. }
  632. return sb.toString();
  633. }
  634. public void detach() {
  635. fDetach = true;
  636. fDocument.removeRange(this);
  637. }
  638. //
  639. // Mutation functions
  640. //
  641. /** Signal other Ranges to update their start/end
  642. * containers/offsets. The data has already been split
  643. * into the two Nodes.
  644. */
  645. void signalSplitData(Node node, Node newNode, int offset) {
  646. fSplitNode = node;
  647. // notify document
  648. fDocument.splitData(node, newNode, offset);
  649. fSplitNode = null;
  650. }
  651. /** Fix up this Range if another Range has split a Text Node
  652. * into 2 Nodes.
  653. */
  654. void receiveSplitData(Node node, Node newNode, int offset) {
  655. if (node == null || newNode == null) return;
  656. if (fSplitNode == node) return;
  657. if (node == fStartContainer
  658. && fStartContainer.getNodeType() == Node.TEXT_NODE) {
  659. if (fStartOffset > offset) {
  660. fStartOffset = fStartOffset - offset;
  661. fStartContainer = newNode;
  662. }
  663. }
  664. if (node == fEndContainer
  665. && fEndContainer.getNodeType() == Node.TEXT_NODE) {
  666. if (fEndOffset > offset) {
  667. fEndOffset = fEndOffset-offset;
  668. fEndContainer = newNode;
  669. }
  670. }
  671. }
  672. /** This function inserts text into a Node and invokes
  673. * a method to fix-up all other Ranges.
  674. */
  675. void deleteData(CharacterData node, int offset, int count) {
  676. fDeleteNode = node;
  677. node.deleteData( offset, count);
  678. fDeleteNode = null;
  679. }
  680. /** This function is called from DOM.
  681. * The text has already beeen inserted.
  682. * Fix-up any offsets.
  683. */
  684. void receiveDeletedText(Node node, int offset, int count) {
  685. if (node == null) return;
  686. if (fDeleteNode == node) return;
  687. if (node == fStartContainer
  688. && fStartContainer.getNodeType() == Node.TEXT_NODE) {
  689. if (fStartOffset > offset+count) {
  690. fStartOffset = offset+(fStartOffset-(offset+count));
  691. } else
  692. if (fStartOffset > offset) {
  693. fStartOffset = offset;
  694. }
  695. }
  696. if (node == fEndContainer
  697. && fEndContainer.getNodeType() == Node.TEXT_NODE) {
  698. if (fEndOffset > offset+count) {
  699. fEndOffset = offset+(fEndOffset-(offset+count));
  700. } else
  701. if (fEndOffset > offset) {
  702. fEndOffset = offset;
  703. }
  704. }
  705. }
  706. /** This function inserts text into a Node and invokes
  707. * a method to fix-up all other Ranges.
  708. */
  709. void insertData(CharacterData node, int index, String insert) {
  710. fInsertNode = node;
  711. node.insertData( index, insert);
  712. fInsertNode = null;
  713. }
  714. /** This function is called from DOM.
  715. * The text has already beeen inserted.
  716. * Fix-up any offsets.
  717. */
  718. void receiveInsertedText(Node node, int index, int len) {
  719. if (node == null) return;
  720. if (fInsertNode == node) return;
  721. if (node == fStartContainer
  722. && fStartContainer.getNodeType() == Node.TEXT_NODE) {
  723. if (index < fStartOffset) {
  724. fStartOffset = fStartOffset+len;
  725. }
  726. }
  727. if (node == fEndContainer
  728. && fEndContainer.getNodeType() == Node.TEXT_NODE) {
  729. if (index < fEndOffset) {
  730. fEndOffset = fEndOffset+len;
  731. }
  732. }
  733. }
  734. /** This function is called from DOM.
  735. * The text has already beeen replaced.
  736. * Fix-up any offsets.
  737. */
  738. void receiveReplacedText(Node node) {
  739. if (node == null) return;
  740. if (node == fStartContainer
  741. && fStartContainer.getNodeType() == Node.TEXT_NODE) {
  742. fStartOffset = 0;
  743. }
  744. if (node == fEndContainer
  745. && fEndContainer.getNodeType() == Node.TEXT_NODE) {
  746. fEndOffset = 0;
  747. }
  748. }
  749. /** This function is called from the DOM.
  750. * This node has already been inserted into the DOM.
  751. * Fix-up any offsets.
  752. */
  753. public void insertedNodeFromDOM(Node node) {
  754. if (node == null) return;
  755. if (fInsertNode == node) return;
  756. Node parent = node.getParentNode();
  757. if (parent == fStartContainer) {
  758. int index = indexOf(node, fStartContainer);
  759. if (index < fStartOffset) {
  760. fStartOffset++;
  761. }
  762. }
  763. if (parent == fEndContainer) {
  764. int index = indexOf(node, fEndContainer);
  765. if (index < fEndOffset) {
  766. fEndOffset++;
  767. }
  768. }
  769. }
  770. /** This function is called within Range
  771. * instead of Node.removeChild,
  772. * so that the range can remember that it is actively
  773. * removing this child.
  774. */
  775. Node fRemoveChild = null;
  776. Node removeChild(Node parent, Node child) {
  777. fRemoveChild = child;
  778. Node n = parent.removeChild(child);
  779. fRemoveChild = null;
  780. return n;
  781. }
  782. /** This function must be called by the DOM _BEFORE_
  783. * a node is deleted, because at that time it is
  784. * connected in the DOM tree, which we depend on.
  785. */
  786. void removeNode(Node node) {
  787. if (node == null) return;
  788. if (fRemoveChild == node) return;
  789. Node parent = node.getParentNode();
  790. if (parent == fStartContainer) {
  791. int index = indexOf(node, fStartContainer);
  792. if (index < fStartOffset) {
  793. fStartOffset--;
  794. }
  795. }
  796. if (parent == fEndContainer) {
  797. int index = indexOf(node, fEndContainer);
  798. if (index < fEndOffset) {
  799. fEndOffset--;
  800. }
  801. }
  802. //startContainer or endContainer or both is/are the ancestor(s) of the Node to be deleted
  803. if (parent != fStartContainer
  804. || parent != fEndContainer) {
  805. if (isAncestorOf(node, fStartContainer)) {
  806. fStartContainer = parent;
  807. fStartOffset = indexOf( node, parent);
  808. }
  809. if (isAncestorOf(node, fEndContainer)) {
  810. fEndContainer = parent;
  811. fEndOffset = indexOf( node, parent);
  812. }
  813. }
  814. }
  815. //
  816. // Utility functions.
  817. //
  818. // parameters for traverseContents(int)
  819. //REVIST: use boolean, since there are only 2 now...
  820. static final int EXTRACT_CONTENTS = 1;
  821. static final int CLONE_CONTENTS = 2;
  822. static final int DELETE_CONTENTS = 3;
  823. /**
  824. * This is the master routine invoked to visit the nodes
  825. * selected by this range. For each such node, different
  826. * actions are taken depending on the value of the
  827. * <code>how</code> argument.
  828. *
  829. * @param how Specifies what type of traversal is being
  830. * requested (extract, clone, or delete).
  831. * Legal values for this argument are:
  832. *
  833. * <ol>
  834. * <li><code>EXTRACT_CONTENTS</code> - will produce
  835. * a document fragment containing the range's content.
  836. * Partially selected nodes are copied, but fully
  837. * selected nodes are moved.
  838. *
  839. * <li><code>CLONE_CONTENTS</code> - will leave the
  840. * context tree of the range undisturbed, but sill
  841. * produced cloned content in a document fragment
  842. *
  843. * <li><code>DELETE_CONTENTS</code> - will delete from
  844. * the context tree of the range, all fully selected
  845. * nodes.
  846. * </ol>
  847. *
  848. * @return Returns a document fragment containing any
  849. * copied or extracted nodes. If the <code>how</code>
  850. * parameter was <code>DELETE_CONTENTS</code>, the
  851. * return value is null.
  852. */
  853. private DocumentFragment traverseContents( int how )
  854. throws DOMException
  855. {
  856. if (fStartContainer == null || fEndContainer == null) {
  857. return null; // REVIST: Throw exception?
  858. }
  859. //Check for a detached range.
  860. if( fDetach) {
  861. throw new DOMException(
  862. DOMException.INVALID_STATE_ERR,
  863. "DOM011 Invalid state");
  864. }
  865. /*
  866. Traversal is accomplished by first determining the
  867. relationship between the endpoints of the range.
  868. For each of four significant relationships, we will
  869. delegate the traversal call to a method that
  870. can make appropriate assumptions.
  871. */
  872. // case 1: same container
  873. if ( fStartContainer == fEndContainer )
  874. return traverseSameContainer( how );
  875. // case 2: Child C of start container is ancestor of end container
  876. // This can be quickly tested by walking the parent chain of
  877. // end container
  878. int endContainerDepth = 0;
  879. for ( Node c = fEndContainer, p = c.getParentNode();
  880. p != null;
  881. c = p, p = p.getParentNode())
  882. {
  883. if (p == fStartContainer)
  884. return traverseCommonStartContainer( c, how );
  885. ++endContainerDepth;
  886. }
  887. // case 3: Child C of container B is ancestor of A
  888. // This can be quickly tested by walking the parent chain of A
  889. int startContainerDepth = 0;
  890. for ( Node c = fStartContainer, p = c.getParentNode();
  891. p != null;
  892. c = p, p = p.getParentNode())
  893. {
  894. if (p == fEndContainer)
  895. return traverseCommonEndContainer( c, how );
  896. ++startContainerDepth;
  897. }
  898. // case 4: There is a common ancestor container. Find the
  899. // ancestor siblings that are children of that container.
  900. int depthDiff = startContainerDepth - endContainerDepth;
  901. Node startNode = fStartContainer;
  902. while (depthDiff > 0) {
  903. startNode = startNode.getParentNode();
  904. depthDiff--;
  905. }
  906. Node endNode = fEndContainer;
  907. while (depthDiff < 0) {
  908. endNode = endNode.getParentNode();
  909. depthDiff++;
  910. }
  911. // ascend the ancestor hierarchy until we have a common parent.
  912. for( Node sp = startNode.getParentNode(), ep = endNode.getParentNode();
  913. sp!=ep;
  914. sp = sp.getParentNode(), ep = ep.getParentNode() )
  915. {
  916. startNode = sp;
  917. endNode = ep;
  918. }
  919. return traverseCommonAncestors( startNode, endNode, how );
  920. }
  921. /**
  922. * Visits the nodes selected by this range when we know
  923. * a-priori that the start and end containers are the same.
  924. * This method is invoked by the generic <code>traverse</code>
  925. * method.
  926. *
  927. * @param how Specifies what type of traversal is being
  928. * requested (extract, clone, or delete).
  929. * Legal values for this argument are:
  930. *
  931. * <ol>
  932. * <li><code>EXTRACT_CONTENTS</code> - will produce
  933. * a document fragment containing the range's content.
  934. * Partially selected nodes are copied, but fully
  935. * selected nodes are moved.
  936. *
  937. * <li><code>CLONE_CONTENTS</code> - will leave the
  938. * context tree of the range undisturbed, but sill
  939. * produced cloned content in a document fragment
  940. *
  941. * <li><code>DELETE_CONTENTS</code> - will delete from
  942. * the context tree of the range, all fully selected
  943. * nodes.
  944. * </ol>
  945. *
  946. * @return Returns a document fragment containing any
  947. * copied or extracted nodes. If the <code>how</code>
  948. * parameter was <code>DELETE_CONTENTS</code>, the
  949. * return value is null.
  950. */
  951. private DocumentFragment traverseSameContainer( int how )
  952. {
  953. DocumentFragment frag = null;
  954. if ( how!=DELETE_CONTENTS)
  955. frag = fDocument.createDocumentFragment();
  956. // If selection is empty, just return the fragment
  957. if ( fStartOffset==fEndOffset )
  958. return frag;
  959. // Text node needs special case handling
  960. if ( fStartContainer.getNodeType()==Node.TEXT_NODE )
  961. {
  962. // get the substring
  963. String s = fStartContainer.getNodeValue();
  964. String sub = s.substring( fStartOffset, fEndOffset );
  965. // set the original text node to its new value
  966. if ( how != CLONE_CONTENTS )
  967. {
  968. fStartContainer.setNodeValue(
  969. s.substring(0, fStartOffset ) +
  970. s.substring(fEndOffset)
  971. );
  972. // Nothing is partially selected, so collapse to start point
  973. collapse( true );
  974. }
  975. if ( how==DELETE_CONTENTS)
  976. return null;
  977. frag.appendChild( fDocument.createTextNode(sub) );
  978. return frag;
  979. }
  980. // Copy nodes between the start/end offsets.
  981. Node n = getSelectedNode( fStartContainer, fStartOffset );
  982. int cnt = fEndOffset - fStartOffset;
  983. while( cnt > 0 )
  984. {
  985. Node sibling = n.getNextSibling();
  986. Node xferNode = traverseFullySelected( n, how );
  987. if ( frag!=null )
  988. frag.appendChild( xferNode );
  989. --cnt;
  990. n = sibling;
  991. }
  992. // Nothing is partially selected, so collapse to start point
  993. if ( how != CLONE_CONTENTS )
  994. collapse( true );
  995. return frag;
  996. }
  997. /**
  998. * Visits the nodes selected by this range when we know
  999. * a-priori that the start and end containers are not the
  1000. * same, but the start container is an ancestor of the
  1001. * end container. This method is invoked by the generic
  1002. * <code>traverse</code> method.
  1003. *
  1004. * @param endAncestor
  1005. * The ancestor of the end container that is a direct child
  1006. * of the start container.
  1007. *
  1008. * @param how Specifies what type of traversal is being
  1009. * requested (extract, clone, or delete).
  1010. * Legal values for this argument are:
  1011. *
  1012. * <ol>
  1013. * <li><code>EXTRACT_CONTENTS</code> - will produce
  1014. * a document fragment containing the range's content.
  1015. * Partially selected nodes are copied, but fully
  1016. * selected nodes are moved.
  1017. *
  1018. * <li><code>CLONE_CONTENTS</code> - will leave the
  1019. * context tree of the range undisturbed, but sill
  1020. * produced cloned content in a document fragment
  1021. *
  1022. * <li><code>DELETE_CONTENTS</code> - will delete from
  1023. * the context tree of the range, all fully selected
  1024. * nodes.
  1025. * </ol>
  1026. *
  1027. * @return Returns a document fragment containing any
  1028. * copied or extracted nodes. If the <code>how</code>
  1029. * parameter was <code>DELETE_CONTENTS</code>, the
  1030. * return value is null.
  1031. */
  1032. private DocumentFragment
  1033. traverseCommonStartContainer( Node endAncestor, int how )
  1034. {
  1035. DocumentFragment frag = null;
  1036. if ( how!=DELETE_CONTENTS)
  1037. frag = fDocument.createDocumentFragment();
  1038. Node n = traverseRightBoundary( endAncestor, how );
  1039. if ( frag!=null )
  1040. frag.appendChild( n );
  1041. int endIdx = indexOf( endAncestor, fStartContainer );
  1042. int cnt = endIdx - fStartOffset;
  1043. if ( cnt <=0 )
  1044. {
  1045. // Collapse to just before the endAncestor, which
  1046. // is partially selected.
  1047. if ( how != CLONE_CONTENTS )
  1048. {
  1049. setEndBefore( endAncestor );
  1050. collapse( false );
  1051. }
  1052. return frag;
  1053. }
  1054. n = endAncestor.getPreviousSibling();
  1055. while( cnt > 0 )
  1056. {
  1057. Node sibling = n.getPreviousSibling();
  1058. Node xferNode = traverseFullySelected( n, how );
  1059. if ( frag!=null )
  1060. frag.insertBefore( xferNode, frag.getFirstChild() );
  1061. --cnt;
  1062. n = sibling;
  1063. }
  1064. // Collapse to just before the endAncestor, which
  1065. // is partially selected.
  1066. if ( how != CLONE_CONTENTS )
  1067. {
  1068. setEndBefore( endAncestor );
  1069. collapse( false );
  1070. }
  1071. return frag;
  1072. }
  1073. /**
  1074. * Visits the nodes selected by this range when we know
  1075. * a-priori that the start and end containers are not the
  1076. * same, but the end container is an ancestor of the
  1077. * start container. This method is invoked by the generic
  1078. * <code>traverse</code> method.
  1079. *
  1080. * @param startAncestor
  1081. * The ancestor of the start container that is a direct
  1082. * child of the end container.
  1083. *
  1084. * @param how Specifies what type of traversal is being
  1085. * requested (extract, clone, or delete).
  1086. * Legal values for this argument are:
  1087. *
  1088. * <ol>
  1089. * <li><code>EXTRACT_CONTENTS</code> - will produce
  1090. * a document fragment containing the range's content.
  1091. * Partially selected nodes are copied, but fully
  1092. * selected nodes are moved.
  1093. *
  1094. * <li><code>CLONE_CONTENTS</code> - will leave the
  1095. * context tree of the range undisturbed, but sill
  1096. * produced cloned content in a document fragment
  1097. *
  1098. * <li><code>DELETE_CONTENTS</code> - will delete from
  1099. * the context tree of the range, all fully selected
  1100. * nodes.
  1101. * </ol>
  1102. *
  1103. * @return Returns a document fragment containing any
  1104. * copied or extracted nodes. If the <code>how</code>
  1105. * parameter was <code>DELETE_CONTENTS</code>, the
  1106. * return value is null.
  1107. */
  1108. private DocumentFragment
  1109. traverseCommonEndContainer( Node startAncestor, int how )
  1110. {
  1111. DocumentFragment frag = null;
  1112. if ( how!=DELETE_CONTENTS)
  1113. frag = fDocument.createDocumentFragment();
  1114. Node n = traverseLeftBoundary( startAncestor, how );
  1115. if ( frag!=null )
  1116. frag.appendChild( n );
  1117. int startIdx = indexOf( startAncestor, fEndContainer );
  1118. ++startIdx; // Because we already traversed it....
  1119. int cnt = fEndOffset - startIdx;
  1120. n = startAncestor.getNextSibling();
  1121. while( cnt > 0 )
  1122. {
  1123. Node sibling = n.getNextSibling();
  1124. Node xferNode = traverseFullySelected( n, how );
  1125. if ( frag!=null )
  1126. frag.appendChild( xferNode );
  1127. --cnt;
  1128. n = sibling;
  1129. }
  1130. if ( how != CLONE_CONTENTS )
  1131. {
  1132. setStartAfter( startAncestor );
  1133. collapse( true );
  1134. }
  1135. return frag;
  1136. }
  1137. /**
  1138. * Visits the nodes selected by this range when we know
  1139. * a-priori that the start and end containers are not
  1140. * the same, and we also know that neither the start
  1141. * nor end container is an ancestor of the other.
  1142. * This method is invoked by
  1143. * the generic <code>traverse</code> method.
  1144. *
  1145. * @param startAncestor
  1146. * Given a common ancestor of the start and end containers,
  1147. * this parameter is the ancestor (or self) of the start
  1148. * container that is a direct child of the common ancestor.
  1149. *
  1150. * @param endAncestor
  1151. * Given a common ancestor of the start and end containers,
  1152. * this parameter is the ancestor (or self) of the end
  1153. * container that is a direct child of the common ancestor.
  1154. *
  1155. * @param how Specifies what type of traversal is being
  1156. * requested (extract, clone, or delete).
  1157. * Legal values for this argument are:
  1158. *
  1159. * <ol>
  1160. * <li><code>EXTRACT_CONTENTS</code> - will produce
  1161. * a document fragment containing the range's content.
  1162. * Partially selected nodes are copied, but fully
  1163. * selected nodes are moved.
  1164. *
  1165. * <li><code>CLONE_CONTENTS</code> - will leave the
  1166. * context tree of the range undisturbed, but sill
  1167. * produced cloned content in a document fragment
  1168. *
  1169. * <li><code>DELETE_CONTENTS</code> - will delete from
  1170. * the context tree of the range, all fully selected
  1171. * nodes.
  1172. * </ol>
  1173. *
  1174. * @return Returns a document fragment containing any
  1175. * copied or extracted nodes. If the <code>how</code>
  1176. * parameter was <code>DELETE_CONTENTS</code>, the
  1177. * return value is null.
  1178. */
  1179. private DocumentFragment
  1180. traverseCommonAncestors( Node startAncestor, Node endAncestor, int how )
  1181. {
  1182. DocumentFragment frag = null;
  1183. if ( how!=DELETE_CONTENTS)
  1184. frag = fDocument.createDocumentFragment();
  1185. Node n = traverseLeftBoundary( startAncestor, how );
  1186. if ( frag!=null )
  1187. frag.appendChild( n );
  1188. Node commonParent = startAncestor.getParentNode();
  1189. int startOffset = indexOf( startAncestor, commonParent );
  1190. int endOffset = indexOf( endAncestor, commonParent );
  1191. ++startOffset;
  1192. int cnt = endOffset - startOffset;
  1193. Node sibling = startAncestor.getNextSibling();
  1194. while( cnt > 0 )
  1195. {
  1196. Node nextSibling = sibling.getNextSibling();
  1197. n = traverseFullySelected( sibling, how );
  1198. if ( frag!=null )
  1199. frag.appendChild( n );
  1200. sibling = nextSibling;
  1201. --cnt;
  1202. }
  1203. n = traverseRightBoundary( endAncestor, how );
  1204. if ( frag!=null )
  1205. frag.appendChild( n );
  1206. if ( how != CLONE_CONTENTS )
  1207. {
  1208. setStartAfter( startAncestor );
  1209. collapse( true );
  1210. }
  1211. return frag;
  1212. }
  1213. /**
  1214. * Traverses the "right boundary" of this range and
  1215. * operates on each "boundary node" according to the
  1216. * <code>how</code> parameter. It is a-priori assumed
  1217. * by this method that the right boundary does
  1218. * not contain the range's start container.
  1219. * <p>
  1220. * A "right boundary" is best visualized by thinking
  1221. * of a sample tree:<pre>
  1222. * A
  1223. * /|\
  1224. * / | \
  1225. * / | \
  1226. * B C D
  1227. * /|\ /|\
  1228. * E F G H I J
  1229. * </pre>
  1230. * Imagine first a range that begins between the
  1231. * "E" and "F" nodes and ends between the
  1232. * "I" and "J" nodes. The start container is
  1233. * "B" and the end container is "D". Given this setup,
  1234. * the following applies:
  1235. * <p>
  1236. * Partially Selected Nodes: B, D<br>
  1237. * Fully Selected Nodes: F, G, C, H, I
  1238. * <p>
  1239. * The "right boundary" is the highest subtree node
  1240. * that contains the ending container. The root of
  1241. * this subtree is always partially selected.
  1242. * <p>
  1243. * In this example, the nodes that are traversed
  1244. * as "right boundary" nodes are: H, I, and D.
  1245. *
  1246. * @param root The node that is the root of the "right boundary" subtree.
  1247. *
  1248. * @param how Specifies what type of traversal is being
  1249. * requested (extract, clone, or delete).
  1250. * Legal values for this argument are:
  1251. *
  1252. * <ol>
  1253. * <li><code>EXTRACT_CONTENTS</code> - will produce
  1254. * a node containing the boundaries content.
  1255. * Partially selected nodes are copied, but fully
  1256. * selected nodes are moved.
  1257. *
  1258. * <li><code>CLONE_CONTENTS</code> - will leave the
  1259. * context tree of the range undisturbed, but will
  1260. * produced cloned content.
  1261. *
  1262. * <li><code>DELETE_CONTENTS</code> - will delete from
  1263. * the context tree of the range, all fully selected
  1264. * nodes within the boundary.
  1265. * </ol>
  1266. *
  1267. * @return Returns a node that is the result of visiting nodes.
  1268. * If the traversal operation is
  1269. * <code>DELETE_CONTENTS</code> the return value is null.
  1270. */
  1271. private Node traverseRightBoundary( Node root, int how )
  1272. {
  1273. Node next = getSelectedNode( fEndContainer, fEndOffset-1 );
  1274. boolean isFullySelected = ( next!=fEndContainer );
  1275. if ( next==root )
  1276. return traverseNode( next, isFullySelected, false, how );
  1277. Node parent = next.getParentNode();
  1278. Node clonedParent = traverseNode( parent, false, false, how );
  1279. while( parent!=null )
  1280. {
  1281. while( next!=null )

Large files files are truncated, but you can click here to view the full file