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
21package com.sun.org.apache.xerces.internal.dom;
22
23import java.util.ArrayList;
24import java.util.HashMap;
25import org.w3c.dom.DOMImplementation;
26import org.w3c.dom.Element;
27import 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 */
45public 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    /** Creates an attribute in the table. */
476    public int createDeferredAttribute(String attrName, String attrValue,
477                                       boolean specified) {
478        return createDeferredAttribute(attrName, null, attrValue, specified);
479    }
480
481    /** Creates an attribute with a URI in the table. */
482    public int createDeferredAttribute(String attrName, String attrURI,
483                                       String attrValue, boolean specified) {
484
485        // create node
486        int nodeIndex = createNode(NodeImpl.ATTRIBUTE_NODE);
487        int chunk = nodeIndex >> CHUNK_SHIFT;
488        int index = nodeIndex & CHUNK_MASK;
489        setChunkValue(fNodeName, attrName, chunk, index);
490        setChunkValue(fNodeURI, attrURI, chunk, index);
491        setChunkValue(fNodeValue, attrValue, chunk, index);
492        int extra = specified ? SPECIFIED : 0;
493        setChunkIndex(fNodeExtra, extra, chunk, index);
494
495        // return node index
496        return nodeIndex;
497
498    } // createDeferredAttribute(String,String,String,boolean):int
499
500    /** Creates an element definition in the table.*/
501    public int createDeferredElementDefinition(String elementName) {
502
503        // create node
504        int nodeIndex = createNode(NodeImpl.ELEMENT_DEFINITION_NODE);
505        int chunk = nodeIndex >> CHUNK_SHIFT;
506        int index = nodeIndex & CHUNK_MASK;
507        setChunkValue(fNodeName, elementName, chunk, index);
508
509        // return node index
510        return nodeIndex;
511
512    } // createDeferredElementDefinition(String):int
513
514    /** Creates a text node in the table. */
515    public int createDeferredTextNode(String data,
516                                      boolean ignorableWhitespace) {
517
518        // create node
519        int nodeIndex = createNode(Node.TEXT_NODE);
520        int chunk = nodeIndex >> CHUNK_SHIFT;
521        int index = nodeIndex & CHUNK_MASK;
522        setChunkValue(fNodeValue, data, chunk, index);
523        // use extra to store ignorableWhitespace info
524        setChunkIndex(fNodeExtra, ignorableWhitespace ?  1 : 0, chunk, index);
525
526        // return node index
527        return nodeIndex;
528
529    } // createDeferredTextNode(String,boolean):int
530
531    /** Creates a CDATA section node in the table. */
532    public int createDeferredCDATASection(String data) {
533
534        // create node
535        int nodeIndex = createNode(Node.CDATA_SECTION_NODE);
536        int chunk = nodeIndex >> CHUNK_SHIFT;
537        int index = nodeIndex & CHUNK_MASK;
538        setChunkValue(fNodeValue, data, chunk, index);
539
540        // return node index
541        return nodeIndex;
542
543    } // createDeferredCDATASection(String):int
544
545    /** Creates a processing instruction node in the table. */
546    public int createDeferredProcessingInstruction(String target,
547                                                   String data) {
548        // create node
549        int nodeIndex = createNode(Node.PROCESSING_INSTRUCTION_NODE);
550        int chunk = nodeIndex >> CHUNK_SHIFT;
551        int index = nodeIndex & CHUNK_MASK;
552        setChunkValue(fNodeName, target, chunk, index);
553        setChunkValue(fNodeValue, data, chunk, index);
554        // return node index
555        return nodeIndex;
556
557    } // createDeferredProcessingInstruction(String,String):int
558
559    /** Creates a comment node in the table. */
560    public int createDeferredComment(String data) {
561
562        // create node
563        int nodeIndex = createNode(Node.COMMENT_NODE);
564        int chunk = nodeIndex >> CHUNK_SHIFT;
565        int index = nodeIndex & CHUNK_MASK;
566        setChunkValue(fNodeValue, data, chunk, index);
567
568        // return node index
569        return nodeIndex;
570
571    } // createDeferredComment(String):int
572
573    /** Creates a clone of the specified node. */
574    public int cloneNode(int nodeIndex, boolean deep) {
575
576        // clone immediate node
577
578        int nchunk = nodeIndex >> CHUNK_SHIFT;
579        int nindex = nodeIndex & CHUNK_MASK;
580        int nodeType = fNodeType[nchunk][nindex];
581        int cloneIndex = createNode((short)nodeType);
582        int cchunk = cloneIndex >> CHUNK_SHIFT;
583        int cindex = cloneIndex & CHUNK_MASK;
584        setChunkValue(fNodeName, fNodeName[nchunk][nindex], cchunk, cindex);
585        setChunkValue(fNodeValue, fNodeValue[nchunk][nindex], cchunk, cindex);
586        setChunkValue(fNodeURI, fNodeURI[nchunk][nindex], cchunk, cindex);
587        int extraIndex = fNodeExtra[nchunk][nindex];
588        if (extraIndex != -1) {
589            if (nodeType != Node.ATTRIBUTE_NODE && nodeType != Node.TEXT_NODE) {
590                extraIndex = cloneNode(extraIndex, false);
591            }
592            setChunkIndex(fNodeExtra, extraIndex, cchunk, cindex);
593        }
594
595        // clone and attach children
596        if (deep) {
597            int prevIndex = -1;
598            int childIndex = getLastChild(nodeIndex, false);
599            while (childIndex != -1) {
600                int clonedChildIndex = cloneNode(childIndex, deep);
601                insertBefore(cloneIndex, clonedChildIndex, prevIndex);
602                prevIndex = clonedChildIndex;
603                childIndex = getRealPrevSibling(childIndex, false);
604            }
605
606
607        }
608
609        // return cloned node index
610        return cloneIndex;
611
612    } // cloneNode(int,boolean):int
613
614    /** Appends a child to the specified parent in the table. */
615    public void appendChild(int parentIndex, int childIndex) {
616
617        // append parent index
618        int pchunk = parentIndex >> CHUNK_SHIFT;
619        int pindex = parentIndex & CHUNK_MASK;
620        int cchunk = childIndex >> CHUNK_SHIFT;
621        int cindex = childIndex & CHUNK_MASK;
622        setChunkIndex(fNodeParent, parentIndex, cchunk, cindex);
623
624        // set previous sibling of new child
625        int olast = getChunkIndex(fNodeLastChild, pchunk, pindex);
626        setChunkIndex(fNodePrevSib, olast, cchunk, cindex);
627
628        // update parent's last child
629        setChunkIndex(fNodeLastChild, childIndex, pchunk, pindex);
630
631    } // appendChild(int,int)
632
633    /** Adds an attribute node to the specified element. */
634    public int setAttributeNode(int elemIndex, int attrIndex) {
635
636        int echunk = elemIndex >> CHUNK_SHIFT;
637        int eindex = elemIndex & CHUNK_MASK;
638        int achunk = attrIndex >> CHUNK_SHIFT;
639        int aindex = attrIndex & CHUNK_MASK;
640
641        // see if this attribute is already here
642        String attrName = getChunkValue(fNodeName, achunk, aindex);
643        int oldAttrIndex = getChunkIndex(fNodeExtra, echunk, eindex);
644        int nextIndex = -1;
645        int oachunk = -1;
646        int oaindex = -1;
647        while (oldAttrIndex != -1) {
648            oachunk = oldAttrIndex >> CHUNK_SHIFT;
649            oaindex = oldAttrIndex & CHUNK_MASK;
650            String oldAttrName = getChunkValue(fNodeName, oachunk, oaindex);
651            if (oldAttrName.equals(attrName)) {
652                break;
653            }
654            nextIndex = oldAttrIndex;
655            oldAttrIndex = getChunkIndex(fNodePrevSib, oachunk, oaindex);
656        }
657
658        // remove old attribute
659        if (oldAttrIndex != -1) {
660
661            // patch links
662            int prevIndex = getChunkIndex(fNodePrevSib, oachunk, oaindex);
663            if (nextIndex == -1) {
664                setChunkIndex(fNodeExtra, prevIndex, echunk, eindex);
665            }
666            else {
667                int pchunk = nextIndex >> CHUNK_SHIFT;
668                int pindex = nextIndex & CHUNK_MASK;
669                setChunkIndex(fNodePrevSib, prevIndex, pchunk, pindex);
670            }
671
672            // remove connections to siblings
673            clearChunkIndex(fNodeType, oachunk, oaindex);
674            clearChunkValue(fNodeName, oachunk, oaindex);
675            clearChunkValue(fNodeValue, oachunk, oaindex);
676            clearChunkIndex(fNodeParent, oachunk, oaindex);
677            clearChunkIndex(fNodePrevSib, oachunk, oaindex);
678            int attrTextIndex =
679                clearChunkIndex(fNodeLastChild, oachunk, oaindex);
680            int atchunk = attrTextIndex >> CHUNK_SHIFT;
681            int atindex = attrTextIndex & CHUNK_MASK;
682            clearChunkIndex(fNodeType, atchunk, atindex);
683            clearChunkValue(fNodeValue, atchunk, atindex);
684            clearChunkIndex(fNodeParent, atchunk, atindex);
685            clearChunkIndex(fNodeLastChild, atchunk, atindex);
686        }
687
688        // add new attribute
689        int prevIndex = getChunkIndex(fNodeExtra, echunk, eindex);
690        setChunkIndex(fNodeExtra, attrIndex, echunk, eindex);
691        setChunkIndex(fNodePrevSib, prevIndex, achunk, aindex);
692
693        // return
694        return oldAttrIndex;
695
696    } // setAttributeNode(int,int):int
697
698
699    /** Adds an attribute node to the specified element. */
700    public void setIdAttributeNode(int elemIndex, int attrIndex) {
701
702        int chunk = attrIndex >> CHUNK_SHIFT;
703        int index = attrIndex & CHUNK_MASK;
704        int extra = getChunkIndex(fNodeExtra, chunk, index);
705        extra = extra | ID;
706        setChunkIndex(fNodeExtra, extra, chunk, index);
707
708        String value = getChunkValue(fNodeValue, chunk, index);
709        putIdentifier(value, elemIndex);
710    }
711
712
713    /** Sets type of attribute */
714    public void setIdAttribute(int attrIndex) {
715
716        int chunk = attrIndex >> CHUNK_SHIFT;
717        int index = attrIndex & CHUNK_MASK;
718        int extra = getChunkIndex(fNodeExtra, chunk, index);
719        extra = extra | ID;
720        setChunkIndex(fNodeExtra, extra, chunk, index);
721    }
722
723    /** Inserts a child before the specified node in the table. */
724    public int insertBefore(int parentIndex, int newChildIndex, int refChildIndex) {
725
726        if (refChildIndex == -1) {
727            appendChild(parentIndex, newChildIndex);
728            return newChildIndex;
729        }
730
731        int nchunk = newChildIndex >> CHUNK_SHIFT;
732        int nindex = newChildIndex & CHUNK_MASK;
733        int rchunk = refChildIndex >> CHUNK_SHIFT;
734        int rindex = refChildIndex & CHUNK_MASK;
735        int previousIndex = getChunkIndex(fNodePrevSib, rchunk, rindex);
736        setChunkIndex(fNodePrevSib, newChildIndex, rchunk, rindex);
737        setChunkIndex(fNodePrevSib, previousIndex, nchunk, nindex);
738
739        return newChildIndex;
740
741    } // insertBefore(int,int,int):int
742
743    /** Sets the last child of the parentIndex to childIndex. */
744    public void setAsLastChild(int parentIndex, int childIndex) {
745        int pchunk = parentIndex >> CHUNK_SHIFT;
746        int pindex = parentIndex & CHUNK_MASK;
747        setChunkIndex(fNodeLastChild, childIndex, pchunk, pindex);
748    } // setAsLastChild(int,int)
749
750    /**
751     * Returns the parent node of the given node.
752     * <em>Calling this method does not free the parent index.</em>
753     */
754    public int getParentNode(int nodeIndex) {
755        return getParentNode(nodeIndex, false);
756    }
757
758    /**
759     * Returns the parent node of the given node.
760     * @param free True to free parent node.
761     */
762    public int getParentNode(int nodeIndex, boolean free) {
763
764        if (nodeIndex == -1) {
765            return -1;
766        }
767
768        int chunk = nodeIndex >> CHUNK_SHIFT;
769        int index = nodeIndex & CHUNK_MASK;
770        return free ? clearChunkIndex(fNodeParent, chunk, index)
771                    : getChunkIndex(fNodeParent, chunk, index);
772
773    } // getParentNode(int):int
774
775    /** Returns the last child of the given node. */
776    public int getLastChild(int nodeIndex) {
777        return getLastChild(nodeIndex, true);
778    }
779
780    /**
781     * Returns the last child of the given node.
782     * @param free True to free child index.
783     */
784    public int getLastChild(int nodeIndex, boolean free) {
785
786        if (nodeIndex == -1) {
787            return -1;
788        }
789
790        int chunk = nodeIndex >> CHUNK_SHIFT;
791        int index = nodeIndex & CHUNK_MASK;
792        return free ? clearChunkIndex(fNodeLastChild, chunk, index)
793                    : getChunkIndex(fNodeLastChild, chunk, index);
794
795    } // getLastChild(int,boolean):int
796
797    /**
798     * Returns the prev sibling of the given node.
799     * This is post-normalization of Text Nodes.
800     */
801    public int getPrevSibling(int nodeIndex) {
802        return getPrevSibling(nodeIndex, true);
803    }
804
805    /**
806     * Returns the prev sibling of the given node.
807     * @param free True to free sibling index.
808     */
809    public int getPrevSibling(int nodeIndex, boolean free) {
810
811        if (nodeIndex == -1) {
812            return -1;
813        }
814
815        int chunk = nodeIndex >> CHUNK_SHIFT;
816        int index = nodeIndex & CHUNK_MASK;
817        int type = getChunkIndex(fNodeType, chunk, index);
818        if (type == Node.TEXT_NODE) {
819            do {
820                nodeIndex = getChunkIndex(fNodePrevSib, chunk, index);
821                if (nodeIndex == -1) {
822                    break;
823                }
824                chunk = nodeIndex >> CHUNK_SHIFT;
825                index = nodeIndex & CHUNK_MASK;
826                type = getChunkIndex(fNodeType, chunk, index);
827            } while (type == Node.TEXT_NODE);
828        }
829        else {
830            nodeIndex = getChunkIndex(fNodePrevSib, chunk, index);
831        }
832
833        return nodeIndex;
834
835    } // getPrevSibling(int,boolean):int
836
837    /**
838     * Returns the <i>real</i> prev sibling of the given node,
839     * directly from the data structures. Used by TextImpl#getNodeValue()
840     * to normalize values.
841     */
842    public int getRealPrevSibling(int nodeIndex) {
843        return getRealPrevSibling(nodeIndex, true);
844    }
845
846    /**
847     * Returns the <i>real</i> prev sibling of the given node.
848     * @param free True to free sibling index.
849     */
850    public int getRealPrevSibling(int nodeIndex, boolean free) {
851
852        if (nodeIndex == -1) {
853            return -1;
854        }
855
856        int chunk = nodeIndex >> CHUNK_SHIFT;
857        int index = nodeIndex & CHUNK_MASK;
858        return free ? clearChunkIndex(fNodePrevSib, chunk, index)
859                    : getChunkIndex(fNodePrevSib, chunk, index);
860
861    } // getReadPrevSibling(int,boolean):int
862
863    /**
864     * Returns the index of the element definition in the table
865     * with the specified name index, or -1 if no such definition
866     * exists.
867     */
868    public int lookupElementDefinition(String elementName) {
869
870        if (fNodeCount > 1) {
871
872            // find doctype
873            int docTypeIndex = -1;
874            int nchunk = 0;
875            int nindex = 0;
876            for (int index = getChunkIndex(fNodeLastChild, nchunk, nindex);
877                 index != -1;
878                 index = getChunkIndex(fNodePrevSib, nchunk, nindex)) {
879
880                nchunk = index >> CHUNK_SHIFT;
881                nindex = index  & CHUNK_MASK;
882                if (getChunkIndex(fNodeType, nchunk, nindex) == Node.DOCUMENT_TYPE_NODE) {
883                    docTypeIndex = index;
884                    break;
885                }
886            }
887
888            // find element definition
889            if (docTypeIndex == -1) {
890                return -1;
891            }
892            nchunk = docTypeIndex >> CHUNK_SHIFT;
893            nindex = docTypeIndex & CHUNK_MASK;
894            for (int index = getChunkIndex(fNodeLastChild, nchunk, nindex);
895                 index != -1;
896                 index = getChunkIndex(fNodePrevSib, nchunk, nindex)) {
897
898                nchunk = index >> CHUNK_SHIFT;
899                nindex = index & CHUNK_MASK;
900                if (getChunkIndex(fNodeType, nchunk, nindex) ==
901                                           NodeImpl.ELEMENT_DEFINITION_NODE
902                 && getChunkValue(fNodeName, nchunk, nindex) == elementName) {
903                    return index;
904                }
905            }
906        }
907
908        return -1;
909
910    } // lookupElementDefinition(String):int
911
912    /** Instantiates the requested node object. */
913    public DeferredNode getNodeObject(int nodeIndex) {
914
915        // is there anything to do?
916        if (nodeIndex == -1) {
917            return null;
918        }
919
920        // get node type
921        int chunk = nodeIndex >> CHUNK_SHIFT;
922        int index = nodeIndex & CHUNK_MASK;
923        int type = getChunkIndex(fNodeType, chunk, index);
924        if (type != Node.TEXT_NODE && type != Node.CDATA_SECTION_NODE) {
925            clearChunkIndex(fNodeType, chunk, index);
926        }
927
928        // create new node
929        DeferredNode node = null;
930        switch (type) {
931
932            //
933            // Standard DOM node types
934            //
935
936            case Node.ATTRIBUTE_NODE: {
937                if (fNamespacesEnabled) {
938                    node = new DeferredAttrNSImpl(this, nodeIndex);
939                } else {
940                    node = new DeferredAttrImpl(this, nodeIndex);
941                }
942                break;
943            }
944
945            case Node.CDATA_SECTION_NODE: {
946                node = new DeferredCDATASectionImpl(this, nodeIndex);
947                break;
948            }
949
950            case Node.COMMENT_NODE: {
951                node = new DeferredCommentImpl(this, nodeIndex);
952                break;
953            }
954
955            // NOTE: Document fragments can never be "fast".
956            //
957            //       The parser will never ask to create a document
958            //       fragment during the parse. Document fragments
959            //       are used by the application *after* the parse.
960            //
961            // case Node.DOCUMENT_FRAGMENT_NODE: { break; }
962            case Node.DOCUMENT_NODE: {
963                // this node is never "fast"
964                node = this;
965                break;
966            }
967
968            case Node.DOCUMENT_TYPE_NODE: {
969                node = new DeferredDocumentTypeImpl(this, nodeIndex);
970                // save the doctype node
971                docType = (DocumentTypeImpl)node;
972                break;
973            }
974
975            case Node.ELEMENT_NODE: {
976
977                if (DEBUG_IDS) {
978                    System.out.println("getNodeObject(ELEMENT_NODE): "+nodeIndex);
979                }
980
981                // create node
982                if (fNamespacesEnabled) {
983                    node = new DeferredElementNSImpl(this, nodeIndex);
984                } else {
985                    node = new DeferredElementImpl(this, nodeIndex);
986                }
987
988                // check to see if this element needs to be
989                // registered for its ID attributes
990                if (fIdElement != null) {
991                    int idIndex = binarySearch(fIdElement, 0,
992                                               fIdCount-1, nodeIndex);
993                    while (idIndex != -1) {
994
995                        if (DEBUG_IDS) {
996                            System.out.println("  id index: "+idIndex);
997                            System.out.println("  fIdName["+idIndex+
998                                               "]: "+fIdName[idIndex]);
999                        }
1000
1001                        // register ID
1002                        String name = fIdName[idIndex];
1003                        if (name != null) {
1004                            if (DEBUG_IDS) {
1005                                System.out.println("  name: "+name);
1006                                System.out.print("getNodeObject()#");
1007                            }
1008                            putIdentifier0(name, (Element)node);
1009                            fIdName[idIndex] = null;
1010                        }
1011
1012                        // continue if there are more IDs for
1013                        // this element
1014                        if (idIndex + 1 < fIdCount &&
1015                            fIdElement[idIndex + 1] == nodeIndex) {
1016                            idIndex++;
1017                        }
1018                        else {
1019                            idIndex = -1;
1020                        }
1021                    }
1022                }
1023                break;
1024            }
1025
1026            case Node.ENTITY_NODE: {
1027                node = new DeferredEntityImpl(this, nodeIndex);
1028                break;
1029            }
1030
1031            case Node.ENTITY_REFERENCE_NODE: {
1032                node = new DeferredEntityReferenceImpl(this, nodeIndex);
1033                break;
1034            }
1035
1036            case Node.NOTATION_NODE: {
1037                node = new DeferredNotationImpl(this, nodeIndex);
1038                break;
1039            }
1040
1041            case Node.PROCESSING_INSTRUCTION_NODE: {
1042                node = new DeferredProcessingInstructionImpl(this, nodeIndex);
1043                break;
1044            }
1045
1046            case Node.TEXT_NODE: {
1047                node = new DeferredTextImpl(this, nodeIndex);
1048                break;
1049            }
1050
1051            //
1052            // non-standard DOM node types
1053            //
1054
1055            case NodeImpl.ELEMENT_DEFINITION_NODE: {
1056                node = new DeferredElementDefinitionImpl(this, nodeIndex);
1057                break;
1058            }
1059
1060            default: {
1061                throw new IllegalArgumentException("type: "+type);
1062            }
1063
1064        } // switch node type
1065
1066        // store and return
1067        if (node != null) {
1068            return node;
1069        }
1070
1071        // error
1072        throw new IllegalArgumentException();
1073
1074    } // createNodeObject(int):Node
1075
1076    /** Returns the name of the given node. */
1077    public String getNodeName(int nodeIndex) {
1078        return getNodeName(nodeIndex, true);
1079    } // getNodeNameString(int):String
1080
1081    /**
1082     * Returns the name of the given node.
1083     * @param free True to free the string index.
1084     */
1085    public String getNodeName(int nodeIndex, boolean free) {
1086
1087        if (nodeIndex == -1) {
1088            return null;
1089        }
1090
1091        int chunk = nodeIndex >> CHUNK_SHIFT;
1092        int index = nodeIndex & CHUNK_MASK;
1093        return free ? clearChunkValue(fNodeName, chunk, index)
1094                    : getChunkValue(fNodeName, chunk, index);
1095
1096    } // getNodeName(int,boolean):String
1097
1098    /** Returns the real value of the given node. */
1099    public String getNodeValueString(int nodeIndex) {
1100        return getNodeValueString(nodeIndex, true);
1101    } // getNodeValueString(int):String
1102
1103    /**
1104     * Returns the real value of the given node.
1105     * @param free True to free the string index.
1106     */
1107    public String getNodeValueString(int nodeIndex, boolean free) {
1108
1109        if (nodeIndex == -1) {
1110            return null;
1111        }
1112
1113        int chunk = nodeIndex >> CHUNK_SHIFT;
1114        int index = nodeIndex & CHUNK_MASK;
1115        String value = free ? clearChunkValue(fNodeValue, chunk, index)
1116                            : getChunkValue(fNodeValue, chunk, index);
1117        if (value == null) {
1118            return null;
1119        }
1120
1121        int type  = getChunkIndex(fNodeType, chunk, index);
1122        if (type == Node.TEXT_NODE) {
1123            int prevSib = getRealPrevSibling(nodeIndex);
1124            if (prevSib != -1 &&
1125                getNodeType(prevSib, false) == Node.TEXT_NODE) {
1126                // append data that is stored in fNodeValue
1127                // REVISIT: for text nodes it works differently than for CDATA
1128                //          nodes.
1129                fStrChunks.add(value);
1130                do {
1131                    // go in reverse order: find last child, then
1132                    // its previous sibling, etc
1133                    chunk = prevSib >> CHUNK_SHIFT;
1134                    index = prevSib & CHUNK_MASK;
1135                    value = getChunkValue(fNodeValue, chunk, index);
1136                    fStrChunks.add(value);
1137                    prevSib = getChunkIndex(fNodePrevSib, chunk, index);
1138                    if (prevSib == -1) {
1139                        break;
1140                    }
1141                } while (getNodeType(prevSib, false) == Node.TEXT_NODE);
1142
1143                int chunkCount = fStrChunks.size();
1144
1145                // add to the buffer in the correct order.
1146                for (int i = chunkCount - 1; i >= 0; i--) {
1147                    fBufferStr.append((String)fStrChunks.get(i));
1148                }
1149
1150                value = fBufferStr.toString();
1151                fStrChunks.clear();
1152                fBufferStr.setLength(0);
1153                return value;
1154            }
1155        }
1156        else if (type == Node.CDATA_SECTION_NODE) {
1157            // find if any other data stored in children
1158            int child = getLastChild(nodeIndex, false);
1159            if (child !=-1) {
1160                // append data that is stored in fNodeValue
1161                fBufferStr.append(value);
1162                while (child !=-1) {
1163                    // go in reverse order: find last child, then
1164                    // its previous sibling, etc
1165                   chunk = child >> CHUNK_SHIFT;
1166                    index = child & CHUNK_MASK;
1167                    value = getChunkValue(fNodeValue, chunk, index);
1168                    fStrChunks.add(value);
1169                    child = getChunkIndex(fNodePrevSib, chunk, index);
1170                }
1171                // add to the buffer in the correct order.
1172                for (int i=fStrChunks.size()-1; i>=0; i--) {
1173                     fBufferStr.append((String)fStrChunks.get(i));
1174                }
1175
1176                value = fBufferStr.toString();
1177                fStrChunks.clear();
1178                fBufferStr.setLength(0);
1179                return value;
1180            }
1181        }
1182
1183        return value;
1184
1185    } // getNodeValueString(int,boolean):String
1186
1187    /**
1188     * Returns the value of the given node.
1189     */
1190    public String getNodeValue(int nodeIndex) {
1191        return getNodeValue(nodeIndex, true);
1192    }
1193
1194        /**
1195         * Clears the type info that is stored in the fNodeValue array
1196         * @param nodeIndex
1197         * @return Object - type information for the attribute/element node
1198         */
1199    public Object getTypeInfo(int nodeIndex) {
1200        if (nodeIndex == -1) {
1201            return null;
1202        }
1203
1204        int chunk = nodeIndex >> CHUNK_SHIFT;
1205        int index = nodeIndex & CHUNK_MASK;
1206
1207
1208        Object value = fNodeValue[chunk] != null ? fNodeValue[chunk][index] : null;
1209        if (value != null) {
1210            fNodeValue[chunk][index] = null;
1211            RefCount c = (RefCount) fNodeValue[chunk][CHUNK_SIZE];
1212            c.fCount--;
1213            if (c.fCount == 0) {
1214                fNodeValue[chunk] = null;
1215            }
1216        }
1217        return value;
1218    }
1219
1220    /**
1221     * Returns the value of the given node.
1222     * @param free True to free the value index.
1223     */
1224    public String getNodeValue(int nodeIndex, boolean free) {
1225
1226        if (nodeIndex == -1) {
1227            return null;
1228        }
1229
1230        int chunk = nodeIndex >> CHUNK_SHIFT;
1231        int index = nodeIndex & CHUNK_MASK;
1232        return free ? clearChunkValue(fNodeValue, chunk, index)
1233                    : getChunkValue(fNodeValue, chunk, index);
1234
1235    } // getNodeValue(int,boolean):String
1236
1237    /**
1238     * Returns the extra info of the given node.
1239     * Used by AttrImpl to store specified value (1 == true).
1240     */
1241    public int getNodeExtra(int nodeIndex) {
1242        return getNodeExtra(nodeIndex, true);
1243    }
1244
1245    /**
1246     * Returns the extra info of the given node.
1247     * @param free True to free the value index.
1248     */
1249    public int getNodeExtra(int nodeIndex, boolean free) {
1250
1251        if (nodeIndex == -1) {
1252            return -1;
1253        }
1254
1255        int chunk = nodeIndex >> CHUNK_SHIFT;
1256        int index = nodeIndex & CHUNK_MASK;
1257        return free ? clearChunkIndex(fNodeExtra, chunk, index)
1258                    : getChunkIndex(fNodeExtra, chunk, index);
1259
1260    } // getNodeExtra(int,boolean):int
1261
1262    /** Returns the type of the given node. */
1263    public short getNodeType(int nodeIndex) {
1264        return getNodeType(nodeIndex, true);
1265    }
1266
1267    /**
1268     * Returns the type of the given node.
1269     * @param free True to free type index.
1270     */
1271    public short getNodeType(int nodeIndex, boolean free) {
1272
1273        if (nodeIndex == -1) {
1274            return -1;
1275        }
1276
1277        int chunk = nodeIndex >> CHUNK_SHIFT;
1278        int index = nodeIndex & CHUNK_MASK;
1279        return free ? (short)clearChunkIndex(fNodeType, chunk, index)
1280                    : (short)getChunkIndex(fNodeType, chunk, index);
1281
1282    } // getNodeType(int):int
1283
1284    /** Returns the attribute value of the given name. */
1285    public String getAttribute(int elemIndex, String name) {
1286        if (elemIndex == -1 || name == null) {
1287            return null;
1288        }
1289        int echunk = elemIndex >> CHUNK_SHIFT;
1290        int eindex = elemIndex & CHUNK_MASK;
1291        int attrIndex = getChunkIndex(fNodeExtra, echunk, eindex);
1292        while (attrIndex != -1) {
1293            int achunk = attrIndex >> CHUNK_SHIFT;
1294            int aindex = attrIndex & CHUNK_MASK;
1295            if (getChunkValue(fNodeName, achunk, aindex) == name) {
1296                return getChunkValue(fNodeValue, achunk, aindex);
1297            }
1298            attrIndex = getChunkIndex(fNodePrevSib, achunk, aindex);
1299        }
1300        return null;
1301    }
1302
1303    /** Returns the URI of the given node. */
1304    public String getNodeURI(int nodeIndex) {
1305        return getNodeURI(nodeIndex, true);
1306    }
1307
1308    /**
1309     * Returns the URI of the given node.
1310     * @param free True to free URI index.
1311     */
1312    public String getNodeURI(int nodeIndex, boolean free) {
1313
1314        if (nodeIndex == -1) {
1315            return null;
1316        }
1317
1318        int chunk = nodeIndex >> CHUNK_SHIFT;
1319        int index = nodeIndex & CHUNK_MASK;
1320        return free ? clearChunkValue(fNodeURI, chunk, index)
1321                    : getChunkValue(fNodeURI, chunk, index);
1322
1323    } // getNodeURI(int,int):String
1324
1325    // identifier maintenance
1326
1327    /** Registers an identifier name with a specified element node. */
1328    public void putIdentifier(String name, int elementNodeIndex) {
1329
1330        if (DEBUG_IDS) {
1331            System.out.println("putIdentifier(" + name + ", "
1332                               + elementNodeIndex + ')' + " // " +
1333                               getChunkValue(fNodeName,
1334                                             elementNodeIndex >> CHUNK_SHIFT,
1335                                             elementNodeIndex & CHUNK_MASK));
1336        }
1337
1338        // initialize arrays
1339        if (fIdName == null) {
1340            fIdName    = new String[64];
1341            fIdElement = new int[64];
1342        }
1343
1344        // resize arrays
1345        if (fIdCount == fIdName.length) {
1346            String idName[] = new String[fIdCount * 2];
1347            System.arraycopy(fIdName, 0, idName, 0, fIdCount);
1348            fIdName = idName;
1349
1350            int idElement[] = new int[idName.length];
1351            System.arraycopy(fIdElement, 0, idElement, 0, fIdCount);
1352            fIdElement = idElement;
1353        }
1354
1355        // store identifier
1356        fIdName[fIdCount] = name;
1357        fIdElement[fIdCount] = elementNodeIndex;
1358        fIdCount++;
1359
1360    } // putIdentifier(String,int)
1361
1362    //
1363    // DEBUG
1364    //
1365
1366    /** Prints out the tables. */
1367    public void print() {
1368
1369        if (DEBUG_PRINT_REF_COUNTS) {
1370            System.out.print("num\t");
1371            System.out.print("type\t");
1372            System.out.print("name\t");
1373            System.out.print("val\t");
1374            System.out.print("par\t");
1375            System.out.print("lch\t");
1376            System.out.print("psib");
1377            System.out.println();
1378            for (int i = 0; i < fNodeType.length; i++) {
1379                if (fNodeType[i] != null) {
1380                    // separator
1381                    System.out.print("--------");
1382                    System.out.print("--------");
1383                    System.out.print("--------");
1384                    System.out.print("--------");
1385                    System.out.print("--------");
1386                    System.out.print("--------");
1387                    System.out.print("--------");
1388                    System.out.println();
1389
1390                    // ref count
1391                    System.out.print(i);
1392                    System.out.print('\t');
1393                    switch (fNodeType[i][CHUNK_SIZE]) {
1394                        case DocumentImpl.ELEMENT_DEFINITION_NODE: { System.out.print("EDef"); break; }
1395                        case Node.DOCUMENT_NODE: { System.out.print("Doc"); break; }
1396                        case Node.DOCUMENT_TYPE_NODE: { System.out.print("DType"); break; }
1397                        case Node.COMMENT_NODE: { System.out.print("Com"); break; }
1398                        case Node.PROCESSING_INSTRUCTION_NODE: { System.out.print("PI"); break; }
1399                        case Node.ELEMENT_NODE: { System.out.print("Elem"); break; }
1400                        case Node.ENTITY_NODE: { System.out.print("Ent"); break; }
1401                        case Node.ENTITY_REFERENCE_NODE: { System.out.print("ERef"); break; }
1402                        case Node.TEXT_NODE: { System.out.print("Text"); break; }
1403                        case Node.ATTRIBUTE_NODE: { System.out.print("Attr"); break; }
1404                        case DeferredNode.TYPE_NODE: { System.out.print("TypeInfo"); break; }
1405                        default: { System.out.print("?"+fNodeType[i][CHUNK_SIZE]); }
1406                    }
1407                    System.out.print('\t');
1408                    System.out.print(fNodeName[i][CHUNK_SIZE]);
1409                    System.out.print('\t');
1410                    System.out.print(fNodeValue[i][CHUNK_SIZE]);
1411                    System.out.print('\t');
1412                    System.out.print(fNodeURI[i][CHUNK_SIZE]);
1413                    System.out.print('\t');
1414                    System.out.print(fNodeParent[i][CHUNK_SIZE]);
1415                    System.out.print('\t');
1416                    System.out.print(fNodeLastChild[i][CHUNK_SIZE]);
1417                    System.out.print('\t');
1418                    System.out.print(fNodePrevSib[i][CHUNK_SIZE]);
1419                    System.out.print('\t');
1420                    System.out.print(fNodeExtra[i][CHUNK_SIZE]);
1421                    System.out.println();
1422                }
1423            }
1424        }
1425
1426        if (DEBUG_PRINT_TABLES) {
1427            // This assumes that the document is small
1428            System.out.println("# start table");
1429            for (int i = 0; i < fNodeCount; i++) {
1430                int chunk = i >> CHUNK_SHIFT;
1431                int index = i & CHUNK_MASK;
1432                if (i % 10 == 0) {
1433                    System.out.print("num\t");
1434                    System.out.print("type\t");
1435                    System.out.print("name\t");
1436                    System.out.print("val\t");
1437                    System.out.print("uri\t");
1438                    System.out.print("par\t");
1439                    System.out.print("lch\t");
1440                    System.out.print("psib\t");
1441                    System.out.print("xtra");
1442                    System.out.println();
1443                }
1444                System.out.print(i);
1445                System.out.print('\t');
1446                switch (getChunkIndex(fNodeType, chunk, index)) {
1447                    case DocumentImpl.ELEMENT_DEFINITION_NODE: { System.out.print("EDef"); break; }
1448                    case Node.DOCUMENT_NODE: { System.out.print("Doc"); break; }
1449                    case Node.DOCUMENT_TYPE_NODE: { System.out.print("DType"); break; }
1450                    case Node.COMMENT_NODE: { System.out.print("Com"); break; }
1451                    case Node.PROCESSING_INSTRUCTION_NODE: { System.out.print("PI"); break; }
1452                    case Node.ELEMENT_NODE: { System.out.print("Elem"); break; }
1453                    case Node.ENTITY_NODE: { System.out.print("Ent"); break; }
1454                    case Node.ENTITY_REFERENCE_NODE: { System.out.print("ERef"); break; }
1455                    case Node.TEXT_NODE: { System.out.print("Text"); break; }
1456                    case Node.ATTRIBUTE_NODE: { System.out.print("Attr"); break; }
1457                    case DeferredNode.TYPE_NODE: { System.out.print("TypeInfo"); break; }
1458                    default: { System.out.print("?"+getChunkIndex(fNodeType, chunk, index)); }
1459                }
1460                System.out.print('\t');
1461                System.out.print(getChunkValue(fNodeName, chunk, index));
1462                System.out.print('\t');
1463                System.out.print(getNodeValue(chunk, index));
1464                System.out.print('\t');
1465                System.out.print(getChunkValue(fNodeURI, chunk, index));
1466                System.out.print('\t');
1467                System.out.print(getChunkIndex(fNodeParent, chunk, index));
1468                System.out.print('\t');
1469                System.out.print(getChunkIndex(fNodeLastChild, chunk, index));
1470                System.out.print('\t');
1471                System.out.print(getChunkIndex(fNodePrevSib, chunk, index));
1472                System.out.print('\t');
1473                System.out.print(getChunkIndex(fNodeExtra, chunk, index));
1474                System.out.println();
1475            }
1476            System.out.println("# end table");
1477        }
1478
1479    } // print()
1480
1481    //
1482    // DeferredNode methods
1483    //
1484
1485    /** Returns the node index. */
1486    public int getNodeIndex() {
1487        return 0;
1488    }
1489
1490    //
1491    // Protected methods
1492    //
1493
1494    /** Synchronizes the node's data. */
1495    protected void synchronizeData() {
1496
1497        // no need to sync in the future
1498        needsSyncData(false);
1499
1500        // fluff up enough nodes to fill identifiers hash
1501        if (fIdElement != null) {
1502
1503            // REVISIT: There has to be a more efficient way of
1504            //          doing this. But keep in mind that the
1505            //          tree can have been altered and re-ordered
1506            //          before all of the element nodes with ID
1507            //          attributes have been registered. For now
1508            //          this is reasonable and safe. -Ac
1509
1510            IntVector path = new IntVector();
1511            for (int i = 0; i < fIdCount; i++) {
1512
1513                // ignore if it's already been registered
1514                int elementNodeIndex = fIdElement[i];
1515                String idName      = fIdName[i];
1516                if (idName == null) {
1517                    continue;
1518                }
1519
1520                // find path from this element to the root
1521                path.removeAllElements();
1522                int index = elementNodeIndex;
1523                do {
1524                    path.addElement(index);
1525                    int pchunk = index >> CHUNK_SHIFT;
1526                    int pindex = index & CHUNK_MASK;
1527                    index = getChunkIndex(fNodeParent, pchunk, pindex);
1528                } while (index != -1);
1529
1530                // Traverse path (backwards), fluffing the elements
1531                // along the way. When this loop finishes, "place"
1532                // will contain the reference to the element node
1533                // we're interested in. -Ac
1534                Node place = this;
1535                for (int j = path.size() - 2; j >= 0; j--) {
1536                    index = path.elementAt(j);
1537                    Node child = place.getLastChild();
1538                    while (child != null) {
1539                        if (child instanceof DeferredNode) {
1540                            int nodeIndex =
1541                                ((DeferredNode)child).getNodeIndex();
1542                            if (nodeIndex == index) {
1543                                place = child;
1544                                break;
1545                            }
1546                        }
1547                        child = child.getPreviousSibling();
1548                    }
1549                }
1550
1551                // register the element
1552                Element element = (Element)place;
1553                putIdentifier0(idName, element);
1554                fIdName[i] = null;
1555
1556                // see if there are more IDs on this element
1557                while (i + 1 < fIdCount &&
1558                    fIdElement[i + 1] == elementNodeIndex) {
1559                    idName = fIdName[++i];
1560                    if (idName == null) {
1561                        continue;
1562                    }
1563                    putIdentifier0(idName, element);
1564                }
1565            }
1566
1567        } // if identifiers
1568
1569    } // synchronizeData()
1570
1571    /**
1572     * Synchronizes the node's children with the internal structure.
1573     * Fluffing the children at once solves a lot of work to keep
1574     * the two structures in sync. The problem gets worse when
1575     * editing the tree -- this makes it a lot easier.
1576     */
1577    protected void synchronizeChildren() {
1578
1579        if (needsSyncData()) {
1580            synchronizeData();
1581            /*
1582             * when we have elements with IDs this method is being recursively
1583             * called from synchronizeData, in which case we've already gone
1584             * through the following and we can now simply stop here.
1585             */
1586            if (!needsSyncChildren()) {
1587                return;
1588            }
1589        }
1590
1591        // we don't want to generate any event for this so turn them off
1592        boolean orig = mutationEvents;
1593        mutationEvents = false;
1594
1595        // no need to sync in the future
1596        needsSyncChildren(false);
1597
1598        getNodeType(0);
1599
1600        // create children and link them as siblings
1601        ChildNode first = null;
1602        ChildNode last = null;
1603        for (int index = getLastChild(0);
1604             index != -1;
1605             index = getPrevSibling(index)) {
1606
1607            ChildNode node = (ChildNode)getNodeObject(index);
1608            if (last == null) {
1609                last = node;
1610            }
1611            else {
1612                first.previousSibling = node;
1613            }
1614            node.ownerNode = this;
1615            node.isOwned(true);
1616            node.nextSibling = first;
1617            first = node;
1618
1619            // save doctype and document type
1620            int type = node.getNodeType();
1621            if (type == Node.ELEMENT_NODE) {
1622                docElement = (ElementImpl)node;
1623            }
1624            else if (type == Node.DOCUMENT_TYPE_NODE) {
1625                docType = (DocumentTypeImpl)node;
1626            }
1627        }
1628
1629        if (first != null) {
1630            firstChild = first;
1631            first.isFirstChild(true);
1632            lastChild(last);
1633        }
1634
1635        // set mutation events flag back to its original value
1636        mutationEvents = orig;
1637
1638    } // synchronizeChildren()
1639
1640    /**
1641     * Synchronizes the node's children with the internal structure.
1642     * Fluffing the children at once solves a lot of work to keep
1643     * the two structures in sync. The problem gets worse when
1644     * editing the tree -- this makes it a lot easier.
1645     * This is not directly used in this class but this method is
1646     * here so that it can be shared by all deferred subclasses of AttrImpl.
1647     */
1648    protected final void synchronizeChildren(AttrImpl a, int nodeIndex) {
1649
1650        // we don't want to generate any event for this so turn them off
1651        boolean orig = getMutationEvents();
1652        setMutationEvents(false);
1653
1654        // no need to sync in the future
1655        a.needsSyncChildren(false);
1656
1657        // create children and link them as siblings or simply store the value
1658        // as a String if all we have is one piece of text
1659        int last = getLastChild(nodeIndex);
1660        int prev = getPrevSibling(last);
1661        if (prev == -1) {
1662            a.value = getNodeValueString(nodeIndex);
1663            a.hasStringValue(true);
1664        }
1665        else {
1666            ChildNode firstNode = null;
1667            ChildNode lastNode = null;
1668            for (int index = last; index != -1;
1669                 index = getPrevSibling(index)) {
1670
1671                ChildNode node = (ChildNode) getNodeObject(index);
1672                if (lastNode == null) {
1673                    lastNode = node;
1674                }
1675                else {
1676                    firstNode.previousSibling = node;
1677                }
1678                node.ownerNode = a;
1679                node.isOwned(true);
1680                node.nextSibling = firstNode;
1681                firstNode = node;
1682            }
1683            if (lastNode != null) {
1684                a.value = firstNode; // firstChild = firstNode
1685                firstNode.isFirstChild(true);
1686                a.lastChild(lastNode);
1687            }
1688            a.hasStringValue(false);
1689        }
1690
1691        // set mutation events flag back to its original value
1692        setMutationEvents(orig);
1693
1694    } // synchronizeChildren(AttrImpl,int):void
1695
1696
1697    /**
1698     * Synchronizes the node's children with the internal structure.
1699     * Fluffing the children at once solves a lot of work to keep
1700     * the two structures in sync. The problem gets worse when
1701     * editing the tree -- this makes it a lot easier.
1702     * This is not directly used in this class but this method is
1703     * here so that it can be shared by all deferred subclasses of ParentNode.
1704     */
1705    protected final void synchronizeChildren(ParentNode p, int nodeIndex) {
1706
1707        // we don't want to generate any event for this so turn them off
1708        boolean orig = getMutationEvents();
1709        setMutationEvents(false);
1710
1711        // no need to sync in the future
1712        p.needsSyncChildren(false);
1713
1714        // create children and link them as siblings
1715        ChildNode firstNode = null;
1716        ChildNode lastNode = null;
1717        for (int index = getLastChild(nodeIndex);
1718             index != -1;
1719             index = getPrevSibling(index)) {
1720
1721            ChildNode node = (ChildNode) getNodeObject(index);
1722            if (lastNode == null) {
1723                lastNode = node;
1724            }
1725            else {
1726                firstNode.previousSibling = node;
1727            }
1728            node.ownerNode = p;
1729            node.isOwned(true);
1730            node.nextSibling = firstNode;
1731            firstNode = node;
1732        }
1733        if (lastNode != null) {
1734            p.firstChild = firstNode;
1735            firstNode.isFirstChild(true);
1736            p.lastChild(lastNode);
1737        }
1738
1739        // set mutation events flag back to its original value
1740        setMutationEvents(orig);
1741
1742    } // synchronizeChildren(ParentNode,int):void
1743
1744    // utility methods
1745
1746    /** Ensures that the internal tables are large enough. */
1747    protected void ensureCapacity(int chunk) {
1748        if (fNodeType == null) {
1749            // create buffers
1750            fNodeType       = new int[INITIAL_CHUNK_COUNT][];
1751            fNodeName       = new Object[INITIAL_CHUNK_COUNT][];
1752            fNodeValue      = new Object[INITIAL_CHUNK_COUNT][];
1753            fNodeParent     = new int[INITIAL_CHUNK_COUNT][];
1754            fNodeLastChild  = new int[INITIAL_CHUNK_COUNT][];
1755            fNodePrevSib    = new int[INITIAL_CHUNK_COUNT][];
1756            fNodeURI        = new Object[INITIAL_CHUNK_COUNT][];
1757            fNodeExtra      = new int[INITIAL_CHUNK_COUNT][];
1758        }
1759        else if (fNodeType.length <= chunk) {
1760            // resize the tables
1761            int newsize = chunk * 2;
1762
1763            int[][] newArray = new int[newsize][];
1764            System.arraycopy(fNodeType, 0, newArray, 0, chunk);
1765            fNodeType = newArray;
1766
1767            Object[][] newStrArray = new Object[newsize][];
1768            System.arraycopy(fNodeName, 0, newStrArray, 0, chunk);
1769            fNodeName = newStrArray;
1770
1771            newStrArray = new Object[newsize][];
1772            System.arraycopy(fNodeValue, 0, newStrArray, 0, chunk);
1773            fNodeValue = newStrArray;
1774
1775            newArray = new int[newsize][];
1776            System.arraycopy(fNodeParent, 0, newArray, 0, chunk);
1777            fNodeParent = newArray;
1778
1779            newArray = new int[newsize][];
1780            System.arraycopy(fNodeLastChild, 0, newArray, 0, chunk);
1781            fNodeLastChild = newArray;
1782
1783            newArray = new int[newsize][];
1784            System.arraycopy(fNodePrevSib, 0, newArray, 0, chunk);
1785            fNodePrevSib = newArray;
1786
1787            newStrArray = new Object[newsize][];
1788            System.arraycopy(fNodeURI, 0, newStrArray, 0, chunk);
1789            fNodeURI = newStrArray;
1790
1791            newArray = new int[newsize][];
1792            System.arraycopy(fNodeExtra, 0, newArray, 0, chunk);
1793            fNodeExtra = newArray;
1794        }
1795        else if (fNodeType[chunk] != null) {
1796            // Done - there's sufficient capacity
1797            return;
1798        }
1799
1800        // create new chunks
1801        createChunk(fNodeType, chunk);
1802        createChunk(fNodeName, chunk);
1803        createChunk(fNodeValue, chunk);
1804        createChunk(fNodeParent, chunk);
1805        createChunk(fNodeLastChild, chunk);
1806        createChunk(fNodePrevSib, chunk);
1807        createChunk(fNodeURI, chunk);
1808        createChunk(fNodeExtra, chunk);
1809
1810        // Done
1811        return;
1812
1813    } // ensureCapacity(int,int)
1814
1815    /** Creates a node of the specified type. */
1816    protected int createNode(short nodeType) {
1817        // ensure tables are large enough
1818        int chunk = fNodeCount >> CHUNK_SHIFT;
1819        int index = fNodeCount & CHUNK_MASK;
1820        ensureCapacity(chunk);
1821
1822        // initialize node
1823        setChunkIndex(fNodeType, nodeType, chunk, index);
1824
1825        // return node index number
1826        return fNodeCount++;
1827
1828    } // createNode(short):int
1829
1830    /**
1831     * Performs a binary search for a target value in an array of
1832     * values. The array of values must be in ascending sorted order
1833     * before calling this method and all array values must be
1834     * non-negative.
1835     *
1836     * @param values  The array of values to search.
1837     * @param start   The starting offset of the search.
1838     * @param end     The ending offset of the search.
1839     * @param target  The target value.
1840     *
1841     * @return This function will return the <i>first</i> occurrence
1842     *         of the target value, or -1 if the target value cannot
1843     *         be found.
1844     */
1845    protected static int binarySearch(final int values[],
1846                                      int start, int end, int target) {
1847
1848        if (DEBUG_IDS) {
1849            System.out.println("binarySearch(), target: "+target);
1850        }
1851
1852        // look for target value
1853        while (start <= end) {
1854
1855            // is this the one we're looking for?
1856            int middle = (start + end) >>> 1;
1857            int value  = values[middle];
1858            if (DEBUG_IDS) {
1859                System.out.print("  value: "+value+", target: "+target+" // ");
1860                print(values, start, end, middle, target);
1861            }
1862            if (value == target) {
1863                while (middle > 0 && values[middle - 1] == target) {
1864                    middle--;
1865                }
1866                if (DEBUG_IDS) {
1867                    System.out.println("FOUND AT "+middle);
1868                }
1869                return middle;
1870            }
1871
1872            // is this point higher or lower?
1873            if (value > target) {
1874                end = middle - 1;
1875            }
1876            else {
1877                start = middle + 1;
1878            }
1879
1880        } // while
1881
1882        // not found
1883        if (DEBUG_IDS) {
1884            System.out.println("NOT FOUND!");
1885        }
1886        return -1;
1887
1888    } // binarySearch(int[],int,int,int):int
1889
1890    //
1891    // Private methods
1892    //
1893    private static final int[] INIT_ARRAY = new int[CHUNK_SIZE + 1];
1894    static {
1895        for (int i = 0; i < CHUNK_SIZE; i++) {
1896            INIT_ARRAY[i] = -1;
1897        }
1898    }
1899    /** Creates the specified chunk in the given array of chunks. */
1900    private final void createChunk(int data[][], int chunk) {
1901        data[chunk] = new int[CHUNK_SIZE + 1];
1902        System.arraycopy(INIT_ARRAY, 0, data[chunk], 0, CHUNK_SIZE);
1903    }
1904
1905    static final class RefCount {
1906        int fCount;
1907    }
1908
1909    private final void createChunk(Object data[][], int chunk) {
1910        data[chunk] = new Object[CHUNK_SIZE + 1];
1911        data[chunk][CHUNK_SIZE] = new RefCount();
1912    }
1913
1914    /**
1915     * Sets the specified value in the given of data at the chunk and index.
1916     *
1917     * @return Returns the old value.
1918     */
1919    private final int setChunkIndex(int data[][], int value,
1920                                    int chunk, int index) {
1921        if (value == -1) {
1922            return clearChunkIndex(data, chunk, index);
1923        }
1924        int [] dataChunk = data[chunk];
1925        // Re-create chunk if it was deleted.
1926        if (dataChunk == null) {
1927            createChunk(data, chunk);
1928            dataChunk = data[chunk];
1929        }
1930        int ovalue = dataChunk[index];
1931        if (ovalue == -1) {
1932            dataChunk[CHUNK_SIZE]++;
1933        }
1934        dataChunk[index] = value;
1935        return ovalue;
1936    }
1937    private final String setChunkValue(Object data[][], Object value,
1938                                       int chunk, int index) {
1939        if (value == null) {
1940            return clearChunkValue(data, chunk, index);
1941        }
1942        Object [] dataChunk = data[chunk];
1943        // Re-create chunk if it was deleted.
1944        if (dataChunk == null) {
1945            createChunk(data, chunk);
1946            dataChunk = data[chunk];
1947        }
1948        String ovalue = (String) dataChunk[index];
1949        if (ovalue == null) {
1950            RefCount c = (RefCount) dataChunk[CHUNK_SIZE];
1951            c.fCount++;
1952        }
1953        dataChunk[index] = value;
1954        return ovalue;
1955    }
1956
1957    /**
1958     * Returns the specified value in the given data at the chunk and index.
1959     */
1960    private final int getChunkIndex(int data[][], int chunk, int index) {
1961        return data[chunk] != null ? data[chunk][index] : -1;
1962    }
1963    private final String getChunkValue(Object data[][], int chunk, int index) {
1964        return data[chunk] != null ? (String) data[chunk][index] : null;
1965    }
1966    private final String getNodeValue(int chunk, int index) {
1967        Object data = fNodeValue[chunk][index];
1968        if (data == null){
1969            return null;
1970        }
1971        else if (data instanceof String){
1972            return (String)data;
1973        }
1974        else {
1975            // type information
1976            return data.toString();
1977        }
1978    }
1979
1980
1981    /**
1982     * Clears the specified value in the given data at the chunk and index.
1983     * Note that this method will clear the given chunk if the reference
1984     * count becomes zero.
1985     *
1986     * @return Returns the old value.
1987     */
1988    private final int clearChunkIndex(int data[][], int chunk, int index) {
1989        int value = data[chunk] != null ? data[chunk][index] : -1;
1990        if (value != -1) {
1991            data[chunk][CHUNK_SIZE]--;
1992            data[chunk][index] = -1;
1993            if (data[chunk][CHUNK_SIZE] == 0) {
1994                data[chunk] = null;
1995            }
1996        }
1997        return value;
1998    }
1999    private final String clearChunkValue(Object data[][],
2000                                         int chunk, int index) {
2001        String value = data[chunk] != null ? (String)data[chunk][index] : null;
2002        if (value != null) {
2003            data[chunk][index] = null;
2004            RefCount c = (RefCount) data[chunk][CHUNK_SIZE];
2005            c.fCount--;
2006            if (c.fCount == 0) {
2007                data[chunk] = null;
2008            }
2009        }
2010        return value;
2011    }
2012
2013    /**
2014     * This version of putIdentifier is needed to avoid fluffing
2015     * all of the paths to ID attributes when a node object is
2016     * created that contains an ID attribute.
2017     */
2018    private final void putIdentifier0(String idName, Element element) {
2019
2020        if (DEBUG_IDS) {
2021            System.out.println("putIdentifier0("+
2022                               idName+", "+
2023                               element+')');
2024        }
2025
2026        // create Map
2027        if (identifiers == null) {
2028            identifiers = new HashMap<>();
2029        }
2030
2031        // save ID and its associated element
2032        identifiers.put(idName, element);
2033
2034    } // putIdentifier0(String,Element)
2035
2036    /** Prints the ID array. */
2037    private static void print(int values[], int start, int end,
2038                              int middle, int target) {
2039
2040        if (DEBUG_IDS) {
2041            System.out.print(start);
2042            System.out.print(" [");
2043            for (int i = start; i < end; i++) {
2044                if (middle == i) {
2045                    System.out.print("!");
2046                }
2047                System.out.print(values[i]);
2048                if (values[i] == target) {
2049                    System.out.print("*");
2050                }
2051                if (i < end - 1) {
2052                    System.out.print(" ");
2053                }
2054            }
2055            System.out.println("] "+end);
2056        }
2057
2058    } // print(int[],int,int,int,int)
2059
2060    //
2061    // Classes
2062    //
2063
2064    /**
2065     * A simple integer vector.
2066     */
2067    static final class IntVector {
2068
2069        //
2070        // Data
2071        //
2072
2073        /** Data. */
2074        private int data[];
2075
2076        /** Size. */
2077        private int size;
2078
2079        //
2080        // Public methods
2081        //
2082
2083        /** Returns the length of this vector. */
2084        public int size() {
2085            return size;
2086        }
2087
2088        /** Returns the element at the specified index. */
2089        public int elementAt(int index) {
2090            return data[index];
2091        }
2092
2093        /** Appends an element to the end of the vector. */
2094        public void addElement(int element) {
2095            ensureCapacity(size + 1);
2096            data[size++] = element;
2097        }
2098
2099        /** Clears the vector. */
2100        public void removeAllElements() {
2101            size = 0;
2102        }
2103
2104        //
2105        // Private methods
2106        //
2107
2108        /** Makes sure that there is enough storage. */
2109        private void ensureCapacity(int newsize) {
2110
2111            if (data == null) {
2112                data = new int[newsize + 15];
2113            }
2114            else if (newsize > data.length) {
2115                int newdata[] = new int[newsize + 15];
2116                System.arraycopy(data, 0, newdata, 0, data.length);
2117                data = newdata;
2118            }
2119
2120        } // ensureCapacity(int)
2121
2122    } // class IntVector
2123
2124} // class DeferredDocumentImpl
2125