0001: /*
0002:
0003: Licensed to the Apache Software Foundation (ASF) under one or more
0004: contributor license agreements. See the NOTICE file distributed with
0005: this work for additional information regarding copyright ownership.
0006: The ASF licenses this file to You under the Apache License, Version 2.0
0007: (the "License"); you may not use this file except in compliance with
0008: the License. You may obtain a copy of the License at
0009:
0010: http://www.apache.org/licenses/LICENSE-2.0
0011:
0012: Unless required by applicable law or agreed to in writing, software
0013: distributed under the License is distributed on an "AS IS" BASIS,
0014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015: See the License for the specific language governing permissions and
0016: limitations under the License.
0017:
0018: */
0019: package org.apache.batik.dom;
0020:
0021: import java.io.Serializable;
0022:
0023: import org.apache.batik.dom.events.DOMMutationEvent;
0024: import org.apache.batik.dom.util.DOMUtilities;
0025: import org.apache.batik.util.XMLConstants;
0026:
0027: import org.w3c.dom.Attr;
0028: import org.w3c.dom.DOMException;
0029: import org.w3c.dom.Element;
0030: import org.w3c.dom.NamedNodeMap;
0031: import org.w3c.dom.Node;
0032: import org.w3c.dom.TypeInfo;
0033: import org.w3c.dom.events.DocumentEvent;
0034: import org.w3c.dom.events.MutationEvent;
0035:
0036: /**
0037: * This class implements the {@link org.w3c.dom.Element} interface.
0038: *
0039: * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
0040: * @version $Id: AbstractElement.java 489226 2006-12-21 00:05:36Z cam $
0041: */
0042: public abstract class AbstractElement extends AbstractParentChildNode
0043: implements Element {
0044:
0045: /**
0046: * The attributes of this element.
0047: */
0048: protected NamedNodeMap attributes;
0049:
0050: /**
0051: * The element type information.
0052: */
0053: protected TypeInfo typeInfo;
0054:
0055: /**
0056: * Creates a new AbstractElement object.
0057: */
0058: protected AbstractElement() {
0059: }
0060:
0061: /**
0062: * Creates a new AbstractElement object.
0063: * @param name The element name for validation purposes.
0064: * @param owner The owner document.
0065: * @exception DOMException
0066: * INVALID_CHARACTER_ERR: if name contains invalid characters,
0067: */
0068: protected AbstractElement(String name, AbstractDocument owner) {
0069: ownerDocument = owner;
0070: if (owner.getStrictErrorChecking()
0071: && !DOMUtilities.isValidName(name)) {
0072: throw createDOMException(
0073: DOMException.INVALID_CHARACTER_ERR, "xml.name",
0074: new Object[] { name });
0075: }
0076: }
0077:
0078: /**
0079: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getNodeType()}.
0080: *
0081: * @return {@link org.w3c.dom.Node#ELEMENT_NODE}
0082: */
0083: public short getNodeType() {
0084: return ELEMENT_NODE;
0085: }
0086:
0087: /**
0088: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#hasAttributes()}.
0089: */
0090: public boolean hasAttributes() {
0091: return attributes != null && attributes.getLength() != 0;
0092: }
0093:
0094: /**
0095: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getAttributes()}.
0096: */
0097: public NamedNodeMap getAttributes() {
0098: return (attributes == null) ? attributes = createAttributes()
0099: : attributes;
0100: }
0101:
0102: /**
0103: * <b>DOM</b>: Implements {@link org.w3c.dom.Element#getTagName()}.
0104: *
0105: * @return {@link #getNodeName()}.
0106: */
0107: public String getTagName() {
0108: return getNodeName();
0109: }
0110:
0111: /**
0112: * <b>DOM</b>: Implements {@link org.w3c.dom.Element#hasAttribute(String)}.
0113: */
0114: public boolean hasAttribute(String name) {
0115: return attributes != null
0116: && attributes.getNamedItem(name) != null;
0117: }
0118:
0119: /**
0120: * <b>DOM</b>: Implements {@link org.w3c.dom.Element#getAttribute(String)}.
0121: */
0122: public String getAttribute(String name) {
0123: if (attributes == null) {
0124: return "";
0125: }
0126: Attr attr = (Attr) attributes.getNamedItem(name);
0127: return (attr == null) ? "" : attr.getValue();
0128: }
0129:
0130: /**
0131: * <b>DOM</b>: Implements {@link
0132: * org.w3c.dom.Element#setAttribute(String,String)}.
0133: */
0134: public void setAttribute(String name, String value)
0135: throws DOMException {
0136: if (attributes == null) {
0137: attributes = createAttributes();
0138: }
0139: Attr attr = getAttributeNode(name);
0140: if (attr == null) {
0141: attr = getOwnerDocument().createAttribute(name);
0142: attr.setValue(value);
0143: attributes.setNamedItem(attr);
0144: } else {
0145: attr.setValue(value);
0146: }
0147: }
0148:
0149: /**
0150: * <b>DOM</b>: Implements {@link
0151: * org.w3c.dom.Element#removeAttribute(String)}.
0152: */
0153: public void removeAttribute(String name) throws DOMException {
0154: if (!hasAttribute(name)) {
0155: return;
0156: }
0157: attributes.removeNamedItem(name);
0158: }
0159:
0160: /**
0161: * <b>DOM</b>: Implements {@link
0162: * org.w3c.dom.Element#getAttributeNode(String)}.
0163: */
0164: public Attr getAttributeNode(String name) {
0165: if (attributes == null) {
0166: return null;
0167: }
0168: return (Attr) attributes.getNamedItem(name);
0169: }
0170:
0171: /**
0172: * <b>DOM</b>: Implements {@link
0173: * org.w3c.dom.Element#setAttributeNode(Attr)}.
0174: */
0175: public Attr setAttributeNode(Attr newAttr) throws DOMException {
0176: if (newAttr == null) {
0177: return null;
0178: }
0179: if (attributes == null) {
0180: attributes = createAttributes();
0181: }
0182: return (Attr) attributes.setNamedItemNS(newAttr);
0183: }
0184:
0185: /**
0186: * <b>DOM</b>: Implements {@link
0187: * org.w3c.dom.Element#removeAttributeNode(Attr)}.
0188: */
0189: public Attr removeAttributeNode(Attr oldAttr) throws DOMException {
0190: if (oldAttr == null) {
0191: return null;
0192: }
0193: if (attributes == null) {
0194: throw createDOMException(DOMException.NOT_FOUND_ERR,
0195: "attribute.missing", new Object[] { oldAttr
0196: .getName() });
0197: }
0198: String nsURI = oldAttr.getNamespaceURI();
0199: return (Attr) attributes.removeNamedItemNS(nsURI,
0200: (nsURI == null ? oldAttr.getNodeName() : oldAttr
0201: .getLocalName()));
0202: }
0203:
0204: /**
0205: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#normalize()}.
0206: */
0207: public void normalize() {
0208: super .normalize();
0209: if (attributes != null) {
0210: NamedNodeMap map = getAttributes();
0211: for (int i = map.getLength() - 1; i >= 0; i--) {
0212: map.item(i).normalize();
0213: }
0214: }
0215: }
0216:
0217: /**
0218: * <b>DOM</b>: Implements {@link
0219: * org.w3c.dom.Element#hasAttributeNS(String,String)}.
0220: */
0221: public boolean hasAttributeNS(String namespaceURI, String localName) {
0222: if (namespaceURI != null && namespaceURI.length() == 0) {
0223: namespaceURI = null;
0224: }
0225: return attributes != null
0226: && attributes.getNamedItemNS(namespaceURI, localName) != null;
0227: }
0228:
0229: /**
0230: * <b>DOM</b>: Implements {@link
0231: * org.w3c.dom.Element#getAttributeNS(String,String)}.
0232: */
0233: public String getAttributeNS(String namespaceURI, String localName) {
0234: if (attributes == null) {
0235: return "";
0236: }
0237: if (namespaceURI != null && namespaceURI.length() == 0) {
0238: namespaceURI = null;
0239: }
0240: Attr attr = (Attr) attributes.getNamedItemNS(namespaceURI,
0241: localName);
0242: return (attr == null) ? "" : attr.getValue();
0243: }
0244:
0245: /**
0246: * <b>DOM</b>: Implements {@link
0247: * org.w3c.dom.Element#setAttributeNS(String,String,String)}.
0248: */
0249: public void setAttributeNS(String namespaceURI,
0250: String qualifiedName, String value) throws DOMException {
0251:
0252: if (attributes == null) {
0253: attributes = createAttributes();
0254: }
0255: if (namespaceURI != null && namespaceURI.length() == 0) {
0256: namespaceURI = null;
0257: }
0258: Attr attr = getAttributeNodeNS(namespaceURI, qualifiedName);
0259: if (attr == null) {
0260: attr = getOwnerDocument().createAttributeNS(namespaceURI,
0261: qualifiedName);
0262: attr.setValue(value);
0263: attributes.setNamedItemNS(attr);
0264: } else {
0265: attr.setValue(value);
0266: }
0267: }
0268:
0269: /**
0270: * <b>DOM</b>: Implements {@link
0271: * org.w3c.dom.Element#removeAttributeNS(String,String)}.
0272: */
0273: public void removeAttributeNS(String namespaceURI, String localName)
0274: throws DOMException {
0275: if (namespaceURI != null && namespaceURI.length() == 0) {
0276: namespaceURI = null;
0277: }
0278: if (!hasAttributeNS(namespaceURI, localName)) {
0279: return;
0280: }
0281: attributes.removeNamedItemNS(namespaceURI, localName);
0282: }
0283:
0284: /**
0285: * <b>DOM</b>: Implements {@link
0286: * org.w3c.dom.Element#getAttributeNodeNS(String,String)}.
0287: */
0288: public Attr getAttributeNodeNS(String namespaceURI, String localName) {
0289: if (namespaceURI != null && namespaceURI.length() == 0) {
0290: namespaceURI = null;
0291: }
0292: if (attributes == null) {
0293: return null;
0294: }
0295: return (Attr) attributes
0296: .getNamedItemNS(namespaceURI, localName);
0297: }
0298:
0299: /**
0300: * <b>DOM</b>: Implements {@link
0301: * org.w3c.dom.Element#setAttributeNodeNS(Attr)}.
0302: */
0303: public Attr setAttributeNodeNS(Attr newAttr) throws DOMException {
0304: if (newAttr == null) {
0305: return null;
0306: }
0307: if (attributes == null) {
0308: attributes = createAttributes();
0309: }
0310: return (Attr) attributes.setNamedItemNS(newAttr);
0311: }
0312:
0313: /**
0314: * <b>DOM</b>: Implements {@link org.w3c.dom.Element#getSchemaTypeInfo()}.
0315: */
0316: public TypeInfo getSchemaTypeInfo() {
0317: if (typeInfo == null) {
0318: typeInfo = new ElementTypeInfo();
0319: }
0320: return typeInfo;
0321: }
0322:
0323: /**
0324: * <b>DOM</b>: Implements
0325: * {@link org.w3c.dom.Element#setIdAttribute(String,boolean)}.
0326: */
0327: public void setIdAttribute(String name, boolean isId)
0328: throws DOMException {
0329: AbstractAttr a = (AbstractAttr) getAttributeNode(name);
0330: if (a == null) {
0331: throw createDOMException(DOMException.NOT_FOUND_ERR,
0332: "attribute.missing", new Object[] { name });
0333: }
0334: if (a.isReadonly()) {
0335: throw createDOMException(
0336: DOMException.NO_MODIFICATION_ALLOWED_ERR,
0337: "readonly.node", new Object[] { name });
0338: }
0339: a.isIdAttr = isId;
0340: }
0341:
0342: /**
0343: * <b>DOM</b>: Implements
0344: * {@link org.w3c.dom.Element#setIdAttributeNS(String,String,boolean)}.
0345: */
0346: public void setIdAttributeNS(String ns, String ln, boolean isId)
0347: throws DOMException {
0348: if (ns != null && ns.length() == 0) {
0349: ns = null;
0350: }
0351: AbstractAttr a = (AbstractAttr) getAttributeNodeNS(ns, ln);
0352: if (a == null) {
0353: throw createDOMException(DOMException.NOT_FOUND_ERR,
0354: "attribute.missing", new Object[] { ns, ln });
0355: }
0356: if (a.isReadonly()) {
0357: throw createDOMException(
0358: DOMException.NO_MODIFICATION_ALLOWED_ERR,
0359: "readonly.node", new Object[] { a.getNodeName() });
0360: }
0361: a.isIdAttr = isId;
0362: }
0363:
0364: /**
0365: * <b>DOM</b>: Implements
0366: * {@link org.w3c.dom.Element#setIdAttributeNode(Attr,boolean)}.
0367: */
0368: public void setIdAttributeNode(Attr attr, boolean isId)
0369: throws DOMException {
0370: AbstractAttr a = (AbstractAttr) attr;
0371: if (a.isReadonly()) {
0372: throw createDOMException(
0373: DOMException.NO_MODIFICATION_ALLOWED_ERR,
0374: "readonly.node", new Object[] { a.getNodeName() });
0375: }
0376: a.isIdAttr = isId;
0377: }
0378:
0379: /**
0380: * Get an ID attribute.
0381: */
0382: protected Attr getIdAttribute() {
0383: NamedNodeMap nnm = getAttributes();
0384: if (nnm == null) {
0385: return null;
0386: }
0387: int len = nnm.getLength();
0388: for (int i = 0; i < len; i++) {
0389: AbstractAttr a = (AbstractAttr) nnm.item(i);
0390: if (a.isId()) {
0391: return a;
0392: }
0393: }
0394: return null;
0395: }
0396:
0397: /**
0398: * Get the ID of this element.
0399: */
0400: protected String getId() {
0401: Attr a = getIdAttribute();
0402: if (a != null) {
0403: String id = a.getNodeValue();
0404: if (id.length() > 0) {
0405: return id;
0406: }
0407: }
0408: return null;
0409: }
0410:
0411: /**
0412: * Called when a child node has been added.
0413: */
0414: protected void nodeAdded(Node node) {
0415: invalidateElementsByTagName(node);
0416: }
0417:
0418: /**
0419: * Called when a child node is going to be removed.
0420: */
0421: protected void nodeToBeRemoved(Node node) {
0422: invalidateElementsByTagName(node);
0423: }
0424:
0425: /**
0426: * Invalidates the ElementsByTagName objects of this node and its parents.
0427: */
0428: private void invalidateElementsByTagName(Node node) {
0429: if (node.getNodeType() != ELEMENT_NODE) {
0430: return;
0431: }
0432: AbstractDocument ad = getCurrentDocument();
0433: String ns = node.getNamespaceURI();
0434: String nm = node.getNodeName();
0435: String ln = (ns == null) ? node.getNodeName() : node
0436: .getLocalName();
0437: for (Node n = this ; n != null; n = n.getParentNode()) {
0438: switch (n.getNodeType()) {
0439: case ELEMENT_NODE: // fall-through is intended
0440: case DOCUMENT_NODE:
0441: ElementsByTagName l = ad.getElementsByTagName(n, nm);
0442: if (l != null) {
0443: l.invalidate();
0444: }
0445: l = ad.getElementsByTagName(n, "*");
0446: if (l != null) {
0447: l.invalidate();
0448: }
0449: ElementsByTagNameNS lns = ad.getElementsByTagNameNS(n,
0450: ns, ln);
0451:
0452: if (lns != null) {
0453: lns.invalidate();
0454: }
0455: lns = ad.getElementsByTagNameNS(n, "*", ln);
0456: if (lns != null) {
0457: lns.invalidate();
0458: }
0459: lns = ad.getElementsByTagNameNS(n, ns, "*");
0460: if (lns != null) {
0461: lns.invalidate();
0462: }
0463: lns = ad.getElementsByTagNameNS(n, "*", "*");
0464: if (lns != null) {
0465: lns.invalidate();
0466: }
0467: }
0468: }
0469:
0470: //
0471: // Invalidate children
0472: //
0473: Node c = node.getFirstChild();
0474: while (c != null) {
0475: invalidateElementsByTagName(c);
0476: c = c.getNextSibling();
0477: }
0478:
0479: }
0480:
0481: /**
0482: * Creates the attribute list.
0483: */
0484: protected NamedNodeMap createAttributes() {
0485: return new NamedNodeHashMap();
0486: }
0487:
0488: /**
0489: * Exports this node to the given document.
0490: * @param n The clone node.
0491: * @param d The destination document.
0492: */
0493: protected Node export(Node n, AbstractDocument d) {
0494: super .export(n, d);
0495: AbstractElement ae = (AbstractElement) n;
0496: if (attributes != null) {
0497: NamedNodeMap map = attributes;
0498: for (int i = map.getLength() - 1; i >= 0; i--) {
0499: AbstractAttr aa = (AbstractAttr) map.item(i);
0500: if (aa.getSpecified()) {
0501: Attr attr = (Attr) aa.deepExport(aa
0502: .cloneNode(false), d);
0503: if (aa instanceof AbstractAttrNS) {
0504: ae.setAttributeNodeNS(attr);
0505: } else {
0506: ae.setAttributeNode(attr);
0507: }
0508: }
0509: }
0510: }
0511: return n;
0512: }
0513:
0514: /**
0515: * Deeply exports this node to the given document.
0516: * @param n The clone node.
0517: * @param d The destination document.
0518: */
0519: protected Node deepExport(Node n, AbstractDocument d) {
0520: super .deepExport(n, d);
0521: AbstractElement ae = (AbstractElement) n;
0522: if (attributes != null) {
0523: NamedNodeMap map = attributes;
0524: for (int i = map.getLength() - 1; i >= 0; i--) {
0525: AbstractAttr aa = (AbstractAttr) map.item(i);
0526: if (aa.getSpecified()) {
0527: Attr attr = (Attr) aa.deepExport(aa
0528: .cloneNode(false), d);
0529: if (aa instanceof AbstractAttrNS) {
0530: ae.setAttributeNodeNS(attr);
0531: } else {
0532: ae.setAttributeNode(attr);
0533: }
0534: }
0535: }
0536: }
0537: return n;
0538: }
0539:
0540: /**
0541: * Copy the fields of the current node into the given node.
0542: * @param n a node of the type of this.
0543: */
0544: protected Node copyInto(Node n) {
0545: super .copyInto(n);
0546: AbstractElement ae = (AbstractElement) n;
0547: if (attributes != null) {
0548: NamedNodeMap map = attributes;
0549: for (int i = map.getLength() - 1; i >= 0; i--) {
0550: AbstractAttr aa = (AbstractAttr) map.item(i).cloneNode(
0551: true);
0552: if (aa instanceof AbstractAttrNS) {
0553: ae.setAttributeNodeNS(aa);
0554: } else {
0555: ae.setAttributeNode(aa);
0556: }
0557: }
0558: }
0559: return n;
0560: }
0561:
0562: /**
0563: * Deeply copy the fields of the current node into the given node.
0564: * @param n a node of the type of this.
0565: */
0566: protected Node deepCopyInto(Node n) {
0567: super .deepCopyInto(n);
0568: AbstractElement ae = (AbstractElement) n;
0569: if (attributes != null) {
0570: NamedNodeMap map = attributes;
0571: for (int i = map.getLength() - 1; i >= 0; i--) {
0572: AbstractAttr aa = (AbstractAttr) map.item(i).cloneNode(
0573: true);
0574: if (aa instanceof AbstractAttrNS) {
0575: ae.setAttributeNodeNS(aa);
0576: } else {
0577: ae.setAttributeNode(aa);
0578: }
0579: }
0580: }
0581: return n;
0582: }
0583:
0584: /**
0585: * Checks the validity of a node to be inserted.
0586: * @param n The node to be inserted.
0587: */
0588: protected void checkChildType(Node n, boolean replace) {
0589: switch (n.getNodeType()) {
0590: case ELEMENT_NODE: // fall-through is intended
0591: case PROCESSING_INSTRUCTION_NODE:
0592: case COMMENT_NODE:
0593: case TEXT_NODE:
0594: case CDATA_SECTION_NODE:
0595: case ENTITY_REFERENCE_NODE:
0596: case DOCUMENT_FRAGMENT_NODE:
0597: break;
0598: default:
0599: throw createDOMException(
0600: DOMException.HIERARCHY_REQUEST_ERR, "child.type",
0601: new Object[] { new Integer(getNodeType()),
0602: getNodeName(),
0603: new Integer(n.getNodeType()),
0604: n.getNodeName() });
0605: }
0606: }
0607:
0608: /**
0609: * Fires a DOMAttrModified event.
0610: * WARNING: public accessor because of compilation problems
0611: * on Solaris. Do not change.
0612: *
0613: * @param name The attribute's name.
0614: * @param node The attribute's node.
0615: * @param oldv The old value of the attribute.
0616: * @param newv The new value of the attribute.
0617: * @param change The modification type.
0618: */
0619: public void fireDOMAttrModifiedEvent(String name, Attr node,
0620: String oldv, String newv, short change) {
0621: switch (change) {
0622: case MutationEvent.ADDITION:
0623: if (((AbstractAttr) node).isId())
0624: ownerDocument.addIdEntry(this , newv);
0625: attrAdded(node, newv);
0626: break;
0627:
0628: case MutationEvent.MODIFICATION:
0629: if (((AbstractAttr) node).isId())
0630: ownerDocument.updateIdEntry(this , oldv, newv);
0631: attrModified(node, oldv, newv);
0632: break;
0633:
0634: default: // MutationEvent.REMOVAL:
0635: if (((AbstractAttr) node).isId())
0636: ownerDocument.removeIdEntry(this , oldv);
0637: attrRemoved(node, oldv);
0638: }
0639: AbstractDocument doc = getCurrentDocument();
0640: if (doc.getEventsEnabled() && !oldv.equals(newv)) {
0641: DocumentEvent de = (DocumentEvent) doc;
0642: DOMMutationEvent ev = (DOMMutationEvent) de
0643: .createEvent("MutationEvents");
0644: ev.initMutationEventNS(
0645: XMLConstants.XML_EVENTS_NAMESPACE_URI,
0646: "DOMAttrModified", true, // canBubbleArg
0647: false, // cancelableArg
0648: node, // relatedNodeArg
0649: oldv, // prevValueArg
0650: newv, // newValueArg
0651: name, // attrNameArg
0652: change); // attrChange
0653: dispatchEvent(ev);
0654: }
0655: }
0656:
0657: /**
0658: * Called when an attribute has been added.
0659: */
0660: protected void attrAdded(Attr node, String newv) {
0661: }
0662:
0663: /**
0664: * Called when an attribute has been modified.
0665: */
0666: protected void attrModified(Attr node, String oldv, String newv) {
0667: }
0668:
0669: /**
0670: * Called when an attribute has been removed.
0671: */
0672: protected void attrRemoved(Attr node, String oldv) {
0673: }
0674:
0675: /**
0676: * An implementation of the {@link org.w3c.dom.NamedNodeMap}.
0677: *
0678: * <br>This Map is not Thread-safe, concurrent updates or reading while updating may give
0679: * unexpected results.
0680: */
0681: public class NamedNodeHashMap implements NamedNodeMap, Serializable {
0682:
0683: /**
0684: * The initial capacity
0685: */
0686: protected static final int INITIAL_CAPACITY = 3;
0687:
0688: /**
0689: * The underlying array
0690: */
0691: protected Entry[] table;
0692:
0693: /**
0694: * The number of entries
0695: */
0696: protected int count;
0697:
0698: /**
0699: * Creates a new NamedNodeHashMap object.
0700: */
0701: public NamedNodeHashMap() {
0702: table = new Entry[INITIAL_CAPACITY];
0703: }
0704:
0705: /**
0706: * <b>DOM</b>: Implements {@link org.w3c.dom.NamedNodeMap#getNamedItem(String)}.
0707: */
0708: public Node getNamedItem(String name) {
0709: if (name == null) {
0710: return null;
0711: }
0712: return get(null, name);
0713: }
0714:
0715: /**
0716: * <b>DOM</b>: Implements {@link org.w3c.dom.NamedNodeMap#setNamedItem(Node)}.
0717: */
0718: public Node setNamedItem(Node arg) throws DOMException {
0719: if (arg == null) {
0720: return null;
0721: }
0722: checkNode(arg);
0723:
0724: return setNamedItem(null, arg.getNodeName(), arg);
0725: }
0726:
0727: /**
0728: * <b>DOM</b>: Implements {@link org.w3c.dom.NamedNodeMap#removeNamedItem(String)}.
0729: */
0730: public Node removeNamedItem(String name) throws DOMException {
0731: return removeNamedItemNS(null, name);
0732: }
0733:
0734: /**
0735: * <b>DOM</b>: Implements {@link org.w3c.dom.NamedNodeMap#item(int)}.
0736: */
0737: public Node item(int index) {
0738: if (index < 0 || index >= count) {
0739: return null;
0740: }
0741: int j = 0;
0742: for (int i = 0; i < table.length; i++) {
0743: Entry e = table[i];
0744: if (e == null) {
0745: continue;
0746: }
0747: do {
0748: if (j++ == index) {
0749: return e.value;
0750: }
0751: e = e.next;
0752: } while (e != null);
0753: }
0754: return null;
0755: }
0756:
0757: /**
0758: * <b>DOM</b>: Implements {@link org.w3c.dom.NamedNodeMap#getLength()}.
0759: */
0760: public int getLength() {
0761: return count;
0762: }
0763:
0764: /**
0765: * <b>DOM</b>: Implements {@link
0766: * org.w3c.dom.NamedNodeMap#getNamedItemNS(String,String)}.
0767: */
0768: public Node getNamedItemNS(String namespaceURI, String localName) {
0769: if (namespaceURI != null && namespaceURI.length() == 0) {
0770: namespaceURI = null;
0771: }
0772: return get(namespaceURI, localName);
0773: }
0774:
0775: /**
0776: * <b>DOM</b>: Implements {@link org.w3c.dom.NamedNodeMap#setNamedItemNS(Node)}.
0777: */
0778: public Node setNamedItemNS(Node arg) throws DOMException {
0779: if (arg == null) {
0780: return null;
0781: }
0782: String nsURI = arg.getNamespaceURI();
0783: return setNamedItem(nsURI, (nsURI == null) ? arg
0784: .getNodeName() : arg.getLocalName(), arg);
0785: }
0786:
0787: /**
0788: * <b>DOM</b>: Implements {@link org.w3c.dom.NamedNodeMap#removeNamedItemNS(String,String)}.
0789: */
0790: public Node removeNamedItemNS(String namespaceURI,
0791: String localName) throws DOMException {
0792: if (isReadonly()) {
0793: throw createDOMException(
0794: DOMException.NO_MODIFICATION_ALLOWED_ERR,
0795: "readonly.node.map", new Object[] {});
0796: }
0797: if (localName == null) {
0798: throw createDOMException(DOMException.NOT_FOUND_ERR,
0799: "attribute.missing", new Object[] { "" });
0800: }
0801: if (namespaceURI != null && namespaceURI.length() == 0) {
0802: namespaceURI = null;
0803: }
0804: AbstractAttr n = (AbstractAttr) remove(namespaceURI,
0805: localName);
0806: if (n == null) {
0807: throw createDOMException(DOMException.NOT_FOUND_ERR,
0808: "attribute.missing", new Object[] { localName });
0809: }
0810: n.setOwnerElement(null);
0811:
0812: // Mutation event
0813: fireDOMAttrModifiedEvent(n.getNodeName(), n, n
0814: .getNodeValue(), "", MutationEvent.REMOVAL);
0815: return n;
0816: }
0817:
0818: /**
0819: * Adds a node to the map.
0820: */
0821: public Node setNamedItem(String ns, String name, Node arg)
0822: throws DOMException {
0823:
0824: if (ns != null && ns.length() == 0) {
0825: ns = null;
0826: }
0827: ((AbstractAttr) arg).setOwnerElement(AbstractElement.this );
0828: AbstractAttr result = (AbstractAttr) put(ns, name, arg);
0829:
0830: if (result != null) {
0831: result.setOwnerElement(null);
0832: fireDOMAttrModifiedEvent(name, result, result
0833: .getNodeValue(), "", MutationEvent.REMOVAL);
0834: }
0835: fireDOMAttrModifiedEvent(name, (Attr) arg, "", arg
0836: .getNodeValue(), MutationEvent.ADDITION);
0837: return result;
0838: }
0839:
0840: /**
0841: * Checks the validity of a node to add.
0842: */
0843: protected void checkNode(Node arg) {
0844: if (isReadonly()) {
0845: throw createDOMException(
0846: DOMException.NO_MODIFICATION_ALLOWED_ERR,
0847: "readonly.node.map", new Object[] {});
0848: }
0849: if (getOwnerDocument() != arg.getOwnerDocument()) {
0850: throw createDOMException(
0851: DOMException.WRONG_DOCUMENT_ERR,
0852: "node.from.wrong.document", new Object[] {
0853: new Integer(arg.getNodeType()),
0854: arg.getNodeName() });
0855: }
0856: if (arg.getNodeType() == ATTRIBUTE_NODE
0857: && ((Attr) arg).getOwnerElement() != null) {
0858: throw createDOMException(
0859: DOMException.WRONG_DOCUMENT_ERR,
0860: "inuse.attribute", new Object[] { arg
0861: .getNodeName() });
0862: }
0863: }
0864:
0865: /**
0866: * Gets the value of a variable
0867: *
0868: * @return the value or null
0869: */
0870: protected Node get(String ns, String nm) {
0871: int hash = hashCode(ns, nm) & 0x7FFFFFFF;
0872: int index = hash % table.length;
0873:
0874: for (Entry e = table[index]; e != null; e = e.next) {
0875: if ((e.hash == hash) && e.match(ns, nm)) {
0876: return e.value;
0877: }
0878: }
0879: return null;
0880: }
0881:
0882: /**
0883: * Sets a new value for the given variable
0884: *
0885: * @return the old value or null
0886: */
0887: protected Node put(String ns, String nm, Node value) {
0888: int hash = hashCode(ns, nm) & 0x7FFFFFFF;
0889: int index = hash % table.length;
0890:
0891: for (Entry e = table[index]; e != null; e = e.next) {
0892: if ((e.hash == hash) && e.match(ns, nm)) {
0893: Node old = e.value;
0894: e.value = value;
0895: return old;
0896: }
0897: }
0898:
0899: // The key is not in the hash table
0900: int len = table.length;
0901: if (count++ >= (len - (len >> 2))) {
0902: // more than 75% loaded: grow
0903: rehash();
0904: index = hash % table.length;
0905: }
0906:
0907: Entry e = new Entry(hash, ns, nm, value, table[index]);
0908: table[index] = e;
0909: return null;
0910: }
0911:
0912: /**
0913: * Removes an entry from the table.
0914: *
0915: * @return the value or null.
0916: */
0917: protected Node remove(String ns, String nm) {
0918: int hash = hashCode(ns, nm) & 0x7FFFFFFF;
0919: int index = hash % table.length;
0920:
0921: Entry p = null;
0922: for (Entry e = table[index]; e != null; e = e.next) {
0923: if ((e.hash == hash) && e.match(ns, nm)) {
0924: Node result = e.value;
0925: if (p == null) {
0926: table[index] = e.next;
0927: } else {
0928: p.next = e.next;
0929: }
0930: count--;
0931: return result;
0932: }
0933: p = e;
0934: }
0935: return null;
0936: }
0937:
0938: /**
0939: * Rehash and grow the table.
0940: */
0941: protected void rehash() {
0942: Entry[] oldTable = table;
0943:
0944: table = new Entry[oldTable.length * 2 + 1];
0945:
0946: for (int i = oldTable.length - 1; i >= 0; i--) {
0947: for (Entry old = oldTable[i]; old != null;) {
0948: Entry e = old;
0949: old = old.next;
0950:
0951: int index = e.hash % table.length;
0952: e.next = table[index];
0953: table[index] = e;
0954: }
0955: }
0956: }
0957:
0958: /**
0959: * Computes a hash code corresponding to the given strings.
0960: */
0961: protected int hashCode(String ns, String nm) {
0962: int result = (ns == null) ? 0 : ns.hashCode();
0963: return result ^ nm.hashCode();
0964: }
0965: }
0966:
0967: /**
0968: * To manage collisions in the attributes map.
0969: * Implements a linked list of <code>Node</code>-objects.
0970: */
0971: protected static class Entry implements Serializable {
0972:
0973: /**
0974: * The hash code, must not change after creation.
0975: */
0976: public int hash; // should be final - would that break Serialization?
0977:
0978: /**
0979: * The namespace URI
0980: */
0981: public String namespaceURI;
0982:
0983: /**
0984: * The node name.
0985: */
0986: public String name;
0987:
0988: /**
0989: * The value
0990: */
0991: public Node value;
0992:
0993: /**
0994: * The next entry
0995: */
0996: public Entry next;
0997:
0998: /**
0999: * Creates a new entry
1000: */
1001: public Entry(int hash, String ns, String nm, Node value,
1002: Entry next) {
1003: this .hash = hash;
1004: this .namespaceURI = ns;
1005: this .name = nm;
1006: this .value = value;
1007: this .next = next;
1008: }
1009:
1010: /**
1011: * Whether this entry match the given keys.
1012: */
1013: public boolean match(String ns, String nm) {
1014: if (namespaceURI != null) {
1015: if (!namespaceURI.equals(ns)) {
1016: return false;
1017: }
1018: } else if (ns != null) {
1019: return false;
1020: }
1021: return name.equals(nm);
1022: }
1023: }
1024:
1025: /**
1026: * Inner class to hold type information about this element.
1027: */
1028: public class ElementTypeInfo implements TypeInfo {
1029:
1030: /**
1031: * Type namespace.
1032: */
1033: public String getTypeNamespace() {
1034: return null;
1035: }
1036:
1037: /**
1038: * Type name.
1039: */
1040: public String getTypeName() {
1041: return null;
1042: }
1043:
1044: /**
1045: * Returns whether this type derives from the given type.
1046: */
1047: public boolean isDerivedFrom(String ns, String name, int method) {
1048: return false;
1049: }
1050: }
1051: }
|