KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > xquark > xpath > datamodel > xerces > dom > NamedNodeMapImpl


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Xerces" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 1999, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57
58 package org.xquark.xpath.datamodel.xerces.dom;
59
60 import java.io.Serializable JavaDoc;
61 import java.util.Enumeration JavaDoc;
62 import java.util.Vector JavaDoc;
63
64 import org.w3c.dom.DOMException JavaDoc;
65 import org.w3c.dom.NamedNodeMap JavaDoc;
66 import org.w3c.dom.Node JavaDoc;
67
68 /**
69  * NamedNodeMaps represent collections of Nodes that can be accessed
70  * by name. Entity and Notation nodes are stored in NamedNodeMaps
71  * attached to the DocumentType. Attributes are placed in a NamedNodeMap
72  * attached to the elem they're related too. However, because attributes
73  * require more work, such as firing mutation events, they are stored in
74  * a subclass of NamedNodeMapImpl.
75  * <P>
76  * Only one Node may be stored per name; attempting to
77  * store another will replace the previous value.
78  * <P>
79  * NOTE: The "primary" storage key is taken from the NodeName attribute of the
80  * node. The "secondary" storage key is the namespaceURI and localName, when
81  * accessed by DOM level 2 nodes. All nodes, even DOM Level 2 nodes are stored
82  * in a single Vector sorted by the primary "nodename" key.
83  * <P>
84  * NOTE: item()'s integer index does _not_ imply that the named nodes
85  * must be stored in an array; that's only an access method. Note too
86  * that these indices are "live"; if someone changes the map's
87  * contents, the indices associated with nodes may change.
88  * <P>
89  *
90  * @version
91  * @since PR-DOM-Level-1-19980818.
92  */

