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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [gnu/] [xml/] [dom/] [DomNode.java] - Blame information for rev 769

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

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