/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Ethan Hugg * Terry Lucas * Milen Nankov * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package org.mozilla.javascript.xml.impl.xmlbeans; import java.io.Serializable; import java.util.*; import org.mozilla.javascript.*; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlCursor.XmlBookmark; import org.apache.xmlbeans.XmlCursor.TokenType; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; class XML extends XMLObjectImpl { static final long serialVersionUID = -630969919086449092L; final static class XScriptAnnotation extends XmlBookmark implements Serializable { private static final long serialVersionUID = 1L; javax.xml.namespace.QName _name; XML _xScriptXML; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Constructurs // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// XScriptAnnotation (XmlCursor curs) { _name = curs.getName(); } } /** * */ final static class NamespaceDeclarations { private int _prefixIdx; private StringBuffer _namespaceDecls; private String _defaultNSURI; NamespaceDeclarations (XmlCursor curs) { _prefixIdx = 0; _namespaceDecls = new StringBuffer(); skipNonElements(curs); _defaultNSURI = curs.namespaceForPrefix(""); if (isAnyDefaultNamespace()) { addDecl("", _defaultNSURI); } } private void addDecl (String prefix, String ns) { _namespaceDecls.append((prefix.length() > 0 ? "declare namespace " + prefix : "default element namespace") + " = \"" + ns + "\"" + "\n"); } String getNextPrefix (String ns) { String prefix = "NS" + _prefixIdx++; _namespaceDecls.append("declare namespace " + prefix + " = " + "\"" + ns + "\"" + "\n"); return prefix; } boolean isAnyDefaultNamespace () { return _defaultNSURI != null ?_defaultNSURI.length() > 0 : false; } String getDeclarations() { return _namespaceDecls.toString(); } } // Fields //static final XML prototype = new XML(); private XScriptAnnotation _anno; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Constructors // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * * @param anno */ private XML(XMLLibImpl lib, XScriptAnnotation anno) { super(lib, lib.xmlPrototype); _anno = anno; _anno._xScriptXML = this; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Public factories for creating a XScript XML object given an XBean cursor. // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static XML createEmptyXML(XMLLibImpl lib) { XScriptAnnotation anno; XmlObject xo = XmlObject.Factory.newInstance(); XmlCursor curs = xo.newCursor(); try { anno = new XScriptAnnotation(curs); curs.setBookmark(anno); } finally { curs.dispose(); } return new XML(lib, anno); } private static XML createXML (XMLLibImpl lib, XmlCursor curs) { if (curs.currentTokenType().isStartdoc()) { curs.toFirstContentToken(); } XScriptAnnotation anno = findAnnotation(curs); return new XML(lib, anno); } /** * Special constructor for making an attribute * */ private static XML createAttributeXML(XMLLibImpl lib, XmlCursor cursor) { if (!cursor.isAttr()) throw new IllegalArgumentException(); XScriptAnnotation anno = new XScriptAnnotation(cursor); cursor.setBookmark(anno); return new XML(lib, anno); } /** * * @param qname * @param value * @return */ static XML createTextElement(XMLLibImpl lib, javax.xml.namespace.QName qname, String value) { XScriptAnnotation anno; XmlObject xo = XmlObject.Factory.newInstance(); XmlCursor cursor = xo.newCursor(); try { cursor.toNextToken(); cursor.beginElement(qname.getLocalPart(), qname.getNamespaceURI()); //if(namespace.length() > 0) // cursor.insertNamespace("", namespace); cursor.insertChars(value); cursor.toStartDoc(); cursor.toNextToken(); anno = new XScriptAnnotation(cursor); cursor.setBookmark(anno); } finally { cursor.dispose(); } return new XML(lib, anno); } static XML createFromXmlObject(XMLLibImpl lib, XmlObject xo) { XScriptAnnotation anno; XmlCursor curs = xo.newCursor(); if (curs.currentTokenType().isStartdoc()) { curs.toFirstContentToken(); } try { anno = new XScriptAnnotation(curs); curs.setBookmark(anno); } finally { curs.dispose(); } return new XML(lib, anno); } static XML createFromJS(XMLLibImpl lib, Object inputObject) { XmlObject xo; boolean isText = false; String frag; if (inputObject == null || inputObject == Undefined.instance) { frag = ""; } else if (inputObject instanceof XMLObjectImpl) { // todo: faster way for XMLObjects? frag = ((XMLObjectImpl) inputObject).toXMLString(0); } else { if (inputObject instanceof Wrapper) { Object wrapped = ((Wrapper)inputObject).unwrap(); if (wrapped instanceof XmlObject) { return createFromXmlObject(lib, (XmlObject)wrapped); } } frag = ScriptRuntime.toString(inputObject); } if (frag.trim().startsWith("<>")) { throw ScriptRuntime.typeError("Invalid use of XML object anonymous tags <>."); } if (frag.indexOf("<") == -1) { // Must be solo text node, wrap in XML fragment isText = true; frag = "" + frag + ""; } XmlOptions options = new XmlOptions(); if (lib.ignoreComments) { options.put(XmlOptions.LOAD_STRIP_COMMENTS); } if (lib.ignoreProcessingInstructions) { options.put(XmlOptions.LOAD_STRIP_PROCINSTS); } if (lib.ignoreWhitespace) { options.put(XmlOptions.LOAD_STRIP_WHITESPACE); } try { xo = XmlObject.Factory.parse(frag, options); // Apply the default namespace Context cx = Context.getCurrentContext(); String defaultURI = lib.getDefaultNamespaceURI(cx); if(defaultURI.length() > 0) { XmlCursor cursor = xo.newCursor(); boolean isRoot = true; while(!cursor.toNextToken().isEnddoc()) { if(!cursor.isStart()) continue; // Check if this element explicitly sets the // default namespace boolean defaultNSDeclared = false; cursor.push(); while(cursor.toNextToken().isAnyAttr()) { if(cursor.isNamespace()) { if(cursor.getName().getLocalPart().length() == 0) { defaultNSDeclared = true; break; } } } cursor.pop(); if(defaultNSDeclared) { cursor.toEndToken(); continue; } // Check if this element's name is in no namespace javax.xml.namespace.QName qname = cursor.getName(); if(qname.getNamespaceURI().length() == 0) { // Change the namespace qname = new javax.xml.namespace.QName(defaultURI, qname.getLocalPart()); cursor.setName(qname); } if(isRoot) { // Declare the default namespace cursor.push(); cursor.toNextToken(); cursor.insertNamespace("", defaultURI); cursor.pop(); isRoot = false; } } cursor.dispose(); } } catch (XmlException xe) { /* todo need to handle namespace prefix not found in XML look for namespace type in the scope change. String errorMsg = "Use of undefined namespace prefix: "; String msg = xe.getError().getMessage(); if (msg.startsWith(errorMsg)) { String prefix = msg.substring(errorMsg.length()); } */ String errMsg = xe.getMessage(); if (errMsg.equals("error: Unexpected end of file after null")) { // Create an empty document. xo = XmlObject.Factory.newInstance(); } else { throw ScriptRuntime.typeError(xe.getMessage()); } } catch (Throwable e) { // todo: TLL Catch specific exceptions during parse. throw ScriptRuntime.typeError("Not Parsable as XML"); } XmlCursor curs = xo.newCursor(); if (curs.currentTokenType().isStartdoc()) { curs.toFirstContentToken(); } if (isText) { // Move it to point to the text node curs.toFirstContentToken(); } XScriptAnnotation anno; try { anno = new XScriptAnnotation(curs); curs.setBookmark(anno); } finally { curs.dispose(); } return new XML(lib, anno); } static XML getFromAnnotation(XMLLibImpl lib, XScriptAnnotation anno) { if (anno._xScriptXML == null) { anno._xScriptXML = new XML(lib, anno); } return anno._xScriptXML; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Private functions: // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * * @param curs * @return */ private static TokenType skipNonElements (XmlCursor curs) { TokenType tt = curs.currentTokenType(); while (tt.isComment() || tt.isProcinst()) { tt = curs.toNextToken(); } return tt; } /** * * @param curs * @return */ protected static XScriptAnnotation findAnnotation(XmlCursor curs) { XmlBookmark anno = curs.getBookmark(XScriptAnnotation.class); if (anno == null) { anno = new XScriptAnnotation(curs); curs.setBookmark(anno); } return (XScriptAnnotation)anno; } /** * * @return */ private XmlOptions getOptions() { XmlOptions options = new XmlOptions(); if (lib.ignoreComments) { options.put(XmlOptions.LOAD_STRIP_COMMENTS); } if (lib.ignoreProcessingInstructions) { options.put(XmlOptions.LOAD_STRIP_PROCINSTS); } if (lib.ignoreWhitespace) { options.put(XmlOptions.LOAD_STRIP_WHITESPACE); } if (lib.prettyPrinting) { options.put(XmlOptions.SAVE_PRETTY_PRINT, null); options.put(XmlOptions.SAVE_PRETTY_PRINT_INDENT, new Integer(lib.prettyIndent)); } return options; } /** * * @param cursor * @param opts * @return */ private static String dumpNode(XmlCursor cursor, XmlOptions opts) { if (cursor.isText()) return cursor.getChars(); if (cursor.isFinish()) return ""; cursor.push(); boolean wanRawText = cursor.isStartdoc() && !cursor.toFirstChild(); cursor.pop(); return wanRawText ? cursor.getTextValue() : cursor.xmlText( opts ); } /** * * @return */ private XmlCursor newCursor () { XmlCursor curs; if (_anno != null) { curs = _anno.createCursor(); if (curs == null) { // Orphaned case. XmlObject doc = XmlObject.Factory.newInstance(); curs = doc.newCursor(); if (_anno._name != null) { curs.toNextToken(); curs.insertElement(_anno._name); curs.toPrevSibling(); } curs.setBookmark(_anno); } } else { XmlObject doc = XmlObject.Factory.newInstance(); curs = doc.newCursor(); } return curs; } /* * fUseStartDoc used by child(int index) the index is at startDoc is the element at the top-level * otherwise we always want to drill in. */ private boolean moveToChild(XmlCursor curs, long index, boolean fFirstChild, boolean fUseStartDoc) { if (index < 0) throw new IllegalArgumentException(); long idxChild = 0; if (!fUseStartDoc && curs.currentTokenType().isStartdoc()) { // We always move to the children of the top node. // todo: This assumes that we want have multiple top-level nodes. Which we should be able tohave. curs.toFirstContentToken(); } TokenType tt = curs.toFirstContentToken(); if (!tt.isNone() && !tt.isEnd()) { while (true) { if (index == idxChild) { return true; } tt = curs.currentTokenType(); if (tt.isText()) { curs.toNextToken(); } else if (tt.isStart()) { // Need to do this we want to be pointing at the text if that after the end token. curs.toEndToken(); curs.toNextToken(); } else if (tt.isComment() || tt.isProcinst()) { continue; } else { break; } idxChild++; } } else if (fFirstChild && index == 0) { // Drill into where first child would be. // curs.toFirstContentToken(); return true; } return false; } /** * * @return */ XmlCursor.TokenType tokenType() { XmlCursor.TokenType result; XmlCursor curs = newCursor(); if (curs.isStartdoc()) { curs.toFirstContentToken(); } result = curs.currentTokenType(); curs.dispose(); return result; } /** * * @param srcCurs * @param destCurs * @param fDontMoveIfSame * @return */ private boolean moveSrcToDest (XmlCursor srcCurs, XmlCursor destCurs, boolean fDontMoveIfSame) { boolean fMovedSomething = true; TokenType tt; do { if (fDontMoveIfSame && srcCurs.isInSameDocument(destCurs) && (srcCurs.comparePosition(destCurs) == 0)) { // If the source and destination are pointing at the same place then there's nothing to move. fMovedSomething = false; break; } // todo ***TLL*** Use replaceContents (when added) and eliminate children removes (see above todo). if (destCurs.currentTokenType().isStartdoc()) { destCurs.toNextToken(); } // todo ***TLL*** Can Eric support notion of copy instead of me copying then moving??? XmlCursor copyCurs = copy(srcCurs); copyCurs.moveXml(destCurs); copyCurs.dispose(); tt = srcCurs.currentTokenType(); } while (!tt.isStart() && !tt.isEnd() && !tt.isEnddoc()); return fMovedSomething; } /** * * @param cursToCopy * @return */ private XmlCursor copy (XmlCursor cursToCopy) { XmlObject xo = XmlObject.Factory.newInstance(); XmlCursor copyCurs = null; if (cursToCopy.currentTokenType().isText()) { try { // Try just as a textnode, to do that we need to wrap the text in a special fragment tag // that is not visible from the XmlCursor. copyCurs = XmlObject.Factory.parse("" + cursToCopy.getChars() + "").newCursor(); if (!cursToCopy.toNextSibling()) { if (cursToCopy.currentTokenType().isText()) { cursToCopy.toNextToken(); // It's not an element it's text so skip it. } } } catch (Exception ex) { throw ScriptRuntime.typeError(ex.getMessage()); } } else { copyCurs = xo.newCursor(); copyCurs.toFirstContentToken(); if (cursToCopy.currentTokenType() == XmlCursor.TokenType.STARTDOC) { cursToCopy.toNextToken(); } cursToCopy.copyXml(copyCurs); if (!cursToCopy.toNextSibling()) // If element skip element. { if (cursToCopy.currentTokenType().isText()) { cursToCopy.toNextToken(); // It's not an element it's text so skip it. } } } copyCurs.toStartDoc(); copyCurs.toFirstContentToken(); return copyCurs; } private static final int APPEND_CHILD = 1; private static final int PREPEND_CHILD = 2; /** * * @param curs * @param xmlToInsert */ private void insertChild(XmlCursor curs, Object xmlToInsert) { if (xmlToInsert == null || xmlToInsert instanceof Undefined) { // Do nothing } else if (xmlToInsert instanceof XmlCursor) { moveSrcToDest((XmlCursor)xmlToInsert, curs, true); } else if (xmlToInsert instanceof XML) { XML xmlValue = (XML) xmlToInsert; // If it's an attribute, then change to text node if (xmlValue.tokenType() == XmlCursor.TokenType.ATTR) { insertChild(curs, xmlValue.toString()); } else { XmlCursor cursToInsert = ((XML) xmlToInsert).newCursor(); moveSrcToDest(cursToInsert, curs, true); cursToInsert.dispose(); } } else if (xmlToInsert instanceof XMLList) { XMLList list = (XMLList) xmlToInsert; for (int i = 0; i < list.length(); i++) { insertChild(curs, list.item(i)); } } else { // Convert to string and make XML out of it String xmlStr = ScriptRuntime.toString(xmlToInsert); XmlObject xo = XmlObject.Factory.newInstance(); // Create an empty document. XmlCursor sourceCurs = xo.newCursor(); sourceCurs.toNextToken(); // To hold the text. sourceCurs.insertChars(xmlStr); sourceCurs.toPrevToken(); // Call us again with the cursor. moveSrcToDest(sourceCurs, curs, true); } } /** * * @param childToMatch * @param xmlToInsert * @param addToType */ private void insertChild(XML childToMatch, Object xmlToInsert, int addToType) { XmlCursor curs = newCursor(); TokenType tt = curs.currentTokenType(); XmlCursor xmlChildCursor = childToMatch.newCursor(); if (tt.isStartdoc()) { tt = curs.toFirstContentToken(); } if (tt.isContainer()) { tt = curs.toNextToken(); while (!tt.isEnd()) { if (tt.isStart()) { // See if this child is the same as the one thep passed in if (curs.comparePosition(xmlChildCursor) == 0) { // Found it if (addToType == APPEND_CHILD) { // Move the cursor to just past the end of this element curs.toEndToken(); curs.toNextToken(); } insertChild(curs, xmlToInsert); break; } } // Skip over child elements if (tt.isStart()) { tt = curs.toEndToken(); } tt = curs.toNextToken(); } } xmlChildCursor.dispose(); curs.dispose(); } /** * * @param curs */ protected void removeToken (XmlCursor curs) { XmlObject xo = XmlObject.Factory.newInstance(); // Don't delete anything move to another document so it gets orphaned nicely. XmlCursor tmpCurs = xo.newCursor(); tmpCurs.toFirstContentToken(); curs.moveXml(tmpCurs); tmpCurs.dispose(); } /** * * @param index */ protected void removeChild(long index) { XmlCursor curs = newCursor(); if (moveToChild(curs, index, false, false)) { removeToken(curs); } curs.dispose(); } /** * * @param name * @return */ protected static javax.xml.namespace.QName computeQName (Object name) { if (name instanceof String) { String ns = null; String localName = null; String fullName = (String)name; localName = fullName; if (fullName.startsWith("\"")) { int idx = fullName.indexOf(":"); if (idx != -1) { ns = fullName.substring(1, idx - 1); // Don't include the "" around the namespace localName = fullName.substring(idx + 1); } } if (ns == null) { return new javax.xml.namespace.QName(localName); } else { return new javax.xml.namespace.QName(ns, localName); } } return null; } /** * * @param destCurs * @param newValue */ private void replace(XmlCursor destCurs, XML newValue) { if (destCurs.isStartdoc()) { // Can't overwrite a whole document (user really wants to overwrite the contents of). destCurs.toFirstContentToken(); } // Orphan the token -- don't delete it outright on the XmlCursor. removeToken(destCurs); XmlCursor srcCurs = newValue.newCursor(); if (srcCurs.currentTokenType().isStartdoc()) { // Cann't append a whole document (user really wants to append the contents of). srcCurs.toFirstContentToken(); } moveSrcToDest(srcCurs, destCurs, false); // Re-link a new annotation to this cursor -- we just deleted the previous annotation on entrance to replace. if (!destCurs.toPrevSibling()) { destCurs.toPrevToken(); } destCurs.setBookmark(new XScriptAnnotation(destCurs)); // todo would be nice if destCurs.toNextSibling went to where the next token if the cursor was pointing at the last token in the stream. destCurs.toEndToken(); destCurs.toNextToken(); srcCurs.dispose(); } /** * * @param currXMLNode * @param xmlValue * @return */ private boolean doPut(XMLName name, XML currXMLNode, XMLObjectImpl xmlValue) { boolean result = false; XmlCursor curs = currXMLNode.newCursor(); try { // Replace the node with this new xml value. XML xml; int toAssignLen = xmlValue.length(); for (int i = 0; i < toAssignLen; i++) { if (xmlValue instanceof XMLList) { xml = ((XMLList) xmlValue).item(i); } else { xml = (XML) xmlValue; } // If it's an attribute or text node, make text node. XmlCursor.TokenType tt = xml.tokenType(); if (tt == XmlCursor.TokenType.ATTR || tt == XmlCursor.TokenType.TEXT) { xml = makeXmlFromString(lib, name, xml.toString()); } if (i == 0) { // 1st assignment is replaceChild all others are appendChild replace(curs, xml); } else { insertChild(curs, xml); } } // We're done we've blown away the node because the rvalue was XML... result = true; } catch (Exception ex) { ex.printStackTrace(); throw ScriptRuntime.typeError(ex.getMessage()); } finally { curs.dispose(); } return result; } /** * Make a text node element with this element name and text value. * * @param name * @param value * @return */ private XML makeXmlFromString(XMLLibImpl lib, XMLName name, String value) { XML result; javax.xml.namespace.QName qname; try { qname = new javax.xml.namespace.QName(name.uri(), name.localName()); } catch(Exception e) { throw ScriptRuntime.typeError(e.getMessage()); } result = createTextElement(lib, qname, value); return result; } /** * * @param name * @return */ private XMLList matchAttributes(XMLName xmlName) { XMLList result = new XMLList(lib); XmlCursor curs = newCursor(); if (curs.currentTokenType().isStartdoc()) { curs.toFirstContentToken(); } if (curs.isStart()) { if (curs.toFirstAttribute()) { do { if (qnameMatches(xmlName, curs.getName())) { result.addToList(createAttributeObject(curs)); } } while (curs.toNextAttribute()); } } curs.dispose(); return result; } /** * * @param attrCurs * @return */ private XML createAttributeObject (XmlCursor attrCurs) { XML result = null; if (attrCurs.currentTokenType().isAttr()) { result = createAttributeXML(lib, attrCurs); } return result; } // // // methods overriding ScriptableObject // // public String getClassName () { return "XML"; } // // // methods overriding IdScriptableObject // // /** * XML[0] should return this, all other indexes are Undefined * * @param index * @param start * @return */ public Object get(int index, Scriptable start) { //Log("get index: " + index); if (index == 0) { return this; } else { return Scriptable.NOT_FOUND; } } /** * Does the named property exist * * @param name * @param start * @return */ boolean hasXMLProperty(XMLName xmlName) { boolean result = false; if (prototypeFlag) { String name = xmlName.localName(); if (getMethod(name) != NOT_FOUND) { result = true; } } else { // Has now should return true if the property would have results > 0 or // if it's a method name String name = xmlName.localName(); if ((getPropertyList(xmlName).length() > 0) || (getMethod(name) != NOT_FOUND)) { result = true; } } return result; } /** * * @param index * @param start * @return */ public boolean has(int index, Scriptable start) { return (index == 0); } /** * * @return */ public Object[] getIds() { Object[] enumObjs; if (prototypeFlag) { enumObjs = new Object[0]; } else { enumObjs = new Object[1]; enumObjs[0] = new Integer(0); } return enumObjs; } /** * * @return */ public Object [] getIdsForDebug() { return getIds(); } /** * * @param name * @param start * @return */ Object getXMLProperty(XMLName xmlName) { Object result = NOT_FOUND; if (prototypeFlag) { String name = xmlName.localName(); result = getMethod(name); } else { result = getPropertyList(xmlName); } return result; } /** * * @param name * @param start * @param value */ void putXMLProperty(XMLName xmlName, Object value) { //Log("put property: " + name + " value: " + value.getClass()); if (prototypeFlag) { } else { // Special-case checks for undefined and null if (value == null) { value = "null"; } else if (value instanceof Undefined) { value = "undefined"; } // Get the named property if (xmlName.isAttributeName()) { setAttribute(xmlName, value); } else if (xmlName.uri() == null && xmlName.localName().equals("*")) { setChildren(value); } else { // Convert text into XML if needed. XMLObjectImpl xmlValue = null; if (value instanceof XMLObjectImpl) { xmlValue = (XMLObjectImpl) value; // Check for attribute type and convert to textNode if (xmlValue instanceof XML) { if (((XML) xmlValue).tokenType() == XmlCursor.TokenType.ATTR) { xmlValue = makeXmlFromString(lib, xmlName, xmlValue.toString()); } } if (xmlValue instanceof XMLList) { for (int i = 0; i < xmlValue.length(); i++) { XML xml = ((XMLList) xmlValue).item(i); if (xml.tokenType() == XmlCursor.TokenType.ATTR) { ((XMLList) xmlValue).replace(i, makeXmlFromString(lib, xmlName, xml.toString())); } } } } else { xmlValue = makeXmlFromString(lib, xmlName, ScriptRuntime.toString(value)); } XMLList matches = getPropertyList(xmlName); if (matches.length() == 0) { appendChild(xmlValue); } else { // Remove all other matches for (int i = 1; i < matches.length(); i++) { removeChild(matches.item(i).childIndex()); } // Replace first match with new value. doPut(xmlName, matches.item(0), xmlValue); } } } } /** * * @param index * @param start * @param value */ public void put(int index, Scriptable start, Object value) { // Spec says assignment to indexed XML object should return type error throw ScriptRuntime.typeError("Assignment to indexed XML is not allowed"); } /** * * @param name */ void deleteXMLProperty(XMLName name) { if (!name.isDescendants() && name.isAttributeName()) { XmlCursor curs = newCursor(); // TODO: Cover the case *::name if (name.localName().equals("*")) { // Delete all attributes. if (curs.toFirstAttribute()) { while (curs.currentTokenType().isAttr()) { curs.removeXml(); } } } else { // Delete an attribute. javax.xml.namespace.QName qname = new javax.xml.namespace.QName( name.uri(), name.localName()); curs.removeAttribute(qname); } curs.dispose(); } else { XMLList matches = getPropertyList(name); matches.remove(); } } /** * * @param index */ public void delete(int index) { if (index == 0) { remove(); } } // // // package utility functions: // // protected XScriptAnnotation getAnnotation () { return _anno; } protected void changeNS (String oldURI, String newURI) { XmlCursor curs = newCursor(); while (curs.toParent()) { /* Goto the top of the document */ } TokenType tt = curs.currentTokenType(); if (tt.isStartdoc()) { tt = curs.toFirstContentToken(); } if (tt.isStart()) { do { if (tt.isStart() || tt.isAttr() || tt.isNamespace()) { javax.xml.namespace.QName currQName = curs.getName(); if (oldURI.equals(currQName.getNamespaceURI())) { curs.setName(new javax.xml.namespace.QName(newURI, currQName.getLocalPart())); } } tt = curs.toNextToken(); } while (!tt.isEnddoc() && !tt.isNone()); } curs.dispose(); } /** * */ void remove () { XmlCursor childCurs = newCursor(); if (childCurs.currentTokenType().isStartdoc()) { // Remove on the document removes all children. TokenType tt = childCurs.toFirstContentToken(); while (!tt.isEnd() && !tt.isEnddoc()) { removeToken(childCurs); tt = childCurs.currentTokenType(); // Now see where we're pointing after the delete -- next token. } } else { removeToken(childCurs); } childCurs.dispose(); } /** * * @param value */ void replaceAll(XML value) { XmlCursor curs = newCursor(); replace(curs, value); _anno = value._anno; curs.dispose(); } /** * * @param attrName * @param value */ void setAttribute(XMLName xmlName, Object value) { if (xmlName.uri() == null && xmlName.localName().equals("*")) { throw ScriptRuntime.typeError("@* assignment not supported."); } XmlCursor curs = newCursor(); String strValue = ScriptRuntime.toString(value); if (curs.currentTokenType().isStartdoc()) { curs.toFirstContentToken(); } javax.xml.namespace.QName qName; try { qName = new javax.xml.namespace.QName(xmlName.uri(), xmlName.localName()); } catch(Exception e) { throw ScriptRuntime.typeError(e.getMessage()); } if (!curs.setAttributeText(qName, strValue)) { if (curs.currentTokenType().isStart()) { // Can only add attributes inside of a start. curs.toNextToken(); } curs.insertAttributeWithValue(qName, strValue); } curs.dispose(); } /** * * @param namespace * @return */ private XMLList allChildNodes(String namespace) { XMLList result = new XMLList(lib); XmlCursor curs = newCursor(); TokenType tt = curs.currentTokenType(); javax.xml.namespace.QName targetProperty = new javax.xml.namespace.QName(namespace, "*"); if (tt.isStartdoc()) { tt = curs.toFirstContentToken(); } if (tt.isContainer()) { tt = curs.toFirstContentToken(); while (!tt.isEnd()) { if (!tt.isStart()) { // Not an element result.addToList(findAnnotation(curs)); // Reset target property to null in this case targetProperty = null; } else { // Match namespace as well if specified if (namespace == null || namespace.length() == 0 || namespace.equals("*") || curs.getName().getNamespaceURI().equals(namespace)) { // Add it to the list result.addToList(findAnnotation(curs)); // Set target property if target name is "*", // Otherwise if target property does not match current, then // set to null if (targetProperty != null) { if (targetProperty.getLocalPart().equals("*")) { targetProperty = curs.getName(); } else if (!targetProperty.getLocalPart().equals(curs.getName().getLocalPart())) { // Not a match, unset target property targetProperty = null; } } } } // Skip over child elements if (tt.isStart()) { tt = curs.toEndToken(); } tt = curs.toNextToken(); } } curs.dispose(); // Set the targets for this XMLList. result.setTargets(this, targetProperty); return result; } /** * * @return */ private XMLList matchDescendantAttributes(XMLName xmlName) { XMLList result = new XMLList(lib); XmlCursor curs = newCursor(); TokenType tt = curs.currentTokenType(); // Set the targets for this XMLList. result.setTargets(this, null); if (tt.isStartdoc()) { tt = curs.toFirstContentToken(); } if (tt.isContainer()) { int nestLevel = 1; while (nestLevel > 0) { tt = curs.toNextToken(); // Only try to match names for attributes if (tt.isAttr()) { if (qnameMatches(xmlName, curs.getName())) { result.addToList(findAnnotation(curs)); } } if (tt.isStart()) { nestLevel++; } else if (tt.isEnd()) { nestLevel--; } else if (tt.isEnddoc()) { // Shouldn't get here, but just in case. break; } } } curs.dispose(); return result; } /** * * @return */ private XMLList matchDescendantChildren(XMLName xmlName) { XMLList result = new XMLList(lib); XmlCursor curs = newCursor(); TokenType tt = curs.currentTokenType(); // Set the targets for this XMLList. result.setTargets(this, null); if (tt.isStartdoc()) { tt = curs.toFirstContentToken(); } if (tt.isContainer()) { int nestLevel = 1; while (nestLevel > 0) { tt = curs.toNextToken(); if (!tt.isAttr() && !tt.isEnd() && !tt.isEnddoc()) { // Only try to match names for elements or processing instructions. if (!tt.isStart() && !tt.isProcinst()) { // Not an element or procinst, only add if qname is all if (xmlName.localName().equals("*")) { result.addToList(findAnnotation(curs)); } } else { if (qnameMatches(xmlName, curs.getName())) { result.addToList(findAnnotation(curs)); } } } if (tt.isStart()) { nestLevel++; } else if (tt.isEnd()) { nestLevel--; } else if (tt.isEnddoc()) { // Shouldn't get here, but just in case. break; } } } curs.dispose(); return result; } /** * * @param tokenType * @return */ private XMLList matchChildren(XmlCursor.TokenType tokenType) { return matchChildren(tokenType, XMLName.formStar()); } /** * * @return */ private XMLList matchChildren(XmlCursor.TokenType tokenType, XMLName name) { XMLList result = new XMLList(lib); XmlCursor curs = newCursor(); TokenType tt = curs.currentTokenType(); javax.xml.namespace.QName qname = new javax.xml.namespace.QName(name.uri(), name.localName()); javax.xml.namespace.QName targetProperty = qname; if (tt.isStartdoc()) { tt = curs.toFirstContentToken(); } if (tt.isContainer()) { tt = curs.toFirstContentToken(); while (!tt.isEnd()) { if (tt == tokenType) { // Only try to match names for elements or processing instructions. if (!tt.isStart() && !tt.isProcinst()) { // Not an element or no name specified. result.addToList(findAnnotation(curs)); // Reset target property to null in this case targetProperty = null; } else { // Match names as well if (qnameMatches(name, curs.getName())) { // Add it to the list result.addToList(findAnnotation(curs)); // Set target property if target name is "*", // Otherwise if target property does not match current, then // set to null if (targetProperty != null) { if (targetProperty.getLocalPart().equals("*")) { targetProperty = curs.getName(); } else if (!targetProperty.getLocalPart().equals(curs.getName().getLocalPart())) { // Not a match, unset target property targetProperty = null; } } } } } // Skip over child elements if (tt.isStart()) { tt = curs.toEndToken(); } tt = curs.toNextToken(); } } curs.dispose(); if (tokenType == XmlCursor.TokenType.START) { // Set the targets for this XMLList. result.setTargets(this, targetProperty); } return result; } /** * * @param template * @param match * @return */ private boolean qnameMatches(XMLName template, javax.xml.namespace.QName match) { boolean matches = false; if (template.uri() == null || template.uri().equals(match.getNamespaceURI())) { // URI OK, test name if (template.localName().equals("*") || template.localName().equals(match.getLocalPart())) { matches = true; } } return matches; } // // // Methods from section 12.4.4 in the spec // // /** * The addNamespace method adds a namespace declaration to the in scope * namespaces for this XML object and returns this XML object. * * @param toAdd */ XML addNamespace(Namespace ns) { // When a namespace is used it will be added automatically // to the inScopeNamespaces set. There is no need to add // Namespaces with undefined prefixes. String nsPrefix = ns.prefix(); if (nsPrefix == null) return this; XmlCursor cursor = newCursor(); try { if(!cursor.isContainer()) return this; javax.xml.namespace.QName qname = cursor.getName(); // Don't add a default namespace declarations to containers // with QNames in no namespace. if(qname.getNamespaceURI().equals("") && nsPrefix.equals("")) return this; // Get all declared namespaces that are in scope Map prefixToURI = NamespaceHelper.getAllNamespaces(lib, cursor); String uri = (String)prefixToURI.get(nsPrefix); if(uri != null) { // Check if the Namespace is not already in scope if(uri.equals(ns.uri())) return this; cursor.push(); // Let's see if we have to delete a namespace declaration while(cursor.toNextToken().isAnyAttr()) { if(cursor.isNamespace()) { qname = cursor.getName(); String prefix = qname.getLocalPart(); if(prefix.equals(nsPrefix)) { // Delete the current Namespace declaration cursor.removeXml(); break; } } } cursor.pop(); } cursor.toNextToken(); cursor.insertNamespace(nsPrefix, ns.uri()); } finally { cursor.dispose(); } return this; } /** * * @param xml * @return */ XML appendChild(Object xml) { XmlCursor curs = newCursor(); if (curs.isStartdoc()) { curs.toFirstContentToken(); } // Move the cursor to the end of this element if (curs.isStart()) { curs.toEndToken(); } insertChild(curs, xml); curs.dispose(); return this; } /** * * @param name * @return */ XMLList attribute(XMLName xmlName) { return matchAttributes(xmlName); } /** * * @return */ XMLList attributes() { XMLName xmlName = XMLName.formStar(); return matchAttributes(xmlName); } XMLList child(long index) { XMLList result = new XMLList(lib); result.setTargets(this, null); result.addToList(getXmlChild(index)); return result; } XMLList child(XMLName xmlName) { if (xmlName == null) return new XMLList(lib); XMLList result; if (xmlName.localName().equals("*")) { result = allChildNodes(xmlName.uri()); } else { result = matchChildren(XmlCursor.TokenType.START, xmlName); } return result; } /** * * @param index * @return */ XML getXmlChild(long index) { XML result = null; XmlCursor curs = newCursor(); if (moveToChild(curs, index, false, true)) { result = createXML(lib, curs); } curs.dispose(); return result; } /** * * @return */ int childIndex() { int index = 0; XmlCursor curs = newCursor(); TokenType tt = curs.currentTokenType(); while (true) { if (tt.isText()) { index++; if (!curs.toPrevSibling()) { break; } } else if (tt.isStart()) { tt = curs.toPrevToken(); if (tt.isEnd()) { curs.toNextToken(); if (!curs.toPrevSibling()) { break; } index++; } else { // Hit the parent start tag so get out we're down counting children. break; } } else if (tt.isComment() || tt.isProcinst()) { curs.toPrevToken(); } else { break; } tt = curs.currentTokenType(); } index = curs.currentTokenType().isStartdoc() ? -1 : index; curs.dispose(); return index; } /** * * @return */ XMLList children() { return allChildNodes(null); } /** * * @return */ XMLList comments() { return matchChildren(XmlCursor.TokenType.COMMENT); } /** * * @param xml * @return */ boolean contains(Object xml) { boolean result = false; if (xml instanceof XML) { result = equivalentXml(xml); } return result; } /** * * @return */ Object copy() { XmlCursor srcCurs = newCursor(); if (srcCurs.isStartdoc()) { srcCurs.toFirstContentToken(); } XML xml = createEmptyXML(lib); XmlCursor destCurs = xml.newCursor(); destCurs.toFirstContentToken(); srcCurs.copyXml(destCurs); destCurs.dispose(); srcCurs.dispose(); return xml; } /** * * @param name * @return */ XMLList descendants(XMLName xmlName) { XMLList result; if (xmlName.isAttributeName()) { result = matchDescendantAttributes(xmlName); } else { result = matchDescendantChildren(xmlName); } return result; } /** * The inScopeNamespaces method returns an Array of Namespace objects * representing the namespaces in scope for this XML object in the * context of its parent. * * @return Array of all Namespaces in scope for this XML Object. */ Object[] inScopeNamespaces() { XmlCursor cursor = newCursor(); Object[] namespaces = NamespaceHelper.inScopeNamespaces(lib, cursor); cursor.dispose(); return namespaces; } /** * * @param child * @param xml */ XML insertChildAfter(Object child, Object xml) { if (child == null) { // Spec says inserting after nothing is the same as prepending prependChild(xml); } else if (child instanceof XML) { insertChild((XML) child, xml, APPEND_CHILD); } return this; } /** * * @param child * @param xml */ XML insertChildBefore(Object child, Object xml) { if (child == null) { // Spec says inserting before nothing is the same as appending appendChild(xml); } else if (child instanceof XML) { insertChild((XML) child, xml, PREPEND_CHILD); } return this; } /** * * @return */ boolean hasOwnProperty(XMLName xmlName) { boolean hasProperty = false; if (prototypeFlag) { String property = xmlName.localName(); hasProperty = (0 != findPrototypeId(property)); } else { hasProperty = (getPropertyList(xmlName).length() > 0); } return hasProperty; } /** * * @return */ boolean hasComplexContent() { return !hasSimpleContent(); } /** * * @return */ boolean hasSimpleContent() { boolean simpleContent = false; XmlCursor curs = newCursor(); if (curs.isAttr() || curs.isText()) { return true; } if (curs.isStartdoc()) { curs.toFirstContentToken(); } simpleContent = !(curs.toFirstChild()); curs.dispose(); return simpleContent; } /** * Length of an XML object is always 1, it's a list of XML objects of size 1. * * @return */ int length() { return 1; } /** * * @return */ String localName() { XmlCursor cursor = newCursor(); if (cursor.isStartdoc()) cursor.toFirstContentToken(); String name = null; if(cursor.isStart() || cursor.isAttr() || cursor.isProcinst()) { javax.xml.namespace.QName qname = cursor.getName(); name = qname.getLocalPart(); } cursor.dispose(); return name; } /** * The name method returns the qualified name associated with this XML object. * * @return The qualified name associated with this XML object. */ QName name() { XmlCursor cursor = newCursor(); if (cursor.isStartdoc()) cursor.toFirstContentToken(); QName name = null; if(cursor.isStart() || cursor.isAttr() || cursor.isProcinst()) { javax.xml.namespace.QName qname = cursor.getName(); if(cursor.isProcinst()) { name = new QName(lib, "", qname.getLocalPart(), ""); } else { String uri = qname.getNamespaceURI(); String prefix = qname.getPrefix(); name = new QName(lib, uri, qname.getLocalPart(), prefix); } } cursor.dispose(); return name; } /** * * @param prefix * @return */ Object namespace(String prefix) { XmlCursor cursor = newCursor(); if (cursor.isStartdoc()) { cursor.toFirstContentToken(); } Object result = null; if (prefix == null) { if(cursor.isStart() || cursor.isAttr()) { Object[] inScopeNS = NamespaceHelper.inScopeNamespaces(lib, cursor); // XXX Is it reaaly necessary to create the second cursor? XmlCursor cursor2 = newCursor(); if (cursor2.isStartdoc()) cursor2.toFirstContentToken(); result = NamespaceHelper.getNamespace(lib, cursor2, inScopeNS); cursor2.dispose(); } } else { Map prefixToURI = NamespaceHelper.getAllNamespaces(lib, cursor); String uri = (String)prefixToURI.get(prefix); result = (uri == null) ? Undefined.instance : new Namespace(lib, prefix, uri); } cursor.dispose(); return result; } /** * * @return */ Object[] namespaceDeclarations() { XmlCursor cursor = newCursor(); Object[] namespaces = NamespaceHelper.namespaceDeclarations(lib, cursor); cursor.dispose(); return namespaces; } /** * * @return */ Object nodeKind() { String result; XmlCursor.TokenType tt = tokenType(); if (tt == XmlCursor.TokenType.ATTR) { result = "attribute"; } else if (tt == XmlCursor.TokenType.TEXT) { result = "text"; } else if (tt == XmlCursor.TokenType.COMMENT) { result = "comment"; } else if (tt == XmlCursor.TokenType.PROCINST) { result = "processing-instruction"; } else if (tt == XmlCursor.TokenType.START) { result = "element"; } else { // A non-existant node has the nodeKind() of text result = "text"; } return result; } /** * */ void normalize() { XmlCursor curs = newCursor(); TokenType tt = curs.currentTokenType(); // Walk through the tokens removing empty text nodes and merging adjacent text nodes. if (tt.isStartdoc()) { tt = curs.toFirstContentToken(); } if (tt.isContainer()) { int nestLevel = 1; String previousText = null; while (nestLevel > 0) { tt = curs.toNextToken(); if (tt == XmlCursor.TokenType.TEXT) { String currentText = curs.getChars().trim(); if (currentText.trim().length() == 0) { // Empty text node, remove. removeToken(curs); curs.toPrevToken(); } else if (previousText == null) { // No previous text node, reset to trimmed version previousText = currentText; } else { // It appears that this case never happens with XBeans. // Previous text node exists, concatenate String newText = previousText + currentText; curs.toPrevToken(); removeToken(curs); removeToken(curs); curs.insertChars(newText); } } else { previousText = null; } if (tt.isStart()) { nestLevel++; } else if (tt.isEnd()) { nestLevel--; } else if (tt.isEnddoc()) { // Shouldn't get here, but just in case. break; } } } curs.dispose(); } /** * * @return */ Object parent() { Object parent; XmlCursor curs = newCursor(); if (curs.isStartdoc()) { // At doc level - no parent parent = Undefined.instance; } else { if (curs.toParent()) { if (curs.isStartdoc()) { // Was top-level - no parent parent = Undefined.instance; } else { parent = getFromAnnotation(lib, findAnnotation(curs)); } } else { // No parent parent = Undefined.instance; } } curs.dispose(); return parent; } /** * * @param xml * @return */ XML prependChild (Object xml) { XmlCursor curs = newCursor(); if (curs.isStartdoc()) { curs.toFirstContentToken(); } // Move the cursor to the first content token curs.toFirstContentToken(); insertChild(curs, xml); curs.dispose(); return this; } /** * * @return */ Object processingInstructions(XMLName xmlName) { return matchChildren(XmlCursor.TokenType.PROCINST, xmlName); } /** * * @param name * @return */ boolean propertyIsEnumerable(Object name) { boolean result; if (name instanceof Integer) { result = (((Integer)name).intValue() == 0); } else if (name instanceof Number) { double x = ((Number)name).doubleValue(); // Check that number is posotive 0 result = (x == 0.0 && 1.0 / x > 0); } else { result = ScriptRuntime.toString(name).equals("0"); } return result; } /** * * @param namespace */ XML removeNamespace(Namespace ns) { XmlCursor cursor = newCursor(); try { if(cursor.isStartdoc()) cursor.toFirstContentToken(); if(!cursor.isStart()) return this; String nsPrefix = ns.prefix(); String nsURI = ns.uri(); Map prefixToURI = new HashMap(); int depth = 1; while(!(cursor.isEnd() && depth == 0)) { if(cursor.isStart()) { // Get the namespaces declared in this element. // The ones with undefined prefixes are not candidates // for removal because they are used. prefixToURI.clear(); NamespaceHelper.getNamespaces(cursor, prefixToURI); ObjArray inScopeNSBag = new ObjArray(); Iterator i = prefixToURI.entrySet().iterator(); while(i.hasNext()) { Map.Entry entry = (Map.Entry)i.next(); ns = new Namespace(lib, (String)entry.getKey(), (String)entry.getValue()); inScopeNSBag.add(ns); } // Add the URI we are looking for to avoid matching // non-existing Namespaces. ns = new Namespace(lib, nsURI); inScopeNSBag.add(ns); Object[] inScopeNS = inScopeNSBag.toArray(); // Check the element name Namespace n = NamespaceHelper.getNamespace(lib, cursor, inScopeNS); if(nsURI.equals(n.uri()) && (nsPrefix == null || nsPrefix.equals(n.prefix()))) { // This namespace is used return this; } // Check the attributes cursor.push(); boolean hasNext = cursor.toFirstAttribute(); while(hasNext) { n = NamespaceHelper.getNamespace(lib, cursor, inScopeNS); if(nsURI.equals(n.uri()) && (nsPrefix == null || nsPrefix.equals(n.prefix()))) { // This namespace is used return this; } hasNext = cursor.toNextAttribute(); } cursor.pop(); if(nsPrefix == null) { // Remove all namespaces declarations that match nsURI i = prefixToURI.entrySet().iterator(); while(i.hasNext()) { Map.Entry entry = (Map.Entry)i.next(); if(entry.getValue().equals(nsURI)) NamespaceHelper.removeNamespace(cursor, (String)entry.getKey()); } } else if(nsURI.equals(prefixToURI.get(nsPrefix))) { // Remove the namespace declaration that matches nsPrefix NamespaceHelper.removeNamespace(cursor, String.valueOf(nsPrefix)); } } switch(cursor.toNextToken().intValue()) { case XmlCursor.TokenType.INT_START: depth++; break; case XmlCursor.TokenType.INT_END: depth--; break; } } } finally { cursor.dispose(); } return this; } XML replace(long index, Object xml) { XMLList xlChildToReplace = child(index); if (xlChildToReplace.length() > 0) { // One exists an that index XML childToReplace = xlChildToReplace.item(0); insertChildAfter(childToReplace, xml); removeChild(index); } return this; } /** * * @param propertyName * @param xml * @return */ XML replace(XMLName xmlName, Object xml) { putXMLProperty(xmlName, xml); return this; } /** * * @param xml */ XML setChildren(Object xml) { // remove all children XMLName xmlName = XMLName.formStar(); XMLList matches = getPropertyList(xmlName); matches.remove(); // append new children appendChild(xml); return this; } /** * * @param name */ void setLocalName(String localName) { XmlCursor cursor = newCursor(); try { if(cursor.isStartdoc()) cursor.toFirstContentToken(); if(cursor.isText() || cursor.isComment()) return; javax.xml.namespace.QName qname = cursor.getName(); cursor.setName(new javax.xml.namespace.QName( qname.getNamespaceURI(), localName, qname.getPrefix())); } finally { cursor.dispose(); } } /** * * @param name */ void setName(QName qname) { XmlCursor cursor = newCursor(); try { if(cursor.isStartdoc()) cursor.toFirstContentToken(); if(cursor.isText() || cursor.isComment()) return; if(cursor.isProcinst()) { String localName = qname.localName(); cursor.setName(new javax.xml.namespace.QName(localName)); } else { String prefix = qname.prefix(); if (prefix == null) { prefix = ""; } cursor.setName(new javax.xml.namespace.QName( qname.uri(), qname.localName(), prefix)); } } finally { cursor.dispose(); } } /** * * @param ns */ void setNamespace(Namespace ns) { XmlCursor cursor = newCursor(); try { if(cursor.isStartdoc()) cursor.toFirstContentToken(); if(cursor.isText() || cursor.isComment() || cursor.isProcinst()) return; String prefix = ns.prefix(); if (prefix == null) { prefix = ""; } cursor.setName(new javax.xml.namespace.QName( ns.uri(), localName(), prefix)); } finally { cursor.dispose(); } } /** * * @return */ XMLList text() { return matchChildren(XmlCursor.TokenType.TEXT); } /** * * @return */ public String toString() { String result; XmlCursor curs = newCursor(); if (curs.isStartdoc()) { curs.toFirstContentToken(); } if (curs.isText()) { result = curs.getChars(); } else if (curs.isStart() && hasSimpleContent()) { result = curs.getTextValue(); } else { result = toXMLString(0); } return result; } String toSource(int indent) { // XXX Does toXMLString always return valid XML literal? return toXMLString(indent); } /** * * @return */ String toXMLString(int indent) { // XXX indent is ignored String result; XmlCursor curs = newCursor(); if (curs.isStartdoc()) { curs.toFirstContentToken(); } try { if (curs.isText()) { result = curs.getChars(); } else if (curs.isAttr()) { result = curs.getTextValue(); } else if (curs.isComment() || curs.isProcinst()) { result = XML.dumpNode(curs, getOptions()); // todo: XBeans-dependent hack here // If it's a comment or PI, take off the xml-frament stuff String start = ""; String end = ""; if (result.startsWith(start)) { result = result.substring(start.length()); } if (result.endsWith(end)) { result = result.substring(0, result.length() - end.length()); } } else { result = XML.dumpNode(curs, getOptions()); } } finally { curs.dispose(); } return result; } /** * * @return */ Object valueOf() { return this; } // // Other public Functions from XMLObject // /** * * @param target * @return */ boolean equivalentXml(Object target) { boolean result = false; if (target instanceof XML) { XML otherXml = (XML) target; // Compare with toString() if either side is text node or attribute // otherwise compare as XML XmlCursor.TokenType thisTT = tokenType(); XmlCursor.TokenType otherTT = otherXml.tokenType(); if (thisTT == XmlCursor.TokenType.ATTR || otherTT == XmlCursor.TokenType.ATTR || thisTT == XmlCursor.TokenType.TEXT || otherTT == XmlCursor.TokenType.TEXT) { result = toString().equals(otherXml.toString()); } else { XmlCursor cursOne = newCursor(); XmlCursor cursTwo = otherXml.newCursor(); result = LogicalEquality.nodesEqual(cursOne, cursTwo); cursOne.dispose(); cursTwo.dispose(); // Old way of comparing by string. // boolean orgPrettyPrinting = prototype.prettyPrinting; // prototype.prettyPrinting = true; // result = toXMLString(0).equals(otherXml.toXMLString(0)); // prototype.prettyPrinting = orgPrettyPrinting; } } else if (target instanceof XMLList) { XMLList otherList = (XMLList) target; if (otherList.length() == 1) { result = equivalentXml(otherList.getXmlFromAnnotation(0)); } } else if (hasSimpleContent()) { String otherStr = ScriptRuntime.toString(target); result = toString().equals(otherStr); } return result; } /** * * @param name * @param start * @return */ XMLList getPropertyList(XMLName name) { XMLList result; // Get the named property if (name.isDescendants()) { result = descendants(name); } else if (name.isAttributeName()) { result = attribute(name); } else { result = child(name); } return result; } protected Object jsConstructor(Context cx, boolean inNewExpr, Object[] args) { if (args.length == 0) { return createFromJS(lib, ""); } else { Object arg0 = args[0]; if (!inNewExpr && arg0 instanceof XML) { // XML(XML) returns the same object. return arg0; } return createFromJS(lib, arg0); } } /** * See ECMA 357, 11_2_2_1, Semantics, 3_f. */ public Scriptable getExtraMethodSource(Context cx) { if (hasSimpleContent()) { String src = toString(); return ScriptRuntime.toObjectOrNull(cx, src); } return null; } XmlObject getXmlObject() { XmlObject xo; XmlCursor cursor = newCursor(); try { xo = cursor.getObject(); } finally { cursor.dispose(); } return xo; } }