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