93 public class NamedNodeMapImpl
94     implements NamedNodeMap JavaDoc, Serializable JavaDoc {
95
96     //
97
// Constants
98
//
99

100     /** Serialization version. */
101     static final long serialVersionUID = -7039242451046758020L;
102
103     //
104
// Data
105
//
106

107     protected short flags;
108
109     protected final static short READONLY = 0x1<<0;
110     protected final static short CHANGED = 0x1<<1;
111     protected final static short HASDEFAULTS = 0x1<<2;
112
113     /** Nodes. */
114     protected Vector JavaDoc nodes;
115
116     protected NodeImpl ownerNode; // the node this map belongs to
117

118     //
119
// Constructors
120
//
121

122     /** Constructs a named node map. */
123     protected NamedNodeMapImpl(NodeImpl ownerNode) {
124         this.ownerNode = ownerNode;
125     }
126
127     //
128
// NamedNodeMap methods
129
//
130

131     /**
132      * Report how many nodes are currently stored in this NamedNodeMap.
133      * Caveat: This is a count rather than an index, so the
134      * highest-numbered node at any time can be accessed via
135      * item(getLength()-1).
136      */

137     public int getLength() {
138         return (nodes != null) ? nodes.size() : 0;
139     }
140
141     /**
142      * Retrieve an item from the map by 0-based index.
143      *
144      * @param index Which item to retrieve. Note that indices are just an
145      * enumeration of the current contents; they aren't guaranteed to be
146      * stable, nor do they imply any promises about the order of the
147      * NamedNodeMap's contents. In other words, DO NOT assume either that
148      * index(i) will always refer to the same entry, or that there is any
149      * stable ordering of entries... and be prepared for double-reporting
150      * or skips as insertion and deletion occur.
151      *
152      * @returns the node which currenly has the specified index, or null
153      * if index is greater than or equal to getLength().
154      */

155     public Node JavaDoc item(int index) {
156         return (nodes != null && index < nodes.size()) ?
157                     (Node JavaDoc)(nodes.elementAt(index)) : null;
158     }
159
160     /**
161      * Retrieve a node by name.
162      *
163      * @param name Name of a node to look up.
164      * @returns the Node (of unspecified sub-class) stored with that name,
165      * or null if no value has been assigned to that name.
166      */

167     public Node JavaDoc getNamedItem(String JavaDoc name) {
168
169         int i = findNamePoint(name,0);
170         return (i < 0) ? null : (Node JavaDoc)(nodes.elementAt(i));
171
172     } // getNamedItem(String):Node
173

174     /**
175      * Introduced in DOM Level 2. <p>
176      * Retrieves a node specified by local name and namespace URI.
177      *
178      * @param namespaceURI The namespace URI of the node to retrieve.
179      * When it is null or an empty string, this
180      * method behaves like getNamedItem.
181      * @param localName The local name of the node to retrieve.
182      * @return Node A Node (of any type) with the specified name, or null if the specified
183      * name did not identify any node in the map.
184      */

185     public Node JavaDoc getNamedItemNS(String JavaDoc namespaceURI, String JavaDoc localName) {
186
187         int i = findNamePoint(namespaceURI, localName);
188         return (i < 0) ? null : (Node JavaDoc)(nodes.elementAt(i));
189
190     } // getNamedItemNS(String,String):Node
191

192     /**
193      * Adds a node using its nodeName attribute.
194      * As the nodeName attribute is used to derive the name which the node must be
195      * stored under, multiple nodes of certain types (those that have a "special" string
196      * value) cannot be stored as the names would clash. This is seen as preferable to
197      * allowing nodes to be aliased.
198      * @see org.w3c.dom.NamedNodeMap#setNamedItem
199      * @return If the new Node replaces an existing node the replaced Node is returned,
200      * otherwise null is returned.
201      * @param arg
202      * A node to store in a named node map. The node will later be
203      * accessible using the value of the namespaceURI and localName
204      * attribute of the node. If a node with those namespace URI and
205      * local name is already present in the map, it is replaced by the new
206      * one.
207      * @exception org.w3c.dom.DOMException The exception description.
208      */

209     public Node JavaDoc setNamedItem(Node JavaDoc arg)
210         throws DOMException JavaDoc {
211
212         if (isReadOnly()) {
213             throw new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR,
214                                        "DOM001 Modification not allowed");
215         }
216         if (arg.getOwnerDocument() != ownerNode.ownerDocument()) {
217             throw new DOMException JavaDoc(DOMException.WRONG_DOCUMENT_ERR,
218                                        "DOM005 Wrong document");
219         }
220
221     int i = findNamePoint(arg.getNodeName(),0);
222         NodeImpl previous = null;
223         if (i >= 0) {
224             previous = (NodeImpl) nodes.elementAt(i);
225             nodes.setElementAt(arg,i);
226         } else {
227             i = -1 - i; // Insert point (may be end of list)
228
if (null == nodes) {
229                 nodes = new Vector JavaDoc(5, 10);
230             }
231             nodes.insertElementAt(arg, i);
232         }
233         return previous;
234
235     } // setNamedItem(Node):Node
236

237     /**
238      * Adds a node using its namespaceURI and localName.
239      * @see org.w3c.dom.NamedNodeMap#setNamedItem
240      * @return If the new Node replaces an existing node the replaced Node is returned,
241      * otherwise null is returned.
242      * @param arg A node to store in a named node map. The node will later be
243      * accessible using the value of the namespaceURI and localName
244      * attribute of the node. If a node with those namespace URI and
245      * local name is already present in the map, it is replaced by the new
246      * one.
247      */

248     public Node JavaDoc setNamedItemNS(Node JavaDoc arg)
249         throws DOMException JavaDoc {
250
251         if (isReadOnly()) {
252             throw new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR,
253                                        "DOM001 Modification not allowed");
254         }
255     
256         if(arg.getOwnerDocument() != ownerNode.ownerDocument()) {
257             throw new DOMException JavaDoc(DOMException.WRONG_DOCUMENT_ERR,
258                                        "DOM005 Wrong document");
259         }
260
261         int i = findNamePoint(arg.getNamespaceURI(), arg.getLocalName());
262         NodeImpl previous = null;
263         if (i >= 0) {
264             previous = (NodeImpl) nodes.elementAt(i);
265             nodes.setElementAt(arg,i);
266         } else {
267             // If we can't find by namespaceURI, localName, then we find by
268
// nodeName so we know where to insert.
269
i = findNamePoint(arg.getNodeName(),0);
270             if (i >=0) {
271                 previous = (NodeImpl) nodes.elementAt(i);
272                 nodes.insertElementAt(arg,i);
273             } else {
274                 i = -1 - i; // Insert point (may be end of list)
275
if (null == nodes) {
276                     nodes = new Vector JavaDoc(5, 10);
277                 }
278                 nodes.insertElementAt(arg, i);
279             }
280         }
281         return previous;
282
283     } // setNamedItem(Node):Node
284

