From 98e2821b38a775737e42a2479a6bc65107210859 Mon Sep 17 00:00:00 2001 From: Elliot Kroo Date: Thu, 11 Mar 2010 15:21:30 -0800 Subject: reorganizing the first level of folders (trunk/branch folders are not the git way :) --- .../mozilla/javascript/xml/impl/xmlbeans/XML.java | 3092 ++++++++++++++++++++ 1 file changed, 3092 insertions(+) create mode 100644 infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java (limited to 'infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java') diff --git a/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java b/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java new file mode 100644 index 0000000..c8818a5 --- /dev/null +++ b/infrastructure/rhino1_7R1/deprecatedsrc/org/mozilla/javascript/xml/impl/xmlbeans/XML.java @@ -0,0 +1,3092 @@ +/* -*- 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; + } +} -- cgit v1.2.3-1-g7c22