~vcs-imports/xena/trunk

« back to all changes in this revision

Viewing changes to ext/src/xerces-2_9_1/src/org/apache/xerces/dom/DeferredDocumentImpl.java

  • Committer: matthewoliver
  • Date: 2009-12-10 03:18:07 UTC
  • Revision ID: vcs-imports@canonical.com-20091210031807-l086qguzdlljtkl9
Merged Xena Testing into Xena Stable for the Xena 5 release.

Show diffs side-by-side

added added

removed removed

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