Source code: org/apache/batik/dom/AbstractNode.java
1 /*
2
3 Copyright 2000-2003 The Apache Software Foundation
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16
17 */
18 package org.apache.batik.dom;
19
20 import java.io.Serializable;
21
22 import org.apache.batik.dom.events.EventSupport;
23 import org.apache.batik.dom.events.NodeEventTarget;
24 import org.apache.batik.dom.util.DOMUtilities;
25 import org.apache.batik.dom.util.XMLSupport;
26 import org.w3c.dom.DOMException;
27 import org.w3c.dom.Document;
28 import org.w3c.dom.NamedNodeMap;
29 import org.w3c.dom.Node;
30 import org.w3c.dom.NodeList;
31 import org.w3c.dom.events.DocumentEvent;
32 import org.w3c.dom.events.Event;
33 import org.w3c.dom.events.EventException;
34 import org.w3c.dom.events.EventListener;
35 import org.w3c.dom.events.MutationEvent;
36
37 /**
38 * This class implements the {@link org.w3c.dom.Node} interface.
39 *
40 * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
41 * @version $Id: AbstractNode.java,v 1.19 2005/03/27 08:58:32 cam Exp $
42 */
43 public abstract class AbstractNode
44 implements ExtendedNode,
45 Serializable {
46
47 /**
48 * An empty instance of NodeList.
49 */
50 protected final static NodeList EMPTY_NODE_LIST = new NodeList() {
51 public Node item(int i) { return null; }
52 public int getLength() { return 0; }
53 };
54
55 /**
56 * The owner document.
57 */
58 protected AbstractDocument ownerDocument;
59
60 /**
61 * The event support.
62 */
63 protected transient EventSupport eventSupport;
64
65 /**
66 * Sets the name of this node.
67 * Do nothing.
68 */
69 public void setNodeName(String v) {
70 }
71
72 /**
73 * Sets the owner document of this node.
74 */
75 public void setOwnerDocument(Document doc) {
76 ownerDocument = (AbstractDocument)doc;
77 }
78
79 /**
80 * Sets the value of the specified attribute. This method only applies
81 * to Attr objects.
82 */
83 public void setSpecified(boolean v) {
84 throw createDOMException(DOMException.INVALID_STATE_ERR,
85 "node.type",
86 new Object[] { new Integer(getNodeType()),
87 getNodeName()});
88 }
89
90 /**
91 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getNodeValue()}.
92 * @return null.
93 */
94 public String getNodeValue() throws DOMException {
95 return null;
96 }
97
98 /**
99 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#setNodeValue(String)}.
100 * Do nothing.
101 */
102 public void setNodeValue(String nodeValue) throws DOMException {
103 }
104
105 /**
106 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getParentNode()}.
107 * @return null.
108 */
109 public Node getParentNode() {
110 return null;
111 }
112
113 /**
114 * Sets the parent node.
115 * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}.
116 */
117 public void setParentNode(Node v) {
118 throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR,
119 "parent.not.allowed",
120 new Object[] { new Integer(getNodeType()),
121 getNodeName() });
122 }
123
124 /**
125 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getChildNodes()}.
126 * @return {@link #EMPTY_NODE_LIST}.
127 */
128 public NodeList getChildNodes() {
129 return EMPTY_NODE_LIST;
130 }
131
132 /**
133 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getFirstChild()}.
134 * @return null.
135 */
136 public Node getFirstChild() {
137 return null;
138 }
139
140 /**
141 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getLastChild()}.
142 * @return null.
143 */
144 public Node getLastChild() {
145 return null;
146 }
147
148 /**
149 * Sets the node immediately preceding this node.
150 * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}.
151 */
152 public void setPreviousSibling(Node n) {
153 throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR,
154 "sibling.not.allowed",
155 new Object[] { new Integer(getNodeType()),
156 getNodeName() });
157 }
158
159 /**
160 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getPreviousSibling()}.
161 * @return null.
162 */
163 public Node getPreviousSibling() {
164 return null;
165 }
166
167 /**
168 * Sets the node immediately following this node.
169 * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}.
170 */
171 public void setNextSibling(Node n) {
172 throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR,
173 "sibling.not.allowed",
174 new Object[] { new Integer(getNodeType()),
175 getNodeName() });
176 }
177
178 /**
179 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getNextSibling()}.
180 * @return null.
181 */
182 public Node getNextSibling() {
183 return null;
184 }
185
186 /**
187 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#hasAttributes()}.
188 * @return false.
189 */
190 public boolean hasAttributes() {
191 return false;
192 }
193
194 /**
195 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getAttributes()}.
196 * @return null.
197 */
198 public NamedNodeMap getAttributes() {
199 return null;
200 }
201
202 /**
203 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getOwnerDocument()}.
204 * @return {@link #ownerDocument}.
205 */
206 public Document getOwnerDocument() {
207 return ownerDocument;
208 }
209
210 /**
211 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getNamespaceURI()}.
212 * @return null.
213 */
214 public String getNamespaceURI() {
215 return null;
216 }
217
218 /**
219 * <b>DOM</b>: Implements {@link
220 * org.w3c.dom.Node#insertBefore(Node, Node)}.
221 * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}.
222 */
223 public Node insertBefore(Node newChild, Node refChild)
224 throws DOMException {
225 throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR,
226 "children.not.allowed",
227 new Object[] { new Integer(getNodeType()),
228 getNodeName() });
229 }
230
231 /**
232 * <b>DOM</b>: Implements {@link
233 * org.w3c.dom.Node#replaceChild(Node, Node)}.
234 * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}.
235 */
236 public Node replaceChild(Node newChild, Node oldChild)
237 throws DOMException {
238 throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR,
239 "children.not.allowed",
240 new Object[] { new Integer(getNodeType()),
241 getNodeName()});
242 }
243
244 /**
245 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#removeChild(Node)}.
246 * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}.
247 */
248 public Node removeChild(Node oldChild) throws DOMException {
249 throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR,
250 "children.not.allowed",
251 new Object[] { new Integer(getNodeType()),
252 getNodeName() });
253 }
254
255 /**
256 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#appendChild(Node)}.
257 * Throws a HIERARCHY_REQUEST_ERR {@link org.w3c.dom.DOMException}.
258 */
259 public Node appendChild(Node newChild) throws DOMException {
260 throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR,
261 "children.not.allowed",
262 new Object[] { new Integer(getNodeType()),
263 getNodeName() });
264 }
265
266 /**
267 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#hasChildNodes()}.
268 * @return false.
269 */
270 public boolean hasChildNodes() {
271 return false;
272 }
273
274 /**
275 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#cloneNode(boolean)}.
276 */
277 public Node cloneNode(boolean deep) {
278 return (deep) ? deepCopyInto(newNode()) : copyInto(newNode());
279 }
280
281 /**
282 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#normalize()}.
283 * Do nothing.
284 */
285 public void normalize() {
286 }
287
288 /**
289 * <b>DOM</b>: Implements {@link
290 * org.w3c.dom.Node#isSupported(String,String)}.
291 */
292 public boolean isSupported(String feature, String version) {
293 return getCurrentDocument().getImplementation().hasFeature(feature,
294 version);
295 }
296
297 /**
298 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getPrefix()}.
299 */
300 public String getPrefix() {
301 return (getNamespaceURI() == null)
302 ? null
303 : DOMUtilities.getPrefix(getNodeName());
304 }
305
306 /**
307 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#setPrefix(String)}.
308 */
309 public void setPrefix(String prefix) throws DOMException {
310 if (isReadonly()) {
311 throw createDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
312 "readonly.node",
313 new Object[] { new Integer(getNodeType()),
314 getNodeName() });
315 }
316 String uri = getNamespaceURI();
317 if (uri == null) {
318 throw createDOMException(DOMException.NAMESPACE_ERR,
319 "namespace",
320 new Object[] { new Integer(getNodeType()),
321 getNodeName() });
322 }
323
324 String name = getLocalName();
325 if (prefix == null) {
326 setNodeName(name);
327 }
328 if (!prefix.equals("") && !DOMUtilities.isValidName(prefix)) {
329 throw createDOMException(DOMException.INVALID_CHARACTER_ERR,
330 "prefix",
331 new Object[] { new Integer(getNodeType()),
332 getNodeName(),
333 prefix });
334 }
335 if (!DOMUtilities.isValidPrefix(prefix)) {
336 throw createDOMException(DOMException.NAMESPACE_ERR,
337 "prefix",
338 new Object[] { new Integer(getNodeType()),
339 getNodeName(),
340 prefix });
341 }
342 if ((prefix.equals("xml") &&
343 !XMLSupport.XML_NAMESPACE_URI.equals(uri)) ||
344 (prefix.equals("xmlns") &&
345 !XMLSupport.XMLNS_NAMESPACE_URI.equals(uri))) {
346 throw createDOMException(DOMException.NAMESPACE_ERR,
347 "namespace.uri",
348 new Object[] { new Integer(getNodeType()),
349 getNodeName(),
350 uri });
351 }
352 setNodeName(prefix + ":" + name);
353 }
354
355 /**
356 * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getLocalName()}.
357 */
358 public String getLocalName() {
359 return (getNamespaceURI() == null)
360 ? null
361 : DOMUtilities.getLocalName(getNodeName());
362 }
363
364 /**
365 * Creates an exception with the appropriate error message.
366 */
367 public DOMException createDOMException(short type,
368 String key,
369 Object[] args) {
370 try {
371 return new DOMException
372 (type, getCurrentDocument().formatMessage(key, args));
373 } catch (Exception e) {
374 return new DOMException(type, key);
375 }
376 }
377
378 // EventTarget ////////////////////////////////////////////////////////////
379
380 /**
381 * <b>DOM</b>: Implements
382 * {@link
383 * org.w3c.dom.events.EventTarget#addEventListener(String,EventListener,boolean)}.
384 */
385 public void addEventListener(String type,
386 EventListener listener,
387 boolean useCapture) {
388 if (eventSupport == null) {
389 eventSupport = new EventSupport();
390 AbstractDocument doc = getCurrentDocument();
391 doc.setEventsEnabled(true);
392 }
393 eventSupport.addEventListener(type, listener, useCapture);
394 }
395
396 /**
397 * <b>DOM</b>: Implements
398 * {@link
399 * org.w3c.dom.events.EventTarget#removeEventListener(String,EventListener,boolean)}.
400 */
401 public void removeEventListener(String type,
402 EventListener listener,
403 boolean useCapture) {
404 if (eventSupport != null) {
405 eventSupport.removeEventListener(type, listener, useCapture);
406 }
407 }
408
409 /**
410 * Implements {@link
411 * org.apache.batik.dom.events.NodeEventTarget#getParentNodeEventTarget()}.
412 */
413 public NodeEventTarget getParentNodeEventTarget() {
414 return (NodeEventTarget)getParentNode();
415 }
416
417 /**
418 * <b>DOM</b>: Implements
419 * {@link org.w3c.dom.events.EventTarget#dispatchEvent(Event)}.
420 */
421 public boolean dispatchEvent(Event evt) throws EventException {
422 return EventSupport.dispatchEvent(this, evt);
423 }
424
425 /**
426 * Returns the event support instance for this node, or null if any.
427 */
428 public EventSupport getEventSupport() {
429 return eventSupport;
430 }
431
432 /**
433 * Recursively fires a DOMNodeInsertedIntoDocument event.
434 */
435 public void fireDOMNodeInsertedIntoDocumentEvent() {
436 AbstractDocument doc = getCurrentDocument();
437 if (doc.getEventsEnabled()) {
438 DocumentEvent de = (DocumentEvent)doc;
439 MutationEvent ev = (MutationEvent)de.createEvent("MutationEvents");
440 ev.initMutationEvent("DOMNodeInsertedIntoDocument",
441 true, // canBubbleArg
442 false, // cancelableArg
443 null, // relatedNodeArg
444 null, // prevValueArg
445 null, // newValueArg
446 null, // attrNameArg
447 MutationEvent.ADDITION);
448 dispatchEvent(ev);
449 }
450 }
451
452 /**
453 * Recursively fires a DOMNodeRemovedFromDocument event.
454 */
455 public void fireDOMNodeRemovedFromDocumentEvent() {
456 AbstractDocument doc = getCurrentDocument();
457 if (doc.getEventsEnabled()) {
458 DocumentEvent de = (DocumentEvent)doc;
459 MutationEvent ev = (MutationEvent)de.createEvent("MutationEvents");
460 ev.initMutationEvent("DOMNodeRemovedFromDocument",
461 true, // canBubbleArg
462 false, // cancelableArg
463 null, // relatedNodeArg
464 null, // prevValueArg
465 null, // newValueArg
466 null, // attrNameArg
467 MutationEvent.REMOVAL);
468 dispatchEvent(ev);
469 }
470 }
471
472 /**
473 * Fires a DOMCharacterDataModified event.
474 */
475 protected void fireDOMCharacterDataModifiedEvent(String oldv,
476 String newv) {
477 AbstractDocument doc = getCurrentDocument();
478 if (doc.getEventsEnabled()) {
479 DocumentEvent de = (DocumentEvent)doc;
480 MutationEvent ev = (MutationEvent)de.createEvent("MutationEvents");
481 ev.initMutationEvent("DOMCharacterDataModified",
482 true, // canBubbleArg
483 false, // cancelableArg
484 null, // relatedNodeArg
485 oldv, // prevValueArg
486 newv, // newValueArg
487 null, // attrNameArg
488 MutationEvent.MODIFICATION);
489 dispatchEvent(ev);
490 }
491 }
492
493 /**
494 * Returns the current document.
495 */
496 protected AbstractDocument getCurrentDocument() {
497 return ownerDocument;
498 }
499
500 /**
501 * Returns a new uninitialized instance of this object's class.
502 */
503 protected abstract Node newNode();
504
505 /**
506 * Exports this node to the given document.
507 */
508 protected Node export(Node n, AbstractDocument d) {
509 AbstractNode p = (AbstractNode)n;
510 p.ownerDocument = d;
511 p.setReadonly(false);
512 return n;
513 }
514
515 /**
516 * Deeply exports this node to the given document.
517 */
518 protected Node deepExport(Node n, AbstractDocument d) {
519 AbstractNode p = (AbstractNode)n;
520 p.ownerDocument = d;
521 p.setReadonly(false);
522 return n;
523 }
524
525 /**
526 * Copy the fields of the current node into the given node.
527 * @param n a node of the type of this.
528 */
529 protected Node copyInto(Node n) {
530 AbstractNode an = (AbstractNode)n;
531 an.ownerDocument = ownerDocument;
532 return n;
533 }
534
535 /**
536 * Deeply copy the fields of the current node into the given node.
537 * @param n a node of the type of this.
538 */
539 protected Node deepCopyInto(Node n) {
540 AbstractNode an = (AbstractNode)n;
541 an.ownerDocument = ownerDocument;
542 return n;
543 }
544
545 /**
546 * Checks the validity of a node to be inserted.
547 */
548 protected void checkChildType(Node n, boolean replace) {
549 throw createDOMException(DOMException.HIERARCHY_REQUEST_ERR,
550 "children.not.allowed",
551 new Object[] { new Integer(getNodeType()),
552 getNodeName() });
553 }
554 }