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.IOException;
0022: import java.io.ObjectInputStream;
0023: import java.io.ObjectOutputStream;
0024: import java.lang.reflect.Method;
0025: import java.util.ArrayList;
0026: import java.util.HashMap;
0027: import java.util.Iterator;
0028: import java.util.LinkedList;
0029: import java.util.List;
0030: import java.util.Locale;
0031: import java.util.Map;
0032: import java.util.MissingResourceException;
0033: import java.util.WeakHashMap;
0034:
0035: import org.apache.batik.dom.events.DocumentEventSupport;
0036: import org.apache.batik.dom.events.EventSupport;
0037: import org.apache.batik.dom.traversal.TraversalSupport;
0038: import org.apache.batik.dom.util.DOMUtilities;
0039: import org.apache.batik.dom.xbl.GenericXBLManager;
0040: import org.apache.batik.dom.xbl.XBLManager;
0041: import org.apache.batik.i18n.Localizable;
0042: import org.apache.batik.i18n.LocalizableSupport;
0043: import org.apache.batik.util.CleanerThread;
0044: import org.apache.batik.util.DOMConstants;
0045: import org.apache.batik.util.SoftDoublyIndexedTable;
0046: import org.apache.batik.util.XMLConstants;
0047:
0048: import org.apache.xml.utils.PrefixResolver;
0049:
0050: import org.apache.xpath.XPath;
0051: import org.apache.xpath.XPathContext;
0052: import org.apache.xpath.objects.XObject;
0053:
0054: import org.w3c.dom.Attr;
0055: import org.w3c.dom.Document;
0056: import org.w3c.dom.DocumentType;
0057: import org.w3c.dom.DOMConfiguration;
0058: import org.w3c.dom.DOMError;
0059: import org.w3c.dom.DOMErrorHandler;
0060: import org.w3c.dom.DOMException;
0061: import org.w3c.dom.DOMImplementation;
0062: import org.w3c.dom.DOMLocator;
0063: import org.w3c.dom.DOMStringList;
0064: import org.w3c.dom.Element;
0065: import org.w3c.dom.events.DocumentEvent;
0066: import org.w3c.dom.events.Event;
0067: import org.w3c.dom.events.MutationNameEvent;
0068: import org.w3c.dom.NamedNodeMap;
0069: import org.w3c.dom.Node;
0070: import org.w3c.dom.NodeList;
0071: import org.w3c.dom.traversal.DocumentTraversal;
0072: import org.w3c.dom.traversal.NodeFilter;
0073: import org.w3c.dom.traversal.NodeIterator;
0074: import org.w3c.dom.traversal.TreeWalker;
0075: import org.w3c.dom.UserDataHandler;
0076: import org.w3c.dom.xpath.XPathEvaluator;
0077: import org.w3c.dom.xpath.XPathException;
0078: import org.w3c.dom.xpath.XPathExpression;
0079: import org.w3c.dom.xpath.XPathNSResolver;
0080: import org.w3c.dom.xpath.XPathResult;
0081:
0082: /**
0083: * This class implements the {@link org.w3c.dom.Document} interface.
0084: *
0085: * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
0086: * @version $Id: AbstractDocument.java 501495 2007-01-30 18:00:36Z dvholten $
0087: */
0088: public abstract class AbstractDocument extends AbstractParentNode
0089: implements Document, DocumentEvent, DocumentTraversal,
0090: Localizable, XPathEvaluator {
0091:
0092: /**
0093: * The error messages bundle class name.
0094: */
0095: protected static final String RESOURCES = "org.apache.batik.dom.resources.Messages";
0096:
0097: /**
0098: * The localizable support for the error messages.
0099: */
0100: protected transient LocalizableSupport localizableSupport = new LocalizableSupport(
0101: RESOURCES, getClass().getClassLoader());
0102:
0103: /**
0104: * The DOM implementation.
0105: */
0106: protected transient DOMImplementation implementation;
0107:
0108: /**
0109: * The traversal support.
0110: */
0111: protected transient TraversalSupport traversalSupport;
0112:
0113: /**
0114: * The DocumentEventSupport.
0115: */
0116: protected transient DocumentEventSupport documentEventSupport;
0117:
0118: /**
0119: * Whether the event dispatching must be done.
0120: */
0121: protected transient boolean eventsEnabled;
0122:
0123: /**
0124: * The ElementsByTagName lists.
0125: */
0126: protected transient WeakHashMap elementsByTagNames;
0127:
0128: /**
0129: * The ElementsByTagNameNS lists.
0130: */
0131: protected transient WeakHashMap elementsByTagNamesNS;
0132:
0133: /**
0134: * Input encoding of this document.
0135: */
0136: protected String inputEncoding;
0137:
0138: /**
0139: * XML encoding of this document.
0140: */
0141: protected String xmlEncoding;
0142:
0143: /**
0144: * XML version of this document.
0145: */
0146: protected String xmlVersion = XMLConstants.XML_VERSION_10;
0147:
0148: /**
0149: * Whether this document is standalone.
0150: */
0151: protected boolean xmlStandalone;
0152:
0153: /**
0154: * The document URI.
0155: */
0156: protected String documentURI;
0157:
0158: /**
0159: * Whether strict error checking is in force.
0160: */
0161: protected boolean strictErrorChecking = true;
0162:
0163: /**
0164: * The DOMConfiguration object for this document.
0165: */
0166: protected DocumentConfiguration domConfig;
0167:
0168: /**
0169: * The XBL manager for this document.
0170: */
0171: protected transient XBLManager xblManager = new GenericXBLManager();
0172:
0173: /**
0174: * The elementsById lists.
0175: * This is keyed on 'id'. the entry is either
0176: * a IdSoftReference to the element or a List of
0177: * IdSoftReferences (if there is more than one element
0178: * owned by this document with a particular 'id').
0179: */
0180: protected transient Map elementsById;
0181:
0182: /**
0183: * Creates a new document.
0184: */
0185: protected AbstractDocument() {
0186: }
0187:
0188: /**
0189: * Creates a new document.
0190: */
0191: public AbstractDocument(DocumentType dt, DOMImplementation impl) {
0192: implementation = impl;
0193: if (dt != null) {
0194: if (dt instanceof GenericDocumentType) {
0195: GenericDocumentType gdt = (GenericDocumentType) dt;
0196: if (gdt.getOwnerDocument() == null)
0197: gdt.setOwnerDocument(this );
0198: }
0199: appendChild(dt);
0200: }
0201: }
0202:
0203: /**
0204: * Sets the input encoding that was used when the document was being
0205: * parsed.
0206: */
0207: public void setDocumentInputEncoding(String ie) {
0208: inputEncoding = ie;
0209: }
0210:
0211: /**
0212: * Sets the XML encoding that was found in the XML prolog.
0213: */
0214: public void setDocumentXmlEncoding(String xe) {
0215: xmlEncoding = xe;
0216: }
0217:
0218: /**
0219: * Implements {@link org.apache.batik.i18n.Localizable#setLocale(Locale)}.
0220: */
0221: public void setLocale(Locale l) {
0222: localizableSupport.setLocale(l);
0223: }
0224:
0225: /**
0226: * Implements {@link org.apache.batik.i18n.Localizable#getLocale()}.
0227: */
0228: public Locale getLocale() {
0229: return localizableSupport.getLocale();
0230: }
0231:
0232: /**
0233: * Implements {@link
0234: * org.apache.batik.i18n.Localizable#formatMessage(String,Object[])}.
0235: */
0236: public String formatMessage(String key, Object[] args)
0237: throws MissingResourceException {
0238: return localizableSupport.formatMessage(key, args);
0239: }
0240:
0241: /**
0242: * Tests whether the event dispatching must be done.
0243: */
0244: public boolean getEventsEnabled() {
0245: return eventsEnabled;
0246: }
0247:
0248: /**
0249: * Sets the eventsEnabled property.
0250: */
0251: public void setEventsEnabled(boolean b) {
0252: eventsEnabled = b;
0253: }
0254:
0255: /**
0256: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getNodeName()}.
0257: * @return "#document".
0258: */
0259: public String getNodeName() {
0260: return "#document";
0261: }
0262:
0263: /**
0264: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getNodeType()}.
0265: * @return {@link org.w3c.dom.Node#DOCUMENT_NODE}
0266: */
0267: public short getNodeType() {
0268: return DOCUMENT_NODE;
0269: }
0270:
0271: /**
0272: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#getDoctype()}.
0273: */
0274: public DocumentType getDoctype() {
0275: for (Node n = getFirstChild(); n != null; n = n
0276: .getNextSibling()) {
0277: if (n.getNodeType() == DOCUMENT_TYPE_NODE) {
0278: return (DocumentType) n;
0279: }
0280: }
0281: return null;
0282: }
0283:
0284: /**
0285: * Sets the document type node.
0286: */
0287: public void setDoctype(DocumentType dt) {
0288: if (dt != null) {
0289: appendChild(dt);
0290: ((ExtendedNode) dt).setReadonly(true);
0291: }
0292: }
0293:
0294: /**
0295: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#getImplementation()}.
0296: * @return {@link #implementation}
0297: */
0298: public DOMImplementation getImplementation() {
0299: return implementation;
0300: }
0301:
0302: /**
0303: * <b>DOM</b>: Implements {@link
0304: * org.w3c.dom.Document#getDocumentElement()}.
0305: */
0306: public Element getDocumentElement() {
0307: for (Node n = getFirstChild(); n != null; n = n
0308: .getNextSibling()) {
0309: if (n.getNodeType() == ELEMENT_NODE) {
0310: return (Element) n;
0311: }
0312: }
0313: return null;
0314: }
0315:
0316: /**
0317: * <b>DOM</b>: Implements {@link
0318: * org.w3c.dom.Document#importNode(Node,boolean)}.
0319: */
0320: public Node importNode(Node importedNode, boolean deep)
0321: throws DOMException {
0322: return importNode(importedNode, deep, false);
0323: }
0324:
0325: /**
0326: * Imports the given node 'importNode' to this document.
0327: * It does so deeply if 'deep' is set to true.
0328: * It will not mark id attributes as id's if 'trimId' is set false.
0329: * this is used primarily for the clone trees of the 'use' element
0330: * so they don't clutter the hashtable.
0331: */
0332: public Node importNode(Node importedNode, boolean deep,
0333: boolean trimId) {
0334: /*
0335: * The trimming of id's is used by the 'use' element to keep
0336: * down the amount of 'bogus' id's in the hashtable.
0337: */
0338: Node result;
0339: switch (importedNode.getNodeType()) {
0340: case ELEMENT_NODE:
0341: Element e = createElementNS(importedNode.getNamespaceURI(),
0342: importedNode.getNodeName());
0343: result = e;
0344: if (importedNode.hasAttributes()) {
0345: NamedNodeMap attr = importedNode.getAttributes();
0346: int len = attr.getLength();
0347: for (int i = 0; i < len; i++) {
0348: Attr a = (Attr) attr.item(i);
0349: if (!a.getSpecified())
0350: continue;
0351: AbstractAttr aa = (AbstractAttr) importNode(a, true);
0352: if (trimId && aa.isId())
0353: aa.setIsId(false); // don't consider this an Id.
0354: e.setAttributeNodeNS(aa);
0355: }
0356: }
0357: break;
0358:
0359: case ATTRIBUTE_NODE:
0360: result = createAttributeNS(importedNode.getNamespaceURI(),
0361: importedNode.getNodeName());
0362: break;
0363:
0364: case TEXT_NODE:
0365: result = createTextNode(importedNode.getNodeValue());
0366: deep = false;
0367: break;
0368:
0369: case CDATA_SECTION_NODE:
0370: result = createCDATASection(importedNode.getNodeValue());
0371: deep = false;
0372: break;
0373:
0374: case ENTITY_REFERENCE_NODE:
0375: result = createEntityReference(importedNode.getNodeName());
0376: break;
0377:
0378: case PROCESSING_INSTRUCTION_NODE:
0379: result = createProcessingInstruction(importedNode
0380: .getNodeName(), importedNode.getNodeValue());
0381: deep = false;
0382: break;
0383:
0384: case COMMENT_NODE:
0385: result = createComment(importedNode.getNodeValue());
0386: deep = false;
0387: break;
0388:
0389: case DOCUMENT_FRAGMENT_NODE:
0390: result = createDocumentFragment();
0391: break;
0392:
0393: default:
0394: throw createDOMException(DOMException.NOT_SUPPORTED_ERR,
0395: "import.node", new Object[] {});
0396: }
0397:
0398: if (importedNode instanceof AbstractNode) {
0399: // Only fire the UserDataHandler if the imported node is from
0400: // Batik's DOM implementation.
0401: fireUserDataHandlers(UserDataHandler.NODE_IMPORTED,
0402: importedNode, result);
0403: }
0404:
0405: if (deep) {
0406: for (Node n = importedNode.getFirstChild(); n != null; n = n
0407: .getNextSibling()) {
0408: result.appendChild(importNode(n, true));
0409: }
0410: }
0411: return result;
0412: }
0413:
0414: /**
0415: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#cloneNode(boolean)}.
0416: */
0417: public Node cloneNode(boolean deep) {
0418: Document n = (Document) newNode();
0419: copyInto(n);
0420: fireUserDataHandlers(UserDataHandler.NODE_CLONED, this , n);
0421: if (deep) {
0422: for (Node c = getFirstChild(); c != null; c = c
0423: .getNextSibling()) {
0424: n.appendChild(n.importNode(c, deep));
0425: }
0426: }
0427: return n;
0428: }
0429:
0430: /**
0431: * Returns whether the given attribute node is an ID attribute.
0432: */
0433: public abstract boolean isId(Attr node);
0434:
0435: /**
0436: * <b>DOM</b>: Implements {@link
0437: * org.w3c.dom.Document#getElementById(String)}.
0438: */
0439: public Element getElementById(String id) {
0440: return getChildElementById(getDocumentElement(), id);
0441: }
0442:
0443: /**
0444: * Finds an element that is in the same document fragment as
0445: * 'requestor' that has 'id'.
0446: */
0447: public Element getChildElementById(Node requestor, String id) {
0448: if ((id == null) || (id.length() == 0))
0449: return null;
0450: if (elementsById == null)
0451: return null;
0452:
0453: Node root = getRoot(requestor);
0454:
0455: Object o = elementsById.get(id);
0456: if (o == null)
0457: return null;
0458: if (o instanceof IdSoftRef) {
0459: o = ((IdSoftRef) o).get();
0460: if (o == null) {
0461: elementsById.remove(id);
0462: return null;
0463: }
0464: Element e = (Element) o;
0465: if (getRoot(e) == root)
0466: return e;
0467: return null;
0468: }
0469:
0470: // Not a IdSoftRef so it must be a list.
0471: List l = (List) o;
0472: Iterator li = l.iterator();
0473: while (li.hasNext()) {
0474: IdSoftRef sr = (IdSoftRef) li.next();
0475: o = sr.get();
0476: if (o == null) {
0477: li.remove();
0478: } else {
0479: Element e = (Element) o;
0480: if (getRoot(e) == root)
0481: return e;
0482: }
0483: }
0484: return null;
0485: }
0486:
0487: protected Node getRoot(Node n) {
0488: Node r = n;
0489: while (n != null) {
0490: r = n;
0491: n = n.getParentNode();
0492: }
0493: return r;
0494: }
0495:
0496: protected class IdSoftRef extends
0497: CleanerThread.SoftReferenceCleared {
0498: String id;
0499: List list;
0500:
0501: IdSoftRef(Object o, String id) {
0502: super (o);
0503: this .id = id;
0504: }
0505:
0506: IdSoftRef(Object o, String id, List list) {
0507: super (o);
0508: this .id = id;
0509: this .list = list;
0510: }
0511:
0512: public void setList(List list) {
0513: this .list = list;
0514: }
0515:
0516: public void cleared() {
0517: if (elementsById == null)
0518: return;
0519: synchronized (elementsById) {
0520: if (list != null)
0521: list.remove(this );
0522: else {
0523: Object o = elementsById.remove(id);
0524: if (o != this ) // oops not us!
0525: elementsById.put(id, o);
0526: }
0527: }
0528: }
0529: }
0530:
0531: /**
0532: * Remove the mapping for <tt>element</tt> to <tt>id</tt>
0533: */
0534: public void removeIdEntry(Element e, String id) {
0535: // Remove old Id mapping if we have one.
0536: if (id == null)
0537: return;
0538: if (elementsById == null)
0539: return;
0540:
0541: synchronized (elementsById) {
0542: Object o = elementsById.get(id);
0543: if (o == null)
0544: return;
0545:
0546: if (o instanceof IdSoftRef) {
0547: elementsById.remove(id);
0548: return;
0549: }
0550:
0551: List l = (List) o;
0552: Iterator li = l.iterator();
0553: while (li.hasNext()) {
0554: IdSoftRef ip = (IdSoftRef) li.next();
0555: o = ip.get();
0556: if (o == null) {
0557: li.remove();
0558: } else if (e == o) {
0559: li.remove();
0560: break;
0561: }
0562: }
0563:
0564: if (l.size() == 0)
0565: elementsById.remove(id);
0566: }
0567: }
0568:
0569: public void addIdEntry(Element e, String id) {
0570: if (id == null)
0571: return;
0572:
0573: if (elementsById == null) {
0574: Map tmp = new HashMap();
0575: synchronized (tmp) {
0576: elementsById = tmp;
0577: elementsById.put(id, new IdSoftRef(e, id));
0578: }
0579: return;
0580: }
0581:
0582: synchronized (elementsById) {
0583: // Add new Id mapping.
0584: Object o = elementsById.get(id);
0585: if (o == null) {
0586: elementsById.put(id, new IdSoftRef(e, id));
0587: return;
0588: }
0589: if (o instanceof IdSoftRef) {
0590: IdSoftRef ip = (IdSoftRef) o;
0591: Object r = ip.get();
0592: if (r == null) { // reference is gone so replace it.
0593: elementsById.put(id, new IdSoftRef(e, id));
0594: return;
0595: }
0596:
0597: // Create new List for this id.
0598: List l = new ArrayList(4);
0599: ip.setList(l);
0600: l.add(ip);
0601: l.add(new IdSoftRef(e, id, l));
0602: elementsById.put(id, l);
0603: return;
0604: }
0605:
0606: List l = (List) o;
0607: l.add(new IdSoftRef(e, id, l));
0608: }
0609: }
0610:
0611: public void updateIdEntry(Element e, String oldId, String newId) {
0612: if ((oldId == newId)
0613: || ((oldId != null) && (oldId.equals(newId))))
0614: return;
0615:
0616: removeIdEntry(e, oldId);
0617:
0618: addIdEntry(e, newId);
0619: }
0620:
0621: /**
0622: * Returns an ElementsByTagName object from the cache, if any.
0623: */
0624: public ElementsByTagName getElementsByTagName(Node n, String ln) {
0625: if (elementsByTagNames == null) {
0626: return null;
0627: }
0628: SoftDoublyIndexedTable t;
0629: t = (SoftDoublyIndexedTable) elementsByTagNames.get(n);
0630: if (t == null) {
0631: return null;
0632: }
0633: return (ElementsByTagName) t.get(null, ln);
0634: }
0635:
0636: /**
0637: * Puts an ElementsByTagName object in the cache.
0638: */
0639: public void putElementsByTagName(Node n, String ln,
0640: ElementsByTagName l) {
0641: if (elementsByTagNames == null) {
0642: elementsByTagNames = new WeakHashMap(11);
0643: }
0644: SoftDoublyIndexedTable t;
0645: t = (SoftDoublyIndexedTable) elementsByTagNames.get(n);
0646: if (t == null) {
0647: elementsByTagNames.put(n, t = new SoftDoublyIndexedTable());
0648: }
0649: t.put(null, ln, l);
0650: }
0651:
0652: /**
0653: * Returns an ElementsByTagNameNS object from the cache, if any.
0654: */
0655: public ElementsByTagNameNS getElementsByTagNameNS(Node n,
0656: String ns, String ln) {
0657: if (elementsByTagNamesNS == null) {
0658: return null;
0659: }
0660: SoftDoublyIndexedTable t;
0661: t = (SoftDoublyIndexedTable) elementsByTagNamesNS.get(n);
0662: if (t == null) {
0663: return null;
0664: }
0665: return (ElementsByTagNameNS) t.get(ns, ln);
0666: }
0667:
0668: /**
0669: * Puts an ElementsByTagNameNS object in the cache.
0670: */
0671: public void putElementsByTagNameNS(Node n, String ns, String ln,
0672: ElementsByTagNameNS l) {
0673: if (elementsByTagNamesNS == null) {
0674: elementsByTagNamesNS = new WeakHashMap(11);
0675: }
0676: SoftDoublyIndexedTable t;
0677: t = (SoftDoublyIndexedTable) elementsByTagNamesNS.get(n);
0678: if (t == null) {
0679: elementsByTagNamesNS.put(n,
0680: t = new SoftDoublyIndexedTable());
0681: }
0682: t.put(ns, ln, l);
0683: }
0684:
0685: // DocumentEvent /////////////////////////////////////////////////////////
0686:
0687: /**
0688: * <b>DOM</b>: Implements {@link
0689: * org.w3c.dom.events.DocumentEvent#createEvent(String)}.
0690: */
0691: public Event createEvent(String eventType) throws DOMException {
0692: if (documentEventSupport == null) {
0693: documentEventSupport = ((AbstractDOMImplementation) implementation)
0694: .createDocumentEventSupport();
0695: }
0696: return documentEventSupport.createEvent(eventType);
0697: }
0698:
0699: /**
0700: * <b>DOM</b>: Implements {@link
0701: * org.w3c.dom.events.DocumentEvent#canDispatch(String,String)}.
0702: */
0703: public boolean canDispatch(String ns, String eventType) {
0704: if (eventType == null) {
0705: return false;
0706: }
0707: if (ns != null && ns.length() == 0) {
0708: ns = null;
0709: }
0710: if (ns == null
0711: || ns.equals(XMLConstants.XML_EVENTS_NAMESPACE_URI)) {
0712: return eventType.equals("Event")
0713: || eventType.equals("MutationEvent")
0714: || eventType.equals("MutationNameEvent")
0715: || eventType.equals("UIEvent")
0716: || eventType.equals("MouseEvent")
0717: || eventType.equals("KeyEvent")
0718: || eventType.equals("KeyboardEvent")
0719: || eventType.equals("TextEvent")
0720: || eventType.equals("CustomEvent");
0721: }
0722: return false;
0723: }
0724:
0725: // DocumentTraversal /////////////////////////////////////////////////////
0726:
0727: /**
0728: * <b>DOM</b>: Implements {@link
0729: * DocumentTraversal#createNodeIterator(Node,int,NodeFilter,boolean)}.
0730: */
0731: public NodeIterator createNodeIterator(Node root, int whatToShow,
0732: NodeFilter filter, boolean entityReferenceExpansion)
0733: throws DOMException {
0734: if (traversalSupport == null) {
0735: traversalSupport = new TraversalSupport();
0736: }
0737: return traversalSupport.createNodeIterator(this , root,
0738: whatToShow, filter, entityReferenceExpansion);
0739: }
0740:
0741: /**
0742: * <b>DOM</b>: Implements {@link
0743: * DocumentTraversal#createTreeWalker(Node,int,NodeFilter,boolean)}.
0744: */
0745: public TreeWalker createTreeWalker(Node root, int whatToShow,
0746: NodeFilter filter, boolean entityReferenceExpansion)
0747: throws DOMException {
0748: return TraversalSupport.createTreeWalker(this , root,
0749: whatToShow, filter, entityReferenceExpansion);
0750: }
0751:
0752: /**
0753: * Detaches the given node iterator from this document.
0754: */
0755: public void detachNodeIterator(NodeIterator it) {
0756: traversalSupport.detachNodeIterator(it);
0757: }
0758:
0759: /**
0760: * Notifies this document that a node will be removed.
0761: */
0762: public void nodeToBeRemoved(Node node) {
0763: if (traversalSupport != null) {
0764: traversalSupport.nodeToBeRemoved(node);
0765: }
0766: }
0767:
0768: /**
0769: * Returns the current document.
0770: */
0771: protected AbstractDocument getCurrentDocument() {
0772: return this ;
0773: }
0774:
0775: /**
0776: * Exports this node to the given document.
0777: * @param n The clone node.
0778: * @param d The destination document.
0779: */
0780: protected Node export(Node n, Document d) {
0781: throw createDOMException(DOMException.NOT_SUPPORTED_ERR,
0782: "import.document", new Object[] {});
0783: }
0784:
0785: /**
0786: * Deeply exports this node to the given document.
0787: * @param n The clone node.
0788: * @param d The destination document.
0789: */
0790: protected Node deepExport(Node n, Document d) {
0791: throw createDOMException(DOMException.NOT_SUPPORTED_ERR,
0792: "import.document", new Object[] {});
0793: }
0794:
0795: /**
0796: * Copy the fields of the current node into the given node.
0797: * @param n a node of the type of this.
0798: */
0799: protected Node copyInto(Node n) {
0800: super .copyInto(n);
0801: AbstractDocument ad = (AbstractDocument) n;
0802: ad.implementation = implementation;
0803: ad.localizableSupport = new LocalizableSupport(RESOURCES,
0804: getClass().getClassLoader());
0805: ad.inputEncoding = inputEncoding;
0806: ad.xmlEncoding = xmlEncoding;
0807: ad.xmlVersion = xmlVersion;
0808: ad.xmlStandalone = xmlStandalone;
0809: ad.documentURI = documentURI;
0810: ad.strictErrorChecking = strictErrorChecking;
0811: // XXX clone DocumentConfiguration?
0812: return n;
0813: }
0814:
0815: /**
0816: * Deeply copy the fields of the current node into the given node.
0817: * @param n a node of the type of this.
0818: */
0819: protected Node deepCopyInto(Node n) {
0820: super .deepCopyInto(n);
0821: AbstractDocument ad = (AbstractDocument) n;
0822: ad.implementation = implementation;
0823: ad.localizableSupport = new LocalizableSupport(RESOURCES,
0824: getClass().getClassLoader());
0825: return n;
0826: }
0827:
0828: /**
0829: * Checks the validity of a node to be inserted.
0830: */
0831: protected void checkChildType(Node n, boolean replace) {
0832: short t = n.getNodeType();
0833: switch (t) {
0834: case ELEMENT_NODE:
0835: case PROCESSING_INSTRUCTION_NODE:
0836: case COMMENT_NODE:
0837: case DOCUMENT_TYPE_NODE:
0838: case DOCUMENT_FRAGMENT_NODE:
0839: break;
0840: default:
0841: throw createDOMException(
0842: DOMException.HIERARCHY_REQUEST_ERR, "child.type",
0843: new Object[] { new Integer(getNodeType()),
0844: getNodeName(), new Integer(t),
0845: n.getNodeName() });
0846: }
0847: if (!replace
0848: && (t == ELEMENT_NODE && getDocumentElement() != null)
0849: || (t == DOCUMENT_TYPE_NODE && getDoctype() != null)) {
0850: throw createDOMException(DOMException.NOT_SUPPORTED_ERR,
0851: "document.child.already.exists", new Object[] {
0852: new Integer(t), n.getNodeName() });
0853: }
0854: }
0855:
0856: /**
0857: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#getInputEncoding()}.
0858: */
0859: public String getInputEncoding() {
0860: return inputEncoding;
0861: }
0862:
0863: /**
0864: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#getXmlEncoding()}.
0865: */
0866: public String getXmlEncoding() {
0867: return xmlEncoding;
0868: }
0869:
0870: /**
0871: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#getXmlStandalone()}.
0872: */
0873: public boolean getXmlStandalone() {
0874: return xmlStandalone;
0875: }
0876:
0877: /**
0878: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#setXmlStandalone(boolean)}.
0879: */
0880: public void setXmlStandalone(boolean b) throws DOMException {
0881: xmlStandalone = b;
0882: }
0883:
0884: /**
0885: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#getXmlVersion()}.
0886: */
0887: public String getXmlVersion() {
0888: return xmlVersion;
0889: }
0890:
0891: /**
0892: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#setXmlVersion(String)}.
0893: */
0894: public void setXmlVersion(String v) throws DOMException {
0895: if (v == null || !v.equals(XMLConstants.XML_VERSION_10)
0896: && !v.equals(XMLConstants.XML_VERSION_11)) {
0897: throw createDOMException(DOMException.NOT_SUPPORTED_ERR,
0898: "xml.version", new Object[] { v });
0899: }
0900: xmlVersion = v;
0901: }
0902:
0903: /**
0904: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#getStrictErrorChecking()}.
0905: */
0906: public boolean getStrictErrorChecking() {
0907: return strictErrorChecking;
0908: }
0909:
0910: /**
0911: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#setStrictErrorChecking(boolean)}.
0912: */
0913: public void setStrictErrorChecking(boolean b) {
0914: strictErrorChecking = b;
0915: }
0916:
0917: /**
0918: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#getDocumentURI()}.
0919: */
0920: public String getDocumentURI() {
0921: return documentURI;
0922: }
0923:
0924: /**
0925: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#setDocumentURI(String)}.
0926: */
0927: public void setDocumentURI(String uri) {
0928: documentURI = uri;
0929: }
0930:
0931: /**
0932: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#getDomConfig()}.
0933: */
0934: public DOMConfiguration getDomConfig() {
0935: if (domConfig == null) {
0936: domConfig = new DocumentConfiguration();
0937: }
0938: return domConfig;
0939: }
0940:
0941: /**
0942: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#adoptNode(Node)}.
0943: */
0944: public Node adoptNode(Node n) throws DOMException {
0945: if (!(n instanceof AbstractNode)) {
0946: return null;
0947: }
0948: switch (n.getNodeType()) {
0949: case Node.DOCUMENT_NODE:
0950: throw createDOMException(DOMException.NOT_SUPPORTED_ERR,
0951: "adopt.document", new Object[] {});
0952: case Node.DOCUMENT_TYPE_NODE:
0953: throw createDOMException(DOMException.NOT_SUPPORTED_ERR,
0954: "adopt.document.type", new Object[] {});
0955: case Node.ENTITY_NODE:
0956: case Node.NOTATION_NODE:
0957: return null;
0958: }
0959: AbstractNode an = (AbstractNode) n;
0960: if (an.isReadonly()) {
0961: throw createDOMException(
0962: DOMException.NO_MODIFICATION_ALLOWED_ERR,
0963: "readonly.node", new Object[] {
0964: new Integer(an.getNodeType()),
0965: an.getNodeName() });
0966: }
0967: Node parent = n.getParentNode();
0968: if (parent != null) {
0969: parent.removeChild(n);
0970: }
0971: adoptNode1((AbstractNode) n);
0972: return n;
0973: }
0974:
0975: /**
0976: * Helper function for {@link #adoptNode(Node)}.
0977: */
0978: protected void adoptNode1(AbstractNode n) {
0979: n.ownerDocument = this ;
0980: switch (n.getNodeType()) {
0981: case Node.ATTRIBUTE_NODE:
0982: AbstractAttr attr = (AbstractAttr) n;
0983: attr.ownerElement = null;
0984: attr.unspecified = false;
0985: break;
0986: case Node.ELEMENT_NODE:
0987: NamedNodeMap nnm = n.getAttributes();
0988: int len = nnm.getLength();
0989: for (int i = 0; i < len; i++) {
0990: attr = (AbstractAttr) nnm.item(i);
0991: if (attr.getSpecified()) {
0992: adoptNode1(attr);
0993: }
0994: }
0995: break;
0996: case Node.ENTITY_REFERENCE_NODE:
0997: while (n.getFirstChild() != null) {
0998: n.removeChild(n.getFirstChild());
0999: }
1000: break;
1001: }
1002:
1003: fireUserDataHandlers(UserDataHandler.NODE_ADOPTED, n, null);
1004:
1005: for (Node m = n.getFirstChild(); m != null; m = m
1006: .getNextSibling()) {
1007: switch (m.getNodeType()) {
1008: case Node.DOCUMENT_TYPE_NODE:
1009: case Node.ENTITY_NODE:
1010: case Node.NOTATION_NODE:
1011: return;
1012: }
1013: adoptNode1((AbstractNode) m);
1014: }
1015: }
1016:
1017: /**
1018: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#renameNode(Node,String,String)}.
1019: */
1020: public Node renameNode(Node n, String ns, String qn) {
1021: AbstractNode an = (AbstractNode) n;
1022: if (an == getDocumentElement()) {
1023: throw createDOMException(DOMException.NOT_SUPPORTED_ERR,
1024: "rename.document.element", new Object[] {});
1025: }
1026: int nt = n.getNodeType();
1027: if (nt != Node.ELEMENT_NODE && nt != Node.ATTRIBUTE_NODE) {
1028: throw createDOMException(DOMException.NOT_SUPPORTED_ERR,
1029: "rename.node", new Object[] { new Integer(nt),
1030: n.getNodeName() });
1031: }
1032: if (xmlVersion.equals(XMLConstants.XML_VERSION_11)
1033: && !DOMUtilities.isValidName11(qn)
1034: || !DOMUtilities.isValidName(qn)) {
1035: throw createDOMException(DOMException.NOT_SUPPORTED_ERR,
1036: "wf.invalid.name", new Object[] { qn });
1037: }
1038: if (n.getOwnerDocument() != this ) {
1039: throw createDOMException(DOMException.NOT_SUPPORTED_ERR,
1040: "node.from.wrong.document", new Object[] {
1041: new Integer(nt), n.getNodeName() });
1042: }
1043: int i = qn.indexOf(':');
1044: if (i == 0 || i == qn.length() - 1) {
1045: throw createDOMException(DOMException.NAMESPACE_ERR,
1046: "qname", new Object[] { new Integer(nt),
1047: n.getNodeName(), qn });
1048: }
1049: String prefix = DOMUtilities.getPrefix(qn);
1050: if (ns != null && ns.length() == 0) {
1051: ns = null;
1052: }
1053: if (prefix != null && ns == null) {
1054: throw createDOMException(DOMException.NAMESPACE_ERR,
1055: "prefix", new Object[] { new Integer(nt),
1056: n.getNodeName(), prefix });
1057: }
1058: if (strictErrorChecking) {
1059: if (XMLConstants.XML_PREFIX.equals(prefix)
1060: && !XMLConstants.XML_NAMESPACE_URI.equals(ns)
1061: || XMLConstants.XMLNS_PREFIX.equals(prefix)
1062: && !XMLConstants.XMLNS_NAMESPACE_URI.equals(ns)) {
1063: throw createDOMException(DOMException.NAMESPACE_ERR,
1064: "namespace", new Object[] { new Integer(nt),
1065: n.getNodeName(), ns });
1066: }
1067: }
1068:
1069: String prevNamespaceURI = n.getNamespaceURI();
1070: String prevNodeName = n.getNodeName();
1071: if (nt == Node.ELEMENT_NODE) {
1072: Node parent = n.getParentNode();
1073: AbstractElement e = (AbstractElement) createElementNS(ns,
1074: qn);
1075:
1076: // Move event handlers across
1077: EventSupport es1 = an.getEventSupport();
1078: if (es1 != null) {
1079: EventSupport es2 = e.getEventSupport();
1080: if (es2 == null) {
1081: AbstractDOMImplementation di = (AbstractDOMImplementation) implementation;
1082: es2 = di.createEventSupport(e);
1083: setEventsEnabled(true);
1084: e.eventSupport = es2;
1085: }
1086: es1.moveEventListeners(e.getEventSupport());
1087: }
1088:
1089: // Move user data across
1090: e.userData = e.userData == null ? null
1091: : (HashMap) an.userData.clone();
1092: e.userDataHandlers = e.userDataHandlers == null ? null
1093: : (HashMap) an.userDataHandlers.clone();
1094:
1095: // Remove from parent
1096: Node next = null;
1097: if (parent != null) {
1098: n.getNextSibling();
1099: parent.removeChild(n);
1100: }
1101:
1102: // Move child nodes across
1103: while (n.getFirstChild() != null) {
1104: e.appendChild(n.getFirstChild());
1105: }
1106:
1107: // Move attributes across
1108: NamedNodeMap nnm = n.getAttributes();
1109: for (int j = 0; j < nnm.getLength(); j++) {
1110: Attr a = (Attr) nnm.item(j);
1111: e.setAttributeNodeNS(a);
1112: }
1113: // while (nnm.getLength() > 0) {
1114: // Attr a = (Attr) nnm.item(0);
1115: // e.setAttributeNodeNS(a);
1116: // }
1117:
1118: // Reinsert into parent
1119: if (parent != null) {
1120: if (next == null) {
1121: parent.appendChild(e);
1122: } else {
1123: parent.insertBefore(next, e);
1124: }
1125: }
1126:
1127: fireUserDataHandlers(UserDataHandler.NODE_RENAMED, n, e);
1128: if (getEventsEnabled()) {
1129: MutationNameEvent ev = (MutationNameEvent) createEvent("MutationNameEvent");
1130: ev.initMutationNameEventNS(
1131: XMLConstants.XML_EVENTS_NAMESPACE_URI,
1132: "DOMElementNameChanged", true, // canBubbleArg
1133: false, // cancelableArg
1134: null, // relatedNodeArg
1135: prevNamespaceURI, prevNodeName);
1136: dispatchEvent(ev);
1137: }
1138: return e;
1139: } else {
1140: if (n instanceof AbstractAttrNS) {
1141: AbstractAttrNS a = (AbstractAttrNS) n;
1142: Element e = a.getOwnerElement();
1143:
1144: // Remove attribute from element
1145: if (e != null) {
1146: e.removeAttributeNode(a);
1147: }
1148:
1149: // Update name
1150: a.namespaceURI = ns;
1151: a.nodeName = qn;
1152:
1153: // Reinsert attribute into element
1154: if (e != null) {
1155: e.setAttributeNodeNS(a);
1156: }
1157:
1158: fireUserDataHandlers(UserDataHandler.NODE_RENAMED, a,
1159: null);
1160: if (getEventsEnabled()) {
1161: MutationNameEvent ev = (MutationNameEvent) createEvent("MutationNameEvent");
1162: ev.initMutationNameEventNS(
1163: XMLConstants.XML_EVENTS_NAMESPACE_URI,
1164: "DOMAttrNameChanged", true, // canBubbleArg
1165: false, // cancelableArg
1166: a, // relatedNodeArg
1167: prevNamespaceURI, prevNodeName);
1168: dispatchEvent(ev);
1169: }
1170: return a;
1171: } else {
1172: AbstractAttr a = (AbstractAttr) n;
1173: Element e = a.getOwnerElement();
1174:
1175: // Remove attribute from element and create new one
1176: if (e != null) {
1177: e.removeAttributeNode(a);
1178: }
1179: AbstractAttr a2 = (AbstractAttr) createAttributeNS(ns,
1180: qn);
1181:
1182: // Move attribute value across
1183: a2.setNodeValue(a.getNodeValue());
1184:
1185: // Move user data across
1186: a2.userData = a.userData == null ? null
1187: : (HashMap) a.userData.clone();
1188: a2.userDataHandlers = a.userDataHandlers == null ? null
1189: : (HashMap) a.userDataHandlers.clone();
1190:
1191: // Reinsert attribute into parent
1192: if (e != null) {
1193: e.setAttributeNodeNS(a2);
1194: }
1195:
1196: fireUserDataHandlers(UserDataHandler.NODE_RENAMED, a,
1197: a2);
1198: if (getEventsEnabled()) {
1199: MutationNameEvent ev = (MutationNameEvent) createEvent("MutationNameEvent");
1200: ev.initMutationNameEventNS(
1201: XMLConstants.XML_EVENTS_NAMESPACE_URI,
1202: "DOMAttrNameChanged", true, // canBubbleArg
1203: false, // cancelableArg
1204: a2, // relatedNodeArg
1205: prevNamespaceURI, prevNodeName);
1206: dispatchEvent(ev);
1207: }
1208: return a2;
1209: }
1210: }
1211: }
1212:
1213: /**
1214: * <b>DOM</b>: Implements {@link org.w3c.dom.Document#normalizeDocument()}.
1215: * XXX Does not handle the 'entities' parameter yet.
1216: */
1217: public void normalizeDocument() {
1218: if (domConfig == null) {
1219: domConfig = new DocumentConfiguration();
1220: }
1221: boolean cdataSections = domConfig
1222: .getBooleanParameter(DOMConstants.DOM_CDATA_SECTIONS_PARAM);
1223: boolean comments = domConfig
1224: .getBooleanParameter(DOMConstants.DOM_COMMENTS_PARAM);
1225: boolean elementContentWhitespace = domConfig
1226: .getBooleanParameter(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE_PARAM);
1227: boolean namespaceDeclarations = domConfig
1228: .getBooleanParameter(DOMConstants.DOM_NAMESPACE_DECLARATIONS_PARAM);
1229: boolean namespaces = domConfig
1230: .getBooleanParameter(DOMConstants.DOM_NAMESPACES_PARAM);
1231: boolean splitCdataSections = domConfig
1232: .getBooleanParameter(DOMConstants.DOM_SPLIT_CDATA_SECTIONS_PARAM);
1233: DOMErrorHandler errorHandler = (DOMErrorHandler) domConfig
1234: .getParameter(DOMConstants.DOM_ERROR_HANDLER_PARAM);
1235: normalizeDocument(getDocumentElement(), cdataSections,
1236: comments, elementContentWhitespace,
1237: namespaceDeclarations, namespaces, splitCdataSections,
1238: errorHandler);
1239: }
1240:
1241: /**
1242: * Helper function for {@link #normalizeDocument()}.
1243: */
1244: protected boolean normalizeDocument(Element e,
1245: boolean cdataSections, boolean comments,
1246: boolean elementContentWhitepace,
1247: boolean namespaceDeclarations, boolean namespaces,
1248: boolean splitCdataSections, DOMErrorHandler errorHandler) {
1249: AbstractElement ae = (AbstractElement) e;
1250: Node n = e.getFirstChild();
1251: while (n != null) {
1252: int nt = n.getNodeType();
1253: if (nt == Node.TEXT_NODE || !cdataSections
1254: && nt == Node.CDATA_SECTION_NODE) {
1255: // coalesce text nodes
1256: Node t = n;
1257: StringBuffer sb = new StringBuffer();
1258: sb.append(t.getNodeValue());
1259: n = n.getNextSibling();
1260: while (n != null
1261: && (n.getNodeType() == Node.TEXT_NODE || !cdataSections
1262: && n.getNodeType() == Node.CDATA_SECTION_NODE)) {
1263: sb.append(n.getNodeValue());
1264: Node next = n.getNextSibling();
1265: e.removeChild(n);
1266: n = next;
1267: }
1268: String s = sb.toString();
1269: if (s.length() == 0) {
1270: Node next = n.getNextSibling();
1271: e.removeChild(n);
1272: n = next;
1273: continue;
1274: }
1275: if (!s.equals(t.getNodeValue())) {
1276: if (!cdataSections && nt == Node.TEXT_NODE) {
1277: n = createTextNode(s);
1278: e.replaceChild(n, t);
1279: } else {
1280: n = t;
1281: t.setNodeValue(s);
1282: }
1283: } else {
1284: n = t;
1285: }
1286: if (!elementContentWhitepace) {
1287: // remove element content whitespace text nodes
1288: nt = n.getNodeType();
1289: if (nt == Node.TEXT_NODE) {
1290: AbstractText tn = (AbstractText) n;
1291: if (tn.isElementContentWhitespace()) {
1292: Node next = n.getNextSibling();
1293: e.removeChild(n);
1294: n = next;
1295: continue;
1296: }
1297: }
1298: }
1299: if (nt == Node.CDATA_SECTION_NODE && splitCdataSections) {
1300: if (!splitCdata(e, n, errorHandler)) {
1301: return false;
1302: }
1303: }
1304: } else if (nt == Node.CDATA_SECTION_NODE
1305: && splitCdataSections) {
1306: // split CDATA sections
1307: if (!splitCdata(e, n, errorHandler)) {
1308: return false;
1309: }
1310: } else if (nt == Node.COMMENT_NODE && !comments) {
1311: // remove comments
1312: Node next = n.getPreviousSibling();
1313: if (next == null) {
1314: next = n.getNextSibling();
1315: }
1316: e.removeChild(n);
1317: n = next;
1318: continue;
1319: }
1320:
1321: n = n.getNextSibling();
1322: }
1323:
1324: NamedNodeMap nnm = e.getAttributes();
1325: LinkedList toRemove = new LinkedList();
1326: HashMap names = new HashMap(); // todo names is not used ?
1327: for (int i = 0; i < nnm.getLength(); i++) {
1328: Attr a = (Attr) nnm.item(i);
1329: String prefix = a.getPrefix();
1330: if (a != null
1331: && XMLConstants.XMLNS_PREFIX.equals(prefix)
1332: || a.getNodeName()
1333: .equals(XMLConstants.XMLNS_PREFIX)) {
1334: if (!namespaceDeclarations) {
1335: // remove namespace declarations
1336: toRemove.add(a);
1337: } else {
1338: // namespace normalization
1339: String ns = a.getNodeValue();
1340: if (a.getNodeValue().equals(
1341: XMLConstants.XMLNS_NAMESPACE_URI)
1342: || !ns
1343: .equals(XMLConstants.XMLNS_NAMESPACE_URI)) {
1344: // XXX report error
1345: } else {
1346: names.put(prefix, ns);
1347: }
1348: }
1349: }
1350: }
1351:
1352: if (!namespaceDeclarations) {
1353: // remove namespace declarations
1354: Iterator i = toRemove.iterator();
1355: while (i.hasNext()) {
1356: e.removeAttributeNode((Attr) i.next());
1357: }
1358: } else {
1359: if (namespaces) {
1360: // normalize element namespace
1361: String ens = e.getNamespaceURI();
1362: if (ens != null) {
1363: String eprefix = e.getPrefix();
1364: if (!compareStrings(ae.lookupNamespaceURI(eprefix),
1365: ens)) {
1366: e
1367: .setAttributeNS(
1368: XMLConstants.XMLNS_NAMESPACE_URI,
1369: eprefix == null ? XMLConstants.XMLNS_PREFIX
1370: : "xmlns:" + eprefix,
1371: ens);
1372: }
1373: } else {
1374: if (e.getLocalName() == null) {
1375: // report error
1376: } else {
1377: if (ae.lookupNamespaceURI(null) == null) {
1378: e.setAttributeNS(
1379: XMLConstants.XMLNS_NAMESPACE_URI,
1380: XMLConstants.XMLNS_PREFIX, "");
1381: }
1382: }
1383: }
1384: // normalize attribute namespaces
1385: nnm = e.getAttributes();
1386: for (int i = 0; i < nnm.getLength(); i++) {
1387: Attr a = (Attr) nnm.item(i);
1388: String ans = a.getNamespaceURI();
1389: if (ans != null) {
1390: String apre = a.getPrefix();
1391: if (apre != null
1392: && (apre
1393: .equals(XMLConstants.XML_PREFIX) || apre
1394: .equals(XMLConstants.XMLNS_PREFIX))
1395: || ans
1396: .equals(XMLConstants.XMLNS_NAMESPACE_URI)) {
1397: continue;
1398: }
1399: String aprens = apre == null ? null : ae
1400: .lookupNamespaceURI(apre);
1401: if (apre == null || aprens == null
1402: || !aprens.equals(ans)) {
1403: String newpre = ae.lookupPrefix(ans);
1404: if (newpre != null) {
1405: a.setPrefix(newpre);
1406: } else {
1407: if (apre != null
1408: && ae.lookupNamespaceURI(apre) == null) {
1409: e
1410: .setAttributeNS(
1411: XMLConstants.XMLNS_NAMESPACE_URI,
1412: XMLConstants.XMLNS_PREFIX
1413: + ':'
1414: + apre, ans);
1415: } else {
1416: int index = 1;
1417: for (;;) {
1418: newpre = "NS" + index;
1419: if (ae.lookupPrefix(newpre) == null) {
1420: e
1421: .setAttributeNS(
1422: XMLConstants.XMLNS_NAMESPACE_URI,
1423: XMLConstants.XMLNS_PREFIX
1424: + ':'
1425: + newpre,
1426: ans);
1427: a.setPrefix(newpre);
1428: break;
1429: }
1430: }
1431: }
1432: }
1433: }
1434: } else {
1435: if (a.getLocalName() == null) {
1436: // report error
1437: }
1438: }
1439: }
1440: }
1441: }
1442:
1443: // check well-formedness
1444: nnm = e.getAttributes();
1445: for (int i = 0; i < nnm.getLength(); i++) {
1446: Attr a = (Attr) nnm.item(i);
1447: if (!checkName(a.getNodeName())) {
1448: if (errorHandler != null) {
1449: if (!errorHandler
1450: .handleError(createDOMError(
1451: DOMConstants.DOM_INVALID_CHARACTER_IN_NODE_NAME_ERROR,
1452: DOMError.SEVERITY_ERROR,
1453: "wf.invalid.name", new Object[] { a
1454: .getNodeName() }, a, null))) {
1455: return false;
1456: }
1457: }
1458: }
1459: if (!checkChars(a.getNodeValue())) {
1460: if (errorHandler != null) {
1461: if (!errorHandler
1462: .handleError(createDOMError(
1463: DOMConstants.DOM_INVALID_CHARACTER_ERROR,
1464: DOMError.SEVERITY_ERROR,
1465: "wf.invalid.character",
1466: new Object[] {
1467: new Integer(
1468: Node.ATTRIBUTE_NODE),
1469: a.getNodeName(),
1470: a.getNodeValue() }, a, null))) {
1471: return false;
1472: }
1473: }
1474: }
1475: }
1476: for (Node m = e.getFirstChild(); m != null; m = m
1477: .getNextSibling()) {
1478: int nt = m.getNodeType();
1479: String s;
1480: switch (nt) {
1481: case Node.TEXT_NODE:
1482: s = m.getNodeValue();
1483: if (!checkChars(s)) {
1484: if (errorHandler != null) {
1485: if (!errorHandler
1486: .handleError(createDOMError(
1487: DOMConstants.DOM_INVALID_CHARACTER_ERROR,
1488: DOMError.SEVERITY_ERROR,
1489: "wf.invalid.character",
1490: new Object[] {
1491: new Integer(m
1492: .getNodeType()),
1493: m.getNodeName(), s },
1494: m, null))) {
1495: return false;
1496: }
1497: }
1498: }
1499: break;
1500: case Node.COMMENT_NODE:
1501: s = m.getNodeValue();
1502: if (!checkChars(s)
1503: || s.indexOf(XMLConstants.XML_DOUBLE_DASH) != -1
1504: || s.charAt(s.length() - 1) == '-') {
1505: if (errorHandler != null) {
1506: if (!errorHandler
1507: .handleError(createDOMError(
1508: DOMConstants.DOM_INVALID_CHARACTER_ERROR,
1509: DOMError.SEVERITY_ERROR,
1510: "wf.invalid.character",
1511: new Object[] {
1512: new Integer(m
1513: .getNodeType()),
1514: m.getNodeName(), s },
1515: m, null))) {
1516: return false;
1517: }
1518: }
1519: }
1520: break;
1521: case Node.CDATA_SECTION_NODE:
1522: s = m.getNodeValue();
1523: if (!checkChars(s)
1524: || s.indexOf(XMLConstants.XML_CDATA_END) != -1) {
1525: if (errorHandler != null) {
1526: if (!errorHandler
1527: .handleError(createDOMError(
1528: DOMConstants.DOM_INVALID_CHARACTER_ERROR,
1529: DOMError.SEVERITY_ERROR,
1530: "wf.invalid.character",
1531: new Object[] {
1532: new Integer(m
1533: .getNodeType()),
1534: m.getNodeName(), s },
1535: m, null))) {
1536: return false;
1537: }
1538: }
1539: }
1540: break;
1541: case Node.PROCESSING_INSTRUCTION_NODE:
1542: if (m.getNodeName().equalsIgnoreCase(
1543: XMLConstants.XML_PREFIX)) {
1544: if (errorHandler != null) {
1545: if (!errorHandler
1546: .handleError(createDOMError(
1547: DOMConstants.DOM_INVALID_CHARACTER_IN_NODE_NAME_ERROR,
1548: DOMError.SEVERITY_ERROR,
1549: "wf.invalid.name",
1550: new Object[] { m.getNodeName() },
1551: m, null))) {
1552: return false;
1553: }
1554: }
1555: }
1556: s = m.getNodeValue();
1557: if (!checkChars(s)
1558: || s
1559: .indexOf(XMLConstants.XML_PROCESSING_INSTRUCTION_END) != -1) {
1560: if (errorHandler != null) {
1561: if (!errorHandler
1562: .handleError(createDOMError(
1563: DOMConstants.DOM_INVALID_CHARACTER_ERROR,
1564: DOMError.SEVERITY_ERROR,
1565: "wf.invalid.character",
1566: new Object[] {
1567: new Integer(m
1568: .getNodeType()),
1569: m.getNodeName(), s },
1570: m, null))) {
1571: return false;
1572: }
1573: }
1574: }
1575: break;
1576: case Node.ELEMENT_NODE:
1577: if (!checkName(m.getNodeName())) {
1578: if (errorHandler != null) {
1579: if (!errorHandler
1580: .handleError(createDOMError(
1581: DOMConstants.DOM_INVALID_CHARACTER_IN_NODE_NAME_ERROR,
1582: DOMError.SEVERITY_ERROR,
1583: "wf.invalid.name",
1584: new Object[] { m.getNodeName() },
1585: m, null))) {
1586: return false;
1587: }
1588: }
1589: }
1590: if (!normalizeDocument((Element) m, cdataSections,
1591: comments, elementContentWhitepace,
1592: namespaceDeclarations, namespaces,
1593: splitCdataSections, errorHandler)) {
1594: return false;
1595: }
1596: break;
1597: }
1598: }
1599: return true;
1600: }
1601:
1602: /**
1603: * Splits the given CDATA node if required.
1604: */
1605: protected boolean splitCdata(Element e, Node n,
1606: DOMErrorHandler errorHandler) {
1607: String s2 = n.getNodeValue();
1608: int index = s2.indexOf(XMLConstants.XML_CDATA_END);
1609: if (index != -1) {
1610: String before = s2.substring(0, index + 2);
1611: String after = s2.substring(index + 2);
1612: n.setNodeValue(before);
1613: Node next = n.getNextSibling();
1614: if (next == null) {
1615: e.appendChild(createCDATASection(after));
1616: } else {
1617: e.insertBefore(createCDATASection(after), next);
1618: }
1619: if (errorHandler != null) {
1620: if (!errorHandler
1621: .handleError(createDOMError(
1622: DOMConstants.DOM_CDATA_SECTIONS_SPLITTED_ERROR,
1623: DOMError.SEVERITY_WARNING,
1624: "cdata.section.split", new Object[] {},
1625: n, null))) {
1626: return false;
1627: }
1628: }
1629: }
1630: return true;
1631: }
1632:
1633: /**
1634: * Checks that the characters in the given string are all valid
1635: * content characters.
1636: */
1637: protected boolean checkChars(String s) {
1638: int len = s.length();
1639: if (xmlVersion.equals(XMLConstants.XML_VERSION_11)) {
1640: for (int i = 0; i < len; i++) {
1641: if (!DOMUtilities.isXML11Character(s.charAt(i))) {
1642: return false;
1643: }
1644: }
1645: } else {
1646: // assume XML 1.0
1647: for (int i = 0; i < len; i++) {
1648: if (!DOMUtilities.isXMLCharacter(s.charAt(i))) {
1649: return false;
1650: }
1651: }
1652: }
1653: return true;
1654: }
1655:
1656: /**
1657: * Checks that the given string is a valid XML name.
1658: */
1659: protected boolean checkName(String s) {
1660: if (xmlVersion.equals(XMLConstants.XML_VERSION_11)) {
1661: return DOMUtilities.isValidName11(s);
1662: }
1663: // assume XML 1.0
1664: return DOMUtilities.isValidName(s);
1665: }
1666:
1667: /**
1668: * Creates a DOMError object with the given parameters.
1669: */
1670: protected DOMError createDOMError(String type, short severity,
1671: String key, Object[] args, Node related, Exception e) {
1672: try {
1673: return new DocumentError(type, severity,
1674: getCurrentDocument().formatMessage(key, args),
1675: related, e);
1676: } catch (Exception ex) {
1677: return new DocumentError(type, severity, key, related, e);
1678: }
1679: }
1680:
1681: /**
1682: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#setTextContent(String)}.
1683: */
1684: public void setTextContent(String s) throws DOMException {
1685: }
1686:
1687: /**
1688: * Sets the XBLManager used for this document.
1689: */
1690: public void setXBLManager(XBLManager m) {
1691: boolean wasProcessing = xblManager.isProcessing();
1692: xblManager.stopProcessing();
1693: if (m == null) {
1694: m = new GenericXBLManager();
1695: }
1696: xblManager = m;
1697: if (wasProcessing) {
1698: xblManager.startProcessing();
1699: }
1700: }
1701:
1702: /**
1703: * Returns the XBLManager used for this document.
1704: */
1705: public XBLManager getXBLManager() {
1706: return xblManager;
1707: }
1708:
1709: /**
1710: * DOMError implementation.
1711: */
1712: protected class DocumentError implements DOMError {
1713:
1714: /**
1715: * The error type.
1716: */
1717: protected String type;
1718:
1719: /**
1720: * The error severity.
1721: */
1722: protected short severity;
1723:
1724: /**
1725: * The error message.
1726: */
1727: protected String message;
1728:
1729: /**
1730: * The error related data.
1731: */
1732: protected Node relatedNode;
1733:
1734: /**
1735: * The exception which cuased this error.
1736: */
1737: protected Object relatedException;
1738:
1739: /**
1740: * The DOMLocator for this error.
1741: */
1742: protected DOMLocator domLocator;
1743:
1744: /**
1745: * Creates a new DocumentError object.
1746: */
1747: public DocumentError(String type, short severity,
1748: String message, Node relatedNode,
1749: Exception relatedException) {
1750: this .type = type;
1751: this .severity = severity;
1752: this .message = message;
1753: this .relatedNode = relatedNode;
1754: this .relatedException = relatedException;
1755: }
1756:
1757: public String getType() {
1758: return type;
1759: }
1760:
1761: public short getSeverity() {
1762: return severity;
1763: }
1764:
1765: public String getMessage() {
1766: return message;
1767: }
1768:
1769: public Object getRelatedData() {
1770: return relatedNode;
1771: }
1772:
1773: public Object getRelatedException() {
1774: return relatedException;
1775: }
1776:
1777: public DOMLocator getLocation() {
1778: if (domLocator == null) {
1779: domLocator = new ErrorLocation(relatedNode);
1780: }
1781: return domLocator;
1782: }
1783:
1784: /**
1785: * The DOMLocator implementation.
1786: */
1787: protected class ErrorLocation implements DOMLocator {
1788:
1789: /**
1790: * The node that caused the error.
1791: */
1792: protected Node node;
1793:
1794: /**
1795: * Create a new ErrorLocation object.
1796: */
1797: public ErrorLocation(Node n) {
1798: node = n;
1799: }
1800:
1801: /**
1802: * Get the line number of the error node.
1803: */
1804: public int getLineNumber() {
1805: return -1;
1806: }
1807:
1808: /**
1809: * Get the column number of the error node.
1810: */
1811: public int getColumnNumber() {
1812: return -1;
1813: }
1814:
1815: /**
1816: * Get the byte offset of the error node.
1817: */
1818: public int getByteOffset() {
1819: return -1;
1820: }
1821:
1822: /**
1823: * Get the UTF-16 offset of the error node.
1824: */
1825: public int getUtf16Offset() {
1826: return -1;
1827: }
1828:
1829: /**
1830: * Get the node.
1831: */
1832: public Node getRelatedNode() {
1833: return node;
1834: }
1835:
1836: /**
1837: * Get the document URI.
1838: */
1839: public String getUri() {
1840: AbstractDocument doc = (AbstractDocument) node
1841: .getOwnerDocument();
1842: return doc.getDocumentURI();
1843: }
1844: }
1845: }
1846:
1847: /**
1848: * DOMConfiguration for this document.
1849: */
1850: protected class DocumentConfiguration implements DOMConfiguration {
1851:
1852: /**
1853: * The boolean parameter names.
1854: */
1855: protected String[] booleanParamNames = {
1856: DOMConstants.DOM_CANONICAL_FORM_PARAM,
1857: DOMConstants.DOM_CDATA_SECTIONS_PARAM,
1858: DOMConstants.DOM_CHECK_CHARACTER_NORMALIZATION_PARAM,
1859: DOMConstants.DOM_COMMENTS_PARAM,
1860: DOMConstants.DOM_DATATYPE_NORMALIZATION_PARAM,
1861: DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE_PARAM,
1862: DOMConstants.DOM_ENTITIES_PARAM,
1863: DOMConstants.DOM_INFOSET_PARAM,
1864: DOMConstants.DOM_NAMESPACES_PARAM,
1865: DOMConstants.DOM_NAMESPACE_DECLARATIONS_PARAM,
1866: DOMConstants.DOM_NORMALIZE_CHARACTERS_PARAM,
1867: DOMConstants.DOM_SPLIT_CDATA_SECTIONS_PARAM,
1868: DOMConstants.DOM_VALIDATE_PARAM,
1869: DOMConstants.DOM_VALIDATE_IF_SCHEMA_PARAM,
1870: DOMConstants.DOM_WELL_FORMED_PARAM };
1871:
1872: /**
1873: * The boolean parameter values.
1874: */
1875: protected boolean[] booleanParamValues = { false, // canonical-form
1876: true, // cdata-sections
1877: false, // check-character-normalization
1878: true, // comments
1879: false, // datatype-normalization
1880: false, // element-content-whitespace
1881: true, // entities
1882: false, // infoset
1883: true, // namespaces
1884: true, // namespace-declarations
1885: false, // normalize-characters
1886: true, // split-cdata-sections
1887: false, // validate
1888: false, // validate-if-schema
1889: true // well-formed
1890: };
1891:
1892: /**
1893: * The read-onlyness of the boolean parameters.
1894: */
1895: protected boolean[] booleanParamReadOnly = { true, // canonical-form
1896: false, // cdata-sections
1897: true, // check-character-normalization
1898: false, // comments
1899: true, // datatype-normalization
1900: false, // element-content-whitespace
1901: false, // entities
1902: false, // infoset
1903: false, // namespaces
1904: false, // namespace-declarations
1905: true, // normalize-characters
1906: false, // split-cdata-sections
1907: true, // validate
1908: true, // validate-if-schema
1909: false // well-formed
1910: };
1911:
1912: /**
1913: * Map of parameter names to array indexes.
1914: */
1915: protected Map booleanParamIndexes = new HashMap();
1916: {
1917: for (int i = 0; i < booleanParamNames.length; i++) {
1918: booleanParamIndexes.put(booleanParamNames[i],
1919: new Integer(i));
1920: }
1921: }
1922:
1923: /**
1924: * Value of the 'error-handler' parameter.
1925: */
1926: protected Object errorHandler;
1927:
1928: /**
1929: * The DOMStringList object containing the parameter names.
1930: */
1931: protected ParameterNameList paramNameList;
1932:
1933: /**
1934: * Sets the given parameter.
1935: */
1936: public void setParameter(String name, Object value) {
1937: if (DOMConstants.DOM_ERROR_HANDLER_PARAM.equals(name)) {
1938: if (value != null
1939: && !(value instanceof DOMErrorHandler)) {
1940: throw createDOMException(
1941: (short) 17 /*DOMException.TYPE_MISMATCH_ERR*/,
1942: "domconfig.param.type",
1943: new Object[] { name });
1944: }
1945: errorHandler = value;
1946: return;
1947: }
1948: Integer i = (Integer) booleanParamIndexes.get(name);
1949: if (i == null) {
1950: throw createDOMException(DOMException.NOT_FOUND_ERR,
1951: "domconfig.param.not.found",
1952: new Object[] { name });
1953: }
1954: if (value == null) {
1955: throw createDOMException(
1956: DOMException.NOT_SUPPORTED_ERR,
1957: "domconfig.param.value", new Object[] { name });
1958: }
1959: if (!(value instanceof Boolean)) {
1960: throw createDOMException(
1961: (short) 17 /*DOMException.TYPE_MISMATCH_ERR*/,
1962: "domconfig.param.type", new Object[] { name });
1963: }
1964: int index = i.intValue();
1965: boolean val = ((Boolean) value).booleanValue();
1966: if (booleanParamReadOnly[index]
1967: && booleanParamValues[index] != val) {
1968: throw createDOMException(
1969: DOMException.NOT_SUPPORTED_ERR,
1970: "domconfig.param.value", new Object[] { name });
1971: }
1972: booleanParamValues[index] = val;
1973: if (name.equals(DOMConstants.DOM_INFOSET_PARAM)) {
1974: setParameter(DOMConstants.DOM_VALIDATE_IF_SCHEMA_PARAM,
1975: Boolean.FALSE);
1976: setParameter(DOMConstants.DOM_ENTITIES_PARAM,
1977: Boolean.FALSE);
1978: setParameter(
1979: DOMConstants.DOM_DATATYPE_NORMALIZATION_PARAM,
1980: Boolean.FALSE);
1981: setParameter(DOMConstants.DOM_CDATA_SECTIONS_PARAM,
1982: Boolean.FALSE);
1983: setParameter(DOMConstants.DOM_WELL_FORMED_PARAM,
1984: Boolean.TRUE);
1985: setParameter(
1986: DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE_PARAM,
1987: Boolean.TRUE);
1988: setParameter(DOMConstants.DOM_COMMENTS_PARAM,
1989: Boolean.TRUE);
1990: setParameter(DOMConstants.DOM_NAMESPACES_PARAM,
1991: Boolean.TRUE);
1992: }
1993: }
1994:
1995: /**
1996: * Gets the value of the given parameter.
1997: */
1998: public Object getParameter(String name) {
1999: if (DOMConstants.DOM_ERROR_HANDLER_PARAM.equals(name)) {
2000: return errorHandler;
2001: }
2002: Integer index = (Integer) booleanParamIndexes.get(name);
2003: if (index == null) {
2004: throw createDOMException(DOMException.NOT_FOUND_ERR,
2005: "domconfig.param.not.found",
2006: new Object[] { name });
2007: }
2008: return booleanParamValues[index.intValue()] ? Boolean.TRUE
2009: : Boolean.FALSE;
2010: }
2011:
2012: /**
2013: * Gets the boolean value of the given parameter.
2014: */
2015: public boolean getBooleanParameter(String name) {
2016: Boolean b = (Boolean) getParameter(name);
2017: return b.booleanValue();
2018: }
2019:
2020: /**
2021: * Returns whether the given parameter can be set to the given value.
2022: */
2023: public boolean canSetParameter(String name, Object value) {
2024: if (name.equals(DOMConstants.DOM_ERROR_HANDLER_PARAM)) {
2025: return value == null
2026: || value instanceof DOMErrorHandler;
2027: }
2028: Integer i = (Integer) booleanParamIndexes.get(name);
2029: if (i == null || value == null
2030: || !(value instanceof Boolean)) {
2031: return false;
2032: }
2033: int index = i.intValue();
2034: boolean val = ((Boolean) value).booleanValue();
2035: return !booleanParamReadOnly[index]
2036: || booleanParamValues[index] == val;
2037: }
2038:
2039: /**
2040: * Returns a DOMStringList of parameter names.
2041: */
2042: public DOMStringList getParameterNames() {
2043: if (paramNameList == null) {
2044: paramNameList = new ParameterNameList();
2045: }
2046: return paramNameList;
2047: }
2048:
2049: /**
2050: * Class to expose the parameter names.
2051: */
2052: protected class ParameterNameList implements DOMStringList {
2053:
2054: /**
2055: * Returns the parameter name at the given index.
2056: */
2057: public String item(int index) {
2058: if (index < 0) {
2059: return null;
2060: }
2061: if (index < booleanParamNames.length) {
2062: return booleanParamNames[index];
2063: }
2064: if (index == booleanParamNames.length) {
2065: return DOMConstants.DOM_ERROR_HANDLER_PARAM;
2066: }
2067: return null;
2068: }
2069:
2070: /**
2071: * Returns the number of parameter names in the list.
2072: */
2073: public int getLength() {
2074: return booleanParamNames.length + 1;
2075: }
2076:
2077: /**
2078: * Returns whether the given parameter name is in the list.
2079: */
2080: public boolean contains(String s) {
2081: if (DOMConstants.DOM_ERROR_HANDLER_PARAM.equals(s)) {
2082: return true;
2083: }
2084: for (int i = 0; i < booleanParamNames.length; i++) {
2085: if (booleanParamNames[i].equals(s)) {
2086: return true;
2087: }
2088: }
2089: return false;
2090: }
2091: }
2092: }
2093:
2094: /**
2095: * <b>DOM</b>: Implements
2096: * {@link org.w3c.dom.xpath.XPathEvaluator#createExpression(String,XPathNSResolver)}.
2097: */
2098: public XPathExpression createExpression(String expression,
2099: XPathNSResolver resolver) throws DOMException,
2100: XPathException {
2101: return new XPathExpr(expression, resolver);
2102: }
2103:
2104: /**
2105: * <b>DOM</b>: Implements
2106: * {@link org.w3c.dom.xpath.XPathEvaluator#createNSResolver(Node)}.
2107: */
2108: public XPathNSResolver createNSResolver(Node n) {
2109: return new XPathNodeNSResolver(n);
2110: }
2111:
2112: /**
2113: * <b>DOM</b>: Implements
2114: * {@link org.w3c.dom.xpath.XPathEvaluator#evaluate(String,Node,XPathNSResolver,short,Object)}.
2115: */
2116: public Object evaluate(String expression, Node contextNode,
2117: XPathNSResolver resolver, short type, Object result)
2118: throws XPathException, DOMException {
2119: XPathExpression xpath = createExpression(expression, resolver);
2120: return xpath.evaluate(contextNode, type, result);
2121: }
2122:
2123: /**
2124: * Creates an exception with the appropriate error message.
2125: */
2126: public XPathException createXPathException(short type, String key,
2127: Object[] args) {
2128: try {
2129: return new XPathException(type, formatMessage(key, args));
2130: } catch (Exception e) {
2131: return new XPathException(type, key);
2132: }
2133: }
2134:
2135: /**
2136: * A compiled XPath expression.
2137: */
2138: protected class XPathExpr implements XPathExpression {
2139:
2140: /**
2141: * The compiled XPath expression.
2142: */
2143: protected XPath xpath;
2144:
2145: /**
2146: * The namespace resolver.
2147: */
2148: protected XPathNSResolver resolver;
2149:
2150: /**
2151: * The Xalan prefix resolver.
2152: */
2153: protected NSPrefixResolver prefixResolver;
2154:
2155: /**
2156: * The XPathContext object.
2157: */
2158: protected XPathContext context;
2159:
2160: /**
2161: * Creates a new XPathExpr object.
2162: */
2163: public XPathExpr(String expr, XPathNSResolver res)
2164: throws DOMException, XPathException {
2165: resolver = res;
2166: prefixResolver = new NSPrefixResolver();
2167: try {
2168: xpath = new XPath(expr, null, prefixResolver,
2169: XPath.SELECT);
2170: context = new XPathContext();
2171: } catch (javax.xml.transform.TransformerException te) {
2172: throw createXPathException(
2173: XPathException.INVALID_EXPRESSION_ERR,
2174: "xpath.invalid.expression", new Object[] {
2175: expr, te.getMessage() });
2176: }
2177: }
2178:
2179: /**
2180: * <b>DOM</b>: Implements
2181: * {@link org.w3c.dom.xpath.XPathExpression#evaluate(Node,short,Object)}.
2182: */
2183: public Object evaluate(Node contextNode, short type, Object res)
2184: throws XPathException, DOMException {
2185: if (contextNode.getNodeType() != DOCUMENT_NODE
2186: && contextNode.getOwnerDocument() != AbstractDocument.this
2187: || contextNode.getNodeType() == DOCUMENT_NODE
2188: && contextNode != AbstractDocument.this ) {
2189: throw createDOMException(
2190: DOMException.WRONG_DOCUMENT_ERR,
2191: "node.from.wrong.document", new Object[] {
2192: new Integer(contextNode.getNodeType()),
2193: contextNode.getNodeName() });
2194: }
2195: if (type < 0 || type > 9) {
2196: throw createDOMException(
2197: DOMException.NOT_SUPPORTED_ERR,
2198: "xpath.invalid.result.type",
2199: new Object[] { new Integer(type) });
2200: }
2201: switch (contextNode.getNodeType()) {
2202: case ENTITY_REFERENCE_NODE:
2203: case ENTITY_NODE:
2204: case DOCUMENT_TYPE_NODE:
2205: case DOCUMENT_FRAGMENT_NODE:
2206: case NOTATION_NODE:
2207: throw createDOMException(
2208: DOMException.NOT_SUPPORTED_ERR,
2209: "xpath.invalid.context.node", new Object[] {
2210: new Integer(contextNode.getNodeType()),
2211: contextNode.getNodeName() });
2212: }
2213: context.reset();
2214: XObject result = null;
2215: try {
2216: result = xpath.execute(context, contextNode,
2217: prefixResolver);
2218: } catch (javax.xml.transform.TransformerException te) {
2219: throw createXPathException(
2220: XPathException.INVALID_EXPRESSION_ERR,
2221: "xpath.error", new Object[] {
2222: xpath.getPatternString(),
2223: te.getMessage() });
2224: }
2225: try {
2226: switch (type) {
2227: case XPathResult.ANY_UNORDERED_NODE_TYPE:
2228: case XPathResult.FIRST_ORDERED_NODE_TYPE:
2229: return convertSingleNode(result, type);
2230: case XPathResult.BOOLEAN_TYPE:
2231: return convertBoolean(result);
2232: case XPathResult.NUMBER_TYPE:
2233: return convertNumber(result);
2234: case XPathResult.ORDERED_NODE_ITERATOR_TYPE:
2235: case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
2236: case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE:
2237: case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE:
2238: return convertNodeIterator(result, type);
2239: case XPathResult.STRING_TYPE:
2240: return convertString(result);
2241: case XPathResult.ANY_TYPE:
2242: switch (result.getType()) {
2243: case XObject.CLASS_BOOLEAN:
2244: return convertBoolean(result);
2245: case XObject.CLASS_NUMBER:
2246: return convertNumber(result);
2247: case XObject.CLASS_STRING:
2248: return convertString(result);
2249: case XObject.CLASS_NODESET:
2250: return convertNodeIterator(
2251: result,
2252: XPathResult.UNORDERED_NODE_ITERATOR_TYPE);
2253: }
2254: }
2255: } catch (javax.xml.transform.TransformerException te) {
2256: throw createXPathException(XPathException.TYPE_ERR,
2257: "xpath.cannot.convert.result", new Object[] {
2258: new Integer(type), te.getMessage() });
2259: }
2260: return null;
2261: }
2262:
2263: /**
2264: * Converts an XObject to a single node XPathResult.
2265: */
2266: protected Result convertSingleNode(XObject xo, short type)
2267: throws javax.xml.transform.TransformerException {
2268: return new Result(xo.nodelist().item(0), type);
2269: }
2270:
2271: /**
2272: * Converts an XObject to a boolean XPathResult.
2273: */
2274: protected Result convertBoolean(XObject xo)
2275: throws javax.xml.transform.TransformerException {
2276: return new Result(xo.bool());
2277: }
2278:
2279: /**
2280: * Converts an XObject to a number XPathResult.
2281: */
2282: protected Result convertNumber(XObject xo)
2283: throws javax.xml.transform.TransformerException {
2284: return new Result(xo.num());
2285: }
2286:
2287: /**
2288: * Converts an XObject to a string XPathResult.
2289: */
2290: protected Result convertString(XObject xo) {
2291: return new Result(xo.str());
2292: }
2293:
2294: /**
2295: * Converts an XObject to a node iterator XPathResult.
2296: */
2297: protected Result convertNodeIterator(XObject xo, short type)
2298: throws javax.xml.transform.TransformerException {
2299: return new Result(xo.nodelist(), type);
2300: }
2301:
2302: /**
2303: * XPathResult implementation.
2304: * XXX Namespace nodes are not handled correctly, since Xalan returns
2305: * namespace nodes as simply the attribute node that caused the
2306: * namespace to be in scope on the element in question. Thus it
2307: * is impossible to tell the difference between a selected
2308: * attribute that begins with 'xmlns' and an XPath namespace node.
2309: */
2310: public class Result implements XPathResult {
2311:
2312: /**
2313: * The result type.
2314: */
2315: protected short resultType;
2316:
2317: /**
2318: * The number value.
2319: */
2320: protected double numberValue;
2321:
2322: /**
2323: * The string value.
2324: */
2325: protected String stringValue;
2326:
2327: /**
2328: * The boolean value.
2329: */
2330: protected boolean booleanValue;
2331:
2332: /**
2333: * The single node value.
2334: */
2335: protected Node singleNodeValue;
2336:
2337: /**
2338: * The NodeList for iterators.
2339: */
2340: protected NodeList iterator;
2341:
2342: /**
2343: * The position of the iterator.
2344: */
2345: protected int iteratorPosition;
2346:
2347: /**
2348: * Creates a new single node Result object.
2349: */
2350: public Result(Node n, short type) {
2351: resultType = type;
2352: singleNodeValue = n;
2353: }
2354:
2355: /**
2356: * Creates a new boolean Result object.
2357: */
2358: public Result(boolean b)
2359: throws javax.xml.transform.TransformerException {
2360: resultType = BOOLEAN_TYPE;
2361: booleanValue = b;
2362: }
2363:
2364: /**
2365: * Creates a new number Result object.
2366: */
2367: public Result(double d)
2368: throws javax.xml.transform.TransformerException {
2369: resultType = NUMBER_TYPE;
2370: numberValue = d;
2371: }
2372:
2373: /**
2374: * Creates a new string Result object.
2375: */
2376: public Result(String s) {
2377: resultType = STRING_TYPE;
2378: stringValue = s;
2379: }
2380:
2381: /**
2382: * Creates a new node iterator Result object.
2383: */
2384: public Result(NodeList nl, short type) {
2385: resultType = type;
2386: iterator = nl;
2387: }
2388:
2389: /**
2390: * Gets the result type.
2391: */
2392: public short getResultType() {
2393: return resultType;
2394: }
2395:
2396: /**
2397: * Gets the boolean value.
2398: */
2399: public boolean getBooleanValue() {
2400: if (resultType != BOOLEAN_TYPE) {
2401: throw createXPathException(XPathException.TYPE_ERR,
2402: "xpath.invalid.result.type",
2403: new Object[] { new Integer(resultType) });
2404: }
2405: return booleanValue;
2406: }
2407:
2408: /**
2409: * Gets the number value.
2410: */
2411: public double getNumberValue() {
2412: if (resultType != NUMBER_TYPE) {
2413: throw createXPathException(XPathException.TYPE_ERR,
2414: "xpath.invalid.result.type",
2415: new Object[] { new Integer(resultType) });
2416: }
2417: return numberValue;
2418: }
2419:
2420: /**
2421: * Gets the string value.
2422: */
2423: public String getStringValue() {
2424: if (resultType != STRING_TYPE) {
2425: throw createXPathException(XPathException.TYPE_ERR,
2426: "xpath.invalid.result.type",
2427: new Object[] { new Integer(resultType) });
2428: }
2429: return stringValue;
2430: }
2431:
2432: /**
2433: * Gets the single node value.
2434: */
2435: public Node getSingleNodeValue() {
2436: if (resultType != ANY_UNORDERED_NODE_TYPE
2437: && resultType != FIRST_ORDERED_NODE_TYPE) {
2438: throw createXPathException(XPathException.TYPE_ERR,
2439: "xpath.invalid.result.type",
2440: new Object[] { new Integer(resultType) });
2441: }
2442: return singleNodeValue;
2443: }
2444:
2445: /**
2446: * Returns whether the iterator has been invalidated by
2447: * document modifications.
2448: */
2449: public boolean getInvalidIteratorState() {
2450: return false;
2451: }
2452:
2453: /**
2454: * Returns the length of the snapshot.
2455: */
2456: public int getSnapshotLength() {
2457: if (resultType != UNORDERED_NODE_SNAPSHOT_TYPE
2458: && resultType != ORDERED_NODE_SNAPSHOT_TYPE) {
2459: throw createXPathException(XPathException.TYPE_ERR,
2460: "xpath.invalid.result.type",
2461: new Object[] { new Integer(resultType) });
2462: }
2463: return iterator.getLength();
2464: }
2465:
2466: /**
2467: * <b>DOM</b>: Implement
2468: * {@link org.w3c.dom.xpath.XPathResult#iterateNext()}.
2469: */
2470: public Node iterateNext() {
2471: if (resultType != UNORDERED_NODE_ITERATOR_TYPE
2472: && resultType != ORDERED_NODE_ITERATOR_TYPE) {
2473: throw createXPathException(XPathException.TYPE_ERR,
2474: "xpath.invalid.result.type",
2475: new Object[] { new Integer(resultType) });
2476: }
2477: return iterator.item(iteratorPosition++);
2478: }
2479:
2480: /**
2481: * Returns the <code>i</code>th item in the snapshot.
2482: */
2483: public Node snapshotItem(int i) {
2484: if (resultType != UNORDERED_NODE_SNAPSHOT_TYPE
2485: && resultType != ORDERED_NODE_SNAPSHOT_TYPE) {
2486: throw createXPathException(XPathException.TYPE_ERR,
2487: "xpath.invalid.result.type",
2488: new Object[] { new Integer(resultType) });
2489: }
2490: return iterator.item(i);
2491: }
2492: }
2493:
2494: /**
2495: * Xalan prefix resolver.
2496: */
2497: protected class NSPrefixResolver implements PrefixResolver {
2498:
2499: /**
2500: * Get the base URI for this resolver. Since this resolver isn't
2501: * associated with a particular node, returns null.
2502: */
2503: public String getBaseIdentifier() {
2504: return null;
2505: }
2506:
2507: /**
2508: * Resolves the given namespace prefix.
2509: */
2510: public String getNamespaceForPrefix(String prefix) {
2511: if (resolver == null) {
2512: return null;
2513: }
2514: return resolver.lookupNamespaceURI(prefix);
2515: }
2516:
2517: /**
2518: * Resolves the given namespace prefix.
2519: */
2520: public String getNamespaceForPrefix(String prefix,
2521: Node context) {
2522: // ignore the context node
2523: if (resolver == null) {
2524: return null;
2525: }
2526: return resolver.lookupNamespaceURI(prefix);
2527: }
2528:
2529: /**
2530: * Returns whether this PrefixResolver handles a null prefix.
2531: */
2532: public boolean handlesNullPrefixes() {
2533: return false;
2534: }
2535: }
2536: }
2537:
2538: /**
2539: * An XPathNSResolver that uses Node.lookupNamespaceURI.
2540: */
2541: protected class XPathNodeNSResolver implements XPathNSResolver {
2542:
2543: /**
2544: * The context node for namespace prefix resolution.
2545: */
2546: protected Node contextNode;
2547:
2548: /**
2549: * Creates a new XPathNodeNSResolver object.
2550: */
2551: public XPathNodeNSResolver(Node n) {
2552: contextNode = n;
2553: }
2554:
2555: /**
2556: * <b>DOM</b>: Implements
2557: * {@link org.w3c.dom.xpath.XPathNSResolver#lookupNamespaceURI(String)}.
2558: */
2559: public String lookupNamespaceURI(String prefix) {
2560: return ((AbstractNode) contextNode)
2561: .lookupNamespaceURI(prefix);
2562: }
2563: }
2564:
2565: // NodeXBL //////////////////////////////////////////////////////////////
2566:
2567: /**
2568: * Get the parent of this node in the fully flattened tree.
2569: */
2570: public Node getXblParentNode() {
2571: return xblManager.getXblParentNode(this );
2572: }
2573:
2574: /**
2575: * Get the list of child nodes of this node in the fully flattened tree.
2576: */
2577: public NodeList getXblChildNodes() {
2578: return xblManager.getXblChildNodes(this );
2579: }
2580:
2581: /**
2582: * Get the list of child nodes of this node in the fully flattened tree
2583: * that are within the same shadow scope.
2584: */
2585: public NodeList getXblScopedChildNodes() {
2586: return xblManager.getXblScopedChildNodes(this );
2587: }
2588:
2589: /**
2590: * Get the first child node of this node in the fully flattened tree.
2591: */
2592: public Node getXblFirstChild() {
2593: return xblManager.getXblFirstChild(this );
2594: }
2595:
2596: /**
2597: * Get the last child node of this node in the fully flattened tree.
2598: */
2599: public Node getXblLastChild() {
2600: return xblManager.getXblLastChild(this );
2601: }
2602:
2603: /**
2604: * Get the node which directly precedes the current node in the
2605: * xblParentNode's xblChildNodes list.
2606: */
2607: public Node getXblPreviousSibling() {
2608: return xblManager.getXblPreviousSibling(this );
2609: }
2610:
2611: /**
2612: * Get the node which directly follows the current node in the
2613: * xblParentNode's xblChildNodes list.
2614: */
2615: public Node getXblNextSibling() {
2616: return xblManager.getXblNextSibling(this );
2617: }
2618:
2619: /**
2620: * Get the first element child of this node in the fully flattened tree.
2621: */
2622: public Element getXblFirstElementChild() {
2623: return xblManager.getXblFirstElementChild(this );
2624: }
2625:
2626: /**
2627: * Get the last element child of this node in the fully flattened tree.
2628: */
2629: public Element getXblLastElementChild() {
2630: return xblManager.getXblLastElementChild(this );
2631: }
2632:
2633: /**
2634: * Get the first element that precedes the current node in the
2635: * xblParentNode's xblChildNodes list.
2636: */
2637: public Element getXblPreviousElementSibling() {
2638: return xblManager.getXblPreviousElementSibling(this );
2639: }
2640:
2641: /**
2642: * Get the first element that follows the current node in the
2643: * xblParentNode's xblChildNodes list.
2644: */
2645: public Element getXblNextElementSibling() {
2646: return xblManager.getXblNextElementSibling(this );
2647: }
2648:
2649: /**
2650: * Get the bound element whose shadow tree this current node resides in.
2651: */
2652: public Element getXblBoundElement() {
2653: return xblManager.getXblBoundElement(this );
2654: }
2655:
2656: /**
2657: * Get the shadow tree of this node.
2658: */
2659: public Element getXblShadowTree() {
2660: return xblManager.getXblShadowTree(this );
2661: }
2662:
2663: /**
2664: * Get the xbl:definition elements currently binding this element.
2665: */
2666: public NodeList getXblDefinitions() {
2667: return xblManager.getXblDefinitions(this );
2668: }
2669:
2670: // Serializable /////////////////////////////////////////////////
2671:
2672: private void writeObject(ObjectOutputStream s) throws IOException {
2673: s.defaultWriteObject();
2674:
2675: s.writeObject(implementation.getClass().getName());
2676: }
2677:
2678: private void readObject(ObjectInputStream s) throws IOException,
2679: ClassNotFoundException {
2680: s.defaultReadObject();
2681:
2682: localizableSupport = new LocalizableSupport(RESOURCES,
2683: getClass().getClassLoader());
2684:
2685: Class c = Class.forName((String) s.readObject());
2686:
2687: try {
2688: Method m = c.getMethod("getDOMImplementation",
2689: (Class[]) null);
2690: implementation = (DOMImplementation) m.invoke(null,
2691: (Object[]) null);
2692: } catch (Exception e) {
2693: try {
2694: implementation = (DOMImplementation) c.newInstance();
2695: } catch (Exception ex) {
2696: }
2697: }
2698: }
2699: }
|