285     /**
286      * Removes a node specified by name.
287      * @param name The name of a node to remove.
288      * @return The node removed from the map if a node with such a name exists.
289      */

290     /***/
291     public Node JavaDoc removeNamedItem(String JavaDoc name)
292         throws DOMException JavaDoc {
293
294         if (isReadOnly()) {
295             throw
296                 new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR,
297                                      "DOM001 Modification not allowed");
298         }
299         int i = findNamePoint(name,0);
300         if (i < 0) {
301             throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR,
302                                        "DOM008 Not found");
303         }
304
305         NodeImpl n = (NodeImpl)nodes.elementAt(i);
306         nodes.removeElementAt(i);
307
308         return n;
309
310     } // removeNamedItem(String):Node
311

312     /**
313      * Introduced in DOM Level 2. <p>
314      * Removes a node specified by local name and namespace URI.
315      * @param namespaceURI
316      * The namespace URI of the node to remove.
317      * When it is null or an empty string, this
318      * method behaves like removeNamedItem.
319      * @param The local name of the node to remove.
320      * @return Node The node removed from the map if a node with such
321      * a local name and namespace URI exists.
322      * @throws NOT_FOUND_ERR: Raised if there is no node named
323      * name in the map.
324
325      */

326      public Node JavaDoc removeNamedItemNS(String JavaDoc namespaceURI, String JavaDoc name)
327         throws DOMException JavaDoc {
328
329         if (isReadOnly()) {
330             throw
331                 new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR,
332                                      "DOM001 Modification not allowed");
333         }
334         int i = findNamePoint(namespaceURI, name);
335         if (i < 0) {
336             throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR,
337                                        "DOM008 Not found");
338         }
339
340         NodeImpl n = (NodeImpl)nodes.elementAt(i);
341         nodes.removeElementAt(i);
342
343         return n;
344
345     } // removeNamedItem(String):Node
346

347     //
348
// Public methods
349
//
350

351     /**
352      * Cloning a NamedNodeMap is a DEEP OPERATION; it always clones
353      * all the nodes contained in the map.
354      */

355      
356     public NamedNodeMapImpl cloneMap(NodeImpl ownerNode) {
357         NamedNodeMapImpl newmap = new NamedNodeMapImpl(ownerNode);
358         newmap.cloneContent(this);
359         return newmap;
360     }
361
362     protected void cloneContent(NamedNodeMapImpl srcmap) {
363         if (srcmap.nodes != null) {
364             nodes = new Vector JavaDoc(srcmap.nodes.size());
365             for (int i = 0; i < srcmap.nodes.size(); ++i) {
366                 NodeImpl n = (NodeImpl) srcmap.nodes.elementAt(i);
367                 NodeImpl clone = (NodeImpl) n.cloneNode(true);
368                 clone.isSpecified(n.isSpecified());
369                 nodes.insertElementAt(clone, i);
370             }
371         }
372     } // cloneMap():NamedNodeMapImpl
373

374     //
375
// Package methods
376
//
377

378     /**
379      * Internal subroutine to allow read-only Nodes to make their contained
380      * NamedNodeMaps readonly too. I expect that in fact the shallow
381      * version of this operation will never be
382      *
383      * @param readOnly boolean true to make read-only, false to permit editing.
384      * @param deep boolean true to pass this request along to the contained
385      * nodes, false to only toggle the NamedNodeMap itself. I expect that
386      * the shallow version of this operation will never be used, but I want
387      * to design it in now, while I'm thinking about it.
388      */

389     void setReadOnly(boolean readOnly, boolean deep) {
390
391         isReadOnly(readOnly);
392         if(deep && nodes != null) {
393             Enumeration JavaDoc e=nodes.elements();
394             while(e.hasMoreElements()) {
395                 ((NodeImpl)e.nextElement()).setReadOnly(readOnly,deep);
396             }
397         }
398
399     } // setReadOnly(boolean,boolean)
400

