OpenCores
URL https://opencores.org/ocsvn/scarts/scarts/trunk

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libjava/] [classpath/] [gnu/] [xml/] [dom/] [DomNode.java] - Blame information for rev 14

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 14 jlechner
/* DomNode.java --
2
   Copyright (C) 1999,2000,2001,2004 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
package gnu.xml.dom;
39
 
40
import java.util.HashMap;
41
import java.util.Iterator;
42
import java.util.Map;
43
 
44
import org.w3c.dom.Document;
45
import org.w3c.dom.DOMException;
46
import org.w3c.dom.DOMImplementation;
47
import org.w3c.dom.NamedNodeMap;
48
import org.w3c.dom.Node;
49
import org.w3c.dom.NodeList;
50
import org.w3c.dom.Text;
51
import org.w3c.dom.UserDataHandler;
52
import org.w3c.dom.events.DocumentEvent;
53
import org.w3c.dom.events.Event;
54
import org.w3c.dom.events.EventException;
55
import org.w3c.dom.events.EventListener;
56
import org.w3c.dom.events.EventTarget;
57
import org.w3c.dom.events.MutationEvent;
58
import org.w3c.dom.traversal.NodeFilter;
59
import org.w3c.dom.traversal.NodeIterator;
60
 
61
/**
62
 * <p> "Node", "EventTarget", and "DocumentEvent" implementation.
63
 * This provides most of the core DOM functionality; only more
64
 * specialized features are provided by subclasses.  Those subclasses may
65
 * have some particular constraints they must implement, by overriding
66
 * methods defined here.  Such constraints are noted here in the method
67
 * documentation. </p>
68
 *
69
 * <p> Note that you can create events with type names prefixed with "USER-",
70
 * and pass them through this DOM.  This lets you use the DOM event scheme
71
 * for application specific purposes, although you must use a predefined event
72
 * structure (such as MutationEvent) to pass data along with those events.
73
 * Test for existence of this feature with the "USER-Events" DOM feature
74
 * name.</p>
75
 *
76
 * <p> Other kinds of events you can send include the "html" events,
77
 * like "load", "unload", "abort", "error", and "blur"; and the mutation
78
 * events.  If this DOM has been compiled with mutation event support
79
 * enabled, it will send mutation events when you change parts of the
80
 * tree; otherwise you may create and send such events yourself, but
81
 * they won't be generated by the DOM itself. </p>
82
 *
83
 * <p> Note that there is a namespace-aware name comparison method,
84
 * <em>nameAndTypeEquals</em>, which compares the names (and types) of
85
 * two nodes in conformance with the "Namespaces in XML" specification.
86
 * While mostly intended for use with elements and attributes, this should
87
 * also be helpful for ProcessingInstruction nodes and some others which
88
 * do not have namespace URIs.
89
 *
90
 * @author David Brownell
91
 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
92
 */
93
public abstract class DomNode
94
  implements Node, NodeList, EventTarget, DocumentEvent, Cloneable, Comparable
95
{
96
 
97
  // package private
98
  //final static String xmlNamespace = "http://www.w3.org/XML/1998/namespace";
99
  //final static String xmlnsURI = "http://www.w3.org/2000/xmlns/";
100
 
101
  // tunable
102
  //    NKIDS_* affects arrays of children (which grow)
103
  // (currently) fixed size:
104
  //    ANCESTORS_* is for event capture/bubbling, # ancestors
105
  //    NOTIFICATIONS_* is for per-node event delivery, # events
106
  private static final int NKIDS_DELTA = 8;
107
  private static final int ANCESTORS_INIT = 20;
108
  private static final int NOTIFICATIONS_INIT = 10;
109
 
110
  // tunable: enable mutation events or not?  Enabling it costs about
111
  // 10-15% in DOM construction time, last time it was measured.
112
 
113
  // package private !!!
114
  static final boolean reportMutations = true;
115
 
116
  // locking protocol changeable only within this class
117
  private static final Object lockNode = new Object();
118
 
119
  // NON-FINAL class data
120
 
121
  // Optimize event dispatch by not allocating memory each time
122
  private static boolean dispatchDataLock;
123
  private static DomNode[] ancestors = new DomNode[ANCESTORS_INIT];
124
  private static ListenerRecord[] notificationSet
125
    = new ListenerRecord[NOTIFICATIONS_INIT];
126
 
127
  // Ditto for the (most common) event object itself!
128
  private static boolean eventDataLock;
129
  private static DomEvent.DomMutationEvent mutationEvent
130
    = new DomEvent.DomMutationEvent(null);
131
 
132
  //
133
  // PER-INSTANCE DATA
134
  //
135
 
136
  DomDocument owner;
137
  DomNode parent; // parent node;
138
  DomNode previous; // previous sibling node
139
  DomNode next; // next sibling node
140
  DomNode first; // first child node
141
  DomNode last; // last child node
142
  int index; // index of this node in its parent's children
143
  int depth; // depth of the node in the document
144
  int length; // number of children
145
  final short nodeType;
146
 
147
  // Bleech ... "package private" so a builder can populate entity refs.
148
  // writable during construction.  DOM spec is nasty.
149
  boolean readonly;
150
 
151
  // event registrations
152
  private ListenerRecord[] listeners;
153
  private int nListeners;
154
 
155
  // DOM Level 3 userData dictionary.
156
  private HashMap userData;
157
  private HashMap userDataHandlers;
158
 
159
  //
160
  // Some of the methods here are declared 'final' because
161
  // knowledge about their implementation is built into this
162
  // class -- for both integrity and performance.
163
  //
164
 
165
  /**
166
   * Reduces space utilization for this node.
167
   */
168
  public void compact()
169
  {
170
    if (listeners != null && listeners.length != nListeners)
171
      {
172
        if (nListeners == 0)
173
          {
174
            listeners = null;
175
          }
176
        else
177
          {
178
            ListenerRecord[] l = new ListenerRecord[nListeners];
179
            System.arraycopy(listeners, 0, l, 0, nListeners);
180
            listeners = l;
181
          }
182
      }
183
  }
184
 
185
  /**
186
   * Constructs a node and associates it with its owner.  Only
187
   * Document and DocumentType nodes may be created with no owner,
188
   * and DocumentType nodes get an owner as soon as they are
189
   * associated with a document.
190
   */
191
  protected DomNode(short nodeType, DomDocument owner)
192
  {
193
    this.nodeType = nodeType;
194
 
195
    if (owner == null)
196
      {
197
        // DOM calls never go down this path
198
        if (nodeType != DOCUMENT_NODE && nodeType != DOCUMENT_TYPE_NODE)
199
          {
200
            throw new IllegalArgumentException ("no owner!");
201
          }
202
      }
203
    this.owner = owner;
204
  }
205
 
206
 
207
  /**
208
   * <b>DOM L1</b>
209
   * Returns null; Element subclasses must override this method.
210
   */
211
  public NamedNodeMap getAttributes()
212
  {
213
    return null;
214
  }
215
 
216
  /**
217
   * <b>DOM L2></b>
218
   * Returns true iff this is an element node with attributes.
219
   */
220
  public boolean hasAttributes()
221
  {
222
    return false;
223
  }
224
 
225
  /**
226
   * <b>DOM L1</b>
227
   * Returns a list, possibly empty, of the children of this node.
228
   * In this implementation, to conserve memory, nodes are the same
229
   * as their list of children.  This can have ramifications for
230
   * subclasses, which may need to provide their own getLength method
231
   * for reasons unrelated to the NodeList method of the same name.
232
   */
233
  public NodeList getChildNodes()
234
  {
235
    return this;
236
  }
237
 
238
  /**
239
   * <b>DOM L1</b>
240
   * Returns the first child of this node, or null if there are none.
241
   */
242
  public Node getFirstChild()
243
  {
244
    return first;
245
  }
246
 
247
  /**
248
   * <b>DOM L1</b>
249
   * Returns the last child of this node, or null if there are none.
250
   */
251
  public Node getLastChild()
252
  {
253
    return last;
254
  }
255
 
256
  /**
257
   * <b>DOM L1</b>
258
   * Returns true if this node has children.
259
   */
260
  public boolean hasChildNodes()
261
  {
262
    return length != 0;
263
  }
264
 
265
 
266
  /**
267
   * Exposes the internal "readonly" flag.  In DOM, children of
268
   * entities and entity references are readonly, as are the
269
   * objects associated with DocumentType objets.
270
   */
271
  public final boolean isReadonly()
272
  {
273
    return readonly;
274
  }
275
 
276
  /**
277
   * Sets the internal "readonly" flag so this subtree can't be changed.
278
   * Subclasses need to override this method for any associated content
279
   * that's not a child node, such as an element's attributes or the
280
   * (few) declarations associated with a DocumentType.
281
   */
282
  public void makeReadonly()
283
  {
284
    readonly = true;
285
    for (DomNode child = first; child != null; child = child.next)
286
      {
287
        child.makeReadonly();
288
      }
289
  }
290
 
291
  /**
292
   * Used to adopt a node to a new document.
293
   */
294
  void setOwner(DomDocument doc)
295
  {
296
    this.owner = doc;
297
    for (DomNode ctx = first; ctx != null; ctx = ctx.next)
298
      {
299
        ctx.setOwner(doc);
300
      }
301
  }
302
 
303
  // just checks the node for inclusion -- may be called many
304
  // times (docfrag) before anything is allowed to change
305
  private void checkMisc(DomNode child)
306
  {
307
    if (readonly && !owner.building)
308
      {
309
        throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
310
                                  null, this, 0);
311
      }
312
    for (DomNode ctx = this; ctx != null; ctx = ctx.parent)
313
      {
314
        if (child == ctx)
315
          {
316
            throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
317
                                      "can't make ancestor into a child",
318
                                      this, 0);
319
          }
320
      }
