/xerces-2_11_0/src/org/apache/xerces/dom/RangeImpl.java

# · Java · 2099 lines · 1288 code · 178 blank · 633 comment · 458 complexity · 8fa017bf99806c9ceba1e62fc3c90960 MD5 · raw file

Large files are truncated click here to view the full file

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