401     /**
402      * Internal subroutine returns this NodeNameMap's (shallow) readOnly value.
403      *
404      */

405     boolean getReadOnly() {
406         return isReadOnly();
407     } // getReadOnly()
408

409
410     //
411
// Protected methods
412
//
413

414     /**
415      * NON-DOM
416      * set the ownerDocument of this node, and the attributes it contains
417      */

418     void setOwnerDocument(DocumentImpl doc) {
419         if (nodes != null) {
420             for (int i = 0; i < nodes.size(); i++) {
421                 ((NodeImpl)item(i)).setOwnerDocument(doc);
422             }
423         }
424     }
425
426     final boolean isReadOnly() {
427         return (flags & READONLY) != 0;
428     }
429
430     final void isReadOnly(boolean value) {
431         flags = (short) (value ? flags | READONLY : flags & ~READONLY);
432     }
433
434     final boolean changed() {
435         return (flags & CHANGED) != 0;
436     }
437
438     final void changed(boolean value) {
439         flags = (short) (value ? flags | CHANGED : flags & ~CHANGED);
440     }
441
442     final boolean hasDefaults() {
443         return (flags & HASDEFAULTS) != 0;
444     }
445
446     final void hasDefaults(boolean value) {
447         flags = (short) (value ? flags | HASDEFAULTS : flags & ~HASDEFAULTS);
448     }
449
450     //
451
// Private methods
452
//
453

454     /**
455      * Subroutine: Locate the named item, or the point at which said item
456      * should be added.
457      *
458      * @param name Name of a node to look up.
459      *
460      * @return If positive or zero, the index of the found item.
461      * If negative, index of the appropriate point at which to insert
462      * the item, encoded as -1-index and hence reconvertable by subtracting
463      * it from -1. (Encoding because I don't want to recompare the strings
464      * but don't want to burn bytes on a datatype to hold a flagged value.)
465      */

466     protected int findNamePoint(String JavaDoc name, int start) {
467
468         // Binary search
469
int i = 0;
470         if(nodes != null) {
471             int first = start;
472             int last = nodes.size() - 1;
473
474             while (first <= last) {
475                 i = (first + last) / 2;
476                 int test = name.compareTo(((Node JavaDoc)(nodes.elementAt(i))).getNodeName());
477                 if(test == 0) {
478                     return i; // Name found
479
}
480                 else if (test < 0) {
481                     last = i - 1;
482                 }
483                 else {
484                     first = i + 1;
485                 }
486             }
487
488             if (first > i) {
489                 i = first;
490             }
491         }
492
493         return -1 - i; // not-found has to be encoded.
494

495     } // findNamePoint(String):int
496

497     
498     /** This findNamePoint is for DOM Level 2 Namespaces.
499      */

500     protected int findNamePoint(String JavaDoc namespaceURI, String JavaDoc name) {
501         
502         if (nodes == null) return -1;
503         if (name == null) return -1;
504         
505         // This is a linear search through the same nodes Vector.
506
// The Vector is sorted on the DOM Level 1 nodename.
507
// The DOM Level 2 NS keys are namespaceURI and Localname,
508
// so we must linear search thru it.
509
// In addition, to get this to work with nodes without any namespace
510
// (namespaceURI and localNames are both null) we then use the nodeName
511
// as a seconday key.
512
for (int i = 0; i < nodes.size(); i++) {
513             NodeImpl a = (NodeImpl)nodes.elementAt(i);
514             String JavaDoc aNamespaceURI = a.getNamespaceURI();
515             String JavaDoc aLocalName = a.getLocalName();
516             if (namespaceURI == null) {
517               if (aNamespaceURI == null
518                   &&
519                   (name.equals(aLocalName)
520                    ||
521                    (aLocalName == null && name.equals(a.getNodeName()))))
522                 return i;
523             } else {
524               if (namespaceURI.equals(aNamespaceURI)
525                   &&
526                   name.equals(aLocalName))
527                 return i;
528             }
529         }
530         return -1;
531     }
532     
533
534 } // class NamedNodeMapImpl
535
Popular Tags