321
 
322
    DomDocument owner = (nodeType == DOCUMENT_NODE) ? (DomDocument) this :
323
      this.owner;
324
    DomDocument childOwner = child.owner;
325
    short childNodeType = child.nodeType;
326
 
327
    if (childOwner != owner)
328
      {
329
        // new in DOM L2, this case -- patch it up later, in reparent()
330
        if (!(childNodeType == DOCUMENT_TYPE_NODE && childOwner == null))
331
          {
332
            throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
333
                                      null, child, 0);
334
          }
335
      }
336
 
337
    // enforce various structural constraints
338
    switch (nodeType)
339
      {
340
      case DOCUMENT_NODE:
341
        switch (childNodeType)
342
          {
343
          case ELEMENT_NODE:
344
          case PROCESSING_INSTRUCTION_NODE:
345
          case COMMENT_NODE:
346
          case DOCUMENT_TYPE_NODE:
347
            return;
348
          }
349
        break;
350
 
351
      case ATTRIBUTE_NODE:
352
        switch (childNodeType)
353
          {
354
          case TEXT_NODE:
355
          case ENTITY_REFERENCE_NODE:
356
            return;
357
          }
358
        break;
359
 
360
      case DOCUMENT_FRAGMENT_NODE:
361
      case ENTITY_REFERENCE_NODE:
362
      case ELEMENT_NODE:
363
      case ENTITY_NODE:
364
        switch (childNodeType)
365
          {
366
          case ELEMENT_NODE:
367
          case TEXT_NODE:
368
          case COMMENT_NODE:
369
          case PROCESSING_INSTRUCTION_NODE:
370
          case CDATA_SECTION_NODE:
371
          case ENTITY_REFERENCE_NODE:
372
            return;
373
          }
374
        break;
375
      }
376
    if (owner.checkingWellformedness)
377
      {
378
        throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
379
                                  "can't append " +
380
                                  nodeTypeToString(childNodeType) +
381
                                  " to node of type " +
382
                                  nodeTypeToString(nodeType),
383
                                  this, 0);
384
      }
385
  }
386
 
387
  // Here's hoping a good optimizer will detect the case when the
388
  // next several methods are never called, and won't allocate
389
  // object code space of any kind.  (Case:  not reporting any
390
  // mutation events.  We can also remove some static variables
391
  // listed above.)
392
 
393
  private void insertionEvent(DomEvent.DomMutationEvent event,
394
                              DomNode target)
395
  {
396
    if (owner == null || owner.building)
397
      {
398
        return;
399
      }
400
    boolean doFree = false;
401
 
402
    if (event == null)
403
      {
404
        event = getMutationEvent();
405
      }
406
    if (event != null)
407
      {
408
        doFree = true;
409
      }
410
    else
411
      {
412
        event = new DomEvent.DomMutationEvent(null);
413
      }
414
    event.initMutationEvent("DOMNodeInserted",
415
                            true /* bubbles */, false /* nocancel */,
416
                            this /* related */, null, null, null, (short) 0);
417
    target.dispatchEvent(event);
418
 
419
    // XXX should really visit every descendant of 'target'
420
    // and sent a DOMNodeInsertedIntoDocument event to it...
421
    // bleech, there's no way to keep that acceptably fast.
422
 
423
    if (doFree)
424
      {
425
        event.target = null;
426
        event.relatedNode = null;
427
        event.currentNode = null;
428
        eventDataLock = false;
429
      } // else we created work for the GC
430
  }
431
 
432
  private void removalEvent(DomEvent.DomMutationEvent event,
433
                            DomNode target)
434
  {
435
    if (owner == null || owner.building)
436
      {
437
        return;
438
      }
439
    boolean doFree = false;
440
 
441
    if (event == null)
442
      {
443
        event = getMutationEvent();
444
      }
445
    if (event != null)
446
      {
447
        doFree = true;
448
      }
449
    else
450
      {
451
        event = new DomEvent.DomMutationEvent(null);
452
      }
453
    event.initMutationEvent("DOMNodeRemoved",
454
                            true /* bubbles */, false /* nocancel */,
455
                            this /* related */, null, null, null, (short) 0);
456
    target.dispatchEvent(event);
457
 
458
    // XXX should really visit every descendant of 'target'
459
    // and sent a DOMNodeRemovedFromDocument event to it...
460
    // bleech, there's no way to keep that acceptably fast.
461
 
462
    event.target = null;
463
    event.relatedNode = null;
464
    event.currentNode = null;
465
    if (doFree)
466
      {
467
        eventDataLock = false;
468
      }
469
    // else we created more work for the GC
470
  }
471
 
472
  //
473
  // Avoid creating lots of memory management work, by using a simple
474
  // allocation strategy for the mutation event objects that get used
475
  // at least once per tree modification.  We can't use stack allocation,
476
  // so we do the next simplest thing -- more or less, static allocation.
477
  // Concurrent notifications should be rare, anyway.
478
  //
479
  // Returns the preallocated object, which needs to be carefully freed,
480
  // or null to indicate the caller needs to allocate their own.
481
  //
482
  static private DomEvent.DomMutationEvent getMutationEvent()
483
  {
484
    synchronized (lockNode)
485
      {
486
        if (eventDataLock)
487
          {
488
            return null;
489
          }
490
        eventDataLock = true;
491
        return mutationEvent;
492
      }
493
  }
494
 
495
  // NOTE:  this is manually inlined in the insertion
496
  // and removal event methods above; change in sync.
497
  static private void freeMutationEvent()
498
  {
499
    // clear fields to enable GC
500
    mutationEvent.clear();
501
    eventDataLock = false;
502
  }
503
 
504
  void setDepth(int depth)
505
  {
506
    this.depth = depth;
507
    for (DomNode ctx = first; ctx != null; ctx = ctx.next)
508
      {
509
        ctx.setDepth(depth + 1);
510
      }
511
  }
512
 
513
  /**
514
   * <b>DOM L1</b>
515
   * Appends the specified node to this node's list of children.
516
   * Document subclasses must override this to enforce the restrictions
517
   * that there be only one element and document type child.
518
   *
519
   * <p> Causes a DOMNodeInserted mutation event to be reported.
520
   * Will first cause a DOMNodeRemoved event to be reported if the
521
   * parameter already has a parent.  If the new child is a document
522
   * fragment node, both events will be reported for each child of
523
   * the fragment; the order in which children are removed and
524
   * inserted is implementation-specific.
525
   *
526
   * <p> If this DOM has been compiled without mutation event support,
527
   * these events will not be reported.
528
   */
529
  public Node appendChild(Node newChild)
