1 /* 2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Oct 2017 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xerces.internal.dom; 23 24 import java.util.ArrayList; 25 import java.util.HashMap; 26 import java.util.List; 27 import org.w3c.dom.DOMImplementation; 28 import org.w3c.dom.Element; 29 import org.w3c.dom.Node; 30 31 /** 32 * The Document interface represents the entire HTML or XML document. 33 * Conceptually, it is the root of the document tree, and provides the 34 * primary access to the document's data. 35 * <P> 36 * Since elements, text nodes, comments, processing instructions, 37 * etc. cannot exist outside the context of a Document, the Document 38 * interface also contains the factory methods needed to create these 39 * objects. The Node objects created have a ownerDocument attribute 40 * which associates them with the Document within whose context they 41 * were created. 42 * 43 * @xerces.internal 44 * 45 * @since PR-DOM-Level-1-19980818. 46 */ 47 public class DeferredDocumentImpl 48 extends DocumentImpl 49 implements DeferredNode { 50 51 // 52 // Constants 53 // 54 55 /** Serialization version. */ 56 static final long serialVersionUID = 5186323580749626857L; 57 58 // debugging 59 60 /** To include code for printing the ref count tables. */ 61 private static final boolean DEBUG_PRINT_REF_COUNTS = false; 62 63 /** To include code for printing the internal tables. */ 64 private static final boolean DEBUG_PRINT_TABLES = false; 65 66 /** To debug identifiers set to true and recompile. */ 67 private static final boolean DEBUG_IDS = false; 68 69 // protected 70 71 /** Chunk shift. */ 72 protected static final int CHUNK_SHIFT = 8; // 2^8 = 256 73 74 /** Chunk size. */ 75 protected static final int CHUNK_SIZE = (1 << CHUNK_SHIFT); 76 77 /** Chunk mask. */ 78 protected static final int CHUNK_MASK = CHUNK_SIZE - 1; 79 80 /** Initial chunk size. */ 81 protected static final int INITIAL_CHUNK_COUNT = (1 << (13 - CHUNK_SHIFT)); // 32 82 83 // 84 // Data 85 // 86 87 // lazy-eval information 88 // To maximize memory consumption the actual semantic of these fields vary 89 // depending on the node type. 90 91 /** Node count. */ 92 protected transient int fNodeCount = 0; 93 94 /** Node types. */ 95 protected transient int fNodeType[][]; 96 97 /** Node names. */ 98 protected transient Object fNodeName[][]; 99 100 /** Node values. */ 101 protected transient Object fNodeValue[][]; 102 103 /** Node parents. */ 104 protected transient int fNodeParent[][]; 105 106 /** Node first children. */ 107 protected transient int fNodeLastChild[][]; 108 109 /** Node prev siblings. */ 110 protected transient int fNodePrevSib[][]; 111 112 /** Node namespace URI. */ 113 protected transient Object fNodeURI[][]; 114 115 /** Extra data. */ 116 protected transient int fNodeExtra[][]; 117 118 /** Identifier count. */ 119 protected transient int fIdCount; 120 121 /** Identifier name indexes. */ 122 protected transient String fIdName[]; 123 124 /** Identifier element indexes. */ 125 protected transient int fIdElement[]; 126 127 /** DOM2: For namespace support in the deferred case. 128 */ 129 // Implementation Note: The deferred element and attribute must know how to 130 // interpret the int representing the qname. 131 protected boolean fNamespacesEnabled = false; 132 133 // 134 // private data 135 // 136 private transient final StringBuilder fBufferStr = new StringBuilder(); 137 private transient final List<String> fStrChunks = new ArrayList<>(); 138 139 // 140 // Constructors 141 // 142 143 /** 144 * NON-DOM: Actually creating a Document is outside the DOM's spec, 145 * since it has to operate in terms of a particular implementation. 146 */ 147 public DeferredDocumentImpl() { 148 this(false); 149 } // <init>() 150 151 /** 152 * NON-DOM: Actually creating a Document is outside the DOM's spec, 153 * since it has to operate in terms of a particular implementation. 154 */ 155 public DeferredDocumentImpl(boolean namespacesEnabled) { 156 this(namespacesEnabled, false); 157 } // <init>(boolean) 158 159 /** Experimental constructor. */ 160 public DeferredDocumentImpl(boolean namespaces, boolean grammarAccess) { 161 super(grammarAccess); 162 163 needsSyncData(true); 164 needsSyncChildren(true); 165 166 fNamespacesEnabled = namespaces; 167 168 } // <init>(boolean,boolean) 169 170 // 171 // Public methods 172 // 173 174 /** 175 * Retrieve information describing the abilities of this particular 176 * DOM implementation. Intended to support applications that may be 177 * using DOMs retrieved from several different sources, potentially 178 * with different underlying representations. 179 */ 180 public DOMImplementation getImplementation() { 181 // Currently implemented as a singleton, since it's hardcoded 182 // information anyway. 183 return DeferredDOMImplementationImpl.getDOMImplementation(); 184 } 185 186 /** Returns the cached parser.getNamespaces() value.*/ 187 boolean getNamespacesEnabled() { 188 return fNamespacesEnabled; 189 } 190 191 void setNamespacesEnabled(boolean enable) { 192 fNamespacesEnabled = enable; 193 } 194 195 // internal factory methods 196 197 /** Creates a document node in the table. */ 198 public int createDeferredDocument() { 199 int nodeIndex = createNode(Node.DOCUMENT_NODE); 200 return nodeIndex; 201 } 202 203 /** Creates a doctype. */ 204 public int createDeferredDocumentType(String rootElementName, 205 String publicId, String systemId) { 206 207 // create node 208 int nodeIndex = createNode(Node.DOCUMENT_TYPE_NODE); 209 int chunk = nodeIndex >> CHUNK_SHIFT; 210 int index = nodeIndex & CHUNK_MASK; 211 212 // save name, public id, system id 213 setChunkValue(fNodeName, rootElementName, chunk, index); 214 setChunkValue(fNodeValue, publicId, chunk, index); 215 setChunkValue(fNodeURI, systemId, chunk, index); 216 217 // return node index 218 return nodeIndex; 219 220 } // createDeferredDocumentType(String,String,String):int 221 222 public void setInternalSubset(int doctypeIndex, String subset) { 223 int chunk = doctypeIndex >> CHUNK_SHIFT; 224 int index = doctypeIndex & CHUNK_MASK; 225 226 // create extra data node to store internal subset 227 int extraDataIndex = createNode(Node.DOCUMENT_TYPE_NODE); 228 int echunk = extraDataIndex >> CHUNK_SHIFT; 229 int eindex = extraDataIndex & CHUNK_MASK; 230 setChunkIndex(fNodeExtra, extraDataIndex, chunk, index); 231 setChunkValue(fNodeValue, subset, echunk, eindex); 232 } 233 234 /** Creates a notation in the table. */ 235 public int createDeferredNotation(String notationName, 236 String publicId, String systemId, String baseURI) { 237 238 // create node 239 int nodeIndex = createNode(Node.NOTATION_NODE); 240 int chunk = nodeIndex >> CHUNK_SHIFT; 241 int index = nodeIndex & CHUNK_MASK; 242 243 244 // create extra data node 245 int extraDataIndex = createNode(Node.NOTATION_NODE); 246 int echunk = extraDataIndex >> CHUNK_SHIFT; 247 int eindex = extraDataIndex & CHUNK_MASK; 248 249 // save name, public id, system id, and notation name 250 setChunkValue(fNodeName, notationName, chunk, index); 251 setChunkValue(fNodeValue, publicId, chunk, index); 252 setChunkValue(fNodeURI, systemId, chunk, index); 253 254 // in extra data node set baseURI value 255 setChunkIndex(fNodeExtra, extraDataIndex, chunk, index); 256 setChunkValue(fNodeName, baseURI, echunk, eindex); 257 258 // return node index 259 return nodeIndex; 260 261 } // createDeferredNotation(String,String,String):int 262 263 /** Creates an entity in the table. */ 264 public int createDeferredEntity(String entityName, String publicId, 265 String systemId, String notationName, 266 String baseURI) { 267 // create node 268 int nodeIndex = createNode(Node.ENTITY_NODE); 269 int chunk = nodeIndex >> CHUNK_SHIFT; 270 int index = nodeIndex & CHUNK_MASK; 271 272 // create extra data node 273 int extraDataIndex = createNode(Node.ENTITY_NODE); 274 int echunk = extraDataIndex >> CHUNK_SHIFT; 275 int eindex = extraDataIndex & CHUNK_MASK; 276 277 // save name, public id, system id, and notation name 278 setChunkValue(fNodeName, entityName, chunk, index); 279 setChunkValue(fNodeValue, publicId, chunk, index); 280 setChunkValue(fNodeURI, systemId, chunk, index); 281 setChunkIndex(fNodeExtra, extraDataIndex, chunk, index); 282 // set other values in the extra chunk 283 // notation 284 setChunkValue(fNodeName, notationName, echunk, eindex); 285 // version L3 286 setChunkValue(fNodeValue, null, echunk, eindex); 287 // encoding L3 288 setChunkValue(fNodeURI, null, echunk, eindex); 289 290 291 int extraDataIndex2 = createNode(Node.ENTITY_NODE); 292 int echunk2 = extraDataIndex2 >> CHUNK_SHIFT; 293 int eindex2 = extraDataIndex2 & CHUNK_MASK; 294 295 setChunkIndex(fNodeExtra, extraDataIndex2, echunk, eindex); 296 297 // baseURI 298 setChunkValue(fNodeName, baseURI, echunk2, eindex2); 299 300 // return node index 301 return nodeIndex; 302 303 } // createDeferredEntity(String,String,String,String):int 304 305 public String getDeferredEntityBaseURI (int entityIndex){ 306 if (entityIndex != -1) { 307 int extraDataIndex = getNodeExtra(entityIndex, false); 308 extraDataIndex = getNodeExtra(extraDataIndex, false); 309 return getNodeName (extraDataIndex, false); 310 } 311 return null; 312 } 313 314 // DOM Level 3: setting encoding and version 315 public void setEntityInfo(int currentEntityDecl, 316 String version, String encoding){ 317 int eNodeIndex = getNodeExtra(currentEntityDecl, false); 318 if (eNodeIndex !=-1) { 319 int echunk = eNodeIndex >> CHUNK_SHIFT; 320 int eindex = eNodeIndex & CHUNK_MASK; 321 setChunkValue(fNodeValue, version, echunk, eindex); 322 setChunkValue(fNodeURI, encoding, echunk, eindex); 323 } 324 } 325 326 // DOM Level 3: sets element TypeInfo 327 public void setTypeInfo(int elementNodeIndex, Object type) { 328 int elementChunk = elementNodeIndex >> CHUNK_SHIFT; 329 int elementIndex = elementNodeIndex & CHUNK_MASK; 330 setChunkValue(fNodeValue, type, elementChunk, elementIndex); 331 } 332 333 /** 334 * DOM Internal 335 * 336 * An attribute specifying the actual encoding of this document. This is 337 * <code>null</code> otherwise. 338 * <br> This attribute represents the property [character encoding scheme] 339 * defined in . 340 */ 341 public void setInputEncoding(int currentEntityDecl, String value){ 342 // get first extra data chunk 343 int nodeIndex = getNodeExtra(currentEntityDecl, false); 344 // get second extra data chunk 345 int extraDataIndex = getNodeExtra(nodeIndex, false); 346 347 int echunk = extraDataIndex >> CHUNK_SHIFT; 348 int eindex = extraDataIndex & CHUNK_MASK; 349 350 setChunkValue(fNodeValue, value, echunk, eindex); 351 352 } 353 354 /** Creates an entity reference node in the table. */ 355 public int createDeferredEntityReference(String name, String baseURI) { 356 357 // create node 358 int nodeIndex = createNode(Node.ENTITY_REFERENCE_NODE); 359 int chunk = nodeIndex >> CHUNK_SHIFT; 360 int index = nodeIndex & CHUNK_MASK; 361 setChunkValue(fNodeName, name, chunk, index); 362 setChunkValue(fNodeValue, baseURI, chunk, index); 363 364 // return node index 365 return nodeIndex; 366 367 } // createDeferredEntityReference(String):int 368 369 370 /** 371 * Creates an element node with a URI in the table and type information. 372 * @deprecated 373 */ 374 @Deprecated 375 public int createDeferredElement(String elementURI, String elementName, 376 Object type) { 377 378 // create node 379 int elementNodeIndex = createNode(Node.ELEMENT_NODE); 380 int elementChunk = elementNodeIndex >> CHUNK_SHIFT; 381 int elementIndex = elementNodeIndex & CHUNK_MASK; 382 setChunkValue(fNodeName, elementName, elementChunk, elementIndex); 383 setChunkValue(fNodeURI, elementURI, elementChunk, elementIndex); 384 setChunkValue(fNodeValue, type, elementChunk, elementIndex); 385 386 // return node index 387 return elementNodeIndex; 388 389 } // createDeferredElement(String,String,Object):int 390 391 /** 392 * Creates an element node in the table. 393 * @deprecated 394 */ 395 @Deprecated 396 public int createDeferredElement(String elementName) { 397 return createDeferredElement(null, elementName); 398 } 399 400 /** 401 * Creates an element node with a URI in the table. 402 */ 403 public int createDeferredElement(String elementURI, String elementName) { 404 405 // create node 406 int elementNodeIndex = createNode(Node.ELEMENT_NODE); 407 int elementChunk = elementNodeIndex >> CHUNK_SHIFT; 408 int elementIndex = elementNodeIndex & CHUNK_MASK; 409 setChunkValue(fNodeName, elementName, elementChunk, elementIndex); 410 setChunkValue(fNodeURI, elementURI, elementChunk, elementIndex); 411 412 // return node index 413 return elementNodeIndex; 414 415 } // createDeferredElement(String,String):int 416 417 418 /** 419 * This method is used by the DOMParser to create attributes. 420 * @param elementNodeIndex 421 * @param attrName 422 * @param attrURI 423 * @param attrValue 424 * @param specified 425 * @param id 426 * @param type 427 * @return int 428 */ 429 public int setDeferredAttribute(int elementNodeIndex, 430 String attrName, 431 String attrURI, 432 String attrValue, 433 boolean specified, 434 boolean id, 435 Object type) { 436 437 // create attribute 438 int attrNodeIndex = createDeferredAttribute(attrName, attrURI, attrValue, specified); 439 int attrChunk = attrNodeIndex >> CHUNK_SHIFT; 440 int attrIndex = attrNodeIndex & CHUNK_MASK; 441 // set attribute's parent to element 442 setChunkIndex(fNodeParent, elementNodeIndex, attrChunk, attrIndex); 443 444 int elementChunk = elementNodeIndex >> CHUNK_SHIFT; 445 int elementIndex = elementNodeIndex & CHUNK_MASK; 446 447 // get element's last attribute 448 int lastAttrNodeIndex = getChunkIndex(fNodeExtra, elementChunk, elementIndex); 449 if (lastAttrNodeIndex != 0) { 450 // add link from new attribute to last attribute 451 setChunkIndex(fNodePrevSib, lastAttrNodeIndex, attrChunk, attrIndex); 452 } 453 // add link from element to new last attribute 454 setChunkIndex(fNodeExtra, attrNodeIndex, elementChunk, elementIndex); 455 456 int extra = getChunkIndex(fNodeExtra, attrChunk, attrIndex); 457 if (id) { 458 extra = extra | ID; 459 setChunkIndex(fNodeExtra, extra, attrChunk, attrIndex); 460 String value = getChunkValue(fNodeValue, attrChunk, attrIndex); 461 putIdentifier(value, elementNodeIndex); 462 } 463 // store type information 464 if (type != null) { 465 int extraDataIndex = createNode(DeferredNode.TYPE_NODE); 466 int echunk = extraDataIndex >> CHUNK_SHIFT; 467 int eindex = extraDataIndex & CHUNK_MASK; 468 469 setChunkIndex(fNodeLastChild, extraDataIndex, attrChunk, attrIndex); 470 setChunkValue(fNodeValue, type, echunk, eindex); 471 } 472 473 // return node index 474 return attrNodeIndex; 475 } 476 477 /** Creates an attribute in the table. */ 478 public int createDeferredAttribute(String attrName, String attrValue, 479 boolean specified) { 480 return createDeferredAttribute(attrName, null, attrValue, specified); 481 } 482 483 /** Creates an attribute with a URI in the table. */ 484 public int createDeferredAttribute(String attrName, String attrURI, 485 String attrValue, boolean specified) { 486 487 // create node 488 int nodeIndex = createNode(NodeImpl.ATTRIBUTE_NODE); 489 int chunk = nodeIndex >> CHUNK_SHIFT; 490 int index = nodeIndex & CHUNK_MASK; 491 setChunkValue(fNodeName, attrName, chunk, index); 492 setChunkValue(fNodeURI, attrURI, chunk, index); 493 setChunkValue(fNodeValue, attrValue, chunk, index); 494 int extra = specified ? SPECIFIED : 0; 495 setChunkIndex(fNodeExtra, extra, chunk, index); 496 497 // return node index 498 return nodeIndex; 499 500 } // createDeferredAttribute(String,String,String,boolean):int 501 502 /** Creates an element definition in the table.*/ 503 public int createDeferredElementDefinition(String elementName) { 504 505 // create node 506 int nodeIndex = createNode(NodeImpl.ELEMENT_DEFINITION_NODE); 507 int chunk = nodeIndex >> CHUNK_SHIFT; 508 int index = nodeIndex & CHUNK_MASK; 509 setChunkValue(fNodeName, elementName, chunk, index); 510 511 // return node index 512 return nodeIndex; 513 514 } // createDeferredElementDefinition(String):int 515 516 /** Creates a text node in the table. */ 517 public int createDeferredTextNode(String data, 518 boolean ignorableWhitespace) { 519 520 // create node 521 int nodeIndex = createNode(Node.TEXT_NODE); 522 int chunk = nodeIndex >> CHUNK_SHIFT; 523 int index = nodeIndex & CHUNK_MASK; 524 setChunkValue(fNodeValue, data, chunk, index); 525 // use extra to store ignorableWhitespace info 526 setChunkIndex(fNodeExtra, ignorableWhitespace ? 1 : 0, chunk, index); 527 528 // return node index 529 return nodeIndex; 530 531 } // createDeferredTextNode(String,boolean):int 532 533 /** Creates a CDATA section node in the table. */ 534 public int createDeferredCDATASection(String data) { 535 536 // create node 537 int nodeIndex = createNode(Node.CDATA_SECTION_NODE); 538 int chunk = nodeIndex >> CHUNK_SHIFT; 539 int index = nodeIndex & CHUNK_MASK; 540 setChunkValue(fNodeValue, data, chunk, index); 541 542 // return node index 543 return nodeIndex; 544 545 } // createDeferredCDATASection(String):int 546 547 /** Creates a processing instruction node in the table. */ 548 public int createDeferredProcessingInstruction(String target, 549 String data) { 550 // create node 551 int nodeIndex = createNode(Node.PROCESSING_INSTRUCTION_NODE); 552 int chunk = nodeIndex >> CHUNK_SHIFT; 553 int index = nodeIndex & CHUNK_MASK; 554 setChunkValue(fNodeName, target, chunk, index); 555 setChunkValue(fNodeValue, data, chunk, index); 556 // return node index 557 return nodeIndex; 558 559 } // createDeferredProcessingInstruction(String,String):int 560 561 /** Creates a comment node in the table. */ 562 public int createDeferredComment(String data) { 563 564 // create node 565 int nodeIndex = createNode(Node.COMMENT_NODE); 566 int chunk = nodeIndex >> CHUNK_SHIFT; 567 int index = nodeIndex & CHUNK_MASK; 568 setChunkValue(fNodeValue, data, chunk, index); 569 570 // return node index 571 return nodeIndex; 572 573 } // createDeferredComment(String):int 574 575 /** Creates a clone of the specified node. */ 576 public int cloneNode(int nodeIndex, boolean deep) { 577 578 // clone immediate node 579 580 int nchunk = nodeIndex >> CHUNK_SHIFT; 581 int nindex = nodeIndex & CHUNK_MASK; 582 int nodeType = fNodeType[nchunk][nindex]; 583 int cloneIndex = createNode((short)nodeType); 584 int cchunk = cloneIndex >> CHUNK_SHIFT; 585 int cindex = cloneIndex & CHUNK_MASK; 586 setChunkValue(fNodeName, fNodeName[nchunk][nindex], cchunk, cindex); 587 setChunkValue(fNodeValue, fNodeValue[nchunk][nindex], cchunk, cindex); 588 setChunkValue(fNodeURI, fNodeURI[nchunk][nindex], cchunk, cindex); 589 int extraIndex = fNodeExtra[nchunk][nindex]; 590 if (extraIndex != -1) { 591 if (nodeType != Node.ATTRIBUTE_NODE && nodeType != Node.TEXT_NODE) { 592 extraIndex = cloneNode(extraIndex, false); 593 } 594 setChunkIndex(fNodeExtra, extraIndex, cchunk, cindex); 595 } 596 597 // clone and attach children 598 if (deep) { 599 int prevIndex = -1; 600 int childIndex = getLastChild(nodeIndex, false); 601 while (childIndex != -1) { 602 int clonedChildIndex = cloneNode(childIndex, deep); 603 insertBefore(cloneIndex, clonedChildIndex, prevIndex); 604 prevIndex = clonedChildIndex; 605 childIndex = getRealPrevSibling(childIndex, false); 606 } 607 608 609 } 610 611 // return cloned node index 612 return cloneIndex; 613 614 } // cloneNode(int,boolean):int 615 616 /** Appends a child to the specified parent in the table. */ 617 public void appendChild(int parentIndex, int childIndex) { 618 619 // append parent index 620 int pchunk = parentIndex >> CHUNK_SHIFT; 621 int pindex = parentIndex & CHUNK_MASK; 622 int cchunk = childIndex >> CHUNK_SHIFT; 623 int cindex = childIndex & CHUNK_MASK; 624 setChunkIndex(fNodeParent, parentIndex, cchunk, cindex); 625 626 // set previous sibling of new child 627 int olast = getChunkIndex(fNodeLastChild, pchunk, pindex); 628 setChunkIndex(fNodePrevSib, olast, cchunk, cindex); 629 630 // update parent's last child 631 setChunkIndex(fNodeLastChild, childIndex, pchunk, pindex); 632 633 } // appendChild(int,int) 634 635 /** Adds an attribute node to the specified element. */ 636 public int setAttributeNode(int elemIndex, int attrIndex) { 637 638 int echunk = elemIndex >> CHUNK_SHIFT; 639 int eindex = elemIndex & CHUNK_MASK; 640 int achunk = attrIndex >> CHUNK_SHIFT; 641 int aindex = attrIndex & CHUNK_MASK; 642 643 // see if this attribute is already here 644 String attrName = getChunkValue(fNodeName, achunk, aindex); 645 int oldAttrIndex = getChunkIndex(fNodeExtra, echunk, eindex); 646 int nextIndex = -1; 647 int oachunk = -1; 648 int oaindex = -1; 649 while (oldAttrIndex != -1) { 650 oachunk = oldAttrIndex >> CHUNK_SHIFT; 651 oaindex = oldAttrIndex & CHUNK_MASK; 652 String oldAttrName = getChunkValue(fNodeName, oachunk, oaindex); 653 if (oldAttrName.equals(attrName)) { 654 break; 655 } 656 nextIndex = oldAttrIndex; 657 oldAttrIndex = getChunkIndex(fNodePrevSib, oachunk, oaindex); 658 } 659 660 // remove old attribute 661 if (oldAttrIndex != -1) { 662 663 // patch links 664 int prevIndex = getChunkIndex(fNodePrevSib, oachunk, oaindex); 665 if (nextIndex == -1) { 666 setChunkIndex(fNodeExtra, prevIndex, echunk, eindex); 667 } 668 else { 669 int pchunk = nextIndex >> CHUNK_SHIFT; 670 int pindex = nextIndex & CHUNK_MASK; 671 setChunkIndex(fNodePrevSib, prevIndex, pchunk, pindex); 672 } 673 674 // remove connections to siblings 675 clearChunkIndex(fNodeType, oachunk, oaindex); 676 clearChunkValue(fNodeName, oachunk, oaindex); 677 clearChunkValue(fNodeValue, oachunk, oaindex); 678 clearChunkIndex(fNodeParent, oachunk, oaindex); 679 clearChunkIndex(fNodePrevSib, oachunk, oaindex); 680 int attrTextIndex = 681 clearChunkIndex(fNodeLastChild, oachunk, oaindex); 682 int atchunk = attrTextIndex >> CHUNK_SHIFT; 683 int atindex = attrTextIndex & CHUNK_MASK; 684 clearChunkIndex(fNodeType, atchunk, atindex); 685 clearChunkValue(fNodeValue, atchunk, atindex); 686 clearChunkIndex(fNodeParent, atchunk, atindex); 687 clearChunkIndex(fNodeLastChild, atchunk, atindex); 688 } 689 690 // add new attribute 691 int prevIndex = getChunkIndex(fNodeExtra, echunk, eindex); 692 setChunkIndex(fNodeExtra, attrIndex, echunk, eindex); 693 setChunkIndex(fNodePrevSib, prevIndex, achunk, aindex); 694 695 // return 696 return oldAttrIndex; 697 698 } // setAttributeNode(int,int):int 699 700 701 /** Adds an attribute node to the specified element. */ 702 public void setIdAttributeNode(int elemIndex, int attrIndex) { 703 704 int chunk = attrIndex >> CHUNK_SHIFT; 705 int index = attrIndex & CHUNK_MASK; 706 int extra = getChunkIndex(fNodeExtra, chunk, index); 707 extra = extra | ID; 708 setChunkIndex(fNodeExtra, extra, chunk, index); 709 710 String value = getChunkValue(fNodeValue, chunk, index); 711 putIdentifier(value, elemIndex); 712 } 713 714 715 /** Sets type of attribute */ 716 public void setIdAttribute(int attrIndex) { 717 718 int chunk = attrIndex >> CHUNK_SHIFT; 719 int index = attrIndex & CHUNK_MASK; 720 int extra = getChunkIndex(fNodeExtra, chunk, index); 721 extra = extra | ID; 722 setChunkIndex(fNodeExtra, extra, chunk, index); 723 } 724 725 /** Inserts a child before the specified node in the table. */ 726 public int insertBefore(int parentIndex, int newChildIndex, int refChildIndex) { 727 728 if (refChildIndex == -1) { 729 appendChild(parentIndex, newChildIndex); 730 return newChildIndex; 731 } 732 733 int nchunk = newChildIndex >> CHUNK_SHIFT; 734 int nindex = newChildIndex & CHUNK_MASK; 735 int rchunk = refChildIndex >> CHUNK_SHIFT; 736 int rindex = refChildIndex & CHUNK_MASK; 737 int previousIndex = getChunkIndex(fNodePrevSib, rchunk, rindex); 738 setChunkIndex(fNodePrevSib, newChildIndex, rchunk, rindex); 739 setChunkIndex(fNodePrevSib, previousIndex, nchunk, nindex); 740 741 return newChildIndex; 742 743 } // insertBefore(int,int,int):int 744 745 /** Sets the last child of the parentIndex to childIndex. */ 746 public void setAsLastChild(int parentIndex, int childIndex) { 747 int pchunk = parentIndex >> CHUNK_SHIFT; 748 int pindex = parentIndex & CHUNK_MASK; 749 setChunkIndex(fNodeLastChild, childIndex, pchunk, pindex); 750 } // setAsLastChild(int,int) 751 752 /** 753 * Returns the parent node of the given node. 754 * <em>Calling this method does not free the parent index.</em> 755 */ 756 public int getParentNode(int nodeIndex) { 757 return getParentNode(nodeIndex, false); 758 } 759 760 /** 761 * Returns the parent node of the given node. 762 * @param free True to free parent node. 763 */ 764 public int getParentNode(int nodeIndex, boolean free) { 765 766 if (nodeIndex == -1) { 767 return -1; 768 } 769 770 int chunk = nodeIndex >> CHUNK_SHIFT; 771 int index = nodeIndex & CHUNK_MASK; 772 return free ? clearChunkIndex(fNodeParent, chunk, index) 773 : getChunkIndex(fNodeParent, chunk, index); 774 775 } // getParentNode(int):int 776 777 /** Returns the last child of the given node. */ 778 public int getLastChild(int nodeIndex) { 779 return getLastChild(nodeIndex, true); 780 } 781 782 /** 783 * Returns the last child of the given node. 784 * @param free True to free child index. 785 */ 786 public int getLastChild(int nodeIndex, boolean free) { 787 788 if (nodeIndex == -1) { 789 return -1; 790 } 791 792 int chunk = nodeIndex >> CHUNK_SHIFT; 793 int index = nodeIndex & CHUNK_MASK; 794 return free ? clearChunkIndex(fNodeLastChild, chunk, index) 795 : getChunkIndex(fNodeLastChild, chunk, index); 796 797 } // getLastChild(int,boolean):int 798 799 /** 800 * Returns the prev sibling of the given node. 801 * This is post-normalization of Text Nodes. 802 */ 803 public int getPrevSibling(int nodeIndex) { 804 return getPrevSibling(nodeIndex, true); 805 } 806 807 /** 808 * Returns the prev sibling of the given node. 809 * @param free True to free sibling index. 810 */ 811 public int getPrevSibling(int nodeIndex, boolean free) { 812 813 if (nodeIndex == -1) { 814 return -1; 815 } 816 817 int chunk = nodeIndex >> CHUNK_SHIFT; 818 int index = nodeIndex & CHUNK_MASK; 819 int type = getChunkIndex(fNodeType, chunk, index); 820 if (type == Node.TEXT_NODE) { 821 do { 822 nodeIndex = getChunkIndex(fNodePrevSib, chunk, index); 823 if (nodeIndex == -1) { 824 break; 825 } 826 chunk = nodeIndex >> CHUNK_SHIFT; 827 index = nodeIndex & CHUNK_MASK; 828 type = getChunkIndex(fNodeType, chunk, index); 829 } while (type == Node.TEXT_NODE); 830 } 831 else { 832 nodeIndex = getChunkIndex(fNodePrevSib, chunk, index); 833 } 834 835 return nodeIndex; 836 837 } // getPrevSibling(int,boolean):int 838 839 /** 840 * Returns the <i>real</i> prev sibling of the given node, 841 * directly from the data structures. Used by TextImpl#getNodeValue() 842 * to normalize values. 843 */ 844 public int getRealPrevSibling(int nodeIndex) { 845 return getRealPrevSibling(nodeIndex, true); 846 } 847 848 /** 849 * Returns the <i>real</i> prev sibling of the given node. 850 * @param free True to free sibling index. 851 */ 852 public int getRealPrevSibling(int nodeIndex, boolean free) { 853 854 if (nodeIndex == -1) { 855 return -1; 856 } 857 858 int chunk = nodeIndex >> CHUNK_SHIFT; 859 int index = nodeIndex & CHUNK_MASK; 860 return free ? clearChunkIndex(fNodePrevSib, chunk, index) 861 : getChunkIndex(fNodePrevSib, chunk, index); 862 863 } // getReadPrevSibling(int,boolean):int 864 865 /** 866 * Returns the index of the element definition in the table 867 * with the specified name index, or -1 if no such definition 868 * exists. 869 */ 870 public int lookupElementDefinition(String elementName) { 871 872 if (fNodeCount > 1) { 873 874 // find doctype 875 int docTypeIndex = -1; 876 int nchunk = 0; 877 int nindex = 0; 878 for (int index = getChunkIndex(fNodeLastChild, nchunk, nindex); 879 index != -1; 880 index = getChunkIndex(fNodePrevSib, nchunk, nindex)) { 881 882 nchunk = index >> CHUNK_SHIFT; 883 nindex = index & CHUNK_MASK; 884 if (getChunkIndex(fNodeType, nchunk, nindex) == Node.DOCUMENT_TYPE_NODE) { 885 docTypeIndex = index; 886 break; 887 } 888 } 889 890 // find element definition 891 if (docTypeIndex == -1) { 892 return -1; 893 } 894 nchunk = docTypeIndex >> CHUNK_SHIFT; 895 nindex = docTypeIndex & CHUNK_MASK; 896 for (int index = getChunkIndex(fNodeLastChild, nchunk, nindex); 897 index != -1; 898 index = getChunkIndex(fNodePrevSib, nchunk, nindex)) { 899 900 nchunk = index >> CHUNK_SHIFT; 901 nindex = index & CHUNK_MASK; 902 if (getChunkIndex(fNodeType, nchunk, nindex) == 903 NodeImpl.ELEMENT_DEFINITION_NODE 904 && getChunkValue(fNodeName, nchunk, nindex) == elementName) { 905 return index; 906 } 907 } 908 } 909 910 return -1; 911 912 } // lookupElementDefinition(String):int 913 914 /** Instantiates the requested node object. */ 915 public DeferredNode getNodeObject(int nodeIndex) { 916 917 // is there anything to do? 918 if (nodeIndex == -1) { 919 return null; 920 } 921 922 // get node type 923 int chunk = nodeIndex >> CHUNK_SHIFT; 924 int index = nodeIndex & CHUNK_MASK; 925 int type = getChunkIndex(fNodeType, chunk, index); 926 if (type != Node.TEXT_NODE && type != Node.CDATA_SECTION_NODE) { 927 clearChunkIndex(fNodeType, chunk, index); 928 } 929 930 // create new node 931 DeferredNode node = null; 932 switch (type) { 933 934 // 935 // Standard DOM node types 936 // 937 938 case Node.ATTRIBUTE_NODE: { 939 if (fNamespacesEnabled) { 940 node = new DeferredAttrNSImpl(this, nodeIndex); 941 } else { 942 node = new DeferredAttrImpl(this, nodeIndex); 943 } 944 break; 945 } 946 947 case Node.CDATA_SECTION_NODE: { 948 node = new DeferredCDATASectionImpl(this, nodeIndex); 949 break; 950 } 951 952 case Node.COMMENT_NODE: { 953 node = new DeferredCommentImpl(this, nodeIndex); 954 break; 955 } 956 957 // NOTE: Document fragments can never be "fast". 958 // 959 // The parser will never ask to create a document 960 // fragment during the parse. Document fragments 961 // are used by the application *after* the parse. 962 // 963 // case Node.DOCUMENT_FRAGMENT_NODE: { break; } 964 case Node.DOCUMENT_NODE: { 965 // this node is never "fast" 966 node = this; 967 break; 968 } 969 970 case Node.DOCUMENT_TYPE_NODE: { 971 node = new DeferredDocumentTypeImpl(this, nodeIndex); 972 // save the doctype node 973 docType = (DocumentTypeImpl)node; 974 break; 975 } 976 977 case Node.ELEMENT_NODE: { 978 979 if (DEBUG_IDS) { 980 System.out.println("getNodeObject(ELEMENT_NODE): "+nodeIndex); 981 } 982 983 // create node 984 if (fNamespacesEnabled) { 985 node = new DeferredElementNSImpl(this, nodeIndex); 986 } else { 987 node = new DeferredElementImpl(this, nodeIndex); 988 } 989 990 // check to see if this element needs to be 991 // registered for its ID attributes 992 if (fIdElement != null) { 993 int idIndex = binarySearch(fIdElement, 0, 994 fIdCount-1, nodeIndex); 995 while (idIndex != -1) { 996 997 if (DEBUG_IDS) { 998 System.out.println(" id index: "+idIndex); 999 System.out.println(" fIdName["+idIndex+ 1000 "]: "+fIdName[idIndex]); 1001 } 1002 1003 // register ID 1004 String name = fIdName[idIndex]; 1005 if (name != null) { 1006 if (DEBUG_IDS) { 1007 System.out.println(" name: "+name); 1008 System.out.print("getNodeObject()#"); 1009 } 1010 putIdentifier0(name, (Element)node); 1011 fIdName[idIndex] = null; 1012 } 1013 1014 // continue if there are more IDs for 1015 // this element 1016 if (idIndex + 1 < fIdCount && 1017 fIdElement[idIndex + 1] == nodeIndex) { 1018 idIndex++; 1019 } 1020 else { 1021 idIndex = -1; 1022 } 1023 } 1024 } 1025 break; 1026 } 1027 1028 case Node.ENTITY_NODE: { 1029 node = new DeferredEntityImpl(this, nodeIndex); 1030 break; 1031 } 1032 1033 case Node.ENTITY_REFERENCE_NODE: { 1034 node = new DeferredEntityReferenceImpl(this, nodeIndex); 1035 break; 1036 } 1037 1038 case Node.NOTATION_NODE: { 1039 node = new DeferredNotationImpl(this, nodeIndex); 1040 break; 1041 } 1042 1043 case Node.PROCESSING_INSTRUCTION_NODE: { 1044 node = new DeferredProcessingInstructionImpl(this, nodeIndex); 1045 break; 1046 } 1047 1048 case Node.TEXT_NODE: { 1049 node = new DeferredTextImpl(this, nodeIndex); 1050 break; 1051 } 1052 1053 // 1054 // non-standard DOM node types 1055 // 1056 1057 case NodeImpl.ELEMENT_DEFINITION_NODE: { 1058 node = new DeferredElementDefinitionImpl(this, nodeIndex); 1059 break; 1060 } 1061 1062 default: { 1063 throw new IllegalArgumentException("type: "+type); 1064 } 1065 1066 } // switch node type 1067 1068 // store and return 1069 if (node != null) { 1070 return node; 1071 } 1072 1073 // error 1074 throw new IllegalArgumentException(); 1075 1076 } // createNodeObject(int):Node 1077 1078 /** Returns the name of the given node. */ 1079 public String getNodeName(int nodeIndex) { 1080 return getNodeName(nodeIndex, true); 1081 } // getNodeNameString(int):String 1082 1083 /** 1084 * Returns the name of the given node. 1085 * @param free True to free the string index. 1086 */ 1087 public String getNodeName(int nodeIndex, boolean free) { 1088 1089 if (nodeIndex == -1) { 1090 return null; 1091 } 1092 1093 int chunk = nodeIndex >> CHUNK_SHIFT; 1094 int index = nodeIndex & CHUNK_MASK; 1095 return free ? clearChunkValue(fNodeName, chunk, index) 1096 : getChunkValue(fNodeName, chunk, index); 1097 1098 } // getNodeName(int,boolean):String 1099 1100 /** Returns the real value of the given node. */ 1101 public String getNodeValueString(int nodeIndex) { 1102 return getNodeValueString(nodeIndex, true); 1103 } // getNodeValueString(int):String 1104 1105 /** 1106 * Returns the real value of the given node. 1107 * @param free True to free the string index. 1108 */ 1109 public String getNodeValueString(int nodeIndex, boolean free) { 1110 1111 if (nodeIndex == -1) { 1112 return null; 1113 } 1114 1115 int chunk = nodeIndex >> CHUNK_SHIFT; 1116 int index = nodeIndex & CHUNK_MASK; 1117 String value = free ? clearChunkValue(fNodeValue, chunk, index) 1118 : getChunkValue(fNodeValue, chunk, index); 1119 if (value == null) { 1120 return null; 1121 } 1122 1123 int type = getChunkIndex(fNodeType, chunk, index); 1124 if (type == Node.TEXT_NODE) { 1125 int prevSib = getRealPrevSibling(nodeIndex); 1126 if (prevSib != -1 && 1127 getNodeType(prevSib, false) == Node.TEXT_NODE) { 1128 // append data that is stored in fNodeValue 1129 // REVISIT: for text nodes it works differently than for CDATA 1130 // nodes. 1131 fStrChunks.add(value); 1132 do { 1133 // go in reverse order: find last child, then 1134 // its previous sibling, etc 1135 chunk = prevSib >> CHUNK_SHIFT; 1136 index = prevSib & CHUNK_MASK; 1137 value = getChunkValue(fNodeValue, chunk, index); 1138 fStrChunks.add(value); 1139 prevSib = getChunkIndex(fNodePrevSib, chunk, index); 1140 if (prevSib == -1) { 1141 break; 1142 } 1143 } while (getNodeType(prevSib, false) == Node.TEXT_NODE); 1144 1145 int chunkCount = fStrChunks.size(); 1146 1147 // add to the buffer in the correct order. 1148 for (int i = chunkCount - 1; i >= 0; i--) { 1149 fBufferStr.append(fStrChunks.get(i)); 1150 } 1151 1152 value = fBufferStr.toString(); 1153 fStrChunks.clear(); 1154 fBufferStr.setLength(0); 1155 return value; 1156 } 1157 } 1158 else if (type == Node.CDATA_SECTION_NODE) { 1159 // find if any other data stored in children 1160 int child = getLastChild(nodeIndex, false); 1161 if (child !=-1) { 1162 // append data that is stored in fNodeValue 1163 fBufferStr.append(value); 1164 while (child !=-1) { 1165 // go in reverse order: find last child, then 1166 // its previous sibling, etc 1167 chunk = child >> CHUNK_SHIFT; 1168 index = child & CHUNK_MASK; 1169 value = getChunkValue(fNodeValue, chunk, index); 1170 fStrChunks.add(value); 1171 child = getChunkIndex(fNodePrevSib, chunk, index); 1172 } 1173 // add to the buffer in the correct order. 1174 for (int i=fStrChunks.size()-1; i>=0; i--) { 1175 fBufferStr.append(fStrChunks.get(i)); 1176 } 1177 1178 value = fBufferStr.toString(); 1179 fStrChunks.clear(); 1180 fBufferStr.setLength(0); 1181 return value; 1182 } 1183 } 1184 1185 return value; 1186 1187 } // getNodeValueString(int,boolean):String 1188 1189 /** 1190 * Returns the value of the given node. 1191 */ 1192 public String getNodeValue(int nodeIndex) { 1193 return getNodeValue(nodeIndex, true); 1194 } 1195 1196 /** 1197 * Clears the type info that is stored in the fNodeValue array 1198 * @param nodeIndex 1199 * @return Object - type information for the attribute/element node 1200 */ 1201 public Object getTypeInfo(int nodeIndex) { 1202 if (nodeIndex == -1) { 1203 return null; 1204 } 1205 1206 int chunk = nodeIndex >> CHUNK_SHIFT; 1207 int index = nodeIndex & CHUNK_MASK; 1208 1209 1210 Object value = fNodeValue[chunk] != null ? fNodeValue[chunk][index] : null; 1211 if (value != null) { 1212 fNodeValue[chunk][index] = null; 1213 RefCount c = (RefCount) fNodeValue[chunk][CHUNK_SIZE]; 1214 c.fCount--; 1215 if (c.fCount == 0) { 1216 fNodeValue[chunk] = null; 1217 } 1218 } 1219 return value; 1220 } 1221 1222 /** 1223 * Returns the value of the given node. 1224 * @param free True to free the value index. 1225 */ 1226 public String getNodeValue(int nodeIndex, boolean free) { 1227 1228 if (nodeIndex == -1) { 1229 return null; 1230 } 1231 1232 int chunk = nodeIndex >> CHUNK_SHIFT; 1233 int index = nodeIndex & CHUNK_MASK; 1234 return free ? clearChunkValue(fNodeValue, chunk, index) 1235 : getChunkValue(fNodeValue, chunk, index); 1236 1237 } // getNodeValue(int,boolean):String 1238 1239 /** 1240 * Returns the extra info of the given node. 1241 * Used by AttrImpl to store specified value (1 == true). 1242 */ 1243 public int getNodeExtra(int nodeIndex) { 1244 return getNodeExtra(nodeIndex, true); 1245 } 1246 1247 /** 1248 * Returns the extra info of the given node. 1249 * @param free True to free the value index. 1250 */ 1251 public int getNodeExtra(int nodeIndex, boolean free) { 1252 1253 if (nodeIndex == -1) { 1254 return -1; 1255 } 1256 1257 int chunk = nodeIndex >> CHUNK_SHIFT; 1258 int index = nodeIndex & CHUNK_MASK; 1259 return free ? clearChunkIndex(fNodeExtra, chunk, index) 1260 : getChunkIndex(fNodeExtra, chunk, index); 1261 1262 } // getNodeExtra(int,boolean):int 1263 1264 /** Returns the type of the given node. */ 1265 public short getNodeType(int nodeIndex) { 1266 return getNodeType(nodeIndex, true); 1267 } 1268 1269 /** 1270 * Returns the type of the given node. 1271 * @param free True to free type index. 1272 */ 1273 public short getNodeType(int nodeIndex, boolean free) { 1274 1275 if (nodeIndex == -1) { 1276 return -1; 1277 } 1278 1279 int chunk = nodeIndex >> CHUNK_SHIFT; 1280 int index = nodeIndex & CHUNK_MASK; 1281 return free ? (short)clearChunkIndex(fNodeType, chunk, index) 1282 : (short)getChunkIndex(fNodeType, chunk, index); 1283 1284 } // getNodeType(int):int 1285 1286 /** Returns the attribute value of the given name. */ 1287 public String getAttribute(int elemIndex, String name) { 1288 if (elemIndex == -1 || name == null) { 1289 return null; 1290 } 1291 int echunk = elemIndex >> CHUNK_SHIFT; 1292 int eindex = elemIndex & CHUNK_MASK; 1293 int attrIndex = getChunkIndex(fNodeExtra, echunk, eindex); 1294 while (attrIndex != -1) { 1295 int achunk = attrIndex >> CHUNK_SHIFT; 1296 int aindex = attrIndex & CHUNK_MASK; 1297 if (getChunkValue(fNodeName, achunk, aindex) == name) { 1298 return getChunkValue(fNodeValue, achunk, aindex); 1299 } 1300 attrIndex = getChunkIndex(fNodePrevSib, achunk, aindex); 1301 } 1302 return null; 1303 } 1304 1305 /** Returns the URI of the given node. */ 1306 public String getNodeURI(int nodeIndex) { 1307 return getNodeURI(nodeIndex, true); 1308 } 1309 1310 /** 1311 * Returns the URI of the given node. 1312 * @param free True to free URI index. 1313 */ 1314 public String getNodeURI(int nodeIndex, boolean free) { 1315 1316 if (nodeIndex == -1) { 1317 return null; 1318 } 1319 1320 int chunk = nodeIndex >> CHUNK_SHIFT; 1321 int index = nodeIndex & CHUNK_MASK; 1322 return free ? clearChunkValue(fNodeURI, chunk, index) 1323 : getChunkValue(fNodeURI, chunk, index); 1324 1325 } // getNodeURI(int,int):String 1326 1327 // identifier maintenance 1328 1329 /** Registers an identifier name with a specified element node. */ 1330 public void putIdentifier(String name, int elementNodeIndex) { 1331 1332 if (DEBUG_IDS) { 1333 System.out.println("putIdentifier(" + name + ", " 1334 + elementNodeIndex + ')' + " // " + 1335 getChunkValue(fNodeName, 1336 elementNodeIndex >> CHUNK_SHIFT, 1337 elementNodeIndex & CHUNK_MASK)); 1338 } 1339 1340 // initialize arrays 1341 if (fIdName == null) { 1342 fIdName = new String[64]; 1343 fIdElement = new int[64]; 1344 } 1345 1346 // resize arrays 1347 if (fIdCount == fIdName.length) { 1348 String idName[] = new String[fIdCount * 2]; 1349 System.arraycopy(fIdName, 0, idName, 0, fIdCount); 1350 fIdName = idName; 1351 1352 int idElement[] = new int[idName.length]; 1353 System.arraycopy(fIdElement, 0, idElement, 0, fIdCount); 1354 fIdElement = idElement; 1355 } 1356 1357 // store identifier 1358 fIdName[fIdCount] = name; 1359 fIdElement[fIdCount] = elementNodeIndex; 1360 fIdCount++; 1361 1362 } // putIdentifier(String,int) 1363 1364 // 1365 // DEBUG 1366 // 1367 1368 /** Prints out the tables. */ 1369 public void print() { 1370 1371 if (DEBUG_PRINT_REF_COUNTS) { 1372 System.out.print("num\t"); 1373 System.out.print("type\t"); 1374 System.out.print("name\t"); 1375 System.out.print("val\t"); 1376 System.out.print("par\t"); 1377 System.out.print("lch\t"); 1378 System.out.print("psib"); 1379 System.out.println(); 1380 for (int i = 0; i < fNodeType.length; i++) { 1381 if (fNodeType[i] != null) { 1382 // separator 1383 System.out.print("--------"); 1384 System.out.print("--------"); 1385 System.out.print("--------"); 1386 System.out.print("--------"); 1387 System.out.print("--------"); 1388 System.out.print("--------"); 1389 System.out.print("--------"); 1390 System.out.println(); 1391 1392 // ref count 1393 System.out.print(i); 1394 System.out.print('\t'); 1395 switch (fNodeType[i][CHUNK_SIZE]) { 1396 case DocumentImpl.ELEMENT_DEFINITION_NODE: { System.out.print("EDef"); break; } 1397 case Node.DOCUMENT_NODE: { System.out.print("Doc"); break; } 1398 case Node.DOCUMENT_TYPE_NODE: { System.out.print("DType"); break; } 1399 case Node.COMMENT_NODE: { System.out.print("Com"); break; } 1400 case Node.PROCESSING_INSTRUCTION_NODE: { System.out.print("PI"); break; } 1401 case Node.ELEMENT_NODE: { System.out.print("Elem"); break; } 1402 case Node.ENTITY_NODE: { System.out.print("Ent"); break; } 1403 case Node.ENTITY_REFERENCE_NODE: { System.out.print("ERef"); break; } 1404 case Node.TEXT_NODE: { System.out.print("Text"); break; } 1405 case Node.ATTRIBUTE_NODE: { System.out.print("Attr"); break; } 1406 case DeferredNode.TYPE_NODE: { System.out.print("TypeInfo"); break; } 1407 default: { System.out.print("?"+fNodeType[i][CHUNK_SIZE]); } 1408 } 1409 System.out.print('\t'); 1410 System.out.print(fNodeName[i][CHUNK_SIZE]); 1411 System.out.print('\t'); 1412 System.out.print(fNodeValue[i][CHUNK_SIZE]); 1413 System.out.print('\t'); 1414 System.out.print(fNodeURI[i][CHUNK_SIZE]); 1415 System.out.print('\t'); 1416 System.out.print(fNodeParent[i][CHUNK_SIZE]); 1417 System.out.print('\t'); 1418 System.out.print(fNodeLastChild[i][CHUNK_SIZE]); 1419 System.out.print('\t'); 1420 System.out.print(fNodePrevSib[i][CHUNK_SIZE]); 1421 System.out.print('\t'); 1422 System.out.print(fNodeExtra[i][CHUNK_SIZE]); 1423 System.out.println(); 1424 } 1425 } 1426 } 1427 1428 if (DEBUG_PRINT_TABLES) { 1429 // This assumes that the document is small 1430 System.out.println("# start table"); 1431 for (int i = 0; i < fNodeCount; i++) { 1432 int chunk = i >> CHUNK_SHIFT; 1433 int index = i & CHUNK_MASK; 1434 if (i % 10 == 0) { 1435 System.out.print("num\t"); 1436 System.out.print("type\t"); 1437 System.out.print("name\t"); 1438 System.out.print("val\t"); 1439 System.out.print("uri\t"); 1440 System.out.print("par\t"); 1441 System.out.print("lch\t"); 1442 System.out.print("psib\t"); 1443 System.out.print("xtra"); 1444 System.out.println(); 1445 } 1446 System.out.print(i); 1447 System.out.print('\t'); 1448 switch (getChunkIndex(fNodeType, chunk, index)) { 1449 case DocumentImpl.ELEMENT_DEFINITION_NODE: { System.out.print("EDef"); break; } 1450 case Node.DOCUMENT_NODE: { System.out.print("Doc"); break; } 1451 case Node.DOCUMENT_TYPE_NODE: { System.out.print("DType"); break; } 1452 case Node.COMMENT_NODE: { System.out.print("Com"); break; } 1453 case Node.PROCESSING_INSTRUCTION_NODE: { System.out.print("PI"); break; } 1454 case Node.ELEMENT_NODE: { System.out.print("Elem"); break; } 1455 case Node.ENTITY_NODE: { System.out.print("Ent"); break; } 1456 case Node.ENTITY_REFERENCE_NODE: { System.out.print("ERef"); break; } 1457 case Node.TEXT_NODE: { System.out.print("Text"); break; } 1458 case Node.ATTRIBUTE_NODE: { System.out.print("Attr"); break; } 1459 case DeferredNode.TYPE_NODE: { System.out.print("TypeInfo"); break; } 1460 default: { System.out.print("?"+getChunkIndex(fNodeType, chunk, index)); } 1461 } 1462 System.out.print('\t'); 1463 System.out.print(getChunkValue(fNodeName, chunk, index)); 1464 System.out.print('\t'); 1465 System.out.print(getNodeValue(chunk, index)); 1466 System.out.print('\t'); 1467 System.out.print(getChunkValue(fNodeURI, chunk, index)); 1468 System.out.print('\t'); 1469 System.out.print(getChunkIndex(fNodeParent, chunk, index)); 1470 System.out.print('\t'); 1471 System.out.print(getChunkIndex(fNodeLastChild, chunk, index)); 1472 System.out.print('\t'); 1473 System.out.print(getChunkIndex(fNodePrevSib, chunk, index)); 1474 System.out.print('\t'); 1475 System.out.print(getChunkIndex(fNodeExtra, chunk, index)); 1476 System.out.println(); 1477 } 1478 System.out.println("# end table"); 1479 } 1480 1481 } // print() 1482 1483 // 1484 // DeferredNode methods 1485 // 1486 1487 /** Returns the node index. */ 1488 public int getNodeIndex() { 1489 return 0; 1490 } 1491 1492 // 1493 // Protected methods 1494 // 1495 1496 /** Synchronizes the node's data. */ 1497 protected void synchronizeData() { 1498 1499 // no need to sync in the future 1500 needsSyncData(false); 1501 1502 // fluff up enough nodes to fill identifiers hash 1503 if (fIdElement != null) { 1504 1505 // REVISIT: There has to be a more efficient way of 1506 // doing this. But keep in mind that the 1507 // tree can have been altered and re-ordered 1508 // before all of the element nodes with ID 1509 // attributes have been registered. For now 1510 // this is reasonable and safe. -Ac 1511 1512 IntVector path = new IntVector(); 1513 for (int i = 0; i < fIdCount; i++) { 1514 1515 // ignore if it's already been registered 1516 int elementNodeIndex = fIdElement[i]; 1517 String idName = fIdName[i]; 1518 if (idName == null) { 1519 continue; 1520 } 1521 1522 // find path from this element to the root 1523 path.removeAllElements(); 1524 int index = elementNodeIndex; 1525 do { 1526 path.addElement(index); 1527 int pchunk = index >> CHUNK_SHIFT; 1528 int pindex = index & CHUNK_MASK; 1529 index = getChunkIndex(fNodeParent, pchunk, pindex); 1530 } while (index != -1); 1531 1532 // Traverse path (backwards), fluffing the elements 1533 // along the way. When this loop finishes, "place" 1534 // will contain the reference to the element node 1535 // we're interested in. -Ac 1536 Node place = this; 1537 for (int j = path.size() - 2; j >= 0; j--) { 1538 index = path.elementAt(j); 1539 Node child = place.getLastChild(); 1540 while (child != null) { 1541 if (child instanceof DeferredNode) { 1542 int nodeIndex = 1543 ((DeferredNode)child).getNodeIndex(); 1544 if (nodeIndex == index) { 1545 place = child; 1546 break; 1547 } 1548 } 1549 child = child.getPreviousSibling(); 1550 } 1551 } 1552 1553 // register the element 1554 Element element = (Element)place; 1555 putIdentifier0(idName, element); 1556 fIdName[i] = null; 1557 1558 // see if there are more IDs on this element 1559 while (i + 1 < fIdCount && 1560 fIdElement[i + 1] == elementNodeIndex) { 1561 idName = fIdName[++i]; 1562 if (idName == null) { 1563 continue; 1564 } 1565 putIdentifier0(idName, element); 1566 } 1567 } 1568 1569 } // if identifiers 1570 1571 } // synchronizeData() 1572 1573 /** 1574 * Synchronizes the node's children with the internal structure. 1575 * Fluffing the children at once solves a lot of work to keep 1576 * the two structures in sync. The problem gets worse when 1577 * editing the tree -- this makes it a lot easier. 1578 */ 1579 protected void synchronizeChildren() { 1580 1581 if (needsSyncData()) { 1582 synchronizeData(); 1583 /* 1584 * when we have elements with IDs this method is being recursively 1585 * called from synchronizeData, in which case we've already gone 1586 * through the following and we can now simply stop here. 1587 */ 1588 if (!needsSyncChildren()) { 1589 return; 1590 } 1591 } 1592 1593 // we don't want to generate any event for this so turn them off 1594 boolean orig = mutationEvents; 1595 mutationEvents = false; 1596 1597 // no need to sync in the future 1598 needsSyncChildren(false); 1599 1600 getNodeType(0); 1601 1602 // create children and link them as siblings 1603 ChildNode first = null; 1604 ChildNode last = null; 1605 for (int index = getLastChild(0); 1606 index != -1; 1607 index = getPrevSibling(index)) { 1608 1609 ChildNode node = (ChildNode)getNodeObject(index); 1610 if (last == null) { 1611 last = node; 1612 } 1613 else { 1614 first.previousSibling = node; 1615 } 1616 node.ownerNode = this; 1617 node.isOwned(true); 1618 node.nextSibling = first; 1619 first = node; 1620 1621 // save doctype and document type 1622 int type = node.getNodeType(); 1623 if (type == Node.ELEMENT_NODE) { 1624 docElement = (ElementImpl)node; 1625 } 1626 else if (type == Node.DOCUMENT_TYPE_NODE) { 1627 docType = (DocumentTypeImpl)node; 1628 } 1629 } 1630 1631 if (first != null) { 1632 firstChild = first; 1633 first.isFirstChild(true); 1634 lastChild(last); 1635 } 1636 1637 // set mutation events flag back to its original value 1638 mutationEvents = orig; 1639 1640 } // synchronizeChildren() 1641 1642 /** 1643 * Synchronizes the node's children with the internal structure. 1644 * Fluffing the children at once solves a lot of work to keep 1645 * the two structures in sync. The problem gets worse when 1646 * editing the tree -- this makes it a lot easier. 1647 * This is not directly used in this class but this method is 1648 * here so that it can be shared by all deferred subclasses of AttrImpl. 1649 */ 1650 protected final void synchronizeChildren(AttrImpl a, int nodeIndex) { 1651 1652 // we don't want to generate any event for this so turn them off 1653 boolean orig = getMutationEvents(); 1654 setMutationEvents(false); 1655 1656 // no need to sync in the future 1657 a.needsSyncChildren(false); 1658 1659 // create children and link them as siblings or simply store the value 1660 // as a String if all we have is one piece of text 1661 int last = getLastChild(nodeIndex); 1662 int prev = getPrevSibling(last); 1663 if (prev == -1) { 1664 a.value = getNodeValueString(nodeIndex); 1665 a.hasStringValue(true); 1666 } 1667 else { 1668 ChildNode firstNode = null; 1669 ChildNode lastNode = null; 1670 for (int index = last; index != -1; 1671 index = getPrevSibling(index)) { 1672 1673 ChildNode node = (ChildNode) getNodeObject(index); 1674 if (lastNode == null) { 1675 lastNode = node; 1676 } 1677 else { 1678 firstNode.previousSibling = node; 1679 } 1680 node.ownerNode = a; 1681 node.isOwned(true); 1682 node.nextSibling = firstNode; 1683 firstNode = node; 1684 } 1685 if (lastNode != null) { 1686 a.value = firstNode; // firstChild = firstNode 1687 firstNode.isFirstChild(true); 1688 a.lastChild(lastNode); 1689 } 1690 a.hasStringValue(false); 1691 } 1692 1693 // set mutation events flag back to its original value 1694 setMutationEvents(orig); 1695 1696 } // synchronizeChildren(AttrImpl,int):void 1697 1698 1699 /** 1700 * Synchronizes the node's children with the internal structure. 1701 * Fluffing the children at once solves a lot of work to keep 1702 * the two structures in sync. The problem gets worse when 1703 * editing the tree -- this makes it a lot easier. 1704 * This is not directly used in this class but this method is 1705 * here so that it can be shared by all deferred subclasses of ParentNode. 1706 */ 1707 protected final void synchronizeChildren(ParentNode p, int nodeIndex) { 1708 1709 // we don't want to generate any event for this so turn them off 1710 boolean orig = getMutationEvents(); 1711 setMutationEvents(false); 1712 1713 // no need to sync in the future 1714 p.needsSyncChildren(false); 1715 1716 // create children and link them as siblings 1717 ChildNode firstNode = null; 1718 ChildNode lastNode = null; 1719 for (int index = getLastChild(nodeIndex); 1720 index != -1; 1721 index = getPrevSibling(index)) { 1722 1723 ChildNode node = (ChildNode) getNodeObject(index); 1724 if (lastNode == null) { 1725 lastNode = node; 1726 } 1727 else { 1728 firstNode.previousSibling = node; 1729 } 1730 node.ownerNode = p; 1731 node.isOwned(true); 1732 node.nextSibling = firstNode; 1733 firstNode = node; 1734 } 1735 if (lastNode != null) { 1736 p.firstChild = firstNode; 1737 firstNode.isFirstChild(true); 1738 p.lastChild(lastNode); 1739 } 1740 1741 // set mutation events flag back to its original value 1742 setMutationEvents(orig); 1743 1744 } // synchronizeChildren(ParentNode,int):void 1745 1746 // utility methods 1747 1748 /** Ensures that the internal tables are large enough. */ 1749 protected void ensureCapacity(int chunk) { 1750 if (fNodeType == null) { 1751 // create buffers 1752 fNodeType = new int[INITIAL_CHUNK_COUNT][]; 1753 fNodeName = new Object[INITIAL_CHUNK_COUNT][]; 1754 fNodeValue = new Object[INITIAL_CHUNK_COUNT][]; 1755 fNodeParent = new int[INITIAL_CHUNK_COUNT][]; 1756 fNodeLastChild = new int[INITIAL_CHUNK_COUNT][]; 1757 fNodePrevSib = new int[INITIAL_CHUNK_COUNT][]; 1758 fNodeURI = new Object[INITIAL_CHUNK_COUNT][]; 1759 fNodeExtra = new int[INITIAL_CHUNK_COUNT][]; 1760 } 1761 else if (fNodeType.length <= chunk) { 1762 // resize the tables 1763 int newsize = chunk * 2; 1764 1765 int[][] newArray = new int[newsize][]; 1766 System.arraycopy(fNodeType, 0, newArray, 0, chunk); 1767 fNodeType = newArray; 1768 1769 Object[][] newStrArray = new Object[newsize][]; 1770 System.arraycopy(fNodeName, 0, newStrArray, 0, chunk); 1771 fNodeName = newStrArray; 1772 1773 newStrArray = new Object[newsize][]; 1774 System.arraycopy(fNodeValue, 0, newStrArray, 0, chunk); 1775 fNodeValue = newStrArray; 1776 1777 newArray = new int[newsize][]; 1778 System.arraycopy(fNodeParent, 0, newArray, 0, chunk); 1779 fNodeParent = newArray; 1780 1781 newArray = new int[newsize][]; 1782 System.arraycopy(fNodeLastChild, 0, newArray, 0, chunk); 1783 fNodeLastChild = newArray; 1784 1785 newArray = new int[newsize][]; 1786 System.arraycopy(fNodePrevSib, 0, newArray, 0, chunk); 1787 fNodePrevSib = newArray; 1788 1789 newStrArray = new Object[newsize][]; 1790 System.arraycopy(fNodeURI, 0, newStrArray, 0, chunk); 1791 fNodeURI = newStrArray; 1792 1793 newArray = new int[newsize][]; 1794 System.arraycopy(fNodeExtra, 0, newArray, 0, chunk); 1795 fNodeExtra = newArray; 1796 } 1797 else if (fNodeType[chunk] != null) { 1798 // Done - there's sufficient capacity 1799 return; 1800 } 1801 1802 // create new chunks 1803 createChunk(fNodeType, chunk); 1804 createChunk(fNodeName, chunk); 1805 createChunk(fNodeValue, chunk); 1806 createChunk(fNodeParent, chunk); 1807 createChunk(fNodeLastChild, chunk); 1808 createChunk(fNodePrevSib, chunk); 1809 createChunk(fNodeURI, chunk); 1810 createChunk(fNodeExtra, chunk); 1811 1812 // Done 1813 return; 1814 1815 } // ensureCapacity(int,int) 1816 1817 /** Creates a node of the specified type. */ 1818 protected int createNode(short nodeType) { 1819 // ensure tables are large enough 1820 int chunk = fNodeCount >> CHUNK_SHIFT; 1821 int index = fNodeCount & CHUNK_MASK; 1822 ensureCapacity(chunk); 1823 1824 // initialize node 1825 setChunkIndex(fNodeType, nodeType, chunk, index); 1826 1827 // return node index number 1828 return fNodeCount++; 1829 1830 } // createNode(short):int 1831 1832 /** 1833 * Performs a binary search for a target value in an array of 1834 * values. The array of values must be in ascending sorted order 1835 * before calling this method and all array values must be 1836 * non-negative. 1837 * 1838 * @param values The array of values to search. 1839 * @param start The starting offset of the search. 1840 * @param end The ending offset of the search. 1841 * @param target The target value. 1842 * 1843 * @return This function will return the <i>first</i> occurrence 1844 * of the target value, or -1 if the target value cannot 1845 * be found. 1846 */ 1847 protected static int binarySearch(final int values[], 1848 int start, int end, int target) { 1849 1850 if (DEBUG_IDS) { 1851 System.out.println("binarySearch(), target: "+target); 1852 } 1853 1854 // look for target value 1855 while (start <= end) { 1856 1857 // is this the one we're looking for? 1858 int middle = (start + end) >>> 1; 1859 int value = values[middle]; 1860 if (DEBUG_IDS) { 1861 System.out.print(" value: "+value+", target: "+target+" // "); 1862 print(values, start, end, middle, target); 1863 } 1864 if (value == target) { 1865 while (middle > 0 && values[middle - 1] == target) { 1866 middle--; 1867 } 1868 if (DEBUG_IDS) { 1869 System.out.println("FOUND AT "+middle); 1870 } 1871 return middle; 1872 } 1873 1874 // is this point higher or lower? 1875 if (value > target) { 1876 end = middle - 1; 1877 } 1878 else { 1879 start = middle + 1; 1880 } 1881 1882 } // while 1883 1884 // not found 1885 if (DEBUG_IDS) { 1886 System.out.println("NOT FOUND!"); 1887 } 1888 return -1; 1889 1890 } // binarySearch(int[],int,int,int):int 1891 1892 // 1893 // Private methods 1894 // 1895 private static final int[] INIT_ARRAY = new int[CHUNK_SIZE + 1]; 1896 static { 1897 for (int i = 0; i < CHUNK_SIZE; i++) { 1898 INIT_ARRAY[i] = -1; 1899 } 1900 } 1901 /** Creates the specified chunk in the given array of chunks. */ 1902 private final void createChunk(int data[][], int chunk) { 1903 data[chunk] = new int[CHUNK_SIZE + 1]; 1904 System.arraycopy(INIT_ARRAY, 0, data[chunk], 0, CHUNK_SIZE); 1905 } 1906 1907 static final class RefCount { 1908 int fCount; 1909 } 1910 1911 private final void createChunk(Object data[][], int chunk) { 1912 data[chunk] = new Object[CHUNK_SIZE + 1]; 1913 data[chunk][CHUNK_SIZE] = new RefCount(); 1914 } 1915 1916 /** 1917 * Sets the specified value in the given of data at the chunk and index. 1918 * 1919 * @return Returns the old value. 1920 */ 1921 private final int setChunkIndex(int data[][], int value, 1922 int chunk, int index) { 1923 if (value == -1) { 1924 return clearChunkIndex(data, chunk, index); 1925 } 1926 int [] dataChunk = data[chunk]; 1927 // Re-create chunk if it was deleted. 1928 if (dataChunk == null) { 1929 createChunk(data, chunk); 1930 dataChunk = data[chunk]; 1931 } 1932 int ovalue = dataChunk[index]; 1933 if (ovalue == -1) { 1934 dataChunk[CHUNK_SIZE]++; 1935 } 1936 dataChunk[index] = value; 1937 return ovalue; 1938 } 1939 private final String setChunkValue(Object data[][], Object value, 1940 int chunk, int index) { 1941 if (value == null) { 1942 return clearChunkValue(data, chunk, index); 1943 } 1944 Object [] dataChunk = data[chunk]; 1945 // Re-create chunk if it was deleted. 1946 if (dataChunk == null) { 1947 createChunk(data, chunk); 1948 dataChunk = data[chunk]; 1949 } 1950 String ovalue = (String) dataChunk[index]; 1951 if (ovalue == null) { 1952 RefCount c = (RefCount) dataChunk[CHUNK_SIZE]; 1953 c.fCount++; 1954 } 1955 dataChunk[index] = value; 1956 return ovalue; 1957 } 1958 1959 /** 1960 * Returns the specified value in the given data at the chunk and index. 1961 */ 1962 private final int getChunkIndex(int data[][], int chunk, int index) { 1963 return data[chunk] != null ? data[chunk][index] : -1; 1964 } 1965 private final String getChunkValue(Object data[][], int chunk, int index) { 1966 return data[chunk] != null ? (String) data[chunk][index] : null; 1967 } 1968 private final String getNodeValue(int chunk, int index) { 1969 Object data = fNodeValue[chunk][index]; 1970 if (data == null){ 1971 return null; 1972 } 1973 else if (data instanceof String){ 1974 return (String)data; 1975 } 1976 else { 1977 // type information 1978 return data.toString(); 1979 } 1980 } 1981 1982 1983 /** 1984 * Clears the specified value in the given data at the chunk and index. 1985 * Note that this method will clear the given chunk if the reference 1986 * count becomes zero. 1987 * 1988 * @return Returns the old value. 1989 */ 1990 private final int clearChunkIndex(int data[][], int chunk, int index) { 1991 int value = data[chunk] != null ? data[chunk][index] : -1; 1992 if (value != -1) { 1993 data[chunk][CHUNK_SIZE]--; 1994 data[chunk][index] = -1; 1995 if (data[chunk][CHUNK_SIZE] == 0) { 1996 data[chunk] = null; 1997 } 1998 } 1999 return value; 2000 } 2001 private final String clearChunkValue(Object data[][], 2002 int chunk, int index) { 2003 String value = data[chunk] != null ? (String)data[chunk][index] : null; 2004 if (value != null) { 2005 data[chunk][index] = null; 2006 RefCount c = (RefCount) data[chunk][CHUNK_SIZE]; 2007 c.fCount--; 2008 if (c.fCount == 0) { 2009 data[chunk] = null; 2010 } 2011 } 2012 return value; 2013 } 2014 2015 /** 2016 * This version of putIdentifier is needed to avoid fluffing 2017 * all of the paths to ID attributes when a node object is 2018 * created that contains an ID attribute. 2019 */ 2020 private final void putIdentifier0(String idName, Element element) { 2021 2022 if (DEBUG_IDS) { 2023 System.out.println("putIdentifier0("+ 2024 idName+", "+ 2025 element+')'); 2026 } 2027 2028 // create Map 2029 if (identifiers == null) { 2030 identifiers = new HashMap<>(); 2031 } 2032 2033 // save ID and its associated element 2034 identifiers.put(idName, element); 2035 2036 } // putIdentifier0(String,Element) 2037 2038 /** Prints the ID array. */ 2039 private static void print(int values[], int start, int end, 2040 int middle, int target) { 2041 2042 if (DEBUG_IDS) { 2043 System.out.print(start); 2044 System.out.print(" ["); 2045 for (int i = start; i < end; i++) { 2046 if (middle == i) { 2047 System.out.print("!"); 2048 } 2049 System.out.print(values[i]); 2050 if (values[i] == target) { 2051 System.out.print("*"); 2052 } 2053 if (i < end - 1) { 2054 System.out.print(" "); 2055 } 2056 } 2057 System.out.println("] "+end); 2058 } 2059 2060 } // print(int[],int,int,int,int) 2061 2062 // 2063 // Classes 2064 // 2065 2066 /** 2067 * A simple integer vector. 2068 */ 2069 static final class IntVector { 2070 2071 // 2072 // Data 2073 // 2074 2075 /** Data. */ 2076 private int data[]; 2077 2078 /** Size. */ 2079 private int size; 2080 2081 // 2082 // Public methods 2083 // 2084 2085 /** Returns the length of this vector. */ 2086 public int size() { 2087 return size; 2088 } 2089 2090 /** Returns the element at the specified index. */ 2091 public int elementAt(int index) { 2092 return data[index]; 2093 } 2094 2095 /** Appends an element to the end of the vector. */ 2096 public void addElement(int element) { 2097 ensureCapacity(size + 1); 2098 data[size++] = element; 2099 } 2100 2101 /** Clears the vector. */ 2102 public void removeAllElements() { 2103 size = 0; 2104 } 2105 2106 // 2107 // Private methods 2108 // 2109 2110 /** Makes sure that there is enough storage. */ 2111 private void ensureCapacity(int newsize) { 2112 2113 if (data == null) { 2114 data = new int[newsize + 15]; 2115 } 2116 else if (newsize > data.length) { 2117 int newdata[] = new int[newsize + 15]; 2118 System.arraycopy(data, 0, newdata, 0, data.length); 2119 data = newdata; 2120 } 2121 2122 } // ensureCapacity(int) 2123 2124 } // class IntVector 2125 2126 } // class DeferredDocumentImpl