From 88a39139949ddec5027586d8245fa68b8188a679 Mon Sep 17 00:00:00 2001 From: "igor%mir2.org" Date: Wed, 28 Jul 2004 23:43:17 +0000 Subject: [PATCH] Committing E4X runtime implementation: see enhancement report 242805. This is based on code contributed to Rhino by AgileDelta, Inc, www.agiledelta.com and in particular by Ethan Hugg Terry Lucas Milen Nankov John Schneider Thanks! --- js/rhino/xmlimplsrc/build.xml | 117 + .../javascript/xmlimpl/LogicalEquality.java | 364 ++ .../mozilla/javascript/xmlimpl/Namespace.java | 309 ++ .../javascript/xmlimpl/NamespaceHelper.java | 348 ++ .../org/mozilla/javascript/xmlimpl/QName.java | 306 ++ .../org/mozilla/javascript/xmlimpl/XML.java | 3085 +++++++++++++++++ .../mozilla/javascript/xmlimpl/XMLCtor.java | 270 ++ .../javascript/xmlimpl/XMLLibImpl.java | 770 ++++ .../mozilla/javascript/xmlimpl/XMLList.java | 1641 +++++++++ .../mozilla/javascript/xmlimpl/XMLName.java | 116 + .../javascript/xmlimpl/XMLObjectImpl.java | 649 ++++ .../javascript/xmlimpl/XMLReference.java | 99 + .../javascript/xmlimpl/XMLWithScope.java | 126 + 13 files changed, 8200 insertions(+) create mode 100644 js/rhino/xmlimplsrc/build.xml create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/LogicalEquality.java create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/NamespaceHelper.java create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLReference.java create mode 100644 js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java diff --git a/js/rhino/xmlimplsrc/build.xml b/js/rhino/xmlimplsrc/build.xml new file mode 100644 index 000000000000..dd3be977cda6 --- /dev/null +++ b/js/rhino/xmlimplsrc/build.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Compiling E4X implementation using ${xbean.jar} + + + + + + + + +Skipping compilation of E4X implementation due to lack of +javax.xml.namespace.*, org.apache.xmlbeans.* classes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/LogicalEquality.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/LogicalEquality.java new file mode 100644 index 000000000000..a4064b1e73f6 --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/LogicalEquality.java @@ -0,0 +1,364 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Ethan Hugg + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (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 NPL, indicate your decision by + * deleting the provisions above and replace 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 NPL or the GPL. + */ + +package org.mozilla.javascript.xmlimpl; + +import org.apache.xmlbeans.XmlCursor; + +import java.util.*; + + +public class LogicalEquality +{ + public static boolean nodesEqual(XmlCursor xmlOne, XmlCursor xmlTwo) + { + boolean result = false; + + if (xmlOne.isStartdoc()) + { + xmlOne.toFirstContentToken(); + } + + if (xmlTwo.isStartdoc()) + { + xmlTwo.toFirstContentToken(); + } + + if (xmlOne.currentTokenType() == xmlTwo.currentTokenType()) + { + if (xmlOne.isEnddoc()) + { + // Both empty + result = true; + } + else if (xmlOne.isAttr()) + { + result = attributesEqual(xmlOne, xmlTwo); + } + else if (xmlOne.isText()) + { + result = textNodesEqual(xmlOne, xmlTwo); + } + else if (xmlOne.isComment()) + { + result = commentsEqual(xmlOne, xmlTwo); + } + else if (xmlOne.isProcinst()) + { + result = processingInstructionsEqual(xmlOne, xmlTwo); + } + else if (xmlOne.isStart()) + { + // Compare root elements + result = elementsEqual(xmlOne, xmlTwo); + } + } + + return result; + } + + private static boolean elementsEqual(XmlCursor xmlOne, XmlCursor xmlTwo) + { + boolean result = true; + + if (!qnamesEqual(xmlOne.getName(), xmlTwo.getName())) + { + result = false; + } + else + { + // These filter out empty text nodes. + nextToken(xmlOne); + nextToken(xmlTwo); + + do + { + if (xmlOne.currentTokenType() != xmlTwo.currentTokenType()) + { + // Not same token + result = false; + break; + } + else if (xmlOne.isEnd()) + { + // Done with this element, step over end + break; + } + else if (xmlOne.isEnddoc()) + { + // Shouldn't get here + break; + } + else if (xmlOne.isAttr()) + { + // This one will move us to the first non-attr token. + result = attributeListsEqual(xmlOne, xmlTwo); + } + else + { + if (xmlOne.isText()) + { + result = textNodesEqual(xmlOne, xmlTwo); + } + else if (xmlOne.isComment()) + { + result = commentsEqual(xmlOne, xmlTwo); + } + else if (xmlOne.isProcinst()) + { + result = processingInstructionsEqual(xmlOne, xmlTwo); + } + else if (xmlOne.isStart()) + { + result = elementsEqual(xmlOne, xmlTwo); + } + else + { + //XML.log("Unknown token type" + xmlOne.currentTokenType()); + } + + // These filter out empty text nodes. + nextToken(xmlOne); + nextToken(xmlTwo); + } + } + while(result); + } + + return result; + } + + /** + * + * @param xmlOne + * @param xmlTwo + * @return + */ + private static boolean attributeListsEqual(XmlCursor xmlOne, XmlCursor xmlTwo) + { + boolean result = true; + TreeMap mapOne = loadAttributeMap(xmlOne); + TreeMap mapTwo = loadAttributeMap(xmlTwo); + + if (mapOne.size() != mapTwo.size()) + { + result = false; + } + else + { + Set keysOne = mapOne.keySet(); + Set keysTwo = mapTwo.keySet(); + Iterator itOne = keysOne.iterator(); + Iterator itTwo = keysTwo.iterator(); + + while (result && itOne.hasNext()) + { + String valueOne = (String) itOne.next(); + String valueTwo = (String) itTwo.next(); + + if (!valueOne.equals(valueTwo)) + { + result = false; + } + else + { + javax.xml.namespace.QName qnameOne = (javax.xml.namespace.QName) mapOne.get(valueOne); + javax.xml.namespace.QName qnameTwo = (javax.xml.namespace.QName) mapTwo.get(valueTwo); + + if (!qnamesEqual(qnameOne, qnameTwo)) + { + result = false; + } + } + } + } + + return result; + } + + /** + * + * @param xml + * @return + */ + private static TreeMap loadAttributeMap(XmlCursor xml) + { + TreeMap result = new TreeMap(); + + while (xml.isAttr()) + { + result.put(xml.getTextValue(), xml.getName()); + nextToken(xml); + } + + return result; + } + + /** + * + * @param xmlOne + * @param xmlTwo + * @return + */ + private static boolean attributesEqual(XmlCursor xmlOne, XmlCursor xmlTwo) + { + boolean result = false; + + if (xmlOne.isAttr() && xmlTwo.isAttr()) + { + if (qnamesEqual(xmlOne.getName(), xmlTwo.getName())) + { + if (xmlOne.getTextValue().equals(xmlTwo.getTextValue())) + { + result = true; + } + } + } + + return result; + } + + /** + * + * @param xmlOne + * @param xmlTwo + * @return + */ + private static boolean textNodesEqual(XmlCursor xmlOne, XmlCursor xmlTwo) + { + boolean result = false; + + if (xmlOne.isText() && xmlTwo.isText()) + { + if (xmlOne.getChars().equals(xmlTwo.getChars())) + { + result = true; + } + } + + return result; + } + + /** + * + * @param xmlOne + * @param xmlTwo + * @return + */ + private static boolean commentsEqual(XmlCursor xmlOne, XmlCursor xmlTwo) + { + boolean result = false; + + if (xmlOne.isComment() && xmlTwo.isComment()) + { + if (xmlOne.getTextValue().equals(xmlTwo.getTextValue())) + { + result = true; + } + } + + return result; + } + + /** + * + * @param xmlOne + * @param xmlTwo + * @return + */ + private static boolean processingInstructionsEqual(XmlCursor xmlOne, XmlCursor xmlTwo) + { + boolean result = false; + + if (xmlOne.isProcinst() && xmlTwo.isProcinst()) + { + if (qnamesEqual(xmlOne.getName(), xmlTwo.getName())) + { + if (xmlOne.getTextValue().equals(xmlTwo.getTextValue())) + { + result = true; + } + } + } + + return result; + } + + /** + * + * @param qnameOne + * @param qnameTwo + * @return + */ + private static boolean qnamesEqual(javax.xml.namespace.QName qnameOne, javax.xml.namespace.QName qnameTwo) + { + boolean result = false; + + if (qnameOne.getNamespaceURI().equals(qnameTwo.getNamespaceURI())) + { + if (qnameOne.getLocalPart().equals(qnameTwo.getLocalPart())) + { + return true; + } + } + + return result; + } + + /** + * filter out empty textNodes here + * + * @param xml + */ + private static void nextToken(XmlCursor xml) + { + do + { + xml.toNextToken(); + + if (!xml.isText()) + { + // Not a text node + break; + } + else if (xml.getChars().trim().length() > 0) + { + // Text node is not empty + break; + } + } + while (true); + } +} diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java new file mode 100644 index 000000000000..9bc7f597f242 --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java @@ -0,0 +1,309 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. 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 Public License (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 NPL, indicate your decision by + * deleting the provisions above and replace 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 NPL or the GPL. + */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; + +/** + * Class Namespace + * + */ +class Namespace extends IdScriptable +{ + private static final Object NAMESPACE_TAG = new Object(); + + private XMLLibImpl lib; + private String prefix; + private String uri; + + public Namespace(XMLLibImpl lib, String uri) + { + if (uri == null) + throw new IllegalArgumentException(); + + this.lib = lib; + this.prefix = (uri.length() == 0) ? "" : null; + this.uri = uri; + } + + + public Namespace(XMLLibImpl lib, String prefix, String uri) + { + if (uri == null) + throw new IllegalArgumentException(); + if (uri.length() == 0) { + // prefix should be "" for empty uri + if (prefix == null) + throw new IllegalArgumentException(); + if (prefix.length() != 0) + throw new IllegalArgumentException(); + } + + this.lib = lib; + this.prefix = prefix; + this.uri = uri; + } + + public void exportAsJSClass(boolean sealed) + { + exportAsJSClass(MAX_PROTOTYPE_ID, lib.globalScope(), sealed); + } + + /** + * + * @return + */ + public String uri() + { + return uri; + } + + /** + * + * @return + */ + public String prefix() + { + return prefix; + } + + /** + * + * @return + */ + public String toString () + { + return uri(); + } + + /** + * + * @return + */ + public String toLocaleString () + { + return toString(); + } + + public boolean equals(Object obj) + { + if (!(obj instanceof Namespace)) return false; + return equivalentValues(obj).booleanValue(); + } + + public Boolean equivalentValues(Object value) + { + if (!(value instanceof Namespace)) return null; + Namespace n = (Namespace)value; + return uri().equals(n.uri()) ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * + * @return + */ + public String getClassName () + { + return "Namespace"; + } + + /** + * + * @param hint + * @return + */ + public Object getDefaultValue (Class hint) + { + return uri(); + } + + protected Scriptable defaultPrototype() + { + Scriptable result = lib.namespacePrototype; + if (result == this) { + result = null; + } + return result; + } + + protected Scriptable defaultParentScope() + { + return lib.globalScope(); + } + +// #string_id_map# + private static final int + Id_prefix = 1, + Id_uri = 2, + MAX_INSTANCE_ID = 2; + +// maxId for superclass + private static int idBase = -1; + + { + if (idBase < 0) idBase = getMaxInstanceId(); + setMaxInstanceId(idBase, idBase + MAX_INSTANCE_ID); + } + + protected int findInstanceIdInfo(String s) + { + int id; +// #generated# Last update: 2004-07-20 19:50:50 CEST + L0: { id = 0; String X = null; + int s_length = s.length(); + if (s_length==3) { X="uri";id=Id_uri; } + else if (s_length==6) { X="prefix";id=Id_prefix; } + if (X!=null && X!=s && !X.equals(s)) id = 0; + } +// #/generated# + + if (id == 0) return super.findInstanceIdInfo(s); + + int attr; + switch (id) { + case Id_prefix: + case Id_uri: + attr = PERMANENT | READONLY; + break; + default: throw new IllegalStateException(); + } + return instanceIdInfo(attr, idBase + id); + } +// #/string_id_map# + + protected String getInstanceIdName(int id) + { + switch (id - idBase) { + case Id_prefix: return "prefix"; + case Id_uri: return "uri"; + } + return super.getInstanceIdName(id); + } + + protected Object getInstanceIdValue(int id) + { + switch (id - idBase) { + case Id_prefix: + if (prefix == null) return Undefined.instance; + return prefix; + case Id_uri: + return uri; + } + return super.getInstanceIdValue(id); + } + + +// #string_id_map# + private static final int + Id_constructor = 1, + Id_toString = 2, + MAX_PROTOTYPE_ID = 2; + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2004-07-20 19:45:27 CEST + L0: { id = 0; String X = null; + int s_length = s.length(); + if (s_length==8) { X="toString";id=Id_toString; } + else if (s_length==11) { X="constructor";id=Id_constructor; } + if (X!=null && X!=s && !X.equals(s)) id = 0; + } +// #/generated# + return id; + } +// #/string_id_map# + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_constructor: arity=2; s="constructor"; break; + case Id_toString: arity=0; s="toString"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(NAMESPACE_TAG, id, s, arity); + } + + public Object execMethod(IdFunction f, + Context cx, + Scriptable scope, + Scriptable thisObj, + Object[] args) + throws JavaScriptException + { + if (!f.hasTag(NAMESPACE_TAG)) { + return super.execMethod(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + if (id == Id_constructor) { + return jsConstructor(cx, (thisObj == null), args); + } else if(id == Id_toString) { + return realThis(thisObj, f).jsFunction_toString(); + } + throw new IllegalArgumentException(String.valueOf(id)); + } + + private Namespace realThis(Scriptable thisObj, IdFunction f) + { + if(!(thisObj instanceof Namespace)) + throw incompatibleCallError(f); + return (Namespace)thisObj; + } + + private Object jsConstructor(Context cx, boolean inNewExpr, Object[] args) + { + if (!inNewExpr && args.length == 1) { + return lib.castToNamespace(cx, args[0]); + } + + if (args.length == 0) { + return lib.constructNamespace(cx); + } else if (args.length == 1) { + return lib.constructNamespace(cx, args[0]); + } else { + return lib.constructNamespace(cx, args[0], args[1]); + } + } + + private String jsFunction_toString() + { + return toString(); + } +} diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/NamespaceHelper.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/NamespaceHelper.java new file mode 100644 index 000000000000..474cf147ee55 --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/NamespaceHelper.java @@ -0,0 +1,348 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (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 NPL, indicate your decision by + * deleting the provisions above and replace 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 NPL or the GPL. + */ + +package org.mozilla.javascript.xmlimpl; + +import java.util.*; +import org.apache.xmlbeans.XmlCursor; + +import org.mozilla.javascript.*; +import org.mozilla.javascript.xml.*; + +class NamespaceHelper +{ + private XMLLibImpl lib; + private final Map prefixToURI = new HashMap(); + private final Map uriToPrefix = new HashMap(); + // A set of URIs that are used without explicit namespace declaration in scope. + private final Set undeclared = new HashSet(); + + private NamespaceHelper(XMLLibImpl lib) + { + this.lib = lib; + // Insert the default namespace + prefixToURI.put("", ""); + Set prefixes = new HashSet(); + prefixes.add(""); + uriToPrefix.put("", prefixes); + } + + /** + * Declared a new namespace + * + * @param prefix + * @param uri + * @param declarations + */ + private void declareNamespace(String prefix, String uri, ObjArray declarations) + { + Set prefixes = (Set)uriToPrefix.get(uri); + if(prefixes == null) + { + prefixes = new HashSet(); + uriToPrefix.put(uri, prefixes); + } + + if(!prefixes.contains(prefix)) + { + String oldURI = (String)prefixToURI.get(prefix); + + // Add the new mapping + prefixes.add(prefix); + prefixToURI.put(prefix, uri); + if(declarations != null) + declarations.add(new Namespace(lib, prefix, uri)); + + if(oldURI != null) + { + // Update the existing mapping + prefixes = (Set)uriToPrefix.get(oldURI); + prefixes.remove(prefix); + } + } + } + + /** + * Updates the internal state of this NamespaceHelper to reflect the + * existance of the XML token pointed to by the cursor. + */ + private void processName(XmlCursor cursor, ObjArray declarations) + { + javax.xml.namespace.QName qname = cursor.getName(); + String uri = qname.getNamespaceURI(); + Set prefixes = (Set)uriToPrefix.get(uri); + if(prefixes == null || prefixes.size() == 0) + { + undeclared.add(uri); + if(declarations != null) + declarations.add(new Namespace(lib, uri)); + } + } + + /** + * Updates the internal state of this NamespaceHelper with the + * namespace information of the element pointed to by the cursor. + */ + private void update(XmlCursor cursor, ObjArray declarations) + { + // Process the Namespace declarations + cursor.push(); + while(cursor.toNextToken().isAnyAttr()) + { + if(cursor.isNamespace()) + { + javax.xml.namespace.QName name = cursor.getName(); + String prefix = name.getLocalPart(); + String uri = name.getNamespaceURI(); + + declareNamespace(prefix, uri, declarations); + } + } + cursor.pop(); + + // Process the element + processName(cursor, declarations); + + // Process the attributes + cursor.push(); + boolean hasNext = cursor.toFirstAttribute(); + while(hasNext) + { + processName(cursor, declarations); + hasNext = cursor.toNextAttribute(); + } + cursor.pop(); + } + + /** + * @return Object[] array of Namespace objects in scope at the cursor. + */ + public static Object[] inScopeNamespaces(XMLLibImpl lib, XmlCursor cursor) + { + ObjArray namespaces = new ObjArray(); + NamespaceHelper helper = new NamespaceHelper(lib); + + cursor.push(); + + int depth = 0; + while(cursor.hasPrevToken()) + { + if(cursor.isContainer()) + { + cursor.push(); + depth++; + } + + cursor.toParent(); + } + + for(int i = 0; i < depth; i++) + { + cursor.pop(); + helper.update(cursor, null); + } + + Iterator i = helper.prefixToURI.entrySet().iterator(); + while(i.hasNext()) + { + Map.Entry entry = (Map.Entry)i.next(); + Namespace ns = new Namespace(lib, (String)entry.getKey(), + (String)entry.getValue()); + namespaces.add(ns); + } + + i = helper.undeclared.iterator(); + while(i.hasNext()) + { + Namespace ns = new Namespace(lib, (String)i.next()); + namespaces.add(ns); + } + + cursor.pop(); + + return namespaces.toArray(); + } + + static Namespace getNamespace(XMLLibImpl lib, XmlCursor cursor, + Object[] inScopeNamespaces) + { + String uri; + String prefix; + + if (cursor.isProcinst()) { + uri = ""; + prefix = ""; + } else { + javax.xml.namespace.QName qname = cursor.getName(); + uri = qname.getNamespaceURI(); + prefix = qname.getPrefix(); + } + + if (inScopeNamespaces == null) + return new Namespace(lib, prefix, uri); + + Namespace result = null; + for (int i = 0; i != inScopeNamespaces.length; ++i) { + Namespace ns = (Namespace)inScopeNamespaces[i]; + if(ns == null) continue; + + String nsURI = ns.uri(); + if(nsURI.equals(uri)) + { + if(prefix.equals(ns.prefix())) + { + result = ns; + break; + } + + if(result == null || + (result.prefix() == null && + ns.prefix() != null)) + result = ns; + } + } + + if(result == null) + result = new Namespace(lib, prefix, uri); + + return result; + } + + /** + * @return List of Namespace objects that are declared in the container pointed to by the cursor. + */ + public static Object[] namespaceDeclarations(XMLLibImpl lib, XmlCursor cursor) + { + ObjArray declarations = new ObjArray(); + NamespaceHelper helper = new NamespaceHelper(lib); + + cursor.push(); + + int depth = 0; + while(cursor.hasPrevToken()) + { + if(cursor.isContainer()) + { + cursor.push(); + depth++; + } + + cursor.toParent(); + } + + for(int i = 0; i < depth - 1; i++) + { + cursor.pop(); + helper.update(cursor, null); + } + + if(depth > 0) + { + cursor.pop(); + helper.update(cursor, declarations); + } + + cursor.pop(); + + return declarations.toArray(); + } + + /** + * @return Prefix to URI map of all namespaces in scope at the cursor. + */ + public static Map getAllNamespaces(XMLLibImpl lib, XmlCursor cursor) + { + NamespaceHelper helper = new NamespaceHelper(lib); + + cursor.push(); + + int depth = 0; + while(cursor.hasPrevToken()) + { + if(cursor.isContainer()) + { + cursor.push(); + depth++; + } + + cursor.toParent(); + } + + for(int i = 0; i < depth; i++) + { + cursor.pop(); + helper.update(cursor, null); + } + + cursor.pop(); + + return helper.prefixToURI; + } + + public static void getNamespaces(XmlCursor cursor, Map prefixToURI) + { + cursor.push(); + while(cursor.toNextToken().isAnyAttr()) + { + if(cursor.isNamespace()) + { + javax.xml.namespace.QName name = cursor.getName(); + String prefix = name.getLocalPart(); + String uri = name.getNamespaceURI(); + + prefixToURI.put(prefix, uri); + } + } + cursor.pop(); + } + + public static void removeNamespace(XmlCursor cursor, String prefix) + { + cursor.push(); + while(cursor.toNextToken().isAnyAttr()) + { + if(cursor.isNamespace()) + { + javax.xml.namespace.QName name = cursor.getName(); + if(name.getLocalPart().equals(prefix)) + { + cursor.removeXml(); + break; + } + } + } + cursor.pop(); + } +} \ No newline at end of file diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java new file mode 100644 index 000000000000..a1d89a577194 --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java @@ -0,0 +1,306 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. 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 Public License (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 NPL, indicate your decision by + * deleting the provisions above and replace 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 NPL or the GPL. + */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; + +/** + * Class QName + * + */ +public final class QName extends IdScriptable +{ + private static final Object QNAME_TAG = new Object(); + + XMLLibImpl lib; + private String prefix; + private String localName; + private String uri; + + static void init(XMLLibImpl lib, boolean sealed) + { + QName obj = new QName(lib, "", "", ""); + lib.qnamePrototype = obj; + obj.exportAsJSClass(MAX_PROTOTYPE_ID, lib.globalScope(), sealed); + } + + public QName(XMLLibImpl lib, String uri, String localName, String prefix) + { + if (localName == null) throw new IllegalArgumentException(); + this.lib = lib; + this.uri = uri; + this.prefix = prefix; + this.localName = localName; + } + + public void exportAsJSClass(boolean sealed) + { + exportAsJSClass(MAX_PROTOTYPE_ID, lib.globalScope(), sealed); + } + + /** + * + * @return + */ + public String toString() + { + String result; + + if (uri == null) + { + result = "*::".concat(localName); + } + else if(uri.length() == 0) + { + result = localName; + } + else + { + result = uri + "::" + localName; + } + + return result; + } + + public String localName() + { + return localName; + } + + public String prefix() + { + return (prefix == null) ? prefix : ""; + } + + public String uri() + { + return uri; + } + + public boolean equals(Object obj) + { + if(!(obj instanceof QName)) return false; + return equivalentValues((QName)obj).booleanValue(); + } + + public Boolean equivalentValues(Object value) + { + if(!(value instanceof QName)) return null; + + boolean result; + QName q = (QName)value; + + if (uri == null) { + result = q.uri == null && localName.equals(q.localName); + } else { + result = uri.equals(q.uri) && localName.equals(q.localName); + } + + return result ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * + * @return + */ + public String getClassName () + { + return "QName"; + } + + /** + * + * @param hint + * @return + */ + public Object getDefaultValue (Class hint) + { + return toString(); + } + + protected Scriptable defaultPrototype() + { + Scriptable result = lib.qnamePrototype; + if (result == this) { + result = null; + } + return result; + } + + protected Scriptable defaultParentScope() + { + return lib.globalScope(); + } + +// #string_id_map# + private static final int + Id_localName = 1, + Id_uri = 2, + MAX_INSTANCE_ID = 2; + +// maxId for superclass + private static int idBase = -1; + + { + if (idBase < 0) idBase = getMaxInstanceId(); + setMaxInstanceId(idBase, idBase + MAX_INSTANCE_ID); + } + + protected int findInstanceIdInfo(String s) + { + int id; +// #generated# Last update: 2004-07-18 12:32:51 CEST + L0: { id = 0; String X = null; + int s_length = s.length(); + if (s_length==3) { X="uri";id=Id_uri; } + else if (s_length==9) { X="localName";id=Id_localName; } + if (X!=null && X!=s && !X.equals(s)) id = 0; + } +// #/generated# + + if (id == 0) return super.findInstanceIdInfo(s); + + int attr; + switch (id) { + case Id_localName: + case Id_uri: + attr = PERMANENT | READONLY; + break; + default: throw new IllegalStateException(); + } + return instanceIdInfo(attr, idBase + id); + } +// #/string_id_map# + + protected String getInstanceIdName(int id) + { + switch (id - idBase) { + case Id_localName: return "localName"; + case Id_uri: return "uri"; + } + return super.getInstanceIdName(id); + } + + protected Object getInstanceIdValue(int id) + { + switch (id - idBase) { + case Id_localName: return localName; + case Id_uri: return uri; + } + return super.getInstanceIdValue(id); + } + +// #string_id_map# + private static final int + Id_constructor = 1, + Id_toString = 2, + MAX_PROTOTYPE_ID = 2; + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2004-07-18 12:32:51 CEST + L0: { id = 0; String X = null; + int s_length = s.length(); + if (s_length==8) { X="toString";id=Id_toString; } + else if (s_length==11) { X="constructor";id=Id_constructor; } + if (X!=null && X!=s && !X.equals(s)) id = 0; + } +// #/generated# + return id; + } +// #/string_id_map# + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_constructor: arity=2; s="constructor"; break; + case Id_toString: arity=0; s="toString"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(QNAME_TAG, id, s, arity); + } + + public Object execMethod(IdFunction f, + Context cx, + Scriptable scope, + Scriptable thisObj, + Object[] args) + throws JavaScriptException + { + if (!f.hasTag(QNAME_TAG)) { + return super.execMethod(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + if (id == Id_constructor) { + return jsConstructor(cx, (thisObj == null), args); + } else if(id == Id_toString) { + return realThis(thisObj, f).jsFunction_toString(); + } + throw new IllegalArgumentException(String.valueOf(id)); + } + + private QName realThis(Scriptable thisObj, IdFunction f) + { + if(!(thisObj instanceof QName)) + throw incompatibleCallError(f); + return (QName)thisObj; + } + + private Object jsConstructor(Context cx, boolean inNewExpr, Object[] args) + { + if (!inNewExpr && args.length == 1) { + return lib.castToQName(cx, args[0]); + } + if (args.length == 0) { + return lib.constructQName(cx, Undefined.instance); + } else if (args.length == 1) { + return lib.constructQName(cx, args[0]); + } else { + return lib.constructQName(cx, args[0], args[1]); + } + } + + private String jsFunction_toString() + { + return toString(); + } + +} diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java new file mode 100644 index 000000000000..45b67920a45b --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java @@ -0,0 +1,3085 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. 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 Public License (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 NPL, indicate your decision by + * deleting the provisions above and replace 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 NPL or the GPL. + */ + +package org.mozilla.javascript.xmlimpl; + +import java.lang.reflect.Member; +import java.util.*; + +import org.mozilla.javascript.*; +import org.mozilla.javascript.xml.*; + +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; + +public class XML extends XMLObjectImpl +{ + final static class XScriptAnnotation extends XmlBookmark + { + javax.xml.namespace.QName _name; + XML _xScriptXML; + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Constructurs + // + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public XScriptAnnotation (XML xml, XmlCursor curs) + { + init(curs); + _xScriptXML = xml; + } + + + public XScriptAnnotation (XmlCursor curs) + { + init(curs); + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Utility functions + // + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public static XML getXML (XMLLibImpl lib, XScriptAnnotation anno) + { + if (anno._xScriptXML == null) + { + anno._xScriptXML = new XML(lib, anno); + } + + return anno._xScriptXML; + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Private functions + // + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + private void init (XmlCursor curs) + { + _name = curs.getName(); + } + }; + + /** + * + */ + final static class NamespaceDeclarations + { + private int _prefixIdx; + private StringBuffer _namespaceDecls; + private String _defaultNSURI; + + + public 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"); + } + + + public String getNextPrefix (String ns) + { + String prefix = "NS" + _prefixIdx++; + + _namespaceDecls.append("declare namespace " + prefix + " = " + "\"" + ns + "\"" + "\n"); + + return prefix; + } + + + public boolean isAnyDefaultNamespace () + { + return _defaultNSURI != null ?_defaultNSURI.length() > 0 : false; + } + + + public String getDeclarations() + { + return _namespaceDecls.toString(); + } + } + + // Fields + //static final XML prototype = new XML(); + private XScriptAnnotation _anno; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Constructors + // + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * + */ + XML(XMLLibImpl lib) + { + super(lib); + XmlObject xo = XmlObject.Factory.newInstance(); + + XmlCursor curs = xo.newCursor(); + try + { + _anno = new XScriptAnnotation(curs); + curs.setBookmark(_anno); + } + finally + { + curs.dispose(); + } + } + + /** + * + * @param anno + */ + private XML(XMLLibImpl lib, XScriptAnnotation anno) + { + super(lib); + _anno = anno; + _anno._xScriptXML = this; + } + + /** + * + * @param inputObject + */ + public XML(XMLLibImpl lib, Object inputObject) + { + super(lib); + XmlObject xo; + boolean isText = false; + String frag; + + if (inputObject == null || inputObject instanceof Undefined) + { + frag = ""; + } + else if (inputObject instanceof XMLObjectImpl) + { + // todo: faster way for XMLObjects? + frag = ((XMLObjectImpl) inputObject).toXMLString(); + } + else + { + 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(); + } + + try + { + _anno = new XScriptAnnotation(curs); + curs.setBookmark(_anno); + } + finally + { + curs.dispose(); + } + } + + /** + * Special constructor for making an attribute + * + */ + private XML(XMLLibImpl lib, XmlCursor cursor) + { + super(lib); + if(cursor == null || !cursor.isAttr()) + { + // Make default empty XML + XmlObject xo = XmlObject.Factory.newInstance(); + + XmlCursor curs = xo.newCursor(); + try + { + _anno = new XScriptAnnotation(this, curs); + curs.setBookmark(_anno); + } + finally + { + curs.dispose(); + } + } + else + { + _anno = new XScriptAnnotation(this, cursor); + cursor.setBookmark(_anno); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Public factories for creating a XScript XML object given an XBean cursor. + // + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + public static XML createXML (XMLLibImpl lib, XmlCursor curs) + { + XML xml = new XML(lib); + + if (curs == null) + { + curs = xml.newCursor(); + + xml._anno = findAnnotation(curs); + + curs.dispose(); + } + else + { + if (curs.currentTokenType().isStartdoc()) + { + curs.toFirstContentToken(); + } + + xml._anno = findAnnotation(curs); + } + + return xml; + } + + /** + * + * @param qname + * @param value + * @return + */ + public static XML createTextElement(XMLLibImpl lib, javax.xml.namespace.QName qname, String value) + { + XmlObject xo = XmlObject.Factory.newInstance(); + XmlCursor cursor = xo.newCursor(); + cursor.toNextToken(); + + cursor.beginElement(qname.getLocalPart(), qname.getNamespaceURI()); + //if(namespace.length() > 0) + // cursor.insertNamespace("", namespace); + cursor.insertChars(value); + + cursor.toStartDoc(); + cursor.toNextToken(); + XScriptAnnotation anno = new XScriptAnnotation(cursor); + cursor.setBookmark(anno); + cursor.dispose(); + + XML result = new XML(lib, anno); + return result; + } + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // 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(); + + 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 = (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.toString()); + + 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 = new XML(lib, attrCurs); + } + + return result; + } + + // + // + // methods overriding ScriptableObject + // + // + + public String getClassName () + { + return "XML"; + } + + protected Scriptable defaultPrototype() + { + Scriptable result = lib.xmlPrototype; + if (result == this) { + result = null; + } + return result; + } + + // + // + // methods overriding IdScriptable + // + // + + /** + * 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 + */ + public 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 + */ + public 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 + */ + public 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.isDescendants()) + { + throw ScriptRuntime.typeError("Assignment to descendants (\"..\") not supported"); + } + 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 = (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 = (XMLList)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, (XML)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 + */ + public 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 = (XMLList)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 + */ + public 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 + */ + public 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 + */ + public XMLList attribute(XMLName xmlName) + { + return matchAttributes(xmlName); + } + + /** + * + * @return + */ + public XMLList attributes() + { + XMLName xmlName = XMLName.formStar(); + return matchAttributes(xmlName); + } + + public XMLList child(long index) + { + XMLList result = new XMLList(lib); + result.setTargets(this, null); + result.addToList(getXmlChild(index)); + return result; + } + + public 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 + */ + public 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 + */ + public XMLList children() + { + return allChildNodes(null); + } + + /** + * + * @return + */ + public XMLList comments() + { + return matchChildren(XmlCursor.TokenType.COMMENT); + } + + /** + * + * @param xml + * @return + */ + public boolean contains(Object xml) + { + boolean result = false; + + if (xml instanceof XML) + { + result = equivalentXml(xml); + } + + return result; + } + + /** + * + * @return + */ + public Object copy() + { + XmlCursor srcCurs = newCursor(); + + if (srcCurs.isStartdoc()) + { + srcCurs.toFirstContentToken(); + } + + XML xml = XML.createXML(lib, null); + + XmlCursor destCurs = xml.newCursor(); + destCurs.toFirstContentToken(); + + srcCurs.copyXml(destCurs); + + destCurs.dispose(); + srcCurs.dispose(); + + return xml; + } + + /** + * + * @param name + * @return + */ + public 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. + */ + public Object[] inScopeNamespaces() + { + XmlCursor cursor = newCursor(); + Object[] namespaces = NamespaceHelper.inScopeNamespaces(lib, cursor); + cursor.dispose(); + return namespaces; + } + + /** + * + * @param child + * @param xml + */ + public 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 + */ + public 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 + */ + public 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 + */ + public boolean hasComplexContent() + { + return !hasSimpleContent(); + } + + /** + * + * @return + */ + public boolean hasSimpleContent() + { + boolean simpleContent = false; + + XmlCursor curs = newCursor(); + + 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 + */ + public int length() + { + return 1; + } + + /** + * + * @return + */ + public 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. + */ + public 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 + */ + public 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 + */ + public Object[] namespaceDeclarations() + { + XmlCursor cursor = newCursor(); + Object[] namespaces = NamespaceHelper.namespaceDeclarations(lib, cursor); + cursor.dispose(); + return namespaces; + } + + /** + * + * @return + */ + public 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; + } + + /** + * + */ + public 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 + */ + public 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 = XScriptAnnotation.getXML(lib, findAnnotation(curs)); + } + } + else + { + // No parent + parent = Undefined.instance; + } + } + + curs.dispose(); + + return parent; + } + + /** + * + * @param xml + * @return + */ + public 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 + */ + public Object processingInstructions(XMLName xmlName) + { + return matchChildren(XmlCursor.TokenType.PROCINST, xmlName); + } + + /** + * + * @param name + * @return + */ + public boolean propertyIsEnumerable(XMLName xmlName) + { + return (getPropertyList(xmlName).length() > 0); + } + + /** + * + * @param namespace + */ + public 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; + } + + public XML replace(long index, Object xml) + { + XMLList xlChildToReplace = (XMLList)child(index); + if (xlChildToReplace.length() > 0) + { + // One exists an that index + XML childToReplace = (XML)xlChildToReplace.item(0); + insertChildAfter(childToReplace, xml); + removeChild(index); + } + return this; + } + + /** + * + * @param propertyName + * @param xml + * @return + */ + public XML replace(XMLName xmlName, Object xml) + { + putXMLProperty(xmlName, xml); + return this; + } + + /** + * + * @param xml + */ + public XML setChildren(Object xml) + { + // remove all children + XMLName xmlName = XMLName.formStar(); + XMLList matches = (XMLList)getPropertyList(xmlName); + matches.remove(); + + // append new children + appendChild(xml); + + return this; + } + + /** + * + * @param name + */ + public 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 + */ + public 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 + */ + public 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 + */ + public 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(); + } + + return result; + } + + /** + * + * @return + */ + public String toXMLString () + { + 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 + */ + public Object valueOf() + { + return this; + } + + // + // Other public Functions from XMLObject + // + + /** + * + * @param target + * @return + */ + public 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().equals(otherXml.toXMLString()); +// 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 new XML(lib); + } else { + Object arg0 = args[0]; + if (!inNewExpr && arg0 instanceof XML) { + // XML(XML) returns the same object. + return arg0; + } + return new XML(lib, arg0); + } + } + +} diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java new file mode 100644 index 000000000000..740fa38b0089 --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java @@ -0,0 +1,270 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (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 NPL, indicate your decision by + * deleting the provisions above and replace 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 NPL or the GPL. + */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; + +class XMLCtor extends IdFunction +{ + private static final Object XMLCTOR_TAG = new Object(); + + private XMLLibImpl lib; + + XMLCtor(XML xml, Object tag, int id, int arity) + { + super(xml, tag, id, arity); + this.lib = xml.lib; + activatePrototypeMap(MAX_FUNCTION_ID); + } + + private void writeSetting(Scriptable target) + { + for (int i = 1; i <= MAX_INSTANCE_ID; ++i) { + int id = idBase + i; + String name = getInstanceIdName(id); + Object value = getInstanceIdValue(id); + ScriptableObject.putProperty(target, name, value); + } + } + + private void readSettings(Scriptable source) + { + for (int i = 1; i <= MAX_INSTANCE_ID; ++i) { + int id = idBase + i; + String name = getInstanceIdName(id); + Object value = ScriptableObject.getProperty(source, name); + if (value == ScriptableObject.NOT_FOUND) { + continue; + } + switch (i) { + case Id_ignoreComments: + case Id_ignoreProcessingInstructions: + case Id_ignoreWhitespace: + case Id_prettyPrinting: + if (!(value instanceof Boolean)) { + continue; + } + break; + case Id_prettyIndent: + if (!(value instanceof Number)) { + continue; + } + break; + default: + throw new IllegalStateException(); + } + setInstanceIdValue(id, value); + } + } + +// #string_id_map# + + private static final int + Id_ignoreComments = 1, + Id_ignoreProcessingInstructions = 2, + Id_ignoreWhitespace = 3, + Id_prettyIndent = 4, + Id_prettyPrinting = 5, + + MAX_INSTANCE_ID = 5; + +// maxId for superclass + private static int idBase = -1; + + { + if (idBase < 0) { + idBase = getMaxInstanceId(); + } + setMaxInstanceId(idBase, idBase + MAX_INSTANCE_ID); + } + + protected int findInstanceIdInfo(String s) { + int id; +// #generated# Last update: 2004-07-19 13:03:52 CEST + L0: { id = 0; String X = null; int c; + L: switch (s.length()) { + case 12: X="prettyIndent";id=Id_prettyIndent; break L; + case 14: c=s.charAt(0); + if (c=='i') { X="ignoreComments";id=Id_ignoreComments; } + else if (c=='p') { X="prettyPrinting";id=Id_prettyPrinting; } + break L; + case 16: X="ignoreWhitespace";id=Id_ignoreWhitespace; break L; + case 28: X="ignoreProcessingInstructions";id=Id_ignoreProcessingInstructions; break L; + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + } +// #/generated# + + if (id == 0) return super.findInstanceIdInfo(s); + + int attr; + switch (id) { + case Id_ignoreComments: + case Id_ignoreProcessingInstructions: + case Id_ignoreWhitespace: + case Id_prettyIndent: + case Id_prettyPrinting: + attr = PERMANENT | DONTENUM; + break; + default: throw new IllegalStateException(); + } + return instanceIdInfo(attr, idBase + id); + } + +// #/string_id_map# + + protected String getInstanceIdName(int id) + { + switch (id - idBase) { + case Id_ignoreComments: return "ignoreComments"; + case Id_ignoreProcessingInstructions: return "ignoreProcessingInstructions"; + case Id_ignoreWhitespace: return "ignoreWhitespace"; + case Id_prettyIndent: return "prettyIndent"; + case Id_prettyPrinting: return "prettyPrinting"; + } + return super.getInstanceIdName(id); + } + + protected Object getInstanceIdValue(int id) + { + switch (id - idBase) { + case Id_ignoreComments: + return ScriptRuntime.wrapBoolean(lib.ignoreComments); + case Id_ignoreProcessingInstructions: + return ScriptRuntime.wrapBoolean(lib.ignoreProcessingInstructions); + case Id_ignoreWhitespace: + return ScriptRuntime.wrapBoolean(lib.ignoreWhitespace); + case Id_prettyIndent: + return ScriptRuntime.wrapInt(lib.prettyIndent); + case Id_prettyPrinting: + return ScriptRuntime.wrapBoolean(lib.prettyPrinting); + } + return super.getInstanceIdValue(id); + } + + protected void setInstanceIdValue(int id, Object value) + { + switch (id - idBase) { + case Id_ignoreComments: + lib.ignoreComments = ScriptRuntime.toBoolean(value); + return; + case Id_ignoreProcessingInstructions: + lib.ignoreProcessingInstructions = ScriptRuntime.toBoolean(value); + return; + case Id_ignoreWhitespace: + lib.ignoreWhitespace = ScriptRuntime.toBoolean(value); + return; + case Id_prettyIndent: + lib.prettyIndent = ScriptRuntime.toInt32(value); + return; + case Id_prettyPrinting: + lib.prettyPrinting = ScriptRuntime.toBoolean(value); + return; + } + super.setInstanceIdValue(id, value); + } + +// #string_id_map# + private static final int + Id_defaultSettings = 1, + Id_settings = 2, + Id_setSettings = 3, + MAX_FUNCTION_ID = 3; + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2004-07-19 13:03:52 CEST + L0: { id = 0; String X = null; + int s_length = s.length(); + if (s_length==8) { X="settings";id=Id_settings; } + else if (s_length==11) { X="setSettings";id=Id_setSettings; } + else if (s_length==15) { X="defaultSettings";id=Id_defaultSettings; } + if (X!=null && X!=s && !X.equals(s)) id = 0; + } +// #/generated# + return id; + } +// #/string_id_map# + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_defaultSettings: arity=0; s="defaultSettings"; break; + case Id_settings: arity=0; s="settings"; break; + case Id_setSettings: arity=1; s="setSettings"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(XMLCTOR_TAG, id, s, arity); + } + + public Object execMethod(IdFunction f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(XMLCTOR_TAG)) { + return super.execMethod(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + switch (id) { + case Id_defaultSettings: { + lib.defaultSettings(); + Scriptable obj = cx.newObject(scope); + writeSetting(obj); + return obj; + } + case Id_settings: { + Scriptable obj = cx.newObject(scope); + writeSetting(obj); + return obj; + } + case Id_setSettings: { + Scriptable obj = null; + if (args.length == 0 + || args[0] == null + || args[0] == Undefined.instance) + { + lib.defaultSettings(); + } else if (args[0] instanceof Scriptable) { + readSettings((Scriptable)args[0]); + } + return Undefined.instance; + } + } + throw new IllegalArgumentException(String.valueOf(id)); + } +} diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java new file mode 100644 index 000000000000..74b8e8e7ed20 --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java @@ -0,0 +1,770 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (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 NPL, indicate your decision by + * deleting the provisions above and replace 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 NPL or the GPL. + */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; +import org.mozilla.javascript.xml.*; + +import org.apache.xmlbeans.XmlCursor; +import org.apache.xmlbeans.XmlObject; + +public final class XMLLibImpl extends XMLLib +{ + private Scriptable globalScope; + + XML xmlPrototype; + XMLList xmlListPrototype; + Namespace namespacePrototype; + QName qnamePrototype; + + + // Environment settings... + boolean ignoreComments; + boolean ignoreProcessingInstructions; + boolean ignoreWhitespace; + boolean prettyPrinting; + int prettyIndent; + + Scriptable globalScope() + { + return globalScope; + } + + private XMLLibImpl(Scriptable globalScope) + { + this.globalScope = globalScope; + defaultSettings(); + } + + public static void init(Context cx, Scriptable scope, boolean sealed) + { + XMLLibImpl lib = new XMLLibImpl(scope); + lib.exportToScope(sealed); + } + + private void exportToScope(boolean sealed) + { + xmlPrototype = new XML(this); + xmlListPrototype = new XMLList(this); + namespacePrototype = new Namespace(this, "", ""); + qnamePrototype = new QName(this, "", "", ""); + + xmlPrototype.exportAsJSClass(sealed); + xmlListPrototype.exportAsJSClass(sealed); + namespacePrototype.exportAsJSClass(sealed); + qnamePrototype.exportAsJSClass(sealed); + } + + void defaultSettings() + { + ignoreComments = true; + ignoreProcessingInstructions = true; + ignoreWhitespace = true; + prettyPrinting = true; + prettyIndent = 2; + } + + boolean ignoreComments() + { + return ignoreComments; + } + + boolean ignoreProcessingInstructions() + { + return ignoreProcessingInstructions; + } + + boolean ignoreWhitespace() + { + return ignoreWhitespace; + } + + boolean prettyPrinting() + { + return prettyPrinting; + } + + int prettyIndent() + { + return prettyIndent; + } + + private XMLName resolveName(Context cx, Object id) + { + if(id instanceof XMLName) return (XMLName)id; + if (id instanceof QName) { + QName qname = (QName)id; + return XMLName.formProperty(qname.uri(), qname.localName()); + } + + String name = ScriptRuntime.toString(id); + boolean isAttributeName = false; + if (name.length() != 0 && name.charAt(0) == '@') { + name = name.substring(1); + isAttributeName = true; + } + + String uri = null; + if(!name.equals("*")) { + if (isAttributeName) { + uri = ""; + } else { + uri = ""; + if (cx == null) { + cx = Context.getCurrentContext(); + } + if (cx != null) { + Object defaultNS = ScriptRuntime.searchDefaultNamespace(cx); + if (defaultNS != null) { + if (defaultNS instanceof Namespace) { + uri = ((Namespace)defaultNS).uri(); + } else { + // Should not happen but for now it could + // due to bad searchDefaultNamespace implementation. + } + } + } + } + } + + XMLName xmlName = XMLName.formProperty(uri, name); + if (isAttributeName) { + xmlName.setAttributeName(); + } + return xmlName; + } + + private Namespace resolveNamespace(String prefix, Scriptable scope) + { + Namespace ns = null; + Scriptable nsScope = scope; + + while (nsScope != null) { + Object obj = ScriptableObject.getProperty(nsScope, prefix); + if (obj instanceof Namespace) { + ns = (Namespace)obj; + break; + } + nsScope = nsScope.getParentScope(); + } + + if (ns == null) { + // Only namespace type allowed to left of :: operator. + throw Context.reportRuntimeError( + ScriptRuntime.getMessage1("msg.namespace.expected", prefix)); + } + + return ns; + } + + XMLName toAttributeNameImpl(Context cx, Object nameValue) + { + String uri; + String localName; + + if (nameValue instanceof String) { + uri = ""; + localName = (String)nameValue; + } else if (nameValue instanceof XMLName) { + XMLName xmlName = (XMLName)nameValue; + if (!xmlName.isAttributeName()) { + xmlName.setAttributeName(); + } + return xmlName; + } else if (nameValue instanceof QName) { + QName qname = (QName)nameValue; + uri = qname.uri(); + localName = qname.localName(); + } else if (nameValue instanceof Scriptable) { + uri = ""; + localName = ScriptRuntime.toString(nameValue); + } else { + throw ScriptRuntime.typeError("Bad attribute name: "+nameValue); + } + XMLName xmlName = XMLName.formProperty(uri, localName); + xmlName.setAttributeName(); + return xmlName; + } + + XMLName toXMLName(Context cx, Object nameValue) + { + XMLName result; + + if (nameValue instanceof XMLName) { + result = (XMLName)nameValue; + } else if (nameValue instanceof QName) { + QName qname = (QName)nameValue; + result = XMLName.formProperty(qname.uri(), qname.localName()); + } else { + String name = ScriptRuntime.toString(nameValue); + result = toXMLNameFromString(cx, name); + } + + return result; + } + + /** + * If value represents Uint32 index, make it available through + * ScriptRuntime.lastUint32Result(cx) and return null. + * Otherwise return the same value as toXMLName(cx, value). + */ + XMLName toXMLNameOrIndex(Context cx, Object value) + { + XMLName result; + + if (value instanceof XMLName) { + result = (XMLName)value; + } else if (value instanceof String) { + String str = (String)value; + long test = ScriptRuntime.testUint32String(str); + if (test >= 0) { + ScriptRuntime.storeUint32Result(cx, test); + result = null; + } else { + result = toXMLNameFromString(cx, str); + } + } else if (value instanceof Number) { + double d = ((Number)value).doubleValue(); + long l = (long)d; + if (l == d && 0 <= l && l <= 0xFFFFFFFFL) { + ScriptRuntime.storeUint32Result(cx, l); + result = null; + } else { + String str = ScriptRuntime.toString(d); + result = toXMLNameFromString(cx, str); + } + } else if (value instanceof QName) { + QName qname = (QName)value; + String uri = qname.uri(); + boolean number = false; + result = null; + if (uri != null && uri.length() == 0) { + // Only in this case qname.toString() can resemble uint32 + long test = ScriptRuntime.testUint32String(uri); + if (test >= 0) { + ScriptRuntime.storeUint32Result(cx, test); + number = true; + } + } + if (!number) { + result = XMLName.formProperty(uri, qname.localName()); + } + } else { + String str = ScriptRuntime.toString(value); + long test = ScriptRuntime.testUint32String(str); + if (test >= 0) { + ScriptRuntime.storeUint32Result(cx, test); + result = null; + } else { + result = toXMLNameFromString(cx, str); + } + } + + return result; + } + + XMLName toXMLNameFromString(Context cx, String name) + { + if (name == null) + throw new IllegalArgumentException(); + + int l = name.length(); + if (l != 0) { + char firstChar = name.charAt(0); + if (firstChar == '*') { + if (l == 1) { + return XMLName.formStar(); + } + } else if (firstChar == '@') { + XMLName xmlName = XMLName.formProperty("", name.substring(1)); + xmlName.setAttributeName(); + return xmlName; + } + } + + String uri = getDefaultNamespaceURI(cx); + + return XMLName.formProperty(uri, name); + } + + Namespace constructNamespace(Context cx, Object uriValue) + { + String prefix; + String uri; + + if (uriValue instanceof Namespace) { + Namespace ns = (Namespace)uriValue; + prefix = ns.prefix(); + uri = ns.uri(); + } else if (uriValue instanceof QName) { + QName qname = (QName)uriValue; + uri = qname.uri(); + if (uri != null) { + prefix = qname.prefix(); + } else { + uri = qname.toString(); + prefix = null; + } + } else { + uri = ScriptRuntime.toString(uriValue); + prefix = (uri.length() == 0) ? "" : null; + } + + return new Namespace(this, prefix, uri); + } + + Namespace castToNamespace(Context cx, Object namescapeObj) + { + if (namescapeObj instanceof Namespace) { + return (Namespace)namescapeObj; + } + return constructNamespace(cx, namescapeObj); + } + + Namespace constructNamespace(Context cx) + { + return new Namespace(this, "", ""); + } + + public Namespace constructNamespace(Context cx, Object prefixValue, + Object uriValue) + { + String prefix; + String uri; + + if (uriValue instanceof QName) { + QName qname = (QName)uriValue; + uri = qname.uri(); + if (uri == null) { + uri = qname.toString(); + } + } else { + uri = ScriptRuntime.toString(uriValue); + } + + if (uri.length() == 0) { + if (prefixValue == Undefined.instance) { + prefix = ""; + } else { + prefix = ScriptRuntime.toString(prefixValue); + if (prefix.length() != 0) { + throw ScriptRuntime.constructError("TypeError", + "Illegal prefix '"+prefix+"' for 'no namespace'."); + } + } + } else if (prefixValue == Undefined.instance) { + prefix = ""; + } else if (!isXMLName(cx, prefixValue)) { + prefix = ""; + } else { + prefix = ScriptRuntime.toString(prefixValue); + } + + return new Namespace(this, prefix, uri); + } + + String getDefaultNamespaceURI(Context cx) + { + String uri = ""; + if (cx == null) { + cx = Context.getCurrentContext(); + } + if (cx != null) { + Object ns = ScriptRuntime.searchDefaultNamespace(cx); + if (ns != null) { + if (ns instanceof Namespace) { + uri = ((Namespace)ns).uri(); + } else { + // Should not happen but for now it could + // due to bad searchDefaultNamespace implementation. + } + } + } + return uri; + } + + Namespace getDefaultNamespace(Context cx) + { + if (cx == null) { + cx = Context.getCurrentContext(); + if (cx == null) { + return namespacePrototype; + } + } + + Namespace result; + Object ns = ScriptRuntime.searchDefaultNamespace(cx); + if (ns == null) { + result = namespacePrototype; + } else { + if (ns instanceof Namespace) { + result = (Namespace)ns; + } else { + // Should not happen but for now it could + // due to bad searchDefaultNamespace implementation. + result = namespacePrototype; + } + } + return result; + } + + QName castToQName(Context cx, Object qnameValue) + { + if (qnameValue instanceof QName) { + return (QName)qnameValue; + } + return constructQName(cx, qnameValue); + } + + QName constructQName(Context cx, Object nameValue) + { + QName result; + + if (nameValue instanceof QName) { + QName qname = (QName)nameValue; + result = new QName(this, qname.uri(), qname.localName(), + qname.prefix()); + } else { + String localName = ScriptRuntime.toString(nameValue); + result = constructQNameFromString(cx, localName); + } + + return result; + } + + /** + * Optimized version of constructQName for String type + */ + QName constructQNameFromString(Context cx, String localName) + { + if (localName == null) + throw new IllegalArgumentException(); + + String uri; + String prefix; + + if ("*".equals(localName)) { + uri = null; + prefix = null; + } else { + Namespace ns = getDefaultNamespace(cx); + uri = ns.uri(); + prefix = ns.prefix(); + } + + return new QName(this, uri, localName, prefix); + } + + QName constructQName(Context cx, Object namespaceValue, Object nameValue) + { + String uri; + String localName; + String prefix; + + if (nameValue instanceof QName) { + QName qname = (QName)nameValue; + localName = qname.localName(); + } else { + localName = ScriptRuntime.toString(nameValue); + } + + Namespace ns; + if (namespaceValue == Undefined.instance) { + if ("*".equals(localName)) { + ns = null; + } else { + ns = getDefaultNamespace(cx); + } + } else if (namespaceValue == null) { + ns = null; + } else if (namespaceValue instanceof Namespace) { + ns = (Namespace)namespaceValue; + } else { + ns = constructNamespace(cx, namespaceValue); + } + + if (ns == null) { + uri = null; + prefix = null; + } else { + uri = ns.uri(); + prefix = ns.prefix(); + } + + return new QName(this, uri, localName, prefix); + } + + // + // + // Overriding XMLLib methods + // + // + + /** + * TODO: Implement this method! + */ + public boolean isXMLName(Context cx, Object name) + { + // TODO: Check if qname.localName() matches NCName + + return true; + } + + public Object toQualifiedName(String namespace, + Object nameValue, + Scriptable scope) + { + String uri; + String localName; + + if (nameValue instanceof QName) { + QName qname = (QName)nameValue; + localName = qname.localName(); + } else { + localName = ScriptRuntime.toString(nameValue); + } + + if ("*".equals(namespace)) { + uri = null; + } else { + uri = resolveNamespace(namespace, scope).uri(); + } + + return XMLName.formProperty(uri, localName); + } + + public Object toAttributeName(Context cx, Object nameValue) + { + return toAttributeNameImpl(cx, nameValue); + } + + public Object toDescendantsName(Context cx, Object name) + { + XMLName xmlName = resolveName(cx, name); + xmlName.setDescendants(); + return xmlName; + } + + /** + * See E4X 11.1 PrimaryExpression : PropertyIdentifier production + */ + public Reference xmlPrimaryReference(Object nameObject, Scriptable scope) + { + if (!(nameObject instanceof XMLName)) + throw new IllegalArgumentException(); + + XMLName xmlName = (XMLName)nameObject; + XMLObjectImpl xmlObj; + for (;;) { + // XML object can only present on scope chain as a wrapper + // of XMLWithScope + if (scope instanceof XMLWithScope) { + xmlObj = (XMLObjectImpl)scope.getPrototype(); + if (xmlObj.hasXMLProperty(xmlName)) { + break; + } + } + scope = scope.getParentScope(); + if (scope == null) { + xmlObj = null; + break; + } + } + + // xmlName == null corresponds to undefined + return new XMLReference(xmlObj, xmlName); + } + + public Scriptable enterXMLWith(XMLObject object, Scriptable scope) + { + return new XMLWithScope(this, scope, object); + } + + public Scriptable enterDotQuery(XMLObject object, Scriptable scope) + { + XMLWithScope xws = new XMLWithScope(this, scope, object); + + // XMLWithScope also handles the .(xxx) DotQuery for XML + // basically DotQuery is a for/in/with statement and in + // the following 3 statements we setup to signal it's + // DotQuery, + // the index and the object being looped over. The + // xws.setPrototype is the scope of the object which is + // is a element of the lhs (XMLList). + xws.setCurrIndex(0); + xws.setDQPrototype(object); + + if (object instanceof XMLList) { + XMLList xl = (XMLList)object; + if (xl.length() > 0) { + xws.setPrototype((Scriptable)(xl.get(0, null))); + } + } + // Always return the outer-most type of XML lValue of + // XML to left of dotQuery. + xws.setXMLList(new XMLList(this)); + return xws; + } + + /** + * Escapes the reserved characters in a value of an attribute + * + * @param value Unescaped text + * @return The escaped text + */ + public String escapeAttributeValue(Object value) + { + String text = ScriptRuntime.toString(value); + + if (text.length() == 0) return text; + + XmlObject xo = XmlObject.Factory.newInstance(); + + XmlCursor cursor = xo.newCursor(); + cursor.toNextToken(); + cursor.beginElement("a"); + cursor.insertAttributeWithValue("a", text); + cursor.dispose(); + + String elementText = xo.toString(); + int begin = elementText.indexOf('"') + 1; + int end = elementText.lastIndexOf('"'); + return (begin < end) ? elementText.substring(begin, end) : ""; + } + + /** + * Escapes the reserved characters in a value of a text node + * + * @param value Unescaped text + * @return The escaped text + */ + public String escapeTextValue(Object value) + { + if (value instanceof XMLObjectImpl) { + return ((XMLObjectImpl)value).toXMLString(); + } + + String text = ScriptRuntime.toString(value); + + if (text.length() == 0) return text; + + XmlObject xo = XmlObject.Factory.newInstance(); + + XmlCursor cursor = xo.newCursor(); + cursor.toNextToken(); + cursor.beginElement("a"); + cursor.insertChars(text); + cursor.dispose(); + + String elementText = xo.toString(); + int begin = elementText.indexOf('>') + 1; + int end = elementText.lastIndexOf('<'); + return (begin < end) ? elementText.substring(begin, end) : ""; + } + + public Object addXMLObjects(Context cx, XMLObject obj1, XMLObject obj2) + { + XMLList listToAdd = new XMLList(this); + + if (obj1 instanceof XMLList) { + XMLList list1 = (XMLList)obj1; + if (list1.length() == 1) { + listToAdd.addToList(list1.item(0)); + } else { + // Might be xmlFragment + xmlFragment + xmlFragment + ...; + // then the result will be an XMLList which we want to be an + // rValue and allow it to be assigned to an lvalue. + listToAdd = new XMLList(this, obj1); + } + } else { + listToAdd.addToList(((XML)obj1)); + } + + if (obj2 instanceof XMLList) { + XMLList list2 = (XMLList)obj2; + for (int i = 0; i < list2.length(); i++) { + listToAdd.addToList(list2.item(i)); + } + } else if (obj2 instanceof XML) { + listToAdd.addToList(((XML)obj2)); + } + + return listToAdd; + } + + public Object toDefaultXmlNamespace(Context cx, Object uriValue) + { + return constructNamespace(cx, uriValue); + } + + /** + * This method exists in order to handle the difference between a XML element property + * and a method on an XML Object that might have the same name. + * + * @param obj + * @param id + * @param scope + * @param thisObj + * @return + */ + static Object getXmlMethod(Object obj, String id, Scriptable scope, + Scriptable thisObj) + { + Scriptable start; + + if (obj instanceof Scriptable) { + start = (Scriptable) obj; + } else { + start = ScriptRuntime.toObject(scope, obj); + } + + Scriptable m = start; + do { + if (m instanceof XMLObjectImpl) { + XMLObjectImpl xmlObject = (XMLObjectImpl) m; + Object result = xmlObject.getMethod(id); + if (result != Scriptable.NOT_FOUND) { + return result; + } + } + m = m.getPrototype(); + } while (m != null); + + return Undefined.instance; + } + + +} diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java new file mode 100644 index 000000000000..b7bae731a531 --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java @@ -0,0 +1,1641 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. 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 Public License (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 NPL, indicate your decision by + * deleting the provisions above and replace 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 NPL or the GPL. + */ + +package org.mozilla.javascript.xmlimpl; + +import java.util.Vector; +import java.util.HashMap; +import java.lang.reflect.Member; + +import org.mozilla.javascript.*; +import org.mozilla.javascript.xml.*; + +import org.apache.xmlbeans.XmlCursor; + +public class XMLList extends XMLObjectImpl implements Function +{ + + static protected class AnnotationList + { + private Vector v; + + + public AnnotationList () + { + v = new Vector(); + } + + + public void add (XML.XScriptAnnotation n) + { + v.add(n); + } + + + public XML.XScriptAnnotation item(int index) + { + return (XML.XScriptAnnotation)(v.get(index)); + } + + + public void remove (int index) + { + v.remove(index); + } + + + public int length() + { + return v.size(); + } + }; + + + // Fields + private AnnotationList _annos; + + private XMLObjectImpl targetObject = null; + private javax.xml.namespace.QName targetProperty = null; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Constructors + // + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * + */ + XMLList(XMLLibImpl lib) + { + super(lib); + _annos = new AnnotationList(); + } + + /** + * + * @param inputObject + */ + XMLList(XMLLibImpl lib, Object inputObject) + { + super(lib); + String frag; + + if (inputObject == null || inputObject instanceof Undefined) + { + frag = ""; + } + else if (inputObject instanceof XML) + { + XML xml = (XML) inputObject; + + _annos = new AnnotationList(); + _annos.add(xml.getAnnotation()); + } + else if (inputObject instanceof XMLList) + { + XMLList xmll = (XMLList) inputObject; + + _annos = new AnnotationList(); + + for (int i = 0; i < xmll._annos.length(); i++) + { + _annos.add(xmll._annos.item(i)); + } + } + else + { + frag = ScriptRuntime.toString(inputObject).trim(); + + if (!frag.startsWith("<>")) + { + frag = "<>" + frag + ""; + } + + frag = "" + frag.substring(2); + if (!frag.endsWith("")) + { + throw ScriptRuntime.typeError("XML with anonymous tag missing end anonymous tag"); + } + + frag = frag.substring(0, frag.length() - 3) + ""; + + XML orgXML = new XML(lib, frag); + + // Now orphan the children and add them to our XMLList. + XMLList children = (XMLList)orgXML.children(); + + _annos = new AnnotationList(); + + for (int i = 0; i < children._annos.length(); i++) + { + // Copy here is so that they'll be orphaned (parent() will be undefined) + _annos.add(((XML) children.item(i).copy()).getAnnotation()); + } + } + } + + // + // + // TargetObject/Property accessors + // + // + + /** + * + * @param object + * @param property + */ + void setTargets(XMLObjectImpl object, javax.xml.namespace.QName property) + { + targetObject = object; + targetProperty = property; + } + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Private functions + // + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * + * @param index + * @return + */ + XML getXmlFromAnnotation(int index) + { + XML retVal; + + if (index >= 0 && index < length()) + { + XML.XScriptAnnotation anno = _annos.item(index); + retVal = XML.XScriptAnnotation.getXML(lib, anno); + } + else + { + retVal = null; + } + + return retVal; + } + + /** + * + * @param index + */ + private void internalRemoveFromList (int index) + { + _annos.remove(index); + } + + /** + * + * @param index + * @param xml + */ + void replace(int index, XML xml) + { + if (index < length()) + { + AnnotationList newAnnoList = new AnnotationList(); + + // Copy upto item to replace. + for (int i = 0; i < index; i++) + { + newAnnoList.add(_annos.item(i)); + } + + newAnnoList.add(xml.getAnnotation()); + + // Skip over old item we're going to replace we've already add new item on above line. + for (int i = index + 1; i < length(); i++) + { + newAnnoList.add(_annos.item(i)); + } + + _annos = newAnnoList; + } + } + + /** + * + * @param index + * @param xml + */ + private void insert(int index, XML xml) + { + if (index < length()) + { + AnnotationList newAnnoList = new AnnotationList(); + + // Copy upto item to insert. + for (int i = 0; i < index; i++) + { + newAnnoList.add(_annos.item(i)); + } + + newAnnoList.add(xml.getAnnotation()); + + for (int i = index; i < length(); i++) + { + newAnnoList.add(_annos.item(i)); + } + + _annos = newAnnoList; + } + } + + // + // + // methods overriding ScriptableObject + // + // + + public String getClassName () + { + return "XMLList"; + } + + protected Scriptable defaultPrototype() + { + Scriptable result = lib.xmlListPrototype; + if (result == this) { + result = null; + } + return result; + } + + // + // + // methods overriding IdScriptable + // + // + + /** + * + * @param index + * @param start + * @return + */ + public Object get(int index, Scriptable start) + { + //Log("get index: " + index); + + if (index >= 0 && index < length()) + { + return getXmlFromAnnotation(index); + } + else + { + return Scriptable.NOT_FOUND; + } + } + + /** + * + * @param name + * @param start + * @return + */ + public boolean hasXMLProperty(XMLName xmlName) + { + boolean result = false; + + // 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 0 <= index && index < length(); + } + + /** + * + * @param name + * @param value + */ + public void putXMLProperty(XMLName xmlName, Object value) + { + //Log("put property: " + name); + + // Special-case checks for undefined and null + if (value == null) + { + value = "null"; + } + else if (value instanceof Undefined) + { + value = "undefined"; + } + + if (length() > 1) + { + throw ScriptRuntime.typeError("Assignment to lists with more that one item is not supported"); + } + else if (length() == 0) + { + // Secret sauce for super-expandos. + // We set an element here, and then add ourselves to our target. + if (targetObject != null && + targetProperty != null && + !targetProperty.getLocalPart().equals("*")) + { + // Add an empty element with our targetProperty name and then set it. + XML xmlValue = XML.createTextElement(lib, targetProperty, ""); + addToList(xmlValue); + + if(xmlName.isAttributeName()) + { + setAttribute(xmlName, value); + } + else + { + XML xml = (XML)item(0); + xml.putXMLProperty(xmlName, value); + + // Update the list with the new item at location 0. + replace(0, (XML)item(0)); + } + + // Now add us to our parent + XMLName name2 = XMLName.formProperty(targetProperty.getNamespaceURI(), targetProperty.getLocalPart()); + targetObject.putXMLProperty(name2, this); + } + else + { + throw ScriptRuntime.typeError("Assignment to empty XMLList without targets not supported"); + } + } + else if(xmlName.isAttributeName()) + { + setAttribute(xmlName, value); + } + else + { + XML xml = (XML)item(0); + xml.putXMLProperty(xmlName, value); + + // Update the list with the new item at location 0. + replace(0, (XML)item(0)); + } + } + + /** + * + * @param name + * @return + */ + public Object getXMLProperty(XMLName name) + { + return getPropertyList(name); + } + + /** + * + * @param index + * @param value + */ + public void put(int index, Scriptable start, Object value) + { + Object parent = Undefined.instance; + // Convert text into XML if needed. + XMLObject xmlValue; + + // Special-case checks for undefined and null + if (value == null) + { + value = "null"; + } + else if (value instanceof Undefined) + { + value = "undefined"; + } + + if (value instanceof XMLObject) + { + xmlValue = (XMLObject) value; + } + else + { + if (targetProperty == null) + { + xmlValue = new XML(lib, value.toString()); + } + else + { + xmlValue = XML.createTextElement(lib, targetProperty, value.toString()); + } + } + + // Find the parent + if (index < length()) + { + parent = item(index).parent(); + } + else + { + // Appending + parent = parent(); + } + + if (parent instanceof XML) + { + // found parent, alter doc + XML xmlParent = (XML) parent; + + if (index < length()) + { + // We're replacing the the node. + XML xmlNode = getXmlFromAnnotation(index); + + if (xmlValue instanceof XML) + { + xmlNode.replaceAll((XML) xmlValue); + replace(index, xmlNode); + } + else if (xmlValue instanceof XMLList) + { + // Replace the first one, and add the rest on the list. + XMLList list = (XMLList) xmlValue; + + if (list.length() > 0) + { + int lastIndexAdded = xmlNode.childIndex(); + xmlNode.replaceAll((XML)list.item(0)); + replace(index, (XML)list.item(0)); + + for (int i = 1; i < list.length(); i++) + { + xmlParent.insertChildAfter(xmlParent.getXmlChild(lastIndexAdded), list.item(i)); + lastIndexAdded++; + insert(index + i, (XML)list.item(i)); + } + } + } + } + else + { + // Appending + xmlParent.appendChild(xmlValue); + addToList(xmlParent.getXmlChild(index)); + } + } + else + { + // Don't all have same parent, no underlying doc to alter + if (index < length()) + { + XML xmlNode = XML.XScriptAnnotation.getXML(lib, _annos.item(index)); + + if (xmlValue instanceof XML) + { + xmlNode.replaceAll((XML) xmlValue); + replace(index, xmlNode); + } + else if (xmlValue instanceof XMLList) + { + // Replace the first one, and add the rest on the list. + XMLList list = (XMLList) xmlValue; + + if (list.length() > 0) + { + xmlNode.replaceAll((XML)list.item(0)); + replace(index, (XML)list.item(0)); + + for (int i = 1; i < list.length(); i++) + { + insert(index + i, (XML)list.item(i)); + } + } + } + } + else + { + addToList(xmlValue); + } + } + } + + + /** + * + * @param name + */ + public void deleteXMLProperty(XMLName name) + { + for (int i = 0; i < length(); i++) + { + XML xml = getXmlFromAnnotation(i); + + if (xml.tokenType() == XmlCursor.TokenType.START) + { + xml.deleteXMLProperty(name); + } + } + } + + /** + * + * @param index + */ + public void delete(int index) + { + if (index >= 0 && index < length()) + { + XML xml = getXmlFromAnnotation(index); + + xml.remove(); + + internalRemoveFromList(index); + } + } + + + /** + * + * @return + */ + public Object[] getIds() + { + Object enumObjs[]; + + if (prototypeFlag) + { + enumObjs = new Object[0]; + } + else + { + enumObjs = new Object[length()]; + + for (int i = 0; i < enumObjs.length; i++) + { + enumObjs[i] = new Integer(i); + } + } + + return enumObjs; + } + + /** + * + * @return + */ + public Object[] getIdsForDebug() + { + return getIds(); + } + + + // XMLList will remove will delete all items in the list (a set delete) this differs from the XMLList delete operator. + void remove () + { + int nLen = length(); + for (int i = nLen - 1; i >= 0; i--) + { + XML xml = getXmlFromAnnotation(i); + if (xml != null) + { + xml.remove(); + internalRemoveFromList(i); + } + } + } + + /** + * + * @param index + * @return + */ + public XML item (int index) + { + return _annos != null ? getXmlFromAnnotation(index) : XML.createXML(lib, null); + } + + + /** + * + * @param name + * @param value + */ + private void setAttribute (XMLName xmlName, Object value) + { + for (int i = 0; i < length(); i++) + { + XML xml = getXmlFromAnnotation(i); + xml.setAttribute(xmlName, value); + } + } + + + /** + * + * @param toAdd + */ + public void addToList(Object toAdd) + { + if (toAdd instanceof Undefined) + { + // Missing argument do nothing... + return; + } + + if (toAdd instanceof XMLList) + { + XMLList xmlSrc = (XMLList)toAdd; + for (int i = 0; i < xmlSrc.length(); i++) + { + _annos.add(((XML)xmlSrc.item(i)).getAnnotation()); + } + } + else if (toAdd instanceof XML) + { + _annos.add(((XML)(toAdd)).getAnnotation()); + } + else if (toAdd instanceof XML.XScriptAnnotation) + { + _annos.add((XML.XScriptAnnotation)toAdd); + } + } + + // + // + // Methods from section 12.4.4 in the spec + // + // + + /** + * + * @param toAdd + */ + public XML addNamespace(Namespace ns) + { + if(length() == 1) + { + return getXmlFromAnnotation(0).addNamespace(ns); + } + else + { + throw ScriptRuntime.typeError("The addNamespace method works only on lists containing one item"); + } + } + + /** + * + * @param xml + * @return + */ + public XML appendChild(Object xml) + { + if (length() == 1) + { + return getXmlFromAnnotation(0).appendChild(xml); + } + else + { + throw ScriptRuntime.typeError("The appendChild method works only on lists containing one item"); + } + } + + /** + * + * @param attr + * @return + */ + public XMLList attribute(XMLName xmlName) + { + XMLList result = new XMLList(lib); + + for (int i = 0; i < length(); i++) + { + XML xml = getXmlFromAnnotation(i); + result.addToList(xml.attribute(xmlName)); + } + + return result; + } + + /** + * + * @return + */ + public XMLList attributes() + { + XMLList result = new XMLList(lib); + + for (int i = 0; i < length(); i++) + { + XML xml = getXmlFromAnnotation(i); + result.addToList(xml.attributes()); + } + + return result; + } + + public XMLList child(long index) + { + XMLList result = new XMLList(lib); + + for (int i = 0; i < length(); i++) + { + result.addToList(getXmlFromAnnotation(i).child(index)); + } + + return result; + } + + public XMLList child(XMLName xmlName) + { + XMLList result = new XMLList(lib); + + for (int i = 0; i < length(); i++) + { + result.addToList(getXmlFromAnnotation(i).child(xmlName)); + } + + return result; + } + + /** + * + * @return + */ + public int childIndex() + { + if (length() == 1) + { + return getXmlFromAnnotation(0).childIndex(); + } + else + { + throw ScriptRuntime.typeError("The childIndex method works only on lists containing one item"); + } + } + + /** + * + * @return + */ + public XMLList children() + { + Vector v = new Vector(); + + for (int i = 0; i < length(); i++) + { + XML xml = getXmlFromAnnotation(i); + + if (xml != null) + { + Object o = xml.children(); + if (o instanceof XMLList) + { + XMLList childList = (XMLList)o; + + int cChildren = childList.length(); + for (int j = 0; j < cChildren; j++) + { + v.addElement(childList.item(j)); + } + } + } + } + + XMLList allChildren = new XMLList(lib); + int sz = v.size(); + + for (int i = 0; i < sz; i++) + { + allChildren.addToList(v.get(i)); + } + + return allChildren; + } + + /** + * + * @return + */ + public XMLList comments() + { + XMLList result = new XMLList(lib); + + for (int i = 0; i < length(); i++) + { + XML xml = getXmlFromAnnotation(i); + + result.addToList(xml.comments()); + } + + return result; + } + + /** + * + * @param xml + * @return + */ + public boolean contains(Object xml) + { + boolean result = false; + + for (int i = 0; i < length(); i++) + { + XML member = getXmlFromAnnotation(i); + + if (member.equivalentXml(xml)) + { + result = true; + break; + } + } + + return result; + } + + /** + * + * @return + */ + public Object copy() + { + XMLList result = new XMLList(lib); + + for (int i = 0; i < length(); i++) + { + XML xml = getXmlFromAnnotation(i); + result.addToList(xml.copy()); + } + + return result; + } + + /** + * + * @return + */ + public XMLList descendants(XMLName xmlName) + { + XMLList result = new XMLList(lib); + + for (int i = 0; i < length(); i++) + { + XML xml = getXmlFromAnnotation(i); + result.addToList(xml.descendants(xmlName)); + } + + return result; + } + + /** + * + * @return + */ + public Object[] inScopeNamespaces() + { + if(length() == 1) + { + return getXmlFromAnnotation(0).inScopeNamespaces(); + } + else + { + throw ScriptRuntime.typeError("The inScopeNamespaces method works only on lists containing one item"); + } + } + + /** + * + * @param child + * @param xml + */ + public XML insertChildAfter(Object child, Object xml) + { + if (length() == 1) + { + return getXmlFromAnnotation(0).insertChildAfter(child, xml); + } + else + { + throw ScriptRuntime.typeError("The insertChildAfter method works only on lists containing one item"); + } + } + + /** + * + * @param child + * @param xml + */ + public XML insertChildBefore(Object child, Object xml) + { + if (length() == 1) + { + return getXmlFromAnnotation(0).insertChildAfter(child, xml); + } + else + { + throw ScriptRuntime.typeError("The insertChildBefore method works only on lists containing one item"); + } + } + + /** + * + * @return + */ + public 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 + */ + public boolean hasComplexContent() + { + boolean complexContent; + int length = length(); + + if (length == 0) + { + complexContent = false; + } + else if (length == 1) + { + complexContent = getXmlFromAnnotation(0).hasComplexContent(); + } + else + { + complexContent = false; + + for (int i = 0; i < length; i++) + { + XML nextElement = getXmlFromAnnotation(i); + if (nextElement.tokenType() == XmlCursor.TokenType.START) + { + complexContent = true; + break; + } + } + } + + return complexContent; + } + + /** + * + * @return + */ + public boolean hasSimpleContent() + { + boolean simpleContent; + int length = length(); + + if (length == 0) + { + simpleContent = true; + } + else if (length == 1) + { + simpleContent = getXmlFromAnnotation(0).hasSimpleContent(); + } + else + { + simpleContent = true; + + for (int i = 0; i < length; i++) + { + XML nextElement = getXmlFromAnnotation(i); + if (nextElement.tokenType() == XmlCursor.TokenType.START) + { + simpleContent = false; + break; + } + } + } + + return simpleContent; + } + + /** + * + * @return + */ + public int length() + { + int result = 0; + + if (_annos != null) + { + result = _annos.length(); + } + + return result; + } + + /** + * + * @return + */ + public String localName() + { + if (length() == 1) + { + return name().localName(); + } + else + { + throw ScriptRuntime.typeError("The localName method works only on lists containing one item"); + } + } + + /** + * + * @return + */ + public QName name() + { + if (length() == 1) + { + return getXmlFromAnnotation(0).name(); + } + else + { + throw ScriptRuntime.typeError("The name method works only on lists containing one item"); + } + } + + /** + * + * @param prefix + * @return + */ + public Object namespace(String prefix) + { + if (length() == 1) + { + return getXmlFromAnnotation(0).namespace(prefix); + } + else + { + throw ScriptRuntime.typeError("The namespace method works only on lists containing one item"); + } + } + + /** + * + * @return + */ + public Object[] namespaceDeclarations() + { + if (length() == 1) + { + return getXmlFromAnnotation(0).namespaceDeclarations(); + } + else + { + throw ScriptRuntime.typeError("The namespaceDeclarations method works only on lists containing one item"); + } + } + + /** + * + * @return + */ + public Object nodeKind() + { + if (length() == 1) + { + return getXmlFromAnnotation(0).nodeKind(); + } + else + { + throw ScriptRuntime.typeError("The nodeKind method works only on lists containing one item"); + } + } + + /** + * + */ + public void normalize() + { + for (int i = 0; i < length(); i++) + { + getXmlFromAnnotation(i).normalize(); + } + } + + /** + * If list is empty, return undefined, if elements have different parents return undefined, + * If they all have the same parent, return that parent. + * + * @return + */ + public Object parent() + { + Object sameParent = Undefined.instance; + + if ((length() == 0) && (targetObject != null) && (targetObject instanceof XML)) + { + sameParent = targetObject; + } + else + { + for (int i = 0; i < length(); i++) + { + Object currParent = getXmlFromAnnotation(i).parent(); + + if (i == 0) + { + // Set the first for the rest to compare to. + sameParent = currParent; + } + else if (sameParent != currParent) + { + sameParent = Undefined.instance; + break; + } + } + } + + // If everything in the list is the sameParent then return that as the parent. + return sameParent; + } + + /** + * + * @param xml + * @return + */ + public XML prependChild(Object xml) + { + if (length() == 1) + { + return getXmlFromAnnotation(0).prependChild(xml); + } + else + { + throw ScriptRuntime.typeError("The prependChild method works only on lists containing one item"); + } + } + + /** + * + * @return + */ + public Object processingInstructions(XMLName xmlName) + { + XMLList result = new XMLList(lib); + + for (int i = 0; i < length(); i++) + { + XML xml = getXmlFromAnnotation(i); + + result.addToList(xml.processingInstructions(xmlName)); + } + + return result; + } + + /** + * + * @param name + * @return + */ + public boolean propertyIsEnumerable(XMLName xmlName) + { + return hasXMLProperty(xmlName); + } + + /** + * + * @param ns + */ + public XML removeNamespace(Namespace ns) + { + if(length() == 1) + { + return getXmlFromAnnotation(0).removeNamespace(ns); + } + else + { + throw ScriptRuntime.typeError("The removeNamespace method works only on lists containing one item"); + } + } + + public XML replace(long index, Object xml) + { + if (length() == 1) + { + return getXmlFromAnnotation(0).replace(index, xml); + } + else + { + throw ScriptRuntime.typeError("The replace method works only on lists containing one item"); + } + } + + /** + * + * @param propertyName + * @param xml + * @return + */ + public XML replace(XMLName xmlName, Object xml) + { + if (length() == 1) + { + return getXmlFromAnnotation(0).replace(xmlName, xml); + } + else + { + throw ScriptRuntime.typeError("The replace method works only on lists containing one item"); + } + } + + /** + * + * @param xml + */ + public XML setChildren(Object xml) + { + if (length() == 1) + { + return getXmlFromAnnotation(0).setChildren(xml); + } + else + { + throw ScriptRuntime.typeError("The setChildren method works only on lists containing one item"); + } + } + + /** + * + * @param name + */ + public void setLocalName(String localName) + { + if (length() == 1) + { + getXmlFromAnnotation(0).setLocalName(localName); + } + else + { + throw ScriptRuntime.typeError("The setLocalName method works only on lists containing one item"); + } + } + + /** + * + * @param name + */ + public void setName(QName qname) + { + if (length() == 1) + { + getXmlFromAnnotation(0).setName(qname); + } + else + { + throw ScriptRuntime.typeError("The setName method works only on lists containing one item"); + } + } + + /** + * + * @param ns + */ + public void setNamespace(Namespace ns) + { + if (length() == 1) + { + getXmlFromAnnotation(0).setNamespace(ns); + } + else + { + throw ScriptRuntime.typeError("The setNamespace method works only on lists containing one item"); + } + } + + /** + * + * * @return + */ + public XMLList text() + { + XMLList result = new XMLList(lib); + + for (int i = 0; i < length(); i++) + { + result.addToList(getXmlFromAnnotation(i).text()); + } + + return result; + } + + /** + * + * @return + */ + public String toString() + { + if (hasSimpleContent()) + { + StringBuffer sb = new StringBuffer(); + + for(int i = 0; i < length(); i++) + { + XML next = getXmlFromAnnotation(i); + sb.append(next.toString()); + } + + return sb.toString(); + } + else + { + return toXMLString(); + } + } + + /** + * + * @return + */ + public String toXMLString() + { + StringBuffer sb = new StringBuffer(); + + for(int i = 0; i < length(); i++) + { + if (i > 0) + { + // Add a line separator + String lineSeparator = System.getProperty("line.separator"); + + sb.append(lineSeparator); + } + + sb.append(getXmlFromAnnotation(i).toXMLString()); + } + + return sb.toString(); + } + + /** + * + * @return + */ + public Object valueOf() + { + return this; + } + + // + // Other public Functions from XMLObject + // + + /** + * + * @param target + * @return + */ + public boolean equivalentXml(Object target) + { + boolean result = false; + + // Zero length list should equate to undefined + if (target instanceof Undefined && length() == 0) + { + result = true; + } + else if (length() == 1) + { + result = getXmlFromAnnotation(0).equivalentXml(target); + } + else if (target instanceof XMLList) + { + XMLList otherList = (XMLList) target; + + if (otherList.length() == length()) + { + result = true; + + for (int i = 0; i < length(); i++) + { + if (!getXmlFromAnnotation(i).equivalentXml(otherList.getXmlFromAnnotation(i))) + { + result = false; + break; + } + } + } + } + + return result; + } + + /** + * + * @param name + * @param start + * @return + */ + private XMLList getPropertyList(XMLName name) + { + XMLList propertyList = new XMLList(lib); + javax.xml.namespace.QName qname = null; + + if (!name.isDescendants() && !name.isAttributeName()) + { + // Only set the targetProperty if this is a regular child get + // and not a descendant or attribute get + qname = new javax.xml.namespace.QName(name.uri(), name.localName()); + } + + propertyList.setTargets(this, qname); + + for (int i = 0; i < length(); i++) + { + propertyList.addToList(getXmlFromAnnotation(i).getPropertyList(name)); + } + + return propertyList; + } + + private Object applyOrCall(boolean isApply, + Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + throws JavaScriptException + { + String methodName = isApply ? "apply" : "call"; + if(!(thisObj instanceof XMLList) || + ((XMLList)thisObj).targetProperty == null) + throw ScriptRuntime.typeError1("msg.isnt.function", + methodName); + + int L = args.length; + Object callThis = + (L == 0 || args[0] == null || args[0] == Undefined.instance) + ? ScriptableObject.getTopLevelScope(scope) + : ScriptRuntime.toObject(cx, scope, args[0]); + + Object[] callArgs; + if (isApply) + { + // Follow Ecma 15.3.4.3 + if (L <= 1) + { + callArgs = ScriptRuntime.emptyArgs; + } + else + { + Object arg1 = args[1]; + if (arg1 == null || arg1 == Undefined.instance) + { + callArgs = ScriptRuntime.emptyArgs; + } + else if (ScriptRuntime.isArrayObject(arg1)) + { + callArgs = ScriptRuntime.getArrayElements((Scriptable)arg1); + } + else + { + throw ScriptRuntime.typeError0("msg.arg.isnt.array"); + } + } + } + else + { + // Follow Ecma 15.3.4.4 + if (L <= 1) + { + callArgs = ScriptRuntime.emptyArgs; + } + else + { + callArgs = new Object[L - 1]; + System.arraycopy(args, 1, callArgs, 0, L - 1); + } + } + + return ScriptRuntime.call(cx, thisObj, callThis, callArgs, scope); + } + + protected Object jsConstructor(Context cx, boolean inNewExpr, + Object[] args) + { + if (args.length == 0) { + return new XMLList(lib); + } else { + Object arg0 = args[0]; + if (!inNewExpr && arg0 instanceof XMLList) { + // XMLList(XMLList) returns the same object. + return arg0; + } + return new XMLList(lib, arg0); + } + } + + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + throws JavaScriptException + { + // This XMLList is being called as a Function. + // Let's find the real Function object. + if(targetProperty == null) + throw ScriptRuntime.typeError1("msg.isnt.function", + ScriptRuntime.toString(this)); + + String methodName = targetProperty.getLocalPart(); + + boolean isApply = methodName.equals("apply"); + if(isApply || methodName.equals("call")) + return applyOrCall(isApply, cx, scope, thisObj, args); + + Object methodObj = XMLLibImpl.getXmlMethod(targetObject, methodName, scope, targetObject); + + if(!(methodObj instanceof Function)) + { + if(thisObj instanceof XMLObjectImpl && + ((XMLObjectImpl)thisObj).hasSimpleContent()) + { + thisObj = ScriptRuntime.toObject(cx, scope, thisObj.toString()); + methodObj = ScriptableObject.getProperty(thisObj, methodName); + } + } + + if(!(methodObj instanceof Function)) + throw ScriptRuntime.typeError1("msg.isnt.function", methodName); + + Function method = (Function)methodObj; + return method.call(cx, scope, thisObj, args); + } + + public Scriptable construct(Context cx, Scriptable scope, Object[] args) + throws JavaScriptException + { + return Undefined.instance; + } +} + + diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java new file mode 100644 index 000000000000..e993fb26126b --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java @@ -0,0 +1,116 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (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 NPL, indicate your decision by + * deleting the provisions above and replace 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 NPL or the GPL. + */ + +package org.mozilla.javascript.xmlimpl; + +class XMLName +{ + private String uri; + private String localName; + private boolean isAttributeName; + private boolean isDescendants; + + private XMLName(String uri, String localName) + { + this.uri = uri; + this.localName = localName; + } + + String uri() + { + return uri; + } + + String localName() + { + return localName; + } + + boolean isAttributeName() + { + return isAttributeName; + } + + void setAttributeName() + { + if (isAttributeName) + throw new IllegalStateException(); + isAttributeName = true; + } + + boolean isDescendants() + { + return isDescendants; + } + + void setDescendants() + { + if (isDescendants) + throw new IllegalStateException(); + isDescendants = true; + } + + public String toString() + { + //return qname.localName(); + StringBuffer buff = new StringBuffer(); + if(isDescendants()) buff.append(".."); + if(isAttributeName()) buff.append('@'); + if(uri() == null) + { + buff.append('*'); + if(localName().equals("*")) + return buff.toString(); + } + else + { + buff.append('"').append(uri()).append('"'); + } + buff.append(':').append(localName()); + return buff.toString(); + } + + static XMLName formStar() + { + return new XMLName(null, "*"); + } + + static XMLName formProperty(String uri, String localName) + { + return new XMLName(uri, localName); + } + +} \ No newline at end of file diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java new file mode 100644 index 000000000000..ba5c3f95706d --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java @@ -0,0 +1,649 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * Ethan Hugg + * Terry Lucas + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (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 NPL, indicate your decision by + * deleting the provisions above and replace 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 NPL or the GPL. + */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; +import org.mozilla.javascript.xml.*; + +/** + * This abstract class describes what all XML objects (XML, XMLList) should have in common. + * + * @see XML + */ +public abstract class XMLObjectImpl extends XMLObject +{ + private static final Object XMLOBJECT_TAG = new Object(); + + protected final XMLLibImpl lib; + protected boolean prototypeFlag; + + protected XMLObjectImpl(XMLLibImpl lib) + { + this.lib = lib; + } + + /** + * ecmaHas(cx, id) calls this after resolving when id to XMLName + * and checking it is not Uint32 index. + */ + public abstract boolean hasXMLProperty(XMLName name); + + /** + * ecmaGet(cx, id) calls this after resolving when id to XMLName + * and checking it is not Uint32 index. + */ + public abstract Object getXMLProperty(XMLName name); + + /** + * ecmaPut(cx, id, value) calls this after resolving when id to XMLName + * and checking it is not Uint32 index. + */ + public abstract void putXMLProperty(XMLName name, Object value); + + /** + * ecmaDelete(cx, id) calls this after resolving when id to XMLName + * and checking it is not Uint32 index. + */ + public abstract void deleteXMLProperty(XMLName name); + + /** + * Test XML equality with target the target. + */ + public abstract boolean equivalentXml(Object target); + + // Methods from section 12.4.4 in the spec + public abstract XML addNamespace(Namespace ns); + public abstract XML appendChild(Object xml); + public abstract XMLList attribute(XMLName xmlName); + public abstract XMLList attributes(); + public abstract XMLList child(long index); + public abstract XMLList child(XMLName xmlName); + public abstract int childIndex(); + public abstract XMLList children(); + public abstract XMLList comments(); + public abstract boolean contains(Object xml); + public abstract Object copy(); + public abstract XMLList descendants(XMLName xmlName); + public abstract Object[] inScopeNamespaces(); + public abstract XML insertChildAfter(Object child, Object xml); + public abstract XML insertChildBefore(Object child, Object xml); + public abstract boolean hasOwnProperty(XMLName xmlName); + public abstract boolean hasComplexContent(); + public abstract boolean hasSimpleContent(); + public abstract int length(); + public abstract String localName(); + public abstract QName name(); + public abstract Object namespace(String prefix); + public abstract Object[] namespaceDeclarations(); + public abstract Object nodeKind(); + public abstract void normalize(); + public abstract Object parent(); + public abstract XML prependChild(Object xml); + public abstract Object processingInstructions(XMLName xmlName); + public abstract boolean propertyIsEnumerable(XMLName xmlName); + public abstract XML removeNamespace(Namespace ns); + public abstract XML replace(long index, Object xml); + public abstract XML replace(XMLName name, Object xml); + public abstract XML setChildren(Object xml); + public abstract void setLocalName(String name); + public abstract void setName(QName xmlName); + public abstract void setNamespace(Namespace ns); + public abstract XMLList text(); + public abstract String toString(); + public abstract String toXMLString(); + public abstract Object valueOf(); + + protected abstract Object jsConstructor(Context cx, boolean inNewExpr, + Object[] args); + + + public final Object getMethod(String id) + { + return super.get(id, this); + } + + // + // + // Methods overriding ScriptableObject + // + // + + public final Object getDefaultValue(Class hint) + { + return toString(); + } + + protected final Scriptable defaultParentScope() + { + return lib.globalScope(); + } + + public void delete(String name) + { + throw new IllegalArgumentException("String: [" + name + "]"); + } + + /** + * XMLObject always compare with any value and equivalentValues + * never returns null for them but rather calls equivalentXml(value) + * and box the result as Boolean. + */ + public final Boolean equivalentValues(Object value) + { + boolean result = equivalentXml(value); + + return result ? Boolean.TRUE : Boolean.FALSE; + } + + // + // + // Methods overriding XMLObject + // + // + + public final XMLLib lib() + { + return lib; + } + + /** + * Implementation of ECMAScript [[Has]] + */ + public final boolean ecmaHas(Context cx, Object id) + { + if (cx == null) cx = Context.getCurrentContext(); + XMLName xmlName = lib.toXMLNameOrIndex(cx, id); + if (xmlName == null) { + long index = ScriptRuntime.lastUint32Result(cx); + // XXX Fix this cast + return has((int)index, this); + } + return hasXMLProperty(xmlName); + } + + /** + * Implementation of ECMAScript [[Get]] + */ + public final Object ecmaGet(Context cx, Object id) + { + if (cx == null) cx = Context.getCurrentContext(); + XMLName xmlName = lib.toXMLNameOrIndex(cx, id); + if (xmlName == null) { + long index = ScriptRuntime.lastUint32Result(cx); + // XXX Fix this cast + Object result = get((int)index, this); + if (result == Scriptable.NOT_FOUND) { + result = Undefined.instance; + } + return result; + } + return getXMLProperty(xmlName); + } + + /** + * Implementation of ECMAScript [[Put]] + */ + public final void ecmaPut(Context cx, Object id, Object value) + { + if (cx == null) cx = Context.getCurrentContext(); + XMLName xmlName = lib.toXMLNameOrIndex(cx, id); + if (xmlName == null) { + long index = ScriptRuntime.lastUint32Result(cx); + // XXX Fix this cast + put((int)index, this, value); + return; + } + putXMLProperty(xmlName, value); + } + + /** + * Implementation of ECMAScript [[Delete]] + */ + public final boolean ecmaDelete(Context cx, Object id) + { + if (cx == null) cx = Context.getCurrentContext(); + XMLName xmlName = lib.toXMLNameOrIndex(cx, id); + if (xmlName == null) { + long index = ScriptRuntime.lastUint32Result(cx); + // XXX Fix this + delete((int)index); + return true; + } + deleteXMLProperty(xmlName); + return true; + } + + // + // + // IdScriptable machinery + // + // + + final void exportAsJSClass(boolean sealed) + { + prototypeFlag = true; + exportAsJSClass(MAX_PROTOTYPE_ID, lib.globalScope(), sealed); + } + +// #string_id_map# + private final static int + Id_constructor = 1, + + Id_addNamespace = 2, + Id_appendChild = 3, + Id_attribute = 4, + Id_attributes = 5, + Id_child = 6, + Id_childIndex = 7, + Id_children = 8, + Id_comments = 9, + Id_contains = 10, + Id_copy = 11, + Id_descendants = 12, + Id_inScopeNamespaces = 13, + Id_insertChildAfter = 14, + Id_insertChildBefore = 15, + Id_hasOwnProperty = 16, + Id_hasComplexContent = 17, + Id_hasSimpleContent = 18, + Id_length = 19, + Id_localName = 20, + Id_name = 21, + Id_namespace = 22, + Id_namespaceDeclarations = 23, + Id_nodeKind = 24, + Id_normalize = 25, + Id_parent = 26, + Id_prependChild = 27, + Id_processingInstructions = 28, + Id_propertyIsEnumerable = 29, + Id_removeNamespace = 30, + Id_replace = 31, + Id_setChildren = 32, + Id_setLocalName = 33, + Id_setName = 34, + Id_setNamespace = 35, + Id_text = 36, + Id_toString = 37, + Id_toXMLString = 38, + Id_valueOf = 39, + + MAX_PROTOTYPE_ID = 39; + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2004-07-19 13:13:35 CEST + L0: { id = 0; String X = null; int c; + L: switch (s.length()) { + case 4: c=s.charAt(0); + if (c=='c') { X="copy";id=Id_copy; } + else if (c=='n') { X="name";id=Id_name; } + else if (c=='t') { X="text";id=Id_text; } + break L; + case 5: X="child";id=Id_child; break L; + case 6: c=s.charAt(0); + if (c=='l') { X="length";id=Id_length; } + else if (c=='p') { X="parent";id=Id_parent; } + break L; + case 7: c=s.charAt(0); + if (c=='r') { X="replace";id=Id_replace; } + else if (c=='s') { X="setName";id=Id_setName; } + else if (c=='v') { X="valueOf";id=Id_valueOf; } + break L; + case 8: switch (s.charAt(2)) { + case 'S': X="toString";id=Id_toString; break L; + case 'd': X="nodeKind";id=Id_nodeKind; break L; + case 'i': X="children";id=Id_children; break L; + case 'm': X="comments";id=Id_comments; break L; + case 'n': X="contains";id=Id_contains; break L; + } break L; + case 9: switch (s.charAt(2)) { + case 'c': X="localName";id=Id_localName; break L; + case 'm': X="namespace";id=Id_namespace; break L; + case 'r': X="normalize";id=Id_normalize; break L; + case 't': X="attribute";id=Id_attribute; break L; + } break L; + case 10: c=s.charAt(0); + if (c=='a') { X="attributes";id=Id_attributes; } + else if (c=='c') { X="childIndex";id=Id_childIndex; } + break L; + case 11: switch (s.charAt(0)) { + case 'a': X="appendChild";id=Id_appendChild; break L; + case 'c': X="constructor";id=Id_constructor; break L; + case 'd': X="descendants";id=Id_descendants; break L; + case 's': X="setChildren";id=Id_setChildren; break L; + case 't': X="toXMLString";id=Id_toXMLString; break L; + } break L; + case 12: c=s.charAt(0); + if (c=='a') { X="addNamespace";id=Id_addNamespace; } + else if (c=='p') { X="prependChild";id=Id_prependChild; } + else if (c=='s') { + c=s.charAt(3); + if (c=='L') { X="setLocalName";id=Id_setLocalName; } + else if (c=='N') { X="setNamespace";id=Id_setNamespace; } + } + break L; + case 14: X="hasOwnProperty";id=Id_hasOwnProperty; break L; + case 15: X="removeNamespace";id=Id_removeNamespace; break L; + case 16: c=s.charAt(0); + if (c=='h') { X="hasSimpleContent";id=Id_hasSimpleContent; } + else if (c=='i') { X="insertChildAfter";id=Id_insertChildAfter; } + break L; + case 17: c=s.charAt(3); + if (c=='C') { X="hasComplexContent";id=Id_hasComplexContent; } + else if (c=='c') { X="inScopeNamespaces";id=Id_inScopeNamespaces; } + else if (c=='e') { X="insertChildBefore";id=Id_insertChildBefore; } + break L; + case 20: X="propertyIsEnumerable";id=Id_propertyIsEnumerable; break L; + case 21: X="namespaceDeclarations";id=Id_namespaceDeclarations; break L; + case 22: X="processingInstructions";id=Id_processingInstructions; break L; + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + } +// #/generated# + return id; + } +// #/string_id_map# + + protected void initPrototypeId(int id) + { + String s; + int arity; + switch (id) { + case Id_constructor: { + IdFunction ctor; + if (this instanceof XML) { + ctor = new XMLCtor((XML)this, XMLOBJECT_TAG, id, 1); + } else { + ctor = new IdFunction(this, XMLOBJECT_TAG, id, 1); + } + initPrototypeConstructor(ctor); + return; + } + + case Id_addNamespace: arity=1; s="addNamespace"; break; + case Id_appendChild: arity=1; s="appendChild"; break; + case Id_attribute: arity=1; s="attribute"; break; + case Id_attributes: arity=0; s="attributes"; break; + case Id_child: arity=1; s="child"; break; + case Id_childIndex: arity=0; s="childIndex"; break; + case Id_children: arity=0; s="children"; break; + case Id_comments: arity=0; s="comments"; break; + case Id_contains: arity=1; s="contains"; break; + case Id_copy: arity=0; s="copy"; break; + case Id_descendants: arity=1; s="descendants"; break; + case Id_hasComplexContent: arity=0; s="hasComplexContent"; break; + case Id_hasOwnProperty: arity=1; s="hasOwnProperty"; break; + case Id_hasSimpleContent: arity=0; s="hasSimpleContent"; break; + case Id_inScopeNamespaces: arity=0; s="inScopeNamespaces"; break; + case Id_insertChildAfter: arity=2; s="insertChildAfter"; break; + case Id_insertChildBefore: arity=2; s="insertChildBefore"; break; + case Id_length: arity=0; s="length"; break; + case Id_localName: arity=0; s="localName"; break; + case Id_name: arity=0; s="name"; break; + case Id_namespace: arity=1; s="namespace"; break; + case Id_namespaceDeclarations: + arity=0; s="namespaceDeclarations"; break; + case Id_nodeKind: arity=0; s="nodeKind"; break; + case Id_normalize: arity=0; s="normalize"; break; + case Id_parent: arity=0; s="parent"; break; + case Id_prependChild: arity=1; s="prependChild"; break; + case Id_processingInstructions: + arity=1; s="processingInstructions"; break; + case Id_propertyIsEnumerable: + arity=1; s="propertyIsEnumerable"; break; + case Id_removeNamespace: arity=1; s="removeNamespace"; break; + case Id_replace: arity=2; s="replace"; break; + case Id_setChildren: arity=1; s="setChildren"; break; + case Id_setLocalName: arity=1; s="setLocalName"; break; + case Id_setName: arity=1; s="setName"; break; + case Id_setNamespace: arity=1; s="setNamespace"; break; + case Id_text: arity=0; s="text"; break; + case Id_toString: arity=0; s="toString"; break; + case Id_toXMLString: arity=0; s="toXMLString"; break; + case Id_valueOf: arity=0; s="valueOf"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(XMLOBJECT_TAG, id, s, arity); + } + + /** + * + * @param f + * @param cx + * @param scope + * @param thisObj + * @param args + * @return + * @throws JavaScriptException + */ + public Object execMethod(IdFunction f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + throws JavaScriptException + { + if (!f.hasTag(XMLOBJECT_TAG)) { + return super.execMethod(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + switch (id) { + case Id_constructor: { + return jsConstructor(cx, thisObj == null, args); + } + case Id_addNamespace: { + Namespace ns = lib.castToNamespace(cx, arg(args, 0)); + return realThis(thisObj, f).addNamespace(ns); + } + case Id_appendChild: + return realThis(thisObj, f).appendChild(arg(args, 0)); + case Id_attribute: { + Object arg0 = arg(args, 0); + if (arg0 == null || arg0 == Undefined.instance) { + // XXX: E4X requires to throw the exception in this case + // (whoch toAttributeName will) but the test suite assumes + // it should be OK. Trust test suite for now. + arg0 = ScriptRuntime.toString(arg0); + } + XMLName xmlName = lib.toAttributeNameImpl(cx, arg0); + return realThis(thisObj, f).attribute(xmlName); + } + case Id_attributes: + return realThis(thisObj, f).attributes(); + case Id_child: { + XMLName xmlName = getXmlNameOrIndex(cx, args, 0); + if (xmlName == null) { + long index = ScriptRuntime.lastUint32Result(cx); + return realThis(thisObj, f).child(index); + } else { + return realThis(thisObj, f).child(xmlName); + } + } + case Id_childIndex: + return new Integer(realThis(thisObj, f).childIndex()); + case Id_children: + return realThis(thisObj, f).children(); + case Id_comments: + return realThis(thisObj, f).comments(); + case Id_contains: + return wrap_boolean(realThis(thisObj, f).contains(arg(args, 0))); + case Id_copy: + return realThis(thisObj, f).copy(); + case Id_descendants: { + XMLName xmlName = (args.length == 0) + ? XMLName.formStar() + : lib.toXMLName(cx, args[0]); + return realThis(thisObj, f).descendants(xmlName); + } + case Id_inScopeNamespaces: { + Object[] array = realThis(thisObj, f).inScopeNamespaces(); + return cx.newArray(scope, array); + } + case Id_insertChildAfter: + return realThis(thisObj, f).insertChildAfter(arg(args, 0), arg(args, 1)); + case Id_insertChildBefore: + return realThis(thisObj, f).insertChildBefore(arg(args, 0), arg(args, 1)); + case Id_hasOwnProperty: { + XMLName xmlName = lib.toXMLName(cx, arg(args, 0)); + return wrap_boolean( + realThis(thisObj, f).hasOwnProperty(xmlName)); + } + case Id_hasComplexContent: + return wrap_boolean(realThis(thisObj, f).hasComplexContent()); + case Id_hasSimpleContent: + return wrap_boolean(realThis(thisObj, f).hasSimpleContent()); + case Id_length: + return new Integer(realThis(thisObj, f).length()); + case Id_localName: + return realThis(thisObj, f).localName(); + case Id_name: + return realThis(thisObj, f).name(); + case Id_namespace: { + String prefix = (args.length > 0) + ? ScriptRuntime.toString(args[0]) : null; + return realThis(thisObj, f).namespace(prefix); + } + case Id_namespaceDeclarations: { + Object[] array = realThis(thisObj, f).namespaceDeclarations(); + return cx.newArray(scope, array); + } + case Id_nodeKind: + return realThis(thisObj, f).nodeKind(); + case Id_normalize: + realThis(thisObj, f).normalize(); + return Undefined.instance; + case Id_parent: + return realThis(thisObj, f).parent(); + case Id_prependChild: + return realThis(thisObj, f).prependChild(arg(args, 0)); + case Id_processingInstructions: { + XMLName xmlName = (args.length > 0) + ? lib.toXMLName(cx, args[0]) + : XMLName.formStar(); + return realThis(thisObj, f).processingInstructions(xmlName); + } + case Id_propertyIsEnumerable: { + XMLName xmlName = lib.toXMLName(cx, arg(args, 0)); + return wrap_boolean( + realThis(thisObj, f).propertyIsEnumerable(xmlName)); + } + case Id_removeNamespace: { + Namespace ns = lib.castToNamespace(cx, arg(args, 0)); + return realThis(thisObj, f).removeNamespace(ns); + } + case Id_replace: { + XMLName xmlName = getXmlNameOrIndex(cx, args, 0); + Object arg1 = arg(args, 1); + if (xmlName == null) { + long index = ScriptRuntime.lastUint32Result(cx); + return realThis(thisObj, f).replace(index, arg1); + } else { + return realThis(thisObj, f).replace(xmlName, arg1); + } + } + case Id_setChildren: + return realThis(thisObj, f).setChildren(arg(args, 0)); + case Id_setLocalName: { + String localName; + Object arg = arg(args, 0); + if (arg instanceof QName) { + localName = ((QName)arg).localName(); + } else { + localName = ScriptRuntime.toString(arg); + } + realThis(thisObj, f).setLocalName(localName); + return Undefined.instance; + } + case Id_setName: { + Object arg = (args.length != 0) ? args[0] : Undefined.instance; + QName qname; + if (arg instanceof QName) { + qname = (QName)arg; + if (qname.uri() == null) { + qname = lib.constructQNameFromString(cx, qname.localName()); + } else { + // E4X 13.4.4.35 requires to always construct QName + qname = lib.constructQName(cx, qname); + } + } else { + qname = lib.constructQName(cx, arg); + } + realThis(thisObj, f).setName(qname); + return Undefined.instance; + } + case Id_setNamespace: { + Namespace ns = lib.castToNamespace(cx, arg(args, 0)); + realThis(thisObj, f).setNamespace(ns); + return Undefined.instance; + } + case Id_text: + return realThis(thisObj, f).text(); + case Id_toString: + return realThis(thisObj, f).toString(); + case Id_toXMLString: + return realThis(thisObj, f).toXMLString(); + case Id_valueOf: + return realThis(thisObj, f).valueOf(); + } + throw new IllegalArgumentException(String.valueOf(id)); + } + + /** + * + * @param thisObj + * @param f + * @return + */ + private XMLObjectImpl realThis(Scriptable thisObj, IdFunction f) + { + if(!(thisObj instanceof XMLObjectImpl)) + throw incompatibleCallError(f); + return (XMLObjectImpl)thisObj; + } + + private static Object arg(Object[] args, int i) + { + return (i < args.length) ? args[i] : Undefined.instance; + } + + private XMLName getXmlNameOrIndex(Context cx, Object[] args, int i) + { + return lib.toXMLNameOrIndex(cx, arg(args, i)); + } + +} \ No newline at end of file diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLReference.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLReference.java new file mode 100644 index 000000000000..289d63769c99 --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLReference.java @@ -0,0 +1,99 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * 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 XMLReference class for E4X extension of Rhino. + * + * The Initial Developer of the Original Code is + * RUnit Software AS. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): Igor Bukanov, igor@fastmail.fm + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; +import org.mozilla.javascript.xml.*; + +class XMLReference extends Reference +{ + private XMLObjectImpl xmlObject; + private XMLName xmlName; + + /** + * When xmlObject == null, it corresponds to undefined in JS, not null + */ + XMLReference(XMLObjectImpl xmlObject, XMLName xmlName) + { + if (xmlName == null) + throw new IllegalArgumentException(); + this.xmlObject = xmlObject; + this.xmlName = xmlName; + } + + public boolean has() + { + if (xmlObject == null) { + return false; + } + return xmlObject.hasXMLProperty(xmlName); + } + + /** + * See E4X 11.1 PrimaryExpression : PropertyIdentifier production + */ + public Object get() + { + if (xmlObject == null) { + throw ScriptRuntime.undefReadError(Undefined.instance, + xmlName.toString()); + } + return xmlObject.getXMLProperty(xmlName); + } + + public Object set(Object value) + { + if (xmlObject == null) { + throw ScriptRuntime.undefWriteError(Undefined.instance, + xmlName.toString(), + value); + } + xmlObject.putXMLProperty(xmlName, value); + return value; + } + + public void delete() + { + if (xmlObject == null) { + return; + } + xmlObject.deleteXMLProperty(xmlName); + } +} + diff --git a/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java new file mode 100644 index 000000000000..d3fd62613d42 --- /dev/null +++ b/js/rhino/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java @@ -0,0 +1,126 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Netscape are + * Copyright (C) 1997-1999 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Ethan Hugg + * Terry Lucas + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (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 NPL, indicate your decision by + * deleting the provisions above and replace 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 NPL or the GPL. + */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; +import org.mozilla.javascript.xml.*; + +final class XMLWithScope extends NativeWith +{ + private XMLLib lib; + private int _currIndex; + private XMLList _xmlList = null; + private Scriptable _dqPrototype; + + XMLWithScope(XMLLib lib, Scriptable parent, XMLObject prototype) + { + super(parent, prototype); + this.lib = lib; + } + + public XMLLib lib() + { + return lib; + } + + public void setCurrIndex (int idx) + { + _currIndex = idx; + } + + public int getCurrIndex () + { + return _currIndex; + } + + public void setXMLList (XMLList l) + { + _xmlList = l; + } + + public Scriptable getDQPrototype() { + return _dqPrototype; + } + + public void setDQPrototype(Scriptable dqPrototype) { + _dqPrototype = dqPrototype; + } + + public Object updateDotQuery(boolean value) + { + // Return null to continue looping + + Scriptable seed = getDQPrototype(); + XMLList xmlL = _xmlList; + + Object result; + if (seed instanceof XMLList) { + // We're a list so keep testing each element of the list if the + // result on the top of stack is true then that element is added + // to our result list. If false, we try the next element. + XMLList orgXmlL = (XMLList)seed; + + int idx = getCurrIndex(); + + if (value) { + xmlL.addToList(orgXmlL.get(idx, null)); + } + + // More elements to test? + if (++idx < orgXmlL.length()) { + // Yes, set our new index, get the next element and + // reset the expression to run with this object as + // the WITH selector. + setCurrIndex(idx); + setPrototype((Scriptable)(orgXmlL.get(idx, null))); + + // continue looping + return null; + } + } else { + // If we're not a XMLList then there's no looping + // just return DQPrototype if the result is true. + if (value) { + xmlL.addToList(seed); + } + } + + return xmlL; + } +}