530
  {
531
    try
532
      {
533
        DomNode child = (DomNode) newChild;
534
 
535
        if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
536
          {
537
            // Append all nodes in the fragment to this node
538
            for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
539
              {
540
                checkMisc(ctx);
541
              }
542
            for (DomNode ctx = child.first; ctx != null; )
543
              {
544
                DomNode ctxNext = ctx.next;
545
                appendChild(ctx);
546
                ctx = ctxNext;
547
              }
548
          }
549
        else
550
          {
551
            checkMisc(child);
552
            if (child.parent != null)
553
              {
554
                child.parent.removeChild(child);
555
              }
556
            child.parent = this;
557
            child.index = length++;
558
            child.setDepth(depth + 1);
559
            child.next = null;
560
            if (last == null)
561
              {
562
                first = child;
563
                child.previous = null;
564
              }
565
            else
566
              {
567
                last.next = child;
568
                child.previous = last;
569
              }
570
            last = child;
571
 
572
            if (reportMutations)
573
              {
574
                insertionEvent(null, child);
575
              }
576
          }
577
 
578
        return child;
579
      }
580
    catch (ClassCastException e)
581
      {
582
        throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
583
                                  null, newChild, 0);
584
    }
585
  }
586
 
587
  /**
588
   * <b>DOM L1</b>
589
   * Inserts the specified node in this node's list of children.
590
   * Document subclasses must override this to enforce the restrictions
591
   * that there be only one element and document type child.
592
   *
593
   * <p> Causes a DOMNodeInserted mutation event to be reported.  Will
594
   * first cause a DOMNodeRemoved event to be reported if the newChild
595
   * parameter already has a parent. If the new child is a document
596
   * fragment node, both events will be reported for each child of
597
   * the fragment; the order in which children are removed and inserted
598
   * is implementation-specific.
599
   *
600
   * <p> If this DOM has been compiled without mutation event support,
601
   * these events will not be reported.
602
   */
603
  public Node insertBefore(Node newChild, Node refChild)
604
  {
605
    if (refChild == null)
606
      {
607
        return appendChild(newChild);
608
      }
609
 
610
    try
611
      {
612
        DomNode child = (DomNode) newChild;
613
        DomNode ref = (DomNode) refChild;
614
 
615
        if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
616
          {
617
            // Append all nodes in the fragment to this node
618
            for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
619
              {
620
                checkMisc(ctx);
621
              }
622
            for (DomNode ctx = child.first; ctx != null; )
623
              {
624
                DomNode ctxNext = ctx.next;
625
                insertBefore(ctx, ref);
626
                ctx = ctxNext;
627
              }
628
          }
629
        else
630
          {
631
            checkMisc(child);
632
            if (ref == null || ref.parent != this)
633
              {
634
                throw new DomDOMException(DOMException.NOT_FOUND_ERR,
635
                                          null, ref, 0);
636
              }
637
            if (ref == child)
638
              {
639
                throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
640
                                          "can't insert node before itself",
641
                                          ref, 0);
642
              }
643
 
644
            if (child.parent != null)
645
              {
646
                child.parent.removeChild(child);
647
              }
648
            child.parent = this;
649
            int i = ref.index;
650
            child.setDepth(depth + 1);
651
            child.next = ref;
652
            if (ref.previous != null)
653
              {
654
                ref.previous.next = child;
655
              }
656
            child.previous = ref.previous;
657
            ref.previous = child;
658
            if (first == ref)
659
              {
660
                first = child;
661
              }
662
            // index renumbering
663
            for (DomNode ctx = child; ctx != null; ctx = ctx.next)
664
              {
665
                ctx.index = i++;
666
              }
667
 
668
            if (reportMutations)
669
              {
670
                insertionEvent(null, child);
671
              }
672
          }
673
 
674
        return child;
675
      }
676
    catch (ClassCastException e)
677
      {
678
        throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
679
                                  null, newChild, 0);
680
      }
681
  }
682
 
683
  /**
684
   * <b>DOM L1</b>
685
   * Replaces the specified node in this node's list of children.
686
   * Document subclasses must override this to test the restrictions
687
   * that there be only one element and document type child.
688
   *
689
   * <p> Causes DOMNodeRemoved and DOMNodeInserted mutation event to be
690
   * reported.  Will cause another DOMNodeRemoved event to be reported if
691
   * the newChild parameter already has a parent.  These events may be
692
   * delivered in any order, except that the event reporting removal
693
   * from such an existing parent will always be delivered before the
694
   * event reporting its re-insertion as a child of some other node.
695
   * The order in which children are removed and inserted is implementation
696
   * specific.
697
   *
698
   * <p> If your application needs to depend on the in which those removal
699
   * and insertion events are delivered, don't use this API.  Instead,
700
   * invoke the removeChild and insertBefore methods directly, to guarantee
701
   * a specific delivery order.  Similarly, don't use document fragments,
702
   * Otherwise your application code may not work on a DOM which implements
703
   * this method differently.
704
   *
705
   * <p> If this DOM has been compiled without mutation event support,
706
   * these events will not be reported.
707
   */
708
  public Node replaceChild(Node newChild, Node refChild)
709
  {
710
    try
711
      {
712
        DomNode child = (DomNode) newChild;
713
        DomNode ref = (DomNode) refChild;
714
 
715
        DomEvent.DomMutationEvent event = getMutationEvent();
716
        boolean doFree = (event != null);
717
 
718
        if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
719
          {
720
            // Append all nodes in the fragment to this node
721
            for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
722
              {
723
                checkMisc(ctx);
724
              }
725
            if (ref == null || ref.parent != this)
726
              {
727
                throw new DomDOMException(DOMException.NOT_FOUND_ERR,
728
                                          null, ref, 0);
729
              }
730
 
731
            if (reportMutations)
732
              {
733
                removalEvent(event, ref);
734
              }
735
            length--;
736
            length += child.length;
737
 
738
            if (child.length == 0)
739
              {
740
                // Removal
741
                if (ref.previous != null)
742
                  {
743
                    ref.previous.next = ref.next;
744
                  }
745
                if (ref.next != null)
746
                  {
747
                    ref.next.previous = ref.previous;
748
                  }
749
                if (first == ref)
750
                  {
751
                    first = ref.next;
752
                  }
753
                if (last == ref)
754
                  {
755
                    last = ref.previous;
756
                  }
757
              }
758
            else
759
              {
760
                int i = ref.index;
761
                for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
762
                  {
763
                    // Insertion
764
                    ctx.parent = this;
765
                    ctx.index = i++;
766
                    ctx.setDepth(ref.depth);
767
                    if (ctx == child.first)
768
                      {
769
                        ctx.previous = ref.previous;
770
                      }
771
                    if (ctx == child.last)
772
                      {
773
                        ctx.next = ref.next;
774
                      }
775
                  }
776
                if (first == ref)
777
                  {
778
                    first = child.first;
779
                  }
780
                if (last == ref)
781
                  {
782
                    last = child.last;
783
                  }
784
              }
785
          }
786
        else
787
          {
788
            checkMisc(child);
789
            if (ref == null || ref.parent != this)
790
              {
791
                throw new DomDOMException(DOMException.NOT_FOUND_ERR,
792
                                          null, ref, 0);
793
              }
794
 
795
            if (reportMutations)
796
              {
797
                removalEvent(event, ref);
798
              }
799
 
800
            if (child.parent != null)
801
              {
802
                child.parent.removeChild(child);
803
              }
804
            child.parent = this;
805
            child.index = ref.index;
806
            child.setDepth(ref.depth);
807
            if (ref.previous != null)
808
              {
809
                ref.previous.next = child;
810
              }
811
            child.previous = ref.previous;
812
            if (ref.next != null)
813
              {
814
                ref.next.previous = child;
815
              }
816
            child.next = ref.next;
817
            if (first == ref)
818
              {
819
                first = child;
820
              }
821
            if (last == ref)
822
              {
823
                last = child;
824
              }
825
 
826
            if (reportMutations)
827
              {
828
                insertionEvent(event, child);
829
              }
830
            if (doFree)
831
              {
832
                freeMutationEvent();
833
              }
834
          }
835
        ref.parent = null;
836
        ref.index = 0;
837
        ref.setDepth(0);
838
        ref.previous = null;
839
        ref.next = null;
840
 
841
        return ref;
842
      }
843
    catch (ClassCastException e)
844
      {
845
        throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
846
                                  null, newChild, 0);
847
      }
848
  }
849
 
850
  /**
851
   * <b>DOM L1</b>
852
   * Removes the specified child from this node's list of children,
853
   * or else reports an exception.
854
   *
855
   * <p> Causes a DOMNodeRemoved mutation event to be reported.
856
   *
857
   * <p> If this DOM has been compiled without mutation event support,
858
   * these events will not be reported.
859
   */
