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