860
  public Node removeChild(Node refChild)
861
  {
862
    try
863
      {
864
        DomNode ref = (DomNode) refChild;
865
 
866
        if (ref == null || ref.parent != this)
867
          {
868
            throw new DomDOMException(DOMException.NOT_FOUND_ERR,
869
                                      null, ref, 0);
870
          }
871
        if (readonly && !owner.building)
872
          {
873
            throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
874
                                      null, this, 0);
875
          }
876
 
877
        for (DomNode child = first; child != null; child = child.next)
878
          {
879
            if (child == ref)
880
              {
881
                if (reportMutations)
882
                  {
883
                    removalEvent(null, child);
884
                  }
885
 
886
                length--;
887
                if (ref.previous != null)
888
                  {
889
                    ref.previous.next = ref.next;
890
                  }
891
                if (ref.next != null)
892
                  {
893
                    ref.next.previous = ref.previous;
894
                  }
895
                if (first == ref)
896
                  {
897
                    first = ref.next;
898
                  }
899
                if (last == ref)
900
                  {
901
                    last = ref.previous;
902
                  }
903
                // renumber indices
904
                int i = 0;
905
                for (DomNode ctx = first; ctx != null; ctx = ctx.next)
906
                  {
907
                    ctx.index = i++;
908
                  }
909
                ref.parent = null;
910
                ref.setDepth(0);
911
                ref.index = 0;
912
                ref.previous = null;
913
                ref.next = null;
914
 
915
                return ref;
916
              }
917
          }
918
        throw new DomDOMException(DOMException.NOT_FOUND_ERR,
919
                                  "that's no child of mine", refChild, 0);
920
      }
921
    catch (ClassCastException e)
922
      {
923
        throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
924
                                  null, refChild, 0);
925
      }
926
  }
927
 
928
  /**
929
   * <b>DOM L1 (NodeList)</b>
930
   * Returns the item with the specified index in this NodeList,
931
   * else null.
932
   */
933
  public Node item(int index)
934
  {
935
    DomNode child = first;
936
    int count = 0;
937
    while (child != null && count < index)
938
      {
939
        child = child.next;
940
        count++;
941
      }
942
    return child;
943
  }
944
 
945
  /**
946
   * <b>DOM L1 (NodeList)</b>
947
   * Returns the number of elements in this NodeList.
948
   * (Note that many interfaces have a "Length" property, not just
949
   * NodeList, and if a node subtype must implement one of those,
950
   * it will also need to override getChildNodes.)
951
   */
952
  public int getLength()
953
  {
954
    return length;
955
  }
956
 
957
  /**
958
   * Minimize extra space consumed by this node to hold children and event
959
   * listeners.
960
   */
961
  public void trimToSize()
962
  {
963
    if (listeners != null && listeners.length != nListeners)
964
      {
965
        ListenerRecord[] newKids = new ListenerRecord[length];
966
        System.arraycopy(listeners, 0, newKids, 0, nListeners);
967
        listeners = newKids;
968
      }
969
  }
970
 
971
  /**
972
   * <b>DOM L1</b>
973
   * Returns the previous sibling, if one is known.
974
   */
975
  public Node getNextSibling()
976
  {
977
    return next;
978
  }
979
 
980
  /**
981
   * <b>DOM L1</b>
982
   * Returns the previous sibling, if one is known.
983
   */
984
  public Node getPreviousSibling()
985
  {
986
    return previous;
987
  }
988
 
989
  /**
990
   * <b>DOM L1</b>
991
   * Returns the parent node, if one is known.
992
   */
993
  public Node getParentNode()
994
  {
995
    return parent;
996
  }
997
 
998
  /**
999
   * <b>DOM L2</b>
1000
   * Consults the DOM implementation to determine if the requested
1001
   * feature is supported.  DocumentType subclasses must override
1002
   * this method, and associate themselves directly with the
1003
   * DOMImplementation node used.  (This method relies on being able
1004
   * to access the DOMImplementation from the owner document, but
1005
   * DocumentType nodes can be created without an owner.)
1006
   */
1007
  public boolean isSupported(String feature, String version)
1008
  {
1009
    Document            doc = owner;
1010
    DOMImplementation   impl = null;
1011
 
1012
    if (doc == null && nodeType == DOCUMENT_NODE)
1013
      {
1014
        doc = (Document) this;
1015
      }
1016
 
1017
    if (doc == null)
1018
      {
1019
        // possible for DocumentType
1020
        throw new IllegalStateException ("unbound ownerDocument");
1021
      }
1022
 
1023
    impl = doc.getImplementation();
1024
    return impl.hasFeature(feature, version);
1025
  }
1026
 
1027
  /**
1028
   * <b>DOM L1 (modified in L2)</b>
1029
   * Returns the owner document.  This is only null for Document nodes,
1030
   * and (new in L2) for DocumentType nodes which have not yet been
1031
   * associated with the rest of their document.
1032
   */
1033
  final public Document getOwnerDocument()
1034
  {
1035
    return owner;
1036
  }
1037
 
1038
  /**
1039
   * <b>DOM L1</b>
1040
   * Does nothing; this must be overridden (along with the
1041
   * getNodeValue method) for nodes with a non-null defined value.
1042
   */
1043
  public void setNodeValue(String value)
1044
  {
1045
  }
1046
 
1047
  /**
1048
   * <b>DOM L1</b>
1049
   * Returns null; this must be overridden for nodes types with
1050
   * a defined value, along with the setNodeValue method.
1051
   */
1052
  public String getNodeValue()
1053
  {
1054
    return null;
1055
  }
1056
 
1057
  /** This forces GCJ compatibility.
1058
   * Without this method GCJ is unable to compile to byte code.
1059
   */
1060
  public final short getNodeType()
1061
  {
1062
    return nodeType;
1063
  }
1064
 
1065
  /** This forces GCJ compatibility.
1066
   * Without this method GCJ seems unable to natively compile GNUJAXP.
1067
   */
1068
  public abstract String getNodeName();
1069
 
1070
  /**
1071
   * <b>DOM L2</b>
1072
   * Does nothing; this must be overridden (along with the
1073
   * getPrefix method) for element and attribute nodes.
1074
   */
1075
  public void setPrefix(String prefix)
1076
  {
1077
  }
1078
 
1079
  /**
1080
   * <b>DOM L2</b>
1081
   * Returns null; this must be overridden for element and
1082
   * attribute nodes.
1083
   */
1084
  public String getPrefix()
1085
  {
1086
    return null;
1087
  }
1088
 
1089
  /**
1090
   * <b>DOM L2</b>
1091
   * Returns null; this must be overridden for element and
1092
   * attribute nodes.
1093
   */
1094
  public String getNamespaceURI()
1095
  {
1096
    return null;
1097
  }
1098
 
1099
  /**
1100
   * <b>DOM L2</b>
1101
   * Returns the node name; this must be overridden for element and
1102
   * attribute nodes.
1103
   */
1104
  public String getLocalName()
1105
  {
1106
    return null;
1107
  }
1108
 
1109
  /**
1110
   * <b>DOM L1</b>
1111
   * Returns a clone of this node which optionally includes cloned
1112
   * versions of child nodes.  Clones are always mutable, except for
1113
   * entity reference nodes.
1114
   */
1115
  public Node cloneNode(boolean deep)
1116
  {
1117
    DomNode node = (DomNode) clone();
1118
 
1119
    if (deep)
1120
      {
1121
        DomDocument doc = (nodeType == DOCUMENT_NODE) ?
1122
          (DomDocument) node : node.owner;
1123
        for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1124
          {
1125
            DomNode newChild = (DomNode) ctx.cloneNode(deep);
1126
            newChild.setOwner(doc);
1127
            node.appendChild(newChild);
1128
          }
1129
      }
1130
 
1131
    if (nodeType == ENTITY_REFERENCE_NODE)
1132
      {
1133
        node.makeReadonly();
1134
      }
1135
    notifyUserDataHandlers(UserDataHandler.NODE_CLONED, this, node);
1136
    return node;
1137
  }
1138
 
1139
  void notifyUserDataHandlers(short op, Node src, Node dst)
1140
  {
1141
    if (userDataHandlers != null)
1142
      {
1143
        for (Iterator i = userDataHandlers.entrySet().iterator(); i.hasNext(); )
1144
          {
1145
            Map.Entry entry = (Map.Entry) i.next();
1146
            String key = (String) entry.getKey();
1147
            UserDataHandler handler = (UserDataHandler) entry.getValue();
1148
            Object data = userData.get(key);
1149
            handler.handle(op, key, data, src, dst);
1150
          }
1151
      }
1152
  }
1153
 
1154
  /**
1155
   * Clones this node; roughly equivalent to cloneNode(false).
1156
   * Element subclasses must provide a new implementation which
1157
   * invokes this method to handle the basics, and then arranges
1158
   * to clone any element attributes directly.  Attribute subclasses
1159
   * must make similar arrangements, ensuring that existing ties to
1160
   * elements are broken by cloning.
1161
   */
1162
  public Object clone()
1163
  {
1164
    try
1165
      {
1166
        DomNode node = (DomNode) super.clone();
1167
 
1168
        node.parent = null;
1169
        node.depth = 0;
1170
        node.index = 0;
1171
        node.length = 0;
1172
        node.first = null;
1173
        node.last = null;
1174
        node.previous = null;
1175
        node.next = null;
1176
 
1177
        node.readonly = false;
1178
        node.listeners = null;
1179
        node.nListeners = 0;
1180
        return node;
1181
 
1182
      }
1183
    catch (CloneNotSupportedException x)
1184
      {
1185
        throw new Error("clone didn't work");
1186
      }
1187
  }
1188
 
1189
  // the elements-by-tagname stuff is needed for both
1190
  // elements and documents ... this is in lieu of a
1191
  // common base class between Node and NodeNS.
1192
 
1193
  /**
1194
   * <b>DOM L1</b>
1195
   * Creates a NodeList giving array-style access to elements with
1196
   * the specified name.  Access is fastest if indices change by
1197
   * small values, and the DOM is not modified.
1198
   */
1199
  public NodeList getElementsByTagName(String tag)
1200
  {
1201
    return new ShadowList(null, tag);
1202
  }
1203
 
1204
  /**
1205
   * <b>DOM L2</b>
1206
   * Creates a NodeList giving array-style access to elements with
1207
   * the specified namespace and local name.  Access is fastest if
1208
   * indices change by small values, and the DOM is not modified.
1209
   */
1210
  public NodeList getElementsByTagNameNS(String namespace, String local)
1211
  {
1212
    return new ShadowList(namespace, local);
1213
  }
1214
 
1215
 
1216
  //
1217
  // This shadow class is GC-able even when the live list it shadows
1218
  // can't be, because of event registration hookups.  Its finalizer
1219
  // makes that live list become GC-able.
1220
  //
1221
  final class ShadowList
1222
    implements NodeList
1223
  {
1224
 
1225
    private LiveNodeList liveList;
1226
 
1227
    ShadowList(String ns, String local)
1228
    {
1229
      liveList = new LiveNodeList(ns, local);
1230
    }
1231
 
1232
    public void finalize()
1233
    {
1234
      liveList.detach();
1235
      liveList = null;
1236
    }
1237
 
1238
    public Node item(int index)
1239
    {
1240
      return liveList.item(index);
1241
    }
1242
 
1243
    public int getLength()
1244
    {
1245
      return liveList.getLength();
1246
    }
1247
  }
1248
 
1249
  final class LiveNodeList
1250
    implements NodeList, EventListener, NodeFilter
1251
  {
1252
 
1253
    private final boolean matchAnyURI;
1254
    private final boolean matchAnyName;
1255
    private final String elementURI;
1256
    private final String elementName;
1257
 
1258
    private DomIterator current;
1259
    private int lastIndex;
1260
 
1261
    LiveNodeList(String uri, String name)
1262
    {
1263
      elementURI = uri;
1264
      elementName = name;
1265
      matchAnyURI = "*".equals(uri);
1266
      matchAnyName = "*".equals(name);
1267
 
1268
      DomNode.this.addEventListener("DOMNodeInserted", this, true);
1269
      DomNode.this.addEventListener("DOMNodeRemoved", this, true);
1270
    }
1271
 
1272
    void detach()
1273
    {
1274
      if (current != null)
1275
        current.detach();
1276
      current = null;
1277
 
1278
      DomNode.this.removeEventListener("DOMNodeInserted", this, true);
1279
      DomNode.this.removeEventListener("DOMNodeRemoved", this, true);
1280
    }
1281
 
1282
    public short acceptNode(Node element)
1283
    {
1284
      if (element == DomNode.this)
1285
        {
1286
          return FILTER_SKIP;
1287
        }
1288
 
1289
      // use namespace-aware matching ...
1290
      if (elementURI != null)
1291
        {
1292
          if (!(matchAnyURI
1293
                || elementURI.equals(element.getNamespaceURI())))
1294
            {
1295
              return FILTER_SKIP;
1296
            }
1297
          if (!(matchAnyName
1298
                || elementName.equals(element.getLocalName())))
1299
            {
1300
              return FILTER_SKIP;
1301
            }
1302
 
1303
          // ... or qName-based kind.
1304
        }
1305
      else
1306
        {
1307
          if (!(matchAnyName
1308
                || elementName.equals(element.getNodeName())))
1309
            {
1310
              return FILTER_SKIP;
1311
            }
1312
        }
1313
      return FILTER_ACCEPT;
1314
    }
1315
 
1316
    private DomIterator createIterator()
1317
    {
1318
      return new DomIterator(DomNode.this,
1319
                             NodeFilter.SHOW_ELEMENT,
1320
                             this,      /* filter */
1321
                             true       /* expand entity refs */
1322
                            );
1323
    }
1324
 
1325
    public void handleEvent(Event e)
1326
    {
1327
      MutationEvent     mutation = (MutationEvent) e;
1328
      Node              related = mutation.getRelatedNode();
1329
 
1330
      // XXX if it's got children ... check all kids too, they
1331
      // will invalidate our saved index
1332
 
1333
      if (related.getNodeType() != Node.ELEMENT_NODE ||
1334
          related.getNodeName() != elementName ||
1335
          related.getNamespaceURI() != elementURI)
1336
        {
1337
          return;
1338
        }
1339
 
1340
      current = null;
1341
    }
1342
 
1343
    public Node item(int index)
1344
    {
1345
      if (current == null)
1346
        {
1347
          current = createIterator();
1348
          lastIndex = -1;
1349
        }
1350
 
1351
      // last node or before?  go backwards
1352
      if (index <= lastIndex) {
1353
        while (index != lastIndex) {
1354
          current.previousNode ();
1355
          lastIndex--;
1356
        }
1357
        Node ret = current.previousNode ();
1358
        current = null;
1359
        return ret;
1360
      }
1361
 
1362
      // somewhere after last node
1363
      while (++lastIndex != index)
1364
        current.nextNode ();
1365
        Node ret = current.nextNode ();
1366
        current = null;
1367
        return ret;
1368
    }
1369
 
1370
    public int getLength()
1371
    {
1372
      int retval = 0;
1373
      NodeIterator iter = createIterator();
1374
 
1375
      while (iter.nextNode() != null)
1376
        {
1377
          retval++;
1378
        }
1379
      current = null;
1380
      return retval;
1381
    }
1382
 
1383
  }
1384
 
1385
  //
1386
  // EventTarget support
1387
  //
1388
  static final class ListenerRecord
1389
  {
1390
 
1391
    String type;
1392
    EventListener listener;
1393
    boolean useCapture;
1394
 
1395
    // XXX use JDK 1.2 java.lang.ref.WeakReference to listener,
1396
    // and we can both get rid of "shadow" classes and remove
1397
    // the need for applications to apply similar trix ... but
1398
    // JDK 1.2 support isn't generally available yet
1399
 
1400
    ListenerRecord(String type, EventListener listener, boolean useCapture)
1401
    {
1402
      this.type = type.intern();
1403
      this.listener = listener;
1404
      this.useCapture = useCapture;
1405
    }
1406
 
1407
    boolean equals(ListenerRecord rec)
1408
    {
1409
      return listener == rec.listener
1410
        && useCapture == rec.useCapture
1411
        && type == rec.type;
1412
    }
1413
 
1414
  }
1415
 
1416
  /**
1417
   * <b>DOM L2 (Events)</b>
1418
   * Returns an instance of the specified type of event object.
1419
   * Understands about DOM Mutation, HTML, and UI events.
1420
   *
1421
   * <p>If the name of the event type begins with "USER-", then an object
1422
   * implementing the "Event" class will be returned; this provides a
1423
   * limited facility for application-defined events to use the DOM event
1424
   * infrastructure.  Alternatively, use one of the standard DOM event
1425
   * classes and initialize it using use such a "USER-" event type name;
1426
   * or defin, instantiate, and initialize an application-specific subclass
1427
   * of DomEvent and pass that to dispatchEvent().
1428
   *
1429
   * @param eventType Identifies the particular DOM feature module
1430
   *    defining the type of event, such as "MutationEvents".
1431
   *    <em>The event "name" is a different kind of "type".</em>
1432
   */
1433
  public Event createEvent(String eventType)
1434
  {
1435
    eventType = eventType.toLowerCase();
1436
 
1437
    if ("mutationevents".equals(eventType))
1438
      {
1439
        return new DomEvent.DomMutationEvent(null);
1440
      }
1441
 
1442
    if ("htmlevents".equals(eventType)
1443
        || "events".equals(eventType)
1444
        || "user-events".equals(eventType))
1445
      {
1446
        return new DomEvent(null);
1447
      }
1448
 
1449
    if ("uievents".equals(eventType))
1450
      {
1451
        return new DomEvent.DomUIEvent(null);
1452
      }
1453
 
1454
    // mouse events 
1455
 
1456
    throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR,
1457
                              eventType, null, 0);
1458
  }
1459
 
1460
  /**
1461
   * <b>DOM L2 (Events)</b>
1462
   * Registers an event listener's interest in a class of events.
1463
   */
1464
  public final void addEventListener(String type,
1465
                                     EventListener listener,
1466
                                     boolean useCapture)
1467
  {
1468
    if (listeners == null)
1469
      {
1470
        listeners = new ListenerRecord[1];
1471
      }
1472
    else if (nListeners == listeners.length)
1473
      {
1474
        ListenerRecord[] newListeners =
1475
          new ListenerRecord[listeners.length + NKIDS_DELTA];
1476
        System.arraycopy(listeners, 0, newListeners, 0, nListeners);
1477
        listeners = newListeners;
1478
      }
1479
 
1480
    // prune duplicates
1481
    ListenerRecord record;
1482
 
1483
    record = new ListenerRecord(type, listener, useCapture);
1484
    for (int i = 0; i < nListeners; i++)
1485
      {
1486
        if (record.equals(listeners[i]))
1487
          {
1488
            return;
1489
          }
1490
      }
1491
    listeners [nListeners++] = record;
1492
  }
1493
 
1494
  // XXX this exception should be discarded from DOM
1495
 
1496
  // this class can be instantiated, unlike the one in the spec
1497
  static final class DomEventException
1498
    extends EventException
1499
  {
1500
 
1501
    DomEventException()
1502
    {
1503
      super(UNSPECIFIED_EVENT_TYPE_ERR, "unspecified event type");
1504
    }
1505
 
1506
  }
1507
 
1508
  /**
1509
   * <b>DOM L2 (Events)</b>
1510
   * Delivers an event to all relevant listeners, returning true if the
1511
   * caller should perform their default action.  Note that the event
1512
   * must have been provided by the createEvent() method on this
1513
   * class, else it can't be dispatched.
1514
   *
1515
   * @see #createEvent
1516
   *
1517
   * @exception NullPointerException When a null event is passed.
1518
   * @exception ClassCastException When the event wasn't provided by
1519
   *    the createEvent method, or otherwise isn't a DomEvent.
1520
   * @exception EventException If the event type wasn't specified
1521
   */
1522
  public final boolean dispatchEvent(Event event)
1523
    throws EventException
1524
  {
1525
    DomEvent e = (DomEvent) event;
1526
    DomNode[] ancestors = null;
1527
    int ancestorMax = 0;
1528
    boolean haveDispatchDataLock = false;
1529
 
1530
    if (e.type == null)
1531
      {
1532
        throw new DomEventException();
1533
      }
1534
 
1535
    e.doDefault = true;
1536
    e.target = this;
1537
 
1538
    //
1539
    // Typical case:  one nonrecursive dispatchEvent call at a time
1540
    // for this class.  If that's our case, we can avoid allocating
1541
    // garbage, which is overall a big win.  Even with advanced GCs
1542
    // that deal well with short-lived garbage, and wayfast allocators,
1543
    // it still helps.
1544
    //
1545
    // Remember -- EVERY mutation goes though here at least once.
1546
    //
1547
    // When populating a DOM tree, trying to send mutation events is
1548
    // the primary cost; this dominates the critical path.
1549
    //
1550
    try
1551
      {
1552
        DomNode current;
1553
        int index;
1554
        boolean haveAncestorRegistrations = false;
1555
        ListenerRecord[] notificationSet;
1556
        int ancestorLen;
1557
 
1558
        synchronized (lockNode)
1559
          {
1560
            if (!dispatchDataLock)
1561
              {
1562
                haveDispatchDataLock = dispatchDataLock = true;
1563
                notificationSet = DomNode.notificationSet;
1564
                ancestors = DomNode.ancestors;
1565
              }
1566
            else
1567
              {
1568
                notificationSet = new ListenerRecord[NOTIFICATIONS_INIT];
1569
                ancestors = new DomNode[ANCESTORS_INIT];
1570
              }
1571
            ancestorLen = ancestors.length;
1572
          }
1573
 
1574
        // XXX autogrow ancestors ... based on statistics
1575
 
1576
        // Climb to the top of this subtree and handle capture, letting
1577
        // each node (from the top down) capture until one stops it or
1578
        // until we get to this one.
1579
 
1580
        for (index = 0, current = parent;
1581
             current != null && index < ancestorLen;
1582
             index++, current = current.parent)
1583
          {
1584
            if (current.nListeners != 0)
1585
              {
1586
                haveAncestorRegistrations = true;
1587
              }
1588
            ancestors [index] = current;
1589
          }
1590
        if (current != null)
1591
          {
1592
            throw new RuntimeException("dispatchEvent capture stack size");
1593
          }
1594
 
1595
        ancestorMax = index;
1596
        e.stop = false;
1597
 
1598
        if (haveAncestorRegistrations)
1599
          {
1600
            e.eventPhase = Event.CAPTURING_PHASE;
1601
            while (!e.stop && index-- > 0)
1602
              {
1603
                current = ancestors [index];
1604
                if (current.nListeners != 0)
1605
                  {
1606
                    notifyNode(e, current, true, notificationSet);
1607
                  }
1608
              }
1609
          }
1610
 
1611
        // Always deliver events to the target node (this)
1612
        // unless stopPropagation was called.  If we saw
1613
        // no registrations yet (typical!), we never will.
1614
        if (!e.stop && nListeners != 0)
1615
          {
1616
            e.eventPhase = Event.AT_TARGET;
1617
            notifyNode (e, this, false, notificationSet);
1618
          }
1619
        else if (!haveAncestorRegistrations)
1620
          {
1621
            e.stop = true;
1622
          }
1623
 
1624
        // If the event bubbles and propagation wasn't halted,
1625
        // walk back up the ancestor list.  Stop bubbling when
1626
        // any bubbled event handler stops it.
1627
 
1628
        if (!e.stop && e.bubbles)
1629
          {
1630
            e.eventPhase = Event.BUBBLING_PHASE;
1631
            for (index = 0;
1632
                 !e.stop
1633
                 && index < ancestorMax
1634
                 && (current = ancestors[index]) != null;
1635
                 index++)
1636
              {
1637
                if (current.nListeners != 0)
1638
                  {
1639
                    notifyNode(e, current, false, notificationSet);
1640
                  }
1641
              }
1642
          }
1643
        e.eventPhase = 0;
1644
 
1645
        // Caller chooses whether to perform the default
1646
        // action based on return from this method.
1647
        return e.doDefault;
1648
 
1649
      }
1650
    finally
1651
      {
1652
        if (haveDispatchDataLock)
1653
          {
1654
            // synchronize to force write ordering
1655
            synchronized (lockNode)
1656
              {
1657
                // null out refs to ensure they'll be GC'd
1658
                for (int i = 0; i < ancestorMax; i++)
1659
                  {
1660
                    ancestors [i] = null;
1661
                  }
1662
                // notificationSet handled by notifyNode
1663
 
1664
                dispatchDataLock = false;
1665
              }
1666
          }
1667
      }
1668
  }
1669
 
1670
  private void notifyNode(DomEvent e,
1671
                          DomNode current,
1672
                          boolean capture,
1673
                          ListenerRecord[] notificationSet)
1674
  {
1675
    int count = 0;
1676
 
1677
    // do any of this set of listeners get notified?
1678
    for (int i = 0; i < current.nListeners; i++)
1679
      {
1680
        ListenerRecord rec = current.listeners[i];
1681
 
1682
        if (rec.useCapture != capture)
1683
          {
1684
            continue;
1685
          }
1686
        if (!e.type.equals (rec.type))
1687
          {
1688
            continue;
1689
          }
1690
        if (count >= notificationSet.length)
1691
          {
1692
            // very simple growth algorithm
1693
            int len = Math.max(notificationSet.length, 1);
1694
            ListenerRecord[] tmp = new ListenerRecord[len * 2];
1695
            System.arraycopy(notificationSet, 0, tmp, 0,
1696
                             notificationSet.length);
1697
            notificationSet = tmp;
1698
          }
1699
        notificationSet[count++] = rec;
1700
      }
1701
 
1702
    // Notify just those listeners
1703
    e.currentNode = current;
1704
    for (int i = 0; i < count; i++)
1705
      {
1706
        try
1707
          {
1708
            // Late in the DOM CR process (3rd or 4th CR?) the
1709
            // removeEventListener spec became asymmetric with respect
1710
            // to addEventListener ... effect is now immediate.
1711
            for (int j = 0; j < current.nListeners; j++)
1712
              {
1713
                if (current.listeners[j].equals(notificationSet[i]))
1714
                  {
1715
                    notificationSet[i].listener.handleEvent(e);
1716
                    break;
1717
                  }
1718
              }
1719
 
1720
          }
1721
        catch (Exception x)
1722
          {
1723
            // ignore all exceptions
1724
          }
1725
        notificationSet[i] = null;              // free for GC
1726
      }
1727
  }
1728
 
1729
  /**
1730
   * <b>DOM L2 (Events)</b>
1731
   * Unregisters an event listener.
1732
   */
1733
  public final void removeEventListener(String type,
1734
                                        EventListener listener,
1735
                                        boolean useCapture)
1736
  {
1737
    for (int i = 0; i < nListeners; i++)
1738
      {
1739
        if (listeners[i].listener != listener)
1740
          {
1741
            continue;
1742
          }
1743
        if (listeners[i].useCapture != useCapture)
1744
          {
1745
            continue;
1746
          }
1747
        if (!listeners[i].type.equals(type))
1748
          {
1749
            continue;
1750
          }
1751
 
1752
        if (nListeners == 1)
1753
          {
1754
            listeners = null;
1755
            nListeners = 0;
1756
          }
1757
        else
1758
          {
1759
            for (int j = i + 1; j < nListeners; j++)
1760
              {
1761
                listeners[i++] = listeners[j++];
1762
              }
1763
            listeners[--nListeners] = null;
1764
          }
1765
        break;
1766
      }
1767
    // no exceptions reported
1768
  }
1769
 
1770
  /**
1771
   * <b>DOM L1 (relocated in DOM L2)</b>
1772
   * In this node and all contained nodes (including attributes if
1773
   * relevant) merge adjacent text nodes.  This is done while ignoring
1774
   * text which happens to use CDATA delimiters).
1775
   */
1776
  public final void normalize()
1777
  {
1778
    // Suspend readonly status
1779
    boolean saved = readonly;
1780
    readonly = false;
1781
    for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1782
      {
1783
        switch (ctx.nodeType)
1784
          {
1785
          case TEXT_NODE:
1786
            while (ctx.next != null && ctx.next.nodeType == TEXT_NODE)
1787
              {
1788
                Text text = (Text) ctx;
1789
                text.appendData(ctx.next.getNodeValue());
1790
                removeChild(ctx.next);
1791
              }
1792
            break;
1793
          case ELEMENT_NODE:
1794
            NamedNodeMap attrs = ctx.getAttributes();
1795
            int len = attrs.getLength();
1796
            for (int i = 0; i < len; i++)
1797
              {
1798
                attrs.item(i).normalize();
1799
              }
1800
            // Fall through
1801
          case DOCUMENT_NODE:
1802
          case DOCUMENT_FRAGMENT_NODE:
1803
          case ATTRIBUTE_NODE:
1804
          case ENTITY_REFERENCE_NODE:
1805
            ctx.normalize();
1806
            break;
1807
          }
1808
      }
1809
    readonly = saved;
1810
  }
1811
 
1812
  /**
1813
   * Returns true iff node types match, and either (a) both nodes have no
1814
   * namespace and their getNodeName() values are the same, or (b) both
1815
   * nodes have the same getNamespaceURI() and same getLocalName() values.
1816
   *
1817
   * <p>Note that notion of a "Per-Element-Type" attribute name scope, as
1818
   * found in a non-normative appendix of the XML Namespaces specification,
1819
   * is not supported here.  Your application must implement that notion,
1820
   * typically by not bothering to check nameAndTypeEquals for attributes
1821
   * without namespace URIs unless you already know their elements are
1822
   * nameAndTypeEquals.
1823
   */
1824
  public boolean nameAndTypeEquals(Node other)
1825
  {
1826
    if (other == this)
1827
      {
1828
        return true;
1829
      }
1830
    // node types must match
1831
    if (nodeType != other.getNodeType())
1832
      {
1833
        return false;
1834
      }
1835
 
1836
    // if both have namespaces, do a "full" comparision
1837
    // this is a "global" partition
1838
    String ns1 = this.getNamespaceURI();
1839
    String ns2 = other.getNamespaceURI();
1840
 
1841
    if (ns1 != null && ns2 != null)
1842
      {
1843
        return ns1.equals(ns2) &&
1844
          equal(getLocalName(), other.getLocalName());
1845
      }
1846
 
1847
    // if neither has a namespace, this is a "no-namespace" name.
1848
    if (ns1 == null && ns2 == null)
1849
      {
1850
        if (!getNodeName().equals(other.getNodeName()))
1851
          {
1852
            return false;
1853
          }
1854
        // can test the non-normative "per-element-type" scope here.
1855
        // if this is an attribute node and both nodes have been bound
1856
        // to elements (!!), then return the nameAndTypeEquals()
1857
        // comparison of those elements.
1858
        return true;
1859
      }
1860
 
1861
    // otherwise they're unequal: one scoped, one not.
1862
    return false;
1863
  }
1864
 
1865
  // DOM Level 3 methods
1866
 
1867
  public String getBaseURI()
1868
  {
1869
    return (parent != null) ? parent.getBaseURI() : null;
1870
  }
1871
 
1872
  public short compareDocumentPosition(Node other)
1873
    throws DOMException
1874
  {
1875
    return (short) compareTo(other);
1876
  }
1877
 
1878
  /**
1879
   * DOM nodes have a natural ordering: document order.
1880
   */
1881
  public final int compareTo(Object other)
1882
  {
1883
    if (other instanceof DomNode)
1884
      {
1885
        DomNode n1 = this;
1886
        DomNode n2 = (DomNode) other;
1887
        if (n1.owner != n2.owner)
1888
          {
1889
            return 0;
1890
          }
1891
        int d1 = n1.depth, d2 = n2.depth;
1892
        int delta = d1 - d2;
1893
        while (d1 > d2)
1894
          {
1895
            n1 = n1.parent;
1896
            d1--;
1897
          }
1898
        while (d2 > d1)
1899
          {
1900
            n2 = n2.parent;
1901
            d2--;
1902
          }
1903
        int c = compareTo2(n1, n2);
1904
        return (c != 0) ? c : delta;
1905
      }
1906
    return 0;
1907
  }
1908
 
1909
  /**
1910
   * Compare two nodes at the same depth.
1911
   */
1912
  final int compareTo2(DomNode n1, DomNode n2)
1913
  {
1914
    if (n1 == n2 || n1.depth == 0 || n2.depth == 0)
1915
      {
1916
        return 0;
1917
      }
1918
    int c = compareTo2(n1.parent, n2.parent);
1919
    return (c != 0) ? c : n1.index - n2.index;
1920
  }
1921
 
1922
  public final String getTextContent()
1923
    throws DOMException
1924
  {
1925
    return getTextContent(true);
1926
  }
1927
 
1928
  final String getTextContent(boolean topLevel)
1929
    throws DOMException
1930
  {
1931
    switch (nodeType)
1932
      {
1933
      case ELEMENT_NODE:
1934
      case ENTITY_NODE:
1935
      case ENTITY_REFERENCE_NODE:
1936
      case DOCUMENT_FRAGMENT_NODE:
1937
        StringBuffer buffer = new StringBuffer();
1938
        for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1939
          {
1940
            String textContent = ctx.getTextContent(false);
1941
            if (textContent != null)
1942
              {
1943
                buffer.append(textContent);
1944
              }
1945
          }
1946
        return buffer.toString();
1947
      case TEXT_NODE:
1948
      case CDATA_SECTION_NODE:
1949
        if (((Text) this).isElementContentWhitespace())
1950
          {
1951
            return "";
1952
          }
1953
        return getNodeValue();
1954
      case ATTRIBUTE_NODE:
1955
        return getNodeValue();
1956
      case COMMENT_NODE:
1957
      case PROCESSING_INSTRUCTION_NODE:
1958
        return topLevel ? getNodeValue() : "";
1959
      default:
1960
        return null;
1961
      }
1962
  }
1963
 
1964
  public void setTextContent(String textContent)
1965
    throws DOMException
1966
  {
1967
    switch (nodeType)
1968
      {
1969
      case ELEMENT_NODE:
1970
      case ATTRIBUTE_NODE:
1971
      case ENTITY_NODE:
1972
      case ENTITY_REFERENCE_NODE:
1973
      case DOCUMENT_FRAGMENT_NODE:
1974
        for (DomNode ctx = first; ctx != null; )
1975
          {
1976
            DomNode n = ctx.next;
1977
            removeChild(ctx);
1978
            ctx = n;
1979
          }
1980
        if (textContent != null)
1981
          {
1982
            Text text = owner.createTextNode(textContent);
1983
            appendChild(text);
1984
          }
1985
        break;
1986
      case TEXT_NODE:
1987
      case CDATA_SECTION_NODE:
1988
      case COMMENT_NODE:
1989
      case PROCESSING_INSTRUCTION_NODE:
1990
        setNodeValue(textContent);
1991
        break;
1992
      }
1993
  }
1994
 
1995
  public boolean isSameNode(Node other)
1996
  {
1997
    return this == other;
1998
  }
1999
 
2000
  public String lookupPrefix(String namespaceURI)
2001
  {
2002
    return (parent == null || parent == owner) ? null :
2003
      parent.lookupPrefix(namespaceURI);
2004
  }
2005
 
2006
  public boolean isDefaultNamespace(String namespaceURI)
2007
  {
2008
    return (parent == null || parent == owner) ? false :
2009
      parent.isDefaultNamespace(namespaceURI);
2010
  }
2011
 
2012
  public String lookupNamespaceURI(String prefix)
2013
  {
2014
    return (parent == null || parent == owner) ? null :
2015
      parent.lookupNamespaceURI(prefix);
2016
  }
2017
 
2018
  public boolean isEqualNode(Node arg)
2019
  {
2020
    if (this == arg)
2021
      {
2022
        return true;
2023
      }
2024
    if (arg == null)
2025
      {
2026
        return false;
2027
      }
2028
    if (nodeType != arg.getNodeType() ||
2029
        !equal(getNodeName(), arg.getNodeName()) ||
2030
        !equal(getLocalName(), arg.getLocalName()) ||
2031
        !equal(getNamespaceURI(), arg.getNamespaceURI()) ||
2032
        !equal(getPrefix(), arg.getPrefix()) ||
2033
        !equal(getNodeValue(), arg.getNodeValue()))
2034
      {
2035
        return false;
2036
      }
2037
    // Children
2038
    Node argCtx = arg.getFirstChild();
2039
    getFirstChild(); // because of DomAttr lazy children
2040
    for (DomNode ctx = first; ctx != null; ctx = ctx.next)
2041
      {
2042
        if (!ctx.isEqualNode(argCtx))
2043
          {
2044
            return false;
2045
          }
2046
        argCtx = argCtx.getNextSibling();
2047
      }
2048
    if (argCtx != null)
2049
      {
2050
        return false;
2051
      }
2052
 
2053
    // TODO Attr NamedNodeMap
2054
    // TODO DocumentType
2055
    return true;
2056
  }
2057
 
2058
  boolean equal(String arg1, String arg2)
2059
  {
2060
    return ((arg1 == null && arg2 == null) ||
2061
            (arg1 != null && arg1.equals(arg2)));
2062
  }
2063
 
2064
  public Object getFeature(String feature, String version)
2065
  {
2066
    DOMImplementation impl = (nodeType == DOCUMENT_NODE) ?
2067
      ((Document) this).getImplementation() : owner.getImplementation();
2068
    if (impl.hasFeature(feature, version))
2069
      {
2070
        return this;
2071
      }
2072
    return null;
2073
  }
2074
 
2075
  public Object setUserData(String key, Object data, UserDataHandler handler)
2076
  {
2077
    if (userData == null)
2078
      {
2079
        userData = new HashMap();
2080
      }
2081
    if (handler != null)
2082
      {
2083
        if (userDataHandlers == null)
2084
          {
2085
            userDataHandlers = new HashMap();
2086
          }
2087
        userDataHandlers.put(key, handler);
2088
      }
2089
    return userData.put(key, data);
2090
  }
2091
 
2092
  public Object getUserData(String key)
2093
  {
2094
    if (userData == null)
2095
      {
2096
        return null;
2097
      }
2098
    return userData.get(key);
2099
  }
2100
 
2101
  public String toString()
2102
  {
2103
    String nodeName = getNodeName();
2104
    String nodeValue = getNodeValue();
2105
    StringBuffer buf = new StringBuffer(getClass().getName());
2106
    buf.append('[');
2107
    if (nodeName != null)
2108
      {
2109
        buf.append(nodeName);
2110
      }
2111
    if (nodeValue != null)
2112
      {
2113
        if (nodeName != null)
2114
          {
2115
            buf.append('=');
2116
          }
2117
        buf.append('\'');
2118
        buf.append(encode(nodeValue));
2119
        buf.append('\'');
2120
      }
2121
    buf.append(']');
2122
    return buf.toString();
2123
  }
2124
 
2125
  String encode(String value)
2126
  {
2127
    StringBuffer buf = null;
2128
    int len = value.length();
2129
    for (int i = 0; i < len; i++)
2130
      {
2131
        char c = value.charAt(i);
2132
        if (c == '\n')
2133
          {
2134
            if (buf == null)
2135
              {
2136
                buf = new StringBuffer(value.substring(0, i));
2137
              }
2138
            buf.append("\\n");
2139
          }
2140
        else if (c == '\r')
2141
          {
2142
            if (buf == null)
2143
              {
2144
                buf = new StringBuffer(value.substring(0, i));
2145
              }
2146
            buf.append("\\r");
2147
          }
2148
        else if (buf != null)
2149
          {
2150
            buf.append(c);
2151
          }
2152
      }
2153
    return (buf != null) ? buf.toString() : value;
2154
  }
2155
 
2156
  String nodeTypeToString(short nodeType)
2157
  {
2158
    switch (nodeType)
2159
      {
2160
      case ELEMENT_NODE:
2161
        return "ELEMENT_NODE";
2162
      case ATTRIBUTE_NODE:
2163
        return "ATTRIBUTE_NODE";
2164
      case TEXT_NODE:
2165
        return "TEXT_NODE";
2166
      case CDATA_SECTION_NODE:
2167
        return "CDATA_SECTION_NODE";
2168
      case DOCUMENT_NODE:
2169
        return "DOCUMENT_NODE";
2170
      case DOCUMENT_TYPE_NODE:
2171
        return "DOCUMENT_TYPE_NODE";
2172
      case COMMENT_NODE:
2173
        return "COMMENT_NODE";
2174
      case PROCESSING_INSTRUCTION_NODE:
2175
        return "PROCESSING_INSTRUCTION_NODE";
2176
      case DOCUMENT_FRAGMENT_NODE:
2177
        return "DOCUMENT_FRAGMENT_NODE";
2178
      case ENTITY_NODE:
2179
        return "ENTITY_NODE";
2180
      case ENTITY_REFERENCE_NODE:
2181
        return "ENTITY_REFERENCE_NODE";
2182
      case NOTATION_NODE:
2183
        return "NOTATION_NODE";
2184
      default:
2185
        return "UNKNOWN";
2186
      }
2187
  }
2188
 
2189
}
2190
 

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.