diff options
Diffstat (limited to 'trunk/infrastructure/rhino1_7R1/xmlimplsrc')
12 files changed, 6018 insertions, 0 deletions
diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/build.xml b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/build.xml new file mode 100644 index 0000000..1aedece --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/build.xml @@ -0,0 +1,165 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- ***** BEGIN LICENSE BLOCK ***** + - Version: MPL 1.1/GPL 2.0 + - + - The contents of this file are subject to the Mozilla Public License Version + - 1.1 (the "License"); you may not use this file except in compliance with + - the License. You may obtain a copy of the License at + - http://www.mozilla.org/MPL/ + - + - Software distributed under the License is distributed on an "AS IS" basis, + - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + - for the specific language governing rights and limitations under the + - License. + - + - The Original Code is Rhino code, released May 6, 1999. + - + - The Initial Developer of the Original Code is + - Netscape Communications Corporation. + - Portions created by the Initial Developer are Copyright (C) 1997-1999 + - the Initial Developer. All Rights Reserved. + - + - Contributor(s): + - + - Alternatively, the contents of this file may be used under the terms of + - the GNU General Public License Version 2 or later (the "GPL"), in which + - case the provisions of the GPL are applicable instead of those above. If + - you wish to allow use of your version of this file only under the terms of + - the GPL and not to allow others to use your version of this file under the + - MPL, indicate your decision by deleting the provisions above and replacing + - them with the notice and other provisions required by the GPL. If you do + - not delete the provisions above, a recipient may use your version of this + - file under either the MPL or the GPL. + - + - ***** END LICENSE BLOCK ***** --> + +<project name="xmlimplsrc" basedir=".." default="compile"> + <!-- + Properties which affect this build file: + + no-e4x: Will cause E4X not to be built + no-xmlbeans: Will cause the old, XMLBeans-based implementation of E4X not to be built + --> + + <property file="build.local.properties"/> + <property file="build.properties"/> + + <!-- + Provide support for the old name for skipping E4X compilation, in case someone is still using it + --> + <condition property="no-e4x"> + <isset property="without-xmlimpl" /> + </condition> + + <path id="xmlbeans.classpath"> + <pathelement location="${xbean.jar}"/> + <pathelement location="${jsr173.jar}"/> + </path> + + <target name="compile" unless="no-e4x"> + <antcall target="e4x-compile" /> + <antcall target="no-e4x-compile" /> + + <antcall target="old-e4x" /> + </target> + + <available property="jdk1.5?" classname="java.lang.ProcessBuilder" /> + + <target name="e4x-compile" if="jdk1.5?"> + <javac + srcdir="xmlimplsrc" + destdir="${classes}" + deprecation="on" + debug="${debug}" + target="${target-jvm}" + source="${source-level}" + /> + </target> + + <target name="no-e4x-compile" unless="jdk1.5?"> + <echo> + Skipping DOM E4X implementation; JDK 1.5+ currently required for compilation. + <!-- + If the compiler is outfitted with DOM3 using the endorsed standards override mechanism, presumably the code + could be built under 1.4. Not tested. + --> + </echo> + </target> + + <target name="old-e4x" unless="no-xmlbeans"> + <antcall target="old-e4x-compile" /> + <antcall target="no-old-e4x-compile" /> + </target> + + <target name="old-e4x-compile" depends="xmlbeans-unzip"> + <echo>Compiling XMLBeans E4X implementation using ${xbean.jar} and ${jsr173.jar}</echo> + <javac + srcdir="deprecatedsrc" + destdir="${classes}" + includes="org/mozilla/javascript/xml/impl/xmlbeans/*.java" + deprecation="on" + debug="${debug}" + classpathref="xmlbeans.classpath" + failonerror="${xmlimpl.compile.failonerror}" + target="${target-jvm}" + source="${source-level}" + /> + </target> + + <target name="no-old-e4x-compile" if="no-xmlbeans"> + <echo> + Skipping compilation of XMLBeans E4X implementation due to missing XMLBeans files + </echo> + </target> + + <target name="copy-source"> + <mkdir dir="${dist.dir}/xmlimplsrc"/> + <copy todir="${dist.dir}/xmlimplsrc"> + <fileset + dir="xmlimplsrc" + includes="**/*.java,**/*.properties,**/*.xml" + /> + </copy> + </target> + + <target name="clean"> + <delete includeEmptyDirs="true"> + <fileset + dir="${classes}" + includes="org/mozilla/javascript/xmlimpl/**" + /> + </delete> + </target> + + <property name="xmlbeans.tmp" location="${build.dir}/tmp-xbean" /> + <property name="xmlbeans.zip" location="${xmlbeans.tmp}/xbean.zip" /> + + <condition property="xmlbeans-present?"> + <and> + <available file="${xbean.jar}" /> + <available file="${jsr173.jar}" /> + </and> + </condition> + + <condition property="xmlbeans-zip-present?"> + <available file="${xmlbeans.zip}" /> + </condition> + + <target name="xmlbeans-get" unless="xmlbeans-zip-present?"> + <property + name="xmlbeans.url" + value="http://www.apache.org/dist/xmlbeans/binaries/xmlbeans-2.2.0.zip" + /> + + <mkdir dir="${xmlbeans.tmp}" /> + <get src="${xmlbeans.url}" dest="${xmlbeans.zip}" ignoreerrors="true" /> + </target> + + <target name="xmlbeans-unzip" unless="xmlbeans-present?"> + <antcall target="xmlbeans-get" /> + <unzip src="${xmlbeans.zip}" dest="${xmlbeans.tmp}" /> + <copy tofile="${xbean.jar}" file="${xmlbeans.tmp}/xmlbeans-2.2.0/lib/xbean.jar" /> + <copy tofile="${jsr173.jar}" file="${xmlbeans.tmp}/xmlbeans-2.2.0/lib/jsr173_1.0_api.jar" /> + <delete dir="${xmlbeans.tmp}" /> + </target> +</project> diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java new file mode 100644 index 0000000..a4cf585 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java @@ -0,0 +1,367 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ethan Hugg + * Terry Lucas + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; + +/** + * Class Namespace + * + */ +class Namespace extends IdScriptableObject +{ + static final long serialVersionUID = -5765755238131301744L; + + private static final Object NAMESPACE_TAG = new Object(); + + private Namespace prototype; + private XmlNode.Namespace ns; + + private Namespace() { + } + + static Namespace create(Scriptable scope, Namespace prototype, XmlNode.Namespace namespace) { + Namespace rv = new Namespace(); + rv.setParentScope(scope); + rv.prototype = prototype; + rv.setPrototype(prototype); + rv.ns = namespace; + return rv; + } + + final XmlNode.Namespace getDelegate() { + return ns; + } + + public void exportAsJSClass(boolean sealed) { + exportAsJSClass(MAX_PROTOTYPE_ID, this.getParentScope(), sealed); + } + + public String uri() { + return ns.getUri(); + } + + public String prefix() { + return ns.getPrefix(); + } + + public String toString() { + return uri(); + } + + public String toLocaleString() { + return toString(); + } + + private boolean equals(Namespace n) { + return uri().equals(n.uri()); + } + + public boolean equals(Object obj) { + if (!(obj instanceof Namespace)) return false; + return equals((Namespace)obj); + } + + protected Object equivalentValues(Object value) { + if (!(value instanceof Namespace)) return Scriptable.NOT_FOUND; + boolean result = equals((Namespace)value); + return result ? Boolean.TRUE : Boolean.FALSE; + } + + public String getClassName() { + return "Namespace"; + } + + public Object getDefaultValue(Class hint) { + return uri(); + } + +// #string_id_map# + private static final int + Id_prefix = 1, + Id_uri = 2, + MAX_INSTANCE_ID = 2; + + protected int getMaxInstanceId() + { + return super.getMaxInstanceId() + MAX_INSTANCE_ID; + } + + protected int findInstanceIdInfo(String s) + { + int id; +// #generated# Last update: 2007-08-20 08:23:22 EDT + 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; + break L0; + } +// #/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, super.getMaxInstanceId() + id); + } +// #/string_id_map# + + protected String getInstanceIdName(int id) + { + switch (id - super.getMaxInstanceId()) { + case Id_prefix: return "prefix"; + case Id_uri: return "uri"; + } + return super.getInstanceIdName(id); + } + + protected Object getInstanceIdValue(int id) + { + switch (id - super.getMaxInstanceId()) { + case Id_prefix: + if (ns.getPrefix() == null) return Undefined.instance; + return ns.getPrefix(); + case Id_uri: + return ns.getUri(); + } + return super.getInstanceIdValue(id); + } + + +// #string_id_map# + private static final int + Id_constructor = 1, + Id_toString = 2, + Id_toSource = 3, + MAX_PROTOTYPE_ID = 3; + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2007-08-20 08:23:22 EDT + L0: { id = 0; String X = null; int c; + int s_length = s.length(); + if (s_length==8) { + c=s.charAt(3); + if (c=='o') { X="toSource";id=Id_toSource; } + else if (c=='t') { 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; + break L0; + } +// #/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; + case Id_toSource: arity=0; s="toSource"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(NAMESPACE_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, + Context cx, + Scriptable scope, + Scriptable thisObj, + Object[] args) + { + if (!f.hasTag(NAMESPACE_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + switch (id) { + case Id_constructor: + return jsConstructor(cx, (thisObj == null), args); + case Id_toString: + return realThis(thisObj, f).toString(); + case Id_toSource: + return realThis(thisObj, f).js_toSource(); + } + throw new IllegalArgumentException(String.valueOf(id)); + } + + private Namespace realThis(Scriptable thisObj, IdFunctionObject f) { + if(!(thisObj instanceof Namespace)) + throw incompatibleCallError(f); + return (Namespace)thisObj; + } + + Namespace newNamespace(String uri) { + Namespace prototype = (this.prototype == null) ? this : this.prototype; + return create( this.getParentScope(), prototype, XmlNode.Namespace.create(uri) ); + } + + Namespace newNamespace(String prefix, String uri) { + if (prefix == null) return newNamespace(uri); + Namespace prototype = (this.prototype == null) ? this : this.prototype; + return create( this.getParentScope(), prototype, XmlNode.Namespace.create(prefix, uri) ); + } + + Namespace constructNamespace(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) { + // TODO Is there a way to push this back into QName so that we can make prefix() private? + prefix = qname.prefix(); + } else { + uri = qname.toString(); + prefix = null; + } + } else { + uri = ScriptRuntime.toString(uriValue); + prefix = (uri.length() == 0) ? "" : null; + } + + return newNamespace(prefix, uri); + } + + Namespace castToNamespace(Object namespaceObj) { + if (namespaceObj instanceof Namespace) { + return (Namespace)namespaceObj; + } + return constructNamespace(namespaceObj); + } + + private Namespace constructNamespace(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.typeError( + "Illegal prefix '"+prefix+"' for 'no namespace'."); + } + } + } else if (prefixValue == Undefined.instance) { + prefix = ""; + } else if (!XMLName.accept(prefixValue)) { + prefix = ""; + } else { + prefix = ScriptRuntime.toString(prefixValue); + } + + return newNamespace(prefix, uri); + } + + private Namespace constructNamespace() { + return newNamespace("", ""); + } + + private Object jsConstructor(Context cx, boolean inNewExpr, Object[] args) + { + if (!inNewExpr && args.length == 1) { + return castToNamespace(args[0]); + } + + if (args.length == 0) { + return constructNamespace(); + } else if (args.length == 1) { + return constructNamespace(args[0]); + } else { + return constructNamespace(args[0], args[1]); + } + } + + private String js_toSource() + { + StringBuffer sb = new StringBuffer(); + sb.append('('); + toSourceImpl(ns.getPrefix(), ns.getUri(), sb); + sb.append(')'); + return sb.toString(); + } + + static void toSourceImpl(String prefix, String uri, StringBuffer sb) + { + sb.append("new Namespace("); + if (uri.length() == 0) { + if (!"".equals(prefix)) throw new IllegalArgumentException(prefix); + } else { + sb.append('\''); + if (prefix != null) { + sb.append(ScriptRuntime.escapeString(prefix, '\'')); + sb.append("', '"); + } + sb.append(ScriptRuntime.escapeString(uri, '\'')); + sb.append('\''); + } + sb.append(')'); + } +} diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java new file mode 100644 index 0000000..90a18cb --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java @@ -0,0 +1,381 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ethan Hugg + * Terry Lucas + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; + +/** + * Class QName + * + */ +final class QName extends IdScriptableObject +{ + static final long serialVersionUID = 416745167693026750L; + + private static final Object QNAME_TAG = new Object(); + + private XMLLibImpl lib; + + private QName prototype; + + private XmlNode.QName delegate; + + private QName() { + } + + static QName create(XMLLibImpl lib, Scriptable scope, QName prototype, XmlNode.QName delegate) { + QName rv = new QName(); + rv.lib = lib; + rv.setParentScope(scope); + rv.prototype = prototype; + rv.setPrototype(prototype); + rv.delegate = delegate; + return rv; + } + +// /** @deprecated */ +// static QName create(XMLLibImpl lib, XmlNode.QName nodeQname) { +// return create(lib, lib.globalScope(), lib.qnamePrototype(), nodeQname); +// } + + void exportAsJSClass(boolean sealed) { + exportAsJSClass(MAX_PROTOTYPE_ID, getParentScope(), sealed); + } + + public String toString() { + // ECMA357 13.3.4.2 + if (delegate.getNamespace() == null) { + return "*::" + localName(); + } else if (delegate.getNamespace().isGlobal()) { + // leave as empty + return localName(); + } else { + return uri() + "::" + localName(); + } + } + + public String localName() { + if (delegate.getLocalName() == null) return "*"; + return delegate.getLocalName(); + } + + /** + @deprecated + + This property is supposed to be invisible and I think we can make it private at some point, though Namespace + might need it + */ + String prefix() { + if (delegate.getNamespace() == null) return null; + return delegate.getNamespace().getPrefix(); + } + + String uri() { + if (delegate.getNamespace() == null) return null; + return delegate.getNamespace().getUri(); + } + + /** @deprecated */ + final XmlNode.QName toNodeQname() { + return delegate; + } + + final XmlNode.QName getDelegate() { + return delegate; + } + + public boolean equals(Object obj) { + if(!(obj instanceof QName)) return false; + return equals((QName)obj); + } + + protected Object equivalentValues(Object value) + { + if(!(value instanceof QName)) return Scriptable.NOT_FOUND; + boolean result = equals((QName)value); + return result ? Boolean.TRUE : Boolean.FALSE; + } + + private boolean equals(QName q) { + return this.delegate.isEqualTo(q.delegate); + } + + public String getClassName() { + return "QName"; + } + + public Object getDefaultValue(Class hint) { + return toString(); + } + +// #string_id_map# + private static final int + Id_localName = 1, + Id_uri = 2, + MAX_INSTANCE_ID = 2; + + protected int getMaxInstanceId() + { + return super.getMaxInstanceId() + MAX_INSTANCE_ID; + } + + protected int findInstanceIdInfo(String s) + { + int id; +// #generated# Last update: 2007-08-20 08:21:41 EDT + 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; + break L0; + } +// #/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, super.getMaxInstanceId() + id); + } +// #/string_id_map# + + protected String getInstanceIdName(int id) + { + switch (id - super.getMaxInstanceId()) { + case Id_localName: return "localName"; + case Id_uri: return "uri"; + } + return super.getInstanceIdName(id); + } + + protected Object getInstanceIdValue(int id) + { + switch (id - super.getMaxInstanceId()) { + 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, + Id_toSource = 3, + MAX_PROTOTYPE_ID = 3; + + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2007-08-20 08:21:41 EDT + L0: { id = 0; String X = null; int c; + int s_length = s.length(); + if (s_length==8) { + c=s.charAt(3); + if (c=='o') { X="toSource";id=Id_toSource; } + else if (c=='t') { 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; + break L0; + } +// #/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; + case Id_toSource: arity=0; s="toSource"; break; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(QNAME_TAG, id, s, arity); + } + + public Object execIdCall(IdFunctionObject f, + Context cx, + Scriptable scope, + Scriptable thisObj, + Object[] args) + { + if (!f.hasTag(QNAME_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + switch (id) { + case Id_constructor: + return jsConstructor(cx, (thisObj == null), args); + case Id_toString: + return realThis(thisObj, f).toString(); + case Id_toSource: + return realThis(thisObj, f).js_toSource(); + } + throw new IllegalArgumentException(String.valueOf(id)); + } + + private QName realThis(Scriptable thisObj, IdFunctionObject f) + { + if(!(thisObj instanceof QName)) + throw incompatibleCallError(f); + return (QName)thisObj; + } + + QName newQName(XMLLibImpl lib, String q_uri, String q_localName, String q_prefix) { + QName prototype = this.prototype; + if (prototype == null) { + prototype = this; + } + XmlNode.Namespace ns = null; + if (q_prefix != null) { + ns = XmlNode.Namespace.create(q_uri, q_prefix); + } else if (q_uri != null) { + ns = XmlNode.Namespace.create(q_uri); + } else { + ns = null; + } + if (q_localName != null && q_localName.equals("*")) q_localName = null; + return create(lib, this.getParentScope(), prototype, XmlNode.QName.create(ns, q_localName)); + } + + // See ECMA357 13.3.2 + QName constructQName(XMLLibImpl lib, Context cx, Object namespace, Object name) { + String nameString = null; + if (name instanceof QName) { + if (namespace == Undefined.instance) { + return (QName)name; + } else { + nameString = ((QName)name).localName(); + } + } + if (name == Undefined.instance) { + nameString = ""; + } else { + nameString = ScriptRuntime.toString(name); + } + + if (namespace == Undefined.instance) { + if ("*".equals(nameString)) { + namespace = null; + } else { + namespace = lib.getDefaultNamespace(cx); + } + } + Namespace namespaceNamespace = null; + if (namespace == null) { + // leave as null + } else if (namespace instanceof Namespace) { + namespaceNamespace = (Namespace)namespace; + } else { + namespaceNamespace = lib.newNamespace(ScriptRuntime.toString(namespace)); + } + String q_localName = nameString; + String q_uri; + String q_prefix; + if (namespace == null) { + q_uri = null; + q_prefix = null; // corresponds to undefined; see QName class + } else { + q_uri = namespaceNamespace.uri(); + q_prefix = namespaceNamespace.prefix(); + } + return newQName(lib, q_uri, q_localName, q_prefix); + } + + QName constructQName(XMLLibImpl lib, Context cx, Object nameValue) { + return constructQName(lib, cx, Undefined.instance, nameValue); + } + + QName castToQName(XMLLibImpl lib, Context cx, Object qnameValue) { + if (qnameValue instanceof QName) { + return (QName)qnameValue; + } + return constructQName(lib, cx, qnameValue); + } + + private Object jsConstructor(Context cx, boolean inNewExpr, Object[] args) { + // See ECMA357 13.3.2 + if (!inNewExpr && args.length == 1) { + return castToQName(lib, cx, args[0]); + } + if (args.length == 0) { + return constructQName(lib, cx, Undefined.instance); + } else if (args.length == 1) { + return constructQName(lib, cx, args[0]); + } else { + return constructQName(lib, cx, args[0], args[1]); + } + } + + private String js_toSource() { + StringBuffer sb = new StringBuffer(); + sb.append('('); + toSourceImpl(uri(), localName(), prefix(), sb); + sb.append(')'); + return sb.toString(); + } + + private static void toSourceImpl(String uri, String localName, String prefix, StringBuffer sb) { + sb.append("new QName("); + if (uri == null && prefix == null) { + if (!"*".equals(localName)) { + sb.append("null, "); + } + } else { + Namespace.toSourceImpl(prefix, uri, sb); + sb.append(", "); + } + sb.append('\''); + sb.append(ScriptRuntime.escapeString(localName, '\'')); + sb.append("')"); + } +} diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java new file mode 100644 index 0000000..090ae1a --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java @@ -0,0 +1,734 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ethan Hugg + * Terry Lucas + * Milen Nankov + * David P. Caldwell <inonit@inonit.com> + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; +import org.mozilla.javascript.xml.XMLObject; + +class XML extends XMLObjectImpl { + static final long serialVersionUID = -630969919086449092L; + + private XmlNode node; + + XML(XMLLibImpl lib, Scriptable scope, XMLObject prototype, XmlNode node) { + super(lib, scope, prototype); + initialize(node); + } + + void initialize(XmlNode node) { + this.node = node; + this.node.setXml(this); + } + + final XML getXML() { + return this; + } + + void replaceWith(XML value) { + // We use the underlying document structure if the node is not + // "standalone," but we need to just replace the XmlNode instance + // otherwise + if (this.node.parent() != null || false) { + this.node.replaceWith(value.node); + } else { + this.initialize(value.node); + } + } + + /** @deprecated I would love to encapsulate this somehow. */ + XML makeXmlFromString(XMLName name, String value) { + try { + return newTextElementXML(this.node, name.toQname(), value.toString()); + } catch(Exception e) { + throw ScriptRuntime.typeError(e.getMessage()); + } + } + + /** @deprecated Rename this, at the very least. But it's not clear it's even necessary */ + XmlNode getAnnotation() { + return node; + } + + // + // Methods from ScriptableObject + // + + // TODO Either cross-reference this next comment with the specification or delete it and change the behavior + // The comment: XML[0] should return this, all other indexes are Undefined + public Object get(int index, Scriptable start) { + if (index == 0) { + return this; + } else { + return Scriptable.NOT_FOUND; + } + } + + public boolean has(int index, Scriptable start) { + return (index == 0); + } + + public void put(int index, Scriptable start, Object value) { + // TODO Clarify the following comment and add a reference to the spec + // The comment: Spec says assignment to indexed XML object should return type error + throw ScriptRuntime.typeError("Assignment to indexed XML is not allowed"); + } + + public Object[] getIds() { + if (isPrototype()) { + return new Object[0]; + } else { + return new Object[] { new Integer(0) }; + } + } + + // TODO This is how I found it but I am not sure it makes sense + public void delete(int index) { + if (index == 0) { + this.remove(); + } + } + + // + // Methods from XMLObjectImpl + // + + boolean hasXMLProperty(XMLName xmlName) { + if (isPrototype()) { + return getMethod(xmlName.localName()) != NOT_FOUND; + } else { + return (getPropertyList(xmlName).length() > 0) || (getMethod(xmlName.localName()) != NOT_FOUND); + } + } + + Object getXMLProperty(XMLName xmlName) { + if (isPrototype()) { + return getMethod(xmlName.localName()); + } else { + return getPropertyList(xmlName); + } + } + + // + // + // Methods that merit further review + // + // + + XmlNode.QName getNodeQname() { + return this.node.getQname(); + } + + XML[] getChildren() { + if (!isElement()) return null; + XmlNode[] children = this.node.getMatchingChildren(XmlNode.Filter.TRUE); + XML[] rv = new XML[children.length]; + for (int i=0; i<rv.length; i++) { + rv[i] = toXML(children[i]); + } + return rv; + } + + XML[] getAttributes() { + XmlNode[] attributes = this.node.getAttributes(); + XML[] rv = new XML[attributes.length]; + for (int i=0; i<rv.length; i++) { + rv[i] = toXML(attributes[i]); + } + return rv; + } + + // Used only by XML, XMLList + XMLList getPropertyList(XMLName name) { + return name.getMyValueOn(this); + } + + void deleteXMLProperty(XMLName name) { + XMLList list = getPropertyList(name); + for (int i=0; i<list.length(); i++) { + list.item(i).node.deleteMe(); + } + } + + void putXMLProperty(XMLName xmlName, Object value) { + if (isPrototype()) { + // TODO Is this really a no-op? Check the spec to be sure + } else { + xmlName.setMyValueOn(this, value); + } + } + + boolean hasOwnProperty(XMLName xmlName) { + boolean hasProperty = false; + + if (isPrototype()) { + String property = xmlName.localName(); + hasProperty = (0 != findPrototypeId(property)); + } else { + hasProperty = (getPropertyList(xmlName).length() > 0); + } + + return hasProperty; + } + + protected Object jsConstructor(Context cx, boolean inNewExpr, Object[] args) { + if (args.length == 0 || args[0] == null || args[0] == Undefined.instance) { + args = new Object[] { "" }; + } + // ECMA 13.4.2 does not appear to specify what to do if multiple arguments are sent. + XML toXml = ecmaToXml(args[0]); + if (inNewExpr) { + return toXml.copy(); + } else { + return toXml; + } + } + + // See ECMA 357, 11_2_2_1, Semantics, 3_f. + public Scriptable getExtraMethodSource(Context cx) { + if (hasSimpleContent()) { + String src = toString(); + return ScriptRuntime.toObjectOrNull(cx, src); + } + return null; + } + + // + // TODO Miscellaneous methods not yet grouped + // + + void removeChild(int index) { + this.node.removeChild(index); + } + + void normalize() { + this.node.normalize(); + } + + private XML toXML(XmlNode node) { + if (node.getXml() == null) { + node.setXml(newXML(node)); + } + return node.getXml(); + } + + void setAttribute(XMLName xmlName, Object value) { + if (!isElement()) throw new IllegalStateException("Can only set attributes on elements."); + // TODO Is this legal, but just not "supported"? If so, support it. + if (xmlName.uri() == null && xmlName.localName().equals("*")) { + throw ScriptRuntime.typeError("@* assignment not supported."); + } + this.node.setAttribute(xmlName.toQname(), ScriptRuntime.toString(value)); + } + + void remove() { + this.node.deleteMe(); + } + + void addMatches(XMLList rv, XMLName name) { + name.addMatches(rv, this); + } + + XMLList elements(XMLName name) { + XMLList rv = newXMLList(); + rv.setTargets(this, name.toQname()); + // TODO Should have an XMLNode.Filter implementation based on XMLName + XmlNode[] elements = this.node.getMatchingChildren(XmlNode.Filter.ELEMENT); + for (int i=0; i<elements.length; i++) { + if (name.matches( toXML(elements[i]) )) { + rv.addToList( toXML(elements[i]) ); + } + } + return rv; + } + + XMLList child(XMLName xmlName) { + // TODO Right now I think this method would allow child( "@xxx" ) to return the xxx attribute, which is wrong + + XMLList rv = newXMLList(); + + // TODO Should this also match processing instructions? If so, we have to change the filter and also the XMLName + // class to add an acceptsProcessingInstruction() method + + XmlNode[] elements = this.node.getMatchingChildren(XmlNode.Filter.ELEMENT); + for (int i=0; i<elements.length; i++) { + if (xmlName.matchesElement(elements[i].getQname())) { + rv.addToList( toXML(elements[i]) ); + } + } + rv.setTargets(this, xmlName.toQname()); + return rv; + } + + XML replace(XMLName xmlName, Object xml) { + putXMLProperty(xmlName, xml); + return this; + } + + XMLList children() { + XMLList rv = newXMLList(); + XMLName all = XMLName.formStar(); + rv.setTargets(this, all.toQname()); + XmlNode[] children = this.node.getMatchingChildren(XmlNode.Filter.TRUE); + for (int i=0; i<children.length; i++) { + rv.addToList( toXML(children[i]) ); + } + return rv; + } + + XMLList child(int index) { + // ECMA357 13.4.4.6 (numeric case) + XMLList result = newXMLList(); + result.setTargets(this, null); + if (index >= 0 && index < this.node.getChildCount()) { + result.addToList(getXmlChild(index)); + } + return result; + } + + XML getXmlChild(int index) { + XmlNode child = this.node.getChild(index); + if (child.getXml() == null) { + child.setXml(newXML(child)); + } + return child.getXml(); + } + + int childIndex() { + return this.node.getChildIndex(); + } + + boolean contains(Object xml) { + if (xml instanceof XML) { + return equivalentXml(xml); + } else { + return false; + } + } + + // Method overriding XMLObjectImpl + boolean equivalentXml(Object target) { + boolean result = false; + + if (target instanceof XML) { + // TODO This is a horrifyingly inefficient way to do this so we should make it better. It may also not work. + return this.node.toXmlString(getProcessor()).equals( ((XML)target).node.toXmlString(getProcessor()) ); + } else if (target instanceof XMLList) { + // TODO Is this right? Check the spec ... + XMLList otherList = (XMLList) target; + + if (otherList.length() == 1) { + result = equivalentXml(otherList.getXML()); + } + } else if (hasSimpleContent()) { + String otherStr = ScriptRuntime.toString(target); + + result = toString().equals(otherStr); + } + + return result; + } + + XMLObjectImpl copy() { + return newXML( this.node.copy() ); + } + + boolean hasSimpleContent() { + if (isComment() || isProcessingInstruction()) return false; + if (isText() || this.node.isAttributeType()) return true; + return !this.node.hasChildElement(); + } + + boolean hasComplexContent() { + return !hasSimpleContent(); + } + + // TODO Cross-reference comment below with spec + // Comment is: Length of an XML object is always 1, it's a list of XML objects of size 1. + int length() { + return 1; + } + + // TODO it is not clear what this method was for ... + boolean is(XML other) { + return this.node.isSameNode(other.node); + } + + Object nodeKind() { + return ecmaClass(); + } + + Object parent() { + XmlNode parent = this.node.parent(); + if (parent == null) return null; + return newXML(this.node.parent()); + } + + boolean propertyIsEnumerable(Object name) + { + boolean result; + if (name instanceof Integer) { + result = (((Integer)name).intValue() == 0); + } else if (name instanceof Number) { + double x = ((Number)name).doubleValue(); + // Check that number is positive 0 + result = (x == 0.0 && 1.0 / x > 0); + } else { + result = ScriptRuntime.toString(name).equals("0"); + } + return result; + } + + Object valueOf() { + return this; + } + + // + // Selection of children + // + + XMLList comments() { + XMLList rv = newXMLList(); + this.node.addMatchingChildren(rv, XmlNode.Filter.COMMENT); + return rv; + } + + XMLList text() { + XMLList rv = newXMLList(); + this.node.addMatchingChildren(rv, XmlNode.Filter.TEXT); + return rv; + } + + XMLList processingInstructions(XMLName xmlName) { + XMLList rv = newXMLList(); + this.node.addMatchingChildren(rv, XmlNode.Filter.PROCESSING_INSTRUCTION(xmlName)); + return rv; + } + + // + // Methods relating to modification of child nodes + // + + // We create all the nodes we are inserting before doing the insert to + // avoid nasty cycles caused by mutability of these objects. For example, + // what if the toString() method of value modifies the XML object we were + // going to insert into? insertAfter might get confused about where to + // insert. This actually came up with SpiderMonkey, leading to a (very) + // long discussion. See bug #354145. + private XmlNode[] getNodesForInsert(Object value) { + if (value instanceof XML) { + return new XmlNode[] { ((XML)value).node }; + } else if (value instanceof XMLList) { + XMLList list = (XMLList)value; + XmlNode[] rv = new XmlNode[list.length()]; + for (int i=0; i<list.length(); i++) { + rv[i] = list.item(i).node; + } + return rv; + } else { + return new XmlNode[] { + XmlNode.createText(getProcessor(), ScriptRuntime.toString(value)) + }; + } + } + + XML replace(int index, Object xml) { + XMLList xlChildToReplace = child(index); + if (xlChildToReplace.length() > 0) { + // One exists an that index + XML childToReplace = xlChildToReplace.item(0); + insertChildAfter(childToReplace, xml); + removeChild(index); + } + return this; + } + + XML prependChild(Object xml) { + if (this.node.isParentType()) { + this.node.insertChildrenAt(0, getNodesForInsert(xml)); + } + return this; + } + + XML appendChild(Object xml) { + if (this.node.isParentType()) { + XmlNode[] nodes = getNodesForInsert(xml); + this.node.insertChildrenAt(this.node.getChildCount(), nodes); + } + return this; + } + + private int getChildIndexOf(XML child) { + for (int i=0; i<this.node.getChildCount(); i++) { + if (this.node.getChild(i).isSameNode(child.node)) { + return i; + } + } + return -1; + } + + XML insertChildBefore(XML child, Object xml) { + if (child == null) { + // Spec says inserting before nothing is the same as appending + appendChild(xml); + } else { + XmlNode[] toInsert = getNodesForInsert(xml); + int index = getChildIndexOf(child); + if (index != -1) { + this.node.insertChildrenAt(index, toInsert); + } + } + + return this; + } + + XML insertChildAfter(XML child, Object xml) { + if (child == null) { + // Spec says inserting after nothing is the same as prepending + prependChild(xml); + } else { + XmlNode[] toInsert = getNodesForInsert(xml); + int index = getChildIndexOf(child); + if (index != -1) { + this.node.insertChildrenAt(index+1, toInsert); + } + } + + return this; + } + + XML setChildren(Object xml) { + // TODO Have not carefully considered the spec but it seems to call for this + if (!isElement()) return this; + + while(this.node.getChildCount() > 0) { + this.node.removeChild(0); + } + XmlNode[] toInsert = getNodesForInsert(xml); + // append new children + this.node.insertChildrenAt(0, toInsert); + + return this; + } + + // + // Name and namespace-related methods + // + + private void addInScopeNamespace(Namespace ns) { + if (!isElement()) { + return; + } + // See ECMA357 9.1.1.13 + // in this implementation null prefix means ECMA undefined + if (ns.prefix() != null) { + if (ns.prefix().length() == 0 && ns.uri().length() == 0) { + return; + } + if (node.getQname().getNamespace().getPrefix().equals(ns.prefix())) { + node.invalidateNamespacePrefix(); + } + node.declareNamespace(ns.prefix(), ns.uri()); + } else { + return; + } + } + + Namespace[] inScopeNamespaces() { + XmlNode.Namespace[] inScope = this.node.getInScopeNamespaces(); + return createNamespaces(inScope); + } + + private XmlNode.Namespace adapt(Namespace ns) { + if (ns.prefix() == null) { + return XmlNode.Namespace.create(ns.uri()); + } else { + return XmlNode.Namespace.create(ns.prefix(), ns.uri()); + } + } + + XML removeNamespace(Namespace ns) { + if (!isElement()) return this; + this.node.removeNamespace(adapt(ns)); + return this; + } + + XML addNamespace(Namespace ns) { + addInScopeNamespace(ns); + return this; + } + + QName name() { + if (isText() || isComment()) return null; + if (isProcessingInstruction()) return newQName("", this.node.getQname().getLocalName(), null); + return newQName(node.getQname()); + } + + Namespace[] namespaceDeclarations() { + XmlNode.Namespace[] declarations = node.getNamespaceDeclarations(); + return createNamespaces(declarations); + } + + Namespace namespace(String prefix) { + if (prefix == null) { + return createNamespace( this.node.getNamespaceDeclaration() ); + } else { + return createNamespace( this.node.getNamespaceDeclaration(prefix) ); + } + } + + String localName() { + if (name() == null) return null; + return name().localName(); + } + + void setLocalName(String localName) { + // ECMA357 13.4.4.34 + if (isText() || isComment()) return; + this.node.setLocalName(localName); + } + + void setName(QName name) { + // See ECMA357 13.4.4.35 + if (isText() || isComment()) return; + if (isProcessingInstruction()) { + // Spec says set the name URI to empty string and then set the [[Name]] property, but I understand this to do the same + // thing, unless we allow colons in processing instruction targets, which I think we do not. + this.node.setLocalName(name.localName()); + return; + } + node.renameNode(name.getDelegate()); + } + + void setNamespace(Namespace ns) { + // See ECMA357 13.4.4.36 + if (isText() || isComment() || isProcessingInstruction()) return; + setName(newQName(ns.uri(), localName(), ns.prefix())); + } + + final String ecmaClass() { + // See ECMA357 9.1 + + // TODO See ECMA357 9.1.1 last paragraph for what defaults should be + + if (node.isTextType()) { + return "text"; + } else if (node.isAttributeType()) { + return "attribute"; + } else if (node.isCommentType()) { + return "comment"; + } else if (node.isProcessingInstructionType()) { + return "processing-instruction"; + } else if (node.isElementType()) { + return "element"; + } else { + throw new RuntimeException("Unrecognized type: " + node); + } + } + + public String getClassName() { + // TODO: This appears to confuse the interpreter if we use the "real" class property from ECMA. Otherwise this code + // would be: + // return ecmaClass(); + return "XML"; + } + + private String ecmaValue() { + return node.ecmaValue(); + } + + private String ecmaToString() { + // See ECMA357 10.1.1 + if (isAttribute() || isText()) { + return ecmaValue(); + } + if (this.hasSimpleContent()) { + StringBuffer rv = new StringBuffer(); + for (int i=0; i < this.node.getChildCount(); i++) { + XmlNode child = this.node.getChild(i); + if (!child.isProcessingInstructionType() && + !child.isCommentType()) + { + // TODO: Probably inefficient; taking clean non-optimized + // solution for now + XML x = new XML(getLib(), getParentScope(), + (XMLObject)getPrototype(), child); + rv.append(x.toString()); + } + } + return rv.toString(); + } + return toXMLString(); + } + + public String toString() { + return ecmaToString(); + } + + String toXMLString() { + return this.node.ecmaToXMLString(getProcessor()); + } + + final boolean isAttribute() { + return node.isAttributeType(); + } + + final boolean isComment() { + return node.isCommentType(); + } + + final boolean isText() { + return node.isTextType(); + } + + final boolean isElement() { + return node.isElementType(); + } + + final boolean isProcessingInstruction() { + return node.isProcessingInstructionType(); + } + + // Support experimental Java interface + org.w3c.dom.Node toDomNode() { + return node.toDomNode(); + } +} diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java new file mode 100644 index 0000000..fac8773 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java @@ -0,0 +1,280 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * David P. Caldwell <inonit@inonit.com> + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; + +class XMLCtor extends IdFunctionObject +{ + static final long serialVersionUID = -8708195078359817341L; + + private static final Object XMLCTOR_TAG = new Object(); + + private XmlProcessor options; +// private XMLLibImpl lib; + + XMLCtor(XML xml, Object tag, int id, int arity) + { + super(xml, tag, id, arity); +// this.lib = xml.lib; + this.options = xml.getProcessor(); + activatePrototypeMap(MAX_FUNCTION_ID); + } + + private void writeSetting(Scriptable target) + { + for (int i = 1; i <= MAX_INSTANCE_ID; ++i) { + int id = super.getMaxInstanceId() + 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 = super.getMaxInstanceId() + i; + String name = getInstanceIdName(id); + Object value = ScriptableObject.getProperty(source, name); + if (value == Scriptable.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; + + protected int getMaxInstanceId() + { + return super.getMaxInstanceId() + MAX_INSTANCE_ID; + } + + protected int findInstanceIdInfo(String s) { + int id; +// #generated# Last update: 2007-08-20 09:01:10 EDT + 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; + break L0; + } +// #/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, super.getMaxInstanceId() + id); + } + +// #/string_id_map# + + protected String getInstanceIdName(int id) + { + switch (id - super.getMaxInstanceId()) { + 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 - super.getMaxInstanceId()) { + case Id_ignoreComments: + return ScriptRuntime.wrapBoolean(options.isIgnoreComments()); + case Id_ignoreProcessingInstructions: + return ScriptRuntime.wrapBoolean(options.isIgnoreProcessingInstructions()); + case Id_ignoreWhitespace: + return ScriptRuntime.wrapBoolean(options.isIgnoreWhitespace()); + case Id_prettyIndent: + return ScriptRuntime.wrapInt(options.getPrettyIndent()); + case Id_prettyPrinting: + return ScriptRuntime.wrapBoolean(options.isPrettyPrinting()); + } + return super.getInstanceIdValue(id); + } + + protected void setInstanceIdValue(int id, Object value) { + switch (id - super.getMaxInstanceId()) { + case Id_ignoreComments: + options.setIgnoreComments(ScriptRuntime.toBoolean(value)); + return; + case Id_ignoreProcessingInstructions: + options.setIgnoreProcessingInstructions(ScriptRuntime.toBoolean(value)); + return; + case Id_ignoreWhitespace: + options.setIgnoreWhitespace(ScriptRuntime.toBoolean(value)); + return; + case Id_prettyIndent: + options.setPrettyIndent(ScriptRuntime.toInt32(value)); + return; + case Id_prettyPrinting: + options.setPrettyPrinting(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: 2007-08-20 09:01:10 EDT + 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; + break L0; + } +// #/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 execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(XMLCTOR_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + switch (id) { + case Id_defaultSettings: { + options.setDefault(); + Scriptable obj = cx.newObject(scope); + writeSetting(obj); + return obj; + } + case Id_settings: { + Scriptable obj = cx.newObject(scope); + writeSetting(obj); + return obj; + } + case Id_setSettings: { + if (args.length == 0 + || args[0] == null + || args[0] == Undefined.instance) + { + options.setDefault(); + } else if (args[0] instanceof Scriptable) { + readSettings((Scriptable)args[0]); + } + return Undefined.instance; + } + } + throw new IllegalArgumentException(String.valueOf(id)); + } + + /** + hasInstance for XML objects works differently than other objects; see ECMA357 13.4.3.10. + */ + public boolean hasInstance(Scriptable instance) { + return (instance instanceof XML || instance instanceof XMLList); + } +} diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java new file mode 100644 index 0000000..6d45240 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java @@ -0,0 +1,606 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * David P. Caldwell <inonit@inonit.com> + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import java.io.Serializable; + +import org.mozilla.javascript.*; +import org.mozilla.javascript.xml.*; + +public final class XMLLibImpl extends XMLLib implements Serializable { + // TODO Document that this only works with JDK 1.5 or backport its + // features to earlier versions + private static final long serialVersionUID = 1L; + + // + // EXPERIMENTAL Java interface + // + + /** + This experimental interface is undocumented. + */ + public static org.w3c.dom.Node toDomNode(Object xmlObject) { + // Could return DocumentFragment for XMLList + // Probably a single node for XMLList with one element + if (xmlObject instanceof XML) { + return ((XML)xmlObject).toDomNode(); + } else { + throw new IllegalArgumentException("xmlObject is not an XML object in JavaScript."); + } + } + + public static void init(Context cx, Scriptable scope, boolean sealed) { + XMLLibImpl lib = new XMLLibImpl(scope); + XMLLib bound = lib.bindToScope(scope); + if (bound == lib) { + lib.exportToScope(sealed); + } + } + + private Scriptable globalScope; + + private XML xmlPrototype; + private XMLList xmlListPrototype; + private Namespace namespacePrototype; + private QName qnamePrototype; + + private XmlProcessor options = new XmlProcessor(); + + private XMLLibImpl(Scriptable globalScope) { + this.globalScope = globalScope; + } + + /** @deprecated */ + QName qnamePrototype() { + return qnamePrototype; + } + + /** @deprecated */ + Scriptable globalScope() { + return globalScope; + } + + XmlProcessor getProcessor() { + return options; + } + + private void exportToScope(boolean sealed) { + xmlPrototype = newXML(XmlNode.createText(options, "")); + xmlListPrototype = newXMLList(); + namespacePrototype = Namespace.create(this.globalScope, null, XmlNode.Namespace.GLOBAL); + qnamePrototype = QName.create(this, this.globalScope, null, XmlNode.QName.create(XmlNode.Namespace.create(""), "")); + + xmlPrototype.exportAsJSClass(sealed); + xmlListPrototype.exportAsJSClass(sealed); + namespacePrototype.exportAsJSClass(sealed); + qnamePrototype.exportAsJSClass(sealed); + } + + /** @deprecated */ + XMLName toAttributeName(Context cx, Object nameValue) { + if (nameValue instanceof XMLName) { + // TODO Will this always be an XMLName of type attribute name? + return (XMLName)nameValue; + } else if (nameValue instanceof QName) { + return XMLName.create( ((QName)nameValue).getDelegate(), true, false ); + } else if (nameValue instanceof Boolean + || nameValue instanceof Number + || nameValue == Undefined.instance + || nameValue == null) { + throw badXMLName(nameValue); + } else { + // TODO Not 100% sure that putting these in global namespace is the right thing to do + String localName = null; + if (nameValue instanceof String) { + localName = (String)nameValue; + } else { + localName = ScriptRuntime.toString(nameValue); + } + if (localName != null && localName.equals("*")) localName = null; + return XMLName.create(XmlNode.QName.create(XmlNode.Namespace.create(""), localName), true, false); + } + } + + private static RuntimeException badXMLName(Object value) + { + String msg; + if (value instanceof Number) { + msg = "Can not construct XML name from number: "; + } else if (value instanceof Boolean) { + msg = "Can not construct XML name from boolean: "; + } else if (value == Undefined.instance || value == null) { + msg = "Can not construct XML name from "; + } else { + throw new IllegalArgumentException(value.toString()); + } + return ScriptRuntime.typeError(msg+ScriptRuntime.toString(value)); + } + + XMLName toXMLNameFromString(Context cx, String name) { + return XMLName.create( getDefaultNamespaceURI(cx), name ); + } + + /** @deprecated */ + 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 if (nameValue instanceof String) { + result = toXMLNameFromString(cx, (String)nameValue); + } else if (nameValue instanceof Boolean + || nameValue instanceof Number + || nameValue == Undefined.instance + || nameValue == null) { + throw badXMLName(nameValue); + } 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 { + throw badXMLName(value); + } + } 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 if (value instanceof Boolean + || value == Undefined.instance + || value == null) + { + throw badXMLName(value); + } 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; + } + + Object addXMLObjects(Context cx, XMLObject obj1, XMLObject obj2) + { + XMLList listToAdd = newXMLList(); + + 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 = newXMLListFrom(obj1); + } + } else { + listToAdd.addToList(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(obj2); + } + + return listToAdd; + } + + private Ref xmlPrimaryReference(Context cx, XMLName xmlName, Scriptable scope) { + XMLObjectImpl xmlObj; + XMLObjectImpl firstXml = null; + 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; + } + if (firstXml == null) { + firstXml = xmlObj; + } + } + scope = scope.getParentScope(); + if (scope == null) { + xmlObj = firstXml; + break; + } + } + + // xmlObj == null corresponds to undefined as the target of + // the reference + if (xmlObj != null) { + xmlName.initXMLObject(xmlObj); + } + return xmlName; + } + + Namespace castToNamespace(Context cx, Object namespaceObj) { + return this.namespacePrototype.castToNamespace(namespaceObj); + } + + private String getDefaultNamespaceURI(Context cx) { + return getDefaultNamespace(cx).uri(); + } + + Namespace newNamespace(String uri) { + return this.namespacePrototype.newNamespace(uri); + } + + Namespace getDefaultNamespace(Context cx) { + if (cx == null) { + cx = Context.getCurrentContext(); + if (cx == null) { + return namespacePrototype; + } + } + + Object ns = ScriptRuntime.searchDefaultNamespace(cx); + if (ns == null) { + return namespacePrototype; + } else { + if (ns instanceof Namespace) { + return (Namespace)ns; + } else { + // TODO Clarify or remove the following comment + // Should not happen but for now it could + // due to bad searchDefaultNamespace implementation. + return namespacePrototype; + } + } + } + + Namespace[] createNamespaces(XmlNode.Namespace[] declarations) { + Namespace[] rv = new Namespace[declarations.length]; + for (int i=0; i<declarations.length; i++) { + rv[i] = this.namespacePrototype.newNamespace(declarations[i].getPrefix(), declarations[i].getUri()); + } + return rv; + } + + // See ECMA357 13.3.2 + QName constructQName(Context cx, Object namespace, Object name) { + return this.qnamePrototype.constructQName(this, cx, namespace, name); + } + + QName newQName(String uri, String localName, String prefix) { + return this.qnamePrototype.newQName(this, uri, localName, prefix); + } + + QName constructQName(Context cx, Object nameValue) { +// return constructQName(cx, Undefined.instance, nameValue); + return this.qnamePrototype.constructQName(this, cx, nameValue); + } + + QName castToQName(Context cx, Object qnameValue) { + return this.qnamePrototype.castToQName(this, cx, qnameValue); + } + + QName newQName(XmlNode.QName qname) { + return QName.create(this, this.globalScope, this.qnamePrototype, qname); + } + + XML newXML(XmlNode node) { + return new XML(this, this.globalScope, this.xmlPrototype, node); + } + + /** + @deprecated I believe this can be replaced by ecmaToXml below. + */ + final XML newXMLFromJs(Object inputObject) { + String frag; + + if (inputObject == null || inputObject == Undefined.instance) { + 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) { + // Solo text node + return newXML(XmlNode.createText(options, frag)); + } + return parse(frag); + } + + private XML parse(String frag) { + try { + return newXML(XmlNode.createElement(options, getDefaultNamespaceURI(Context.getCurrentContext()), frag)); + } catch (org.xml.sax.SAXException e) { + throw ScriptRuntime.typeError("Cannot parse XML: " + e.getMessage()); + } + } + + final XML ecmaToXml(Object object) { + // See ECMA357 10.3 + if (object == null || object == Undefined.instance) throw ScriptRuntime.typeError("Cannot convert " + object + " to XML"); + if (object instanceof XML) return (XML)object; + if (object instanceof XMLList) { + XMLList list = (XMLList)object; + if (list.getXML() != null) { + return list.getXML(); + } else { + throw ScriptRuntime.typeError("Cannot convert list of >1 element to XML"); + } + } + // TODO Technically we should fail on anything except a String, Number or Boolean + // See ECMA357 10.3 + // Extension: if object is a DOM node, use that to construct the XML + // object. + if (object instanceof Wrapper) { + object = ((Wrapper) object).unwrap(); + } + if (object instanceof org.w3c.dom.Node) { + org.w3c.dom.Node node = (org.w3c.dom.Node) object; + return newXML(XmlNode.createElementFromNode(node)); + } + // Instead we just blindly cast to a String and let them convert anything. + String s = ScriptRuntime.toString(object); + // TODO Could this get any uglier? + if (s.length() > 0 && s.charAt(0) == '<') { + return parse(s); + } else { + return newXML(XmlNode.createText(options, s)); + } + } + + final XML newTextElementXML(XmlNode reference, XmlNode.QName qname, String value) { + return newXML(XmlNode.newElementWithText(options, reference, qname, value)); + } + + XMLList newXMLList() { + return new XMLList(this, this.globalScope, this.xmlListPrototype); + } + + final XMLList newXMLListFrom(Object inputObject) { + XMLList rv = newXMLList(); + + if (inputObject == null || inputObject instanceof Undefined) { + return rv; + } else if (inputObject instanceof XML) { + XML xml = (XML) inputObject; + rv.getNodeList().add(xml); + return rv; + } else if (inputObject instanceof XMLList) { + XMLList xmll = (XMLList) inputObject; + rv.getNodeList().add(xmll.getNodeList()); + return rv; + } else { + String frag = ScriptRuntime.toString(inputObject).trim(); + + if (!frag.startsWith("<>")) { + frag = "<>" + frag + "</>"; + } + + frag = "<fragment>" + frag.substring(2); + if (!frag.endsWith("</>")) { + throw ScriptRuntime.typeError("XML with anonymous tag missing end anonymous tag"); + } + + frag = frag.substring(0, frag.length() - 3) + "</fragment>"; + + XML orgXML = newXMLFromJs(frag); + + // Now orphan the children and add them to our XMLList. + XMLList children = orgXML.children(); + + for (int i = 0; i < children.getNodeList().length(); i++) { + // Copy here is so that they'll be orphaned (parent() will be undefined) + rv.getNodeList().add(((XML) children.item(i).copy())); + } + return rv; + } + } + + XmlNode.QName toNodeQName(Context cx, Object namespaceValue, Object nameValue) { + // This is duplication of constructQName(cx, namespaceValue, nameValue) + // but for XMLName + + String localName; + + if (nameValue instanceof QName) { + QName qname = (QName)nameValue; + localName = qname.localName(); + } else { + localName = ScriptRuntime.toString(nameValue); + } + + XmlNode.Namespace ns; + if (namespaceValue == Undefined.instance) { + if ("*".equals(localName)) { + ns = null; + } else { + ns = getDefaultNamespace(cx).getDelegate(); + } + } else if (namespaceValue == null) { + ns = null; + } else if (namespaceValue instanceof Namespace) { + ns = ((Namespace)namespaceValue).getDelegate(); + } else { + ns = this.namespacePrototype.constructNamespace(namespaceValue).getDelegate(); + } + + if (localName != null && localName.equals("*")) localName = null; + return XmlNode.QName.create(ns, localName); + } + + XmlNode.QName toNodeQName(Context cx, String name, boolean attribute) { + XmlNode.Namespace defaultNamespace = getDefaultNamespace(cx).getDelegate(); + if (name != null && name.equals("*")) { + return XmlNode.QName.create(null, null); + } else { + if (attribute) { + return XmlNode.QName.create(XmlNode.Namespace.GLOBAL, name); + } else { + return XmlNode.QName.create(defaultNamespace, name); + } + } + } + + /** + @deprecated Too general; this should be split into overloaded methods. + Is that possible? + */ + XmlNode.QName toNodeQName(Context cx, Object nameValue, boolean attribute) { + if (nameValue instanceof XMLName) { + return ((XMLName)nameValue).toQname(); + } else if (nameValue instanceof QName) { + QName qname = (QName)nameValue; + return qname.getDelegate(); + } else if ( + nameValue instanceof Boolean + || nameValue instanceof Number + || nameValue == Undefined.instance + || nameValue == null + ) { + throw badXMLName(nameValue); + } else { + String local = null; + if (nameValue instanceof String) { + local = (String)nameValue; + } else { + local = ScriptRuntime.toString(nameValue); + } + return toNodeQName(cx, local, attribute); + } + } + + // + // Override methods from XMLLib + // + + public boolean isXMLName(Context _cx, Object nameObj) { + return XMLName.accept(nameObj); + } + + public Object toDefaultXmlNamespace(Context cx, Object uriValue) { + return this.namespacePrototype.constructNamespace(uriValue); + } + + public String escapeTextValue(Object o) { + return options.escapeTextValue(o); + } + + public String escapeAttributeValue(Object o) { + return options.escapeAttributeValue(o); + } + + public Ref nameRef(Context cx, Object name, Scriptable scope, int memberTypeFlags) { + if ((memberTypeFlags & Node.ATTRIBUTE_FLAG) == 0) { + // should only be called foir cases like @name or @[expr] + throw Kit.codeBug(); + } + XMLName xmlName = toAttributeName(cx, name); + return xmlPrimaryReference(cx, xmlName, scope); + } + + public Ref nameRef(Context cx, Object namespace, Object name, Scriptable scope, int memberTypeFlags) { + XMLName xmlName = XMLName.create(toNodeQName(cx, namespace, name), false, false); + + // No idea what is coming in from the parser in this case; is it detecting the "@"? + if ((memberTypeFlags & Node.ATTRIBUTE_FLAG) != 0) { + if (!xmlName.isAttributeName()) { + xmlName.setAttributeName(); + } + } + + return xmlPrimaryReference(cx, xmlName, scope); + } +} diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java new file mode 100644 index 0000000..59dcca3 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java @@ -0,0 +1,765 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ethan Hugg + * Terry Lucas + * Milen Nankov + * David P. Caldwell <inonit@inonit.com> + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; +import org.mozilla.javascript.xml.*; + +class XMLList extends XMLObjectImpl implements Function { + static final long serialVersionUID = -4543618751670781135L; + + private XmlNode.List _annos; + private XMLObjectImpl targetObject = null; + private XmlNode.QName targetProperty = null; + + XMLList(XMLLibImpl lib, Scriptable scope, XMLObject prototype) { + super(lib, scope, prototype); + _annos = new XmlNode.List(); + } + + /** @deprecated Will probably end up unnecessary as we move things around */ + XmlNode.List getNodeList() { + return _annos; + } + + // TODO Should be XMLObjectImpl, XMLName? + void setTargets(XMLObjectImpl object, XmlNode.QName property) { + targetObject = object; + targetProperty = property; + } + + /** @deprecated */ + private XML getXmlFromAnnotation(int index) { + return getXML(_annos, index); + } + + XML getXML() { + if (length() == 1) return getXmlFromAnnotation(0); + return null; + } + + private void internalRemoveFromList(int index) { + _annos.remove(index); + } + + void replace(int index, XML xml) { + if (index < length()) { + XmlNode.List newAnnoList = new XmlNode.List(); + newAnnoList.add(_annos, 0, index); + newAnnoList.add(xml); + newAnnoList.add(_annos, index+1, length()); + _annos = newAnnoList; + } + } + + private void insert(int index, XML xml) { + if (index < length()) { + XmlNode.List newAnnoList = new XmlNode.List(); + newAnnoList.add(_annos, 0, index); + newAnnoList.add(xml); + newAnnoList.add(_annos, index, length()); + _annos = newAnnoList; + } + } + + // + // + // methods overriding ScriptableObject + // + // + + public String getClassName() { + return "XMLList"; + } + + // + // + // methods overriding IdScriptableObject + // + // + + public Object get(int index, Scriptable start) { + //Log("get index: " + index); + + if (index >= 0 && index < length()) { + return getXmlFromAnnotation(index); + } else { + return Scriptable.NOT_FOUND; + } + } + + 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; + } + + public boolean has(int index, Scriptable start) { + return 0 <= index && index < length(); + } + + 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 than 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.getLocalName() != null) { + // Add an empty element with our targetProperty name and then set it. + XML xmlValue = newTextElementXML(null, targetProperty, null); + addToList(xmlValue); + + if(xmlName.isAttributeName()) { + setAttribute(xmlName, value); + } else { + XML xml = item(0); + xml.putXMLProperty(xmlName, value); + + // Update the list with the new item at location 0. + replace(0, item(0)); + } + + // Now add us to our parent + XMLName name2 = XMLName.formProperty(targetProperty.getUri(), targetProperty.getLocalName()); + 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 = item(0); + xml.putXMLProperty(xmlName, value); + + // Update the list with the new item at location 0. + replace(0, item(0)); + } + } + + Object getXMLProperty(XMLName name) { + return getPropertyList(name); + } + + private void replaceNode(XML xml, XML with) { + xml.replaceWith(with); + } + + 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 = newXMLFromJs(value.toString()); + } else { + // Note that later in the code, we will use this as an argument to replace(int,value) + // So we will be "replacing" this element with itself + // There may well be a better way to do this + // TODO Find a way to refactor this whole method and simplify it + xmlValue = item(index); + ((XML)xmlValue).setChildren(value); + } + } + + // 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) { + replaceNode(xmlNode, (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(); + replaceNode(xmlNode, list.item(0)); + replace(index, list.item(0)); + + for (int i = 1; i < list.length(); i++) { + xmlParent.insertChildAfter(xmlParent.getXmlChild(lastIndexAdded), list.item(i)); + lastIndexAdded++; + insert(index + i, 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 = getXML(_annos, index); + + if (xmlValue instanceof XML) { + replaceNode(xmlNode, (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) { + replaceNode(xmlNode, list.item(0)); + replace(index, list.item(0)); + + for (int i = 1; i < list.length(); i++) { + insert(index + i, list.item(i)); + } + } + } + } else { + addToList(xmlValue); + } + } + } + + private XML getXML(XmlNode.List _annos, int index) { + if (index >= 0 && index < length()) { + return xmlFromNode(_annos.item(index)); + } else { + return null; + } + } + + void deleteXMLProperty(XMLName name) { + for (int i = 0; i < length(); i++) { + XML xml = getXmlFromAnnotation(i); + + if (xml.isElement()) { + xml.deleteXMLProperty(name); + } + } + } + + public void delete(int index) { + if (index >= 0 && index < length()) { + XML xml = getXmlFromAnnotation(index); + + xml.remove(); + + internalRemoveFromList(index); + } + } + + public Object[] getIds() { + Object enumObjs[]; + + if (isPrototype()) { + enumObjs = new Object[0]; + } else { + enumObjs = new Object[length()]; + + for (int i = 0; i < enumObjs.length; i++) { + enumObjs[i] = new Integer(i); + } + } + + return enumObjs; + } + + 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); + } + } + } + + XML item(int index) { + return _annos != null + ? getXmlFromAnnotation(index) : createEmptyXML(); + } + + private void setAttribute(XMLName xmlName, Object value) { + for (int i = 0; i < length(); i++) { + XML xml = getXmlFromAnnotation(i); + xml.setAttribute(xmlName, value); + } + } + + void addToList(Object toAdd) { + _annos.addToList(toAdd); + } + + // + // + // Methods from section 12.4.4 in the spec + // + // + + XMLList child(int index) { + XMLList result = newXMLList(); + + for (int i = 0; i < length(); i++) { + result.addToList(getXmlFromAnnotation(i).child(index)); + } + + return result; + } + + XMLList child(XMLName xmlName) { + XMLList result = newXMLList(); + + for (int i = 0; i < length(); i++) { + result.addToList(getXmlFromAnnotation(i).child(xmlName)); + } + + return result; + } + + void addMatches(XMLList rv, XMLName name) { + for (int i=0; i<length(); i++) { + getXmlFromAnnotation(i).addMatches(rv, name); + } + } + + XMLList children() { + java.util.Vector v = new java.util.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 = newXMLList(); + int sz = v.size(); + + for (int i = 0; i < sz; i++) { + allChildren.addToList(v.get(i)); + } + + return allChildren; + } + + XMLList comments() { + XMLList result = newXMLList(); + + for (int i = 0; i < length(); i++) { + XML xml = getXmlFromAnnotation(i); + result.addToList(xml.comments()); + } + + return result; + } + + XMLList elements(XMLName name) { + XMLList rv = newXMLList(); + for (int i=0; i<length(); i++) { + XML xml = getXmlFromAnnotation(i); + rv.addToList(xml.elements(name)); + } + return rv; + } + + 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; + } + + XMLObjectImpl copy() { + XMLList result = newXMLList(); + + for (int i = 0; i < length(); i++) { + XML xml = getXmlFromAnnotation(i); + result.addToList(xml.copy()); + } + + return result; + } + + boolean hasOwnProperty(XMLName xmlName) { + if (isPrototype()) { + String property = xmlName.localName(); + return (findPrototypeId(property) != 0); + } else { + return (getPropertyList(xmlName).length() > 0); + } + } + + 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.isElement()) { + complexContent = true; + break; + } + } + } + + return complexContent; + } + + boolean hasSimpleContent() { + if (length() == 0) { + return true; + } else if (length() == 1) { + return getXmlFromAnnotation(0).hasSimpleContent(); + } else { + for (int i=0; i<length(); i++) { + XML nextElement = getXmlFromAnnotation(i); + if (nextElement.isElement()) { + return false; + } + } + return true; + } + } + + int length() { + int result = 0; + + if (_annos != null) { + result = _annos.length(); + } + + return result; + } + + 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 + */ + Object parent() { + if (length() == 0) return Undefined.instance; + + XML candidateParent = null; + + for (int i = 0; i < length(); i++) { + Object currParent = getXmlFromAnnotation(i).parent(); + if (!(currParent instanceof XML)) return Undefined.instance; + XML xml = (XML)currParent; + if (i == 0) { + // Set the first for the rest to compare to. + candidateParent = xml; + } else { + if (candidateParent.is(xml)) { + // keep looking + } else { + return Undefined.instance; + } + } + } + return candidateParent; + } + + XMLList processingInstructions(XMLName xmlName) { + XMLList result = newXMLList(); + + for (int i = 0; i < length(); i++) { + XML xml = getXmlFromAnnotation(i); + + result.addToList(xml.processingInstructions(xmlName)); + } + + return result; + } + + boolean propertyIsEnumerable(Object name) { + long index; + if (name instanceof Integer) { + index = ((Integer)name).intValue(); + } else if (name instanceof Number) { + double x = ((Number)name).doubleValue(); + index = (long)x; + if (index != x) { + return false; + } + if (index == 0 && 1.0 / x < 0) { + // Negative 0 + return false; + } + } else { + String s = ScriptRuntime.toString(name); + index = ScriptRuntime.testUint32String(s); + } + return (0 <= index && index < length()); + } + + XMLList text() { + XMLList result = newXMLList(); + + for (int i = 0; i < length(); i++) { + result.addToList(getXmlFromAnnotation(i).text()); + } + + return result; + } + + public String toString() { + // ECMA357 10.1.2 + if (hasSimpleContent()) { + StringBuffer sb = new StringBuffer(); + + for(int i = 0; i < length(); i++) { + XML next = getXmlFromAnnotation(i); + if (next.isComment() || next.isProcessingInstruction()) { + // do nothing + } else { + sb.append(next.toString()); + } + } + + return sb.toString(); + } else { + return toXMLString(); + } + } + + String toXMLString() { + // See ECMA 10.2.1 + StringBuffer sb = new StringBuffer(); + + for (int i=0; i<length(); i++) { + if (getProcessor().isPrettyPrinting() && i != 0) { + sb.append('\n'); + } + sb.append(getXmlFromAnnotation(i).toXMLString()); + } + return sb.toString(); + } + + Object valueOf() { + return this; + } + + // + // Other public Functions from XMLObject + // + + 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; + } + + private XMLList getPropertyList(XMLName name) { + XMLList propertyList = newXMLList(); + XmlNode.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 = name.toQname(); + } + + 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) { + String methodName = isApply ? "apply" : "call"; + if(!(thisObj instanceof XMLList) || + ((XMLList)thisObj).targetProperty == null) + throw ScriptRuntime.typeError1("msg.isnt.function", + methodName); + + return ScriptRuntime.applyOrCall(isApply, cx, scope, thisObj, args); + } + + protected Object jsConstructor(Context cx, boolean inNewExpr, + Object[] args) { + if (args.length == 0) { + return newXMLList(); + } else { + Object arg0 = args[0]; + if (!inNewExpr && arg0 instanceof XMLList) { + // XMLList(XMLList) returns the same object. + return arg0; + } + return newXMLListFrom(arg0); + } + } + + /** + * See ECMA 357, 11_2_2_1, Semantics, 3_e. + */ + public Scriptable getExtraMethodSource(Context cx) { + if (length() == 1) { + return getXmlFromAnnotation(0); + } + return null; + } + + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) { + // This XMLList is being called as a Function. + // Let's find the real Function object. + if(targetProperty == null) + throw ScriptRuntime.notFunctionError(this); + + String methodName = targetProperty.getLocalName(); + + boolean isApply = methodName.equals("apply"); + if(isApply || methodName.equals("call")) + return applyOrCall(isApply, cx, scope, thisObj, args); + + Callable method = ScriptRuntime.getElemFunctionAndThis( + this, methodName, cx); + // Call lastStoredScriptable to clear stored thisObj + // but ignore the result as the method should use the supplied + // thisObj, not one from redirected call + ScriptRuntime.lastStoredScriptable(cx); + return method.call(cx, scope, thisObj, args); + } + + public Scriptable construct(Context cx, Scriptable scope, Object[] args) { + throw ScriptRuntime.typeError1("msg.not.ctor", "XMLList"); + } +} + + diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java new file mode 100644 index 0000000..edd7525 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java @@ -0,0 +1,469 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * Milen Nankov + * David P. Caldwell <inonit@inonit.com> + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; + +class XMLName extends Ref { + static final long serialVersionUID = 3832176310755686977L; + + private static boolean isNCNameStartChar(int c) { + if ((c & ~0x7F) == 0) { + // Optimize for ASCII and use A..Z < _ < a..z + if (c >= 'a') { + return c <= 'z'; + } else if (c >= 'A') { + if (c <= 'Z') { + return true; + } + return c == '_'; + } + } else if ((c & ~0x1FFF) == 0) { + return (0xC0 <= c && c <= 0xD6) + || (0xD8 <= c && c <= 0xF6) + || (0xF8 <= c && c <= 0x2FF) + || (0x370 <= c && c <= 0x37D) + || 0x37F <= c; + } + return (0x200C <= c && c <= 0x200D) + || (0x2070 <= c && c <= 0x218F) + || (0x2C00 <= c && c <= 0x2FEF) + || (0x3001 <= c && c <= 0xD7FF) + || (0xF900 <= c && c <= 0xFDCF) + || (0xFDF0 <= c && c <= 0xFFFD) + || (0x10000 <= c && c <= 0xEFFFF); + } + + private static boolean isNCNameChar(int c) { + if ((c & ~0x7F) == 0) { + // Optimize for ASCII and use - < . < 0..9 < A..Z < _ < a..z + if (c >= 'a') { + return c <= 'z'; + } else if (c >= 'A') { + if (c <= 'Z') { + return true; + } + return c == '_'; + } else if (c >= '0') { + return c <= '9'; + } else { + return c == '-' || c == '.'; + } + } else if ((c & ~0x1FFF) == 0) { + return isNCNameStartChar(c) || c == 0xB7 + || (0x300 <= c && c <= 0x36F); + } + return isNCNameStartChar(c) || (0x203F <= c && c <= 0x2040); + } + + // This means "accept" in the parsing sense + // See ECMA357 13.1.2.1 + static boolean accept(Object nameObj) { + String name; + try { + name = ScriptRuntime.toString(nameObj); + } catch (EcmaError ee) { + if ("TypeError".equals(ee.getName())) { + return false; + } + throw ee; + } + + // See http://w3.org/TR/xml-names11/#NT-NCName + int length = name.length(); + if (length != 0) { + if (isNCNameStartChar(name.charAt(0))) { + for (int i = 1; i != length; ++i) { + if (!isNCNameChar(name.charAt(i))) { + return false; + } + } + return true; + } + } + + return false; + } + + private XmlNode.QName qname; + private boolean isAttributeName; + private boolean isDescendants; + private XMLObjectImpl xmlObject; + + private XMLName() { + } + + static XMLName formStar() { + XMLName rv = new XMLName(); + rv.qname = XmlNode.QName.create(null, null); + return rv; + } + + /** @deprecated */ + static XMLName formProperty(XmlNode.Namespace namespace, String localName) { + if (localName != null && localName.equals("*")) localName = null; + XMLName rv = new XMLName(); + rv.qname = XmlNode.QName.create(namespace, localName); + return rv; + } + + /** @deprecated */ + static XMLName formProperty(String uri, String localName) { + return formProperty(XmlNode.Namespace.create(uri), localName); + } + + /** @deprecated */ + static XMLName create(String defaultNamespaceUri, 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; + } + } + + return XMLName.formProperty(defaultNamespaceUri, name); + } + + static XMLName create(XmlNode.QName qname, boolean attribute, boolean descendants) { + XMLName rv = new XMLName(); + rv.qname = qname; + rv.isAttributeName = attribute; + rv.isDescendants = descendants; + return rv; + } + + /** @deprecated */ + static XMLName create(XmlNode.QName qname) { + return create(qname, false, false); + } + + void initXMLObject(XMLObjectImpl xmlObject) { + if (xmlObject == null) throw new IllegalArgumentException(); + if (this.xmlObject != null) throw new IllegalStateException(); + this.xmlObject = xmlObject; + } + + String uri() { + if (qname.getNamespace() == null) return null; + return qname.getNamespace().getUri(); + } + + String localName() { + if (qname.getLocalName() == null) return "*"; + return qname.getLocalName(); + } + + private void addDescendantChildren(XMLList list, XML target) { + XMLName xmlName = this; + if (target.isElement()) { + XML[] children = target.getChildren(); + for (int i=0; i<children.length; i++) { + if (xmlName.matches( children[i] )) { + list.addToList( children[i] ); + } + addDescendantChildren(list, children[i]); + } + } + } + + void addMatchingAttributes(XMLList list, XML target) { + XMLName name = this; + if (target.isElement()) { + XML[] attributes = target.getAttributes(); + for (int i=0; i<attributes.length; i++) { + if (name.matches( attributes[i]) ) { + list.addToList( attributes[i] ); + } + } + } + } + + private void addDescendantAttributes(XMLList list, XML target) { + if (target.isElement()) { + addMatchingAttributes(list, target); + XML[] children = target.getChildren(); + for (int i=0; i<children.length; i++) { + addDescendantAttributes(list, children[i]); + } + } + } + + XMLList matchDescendantAttributes(XMLList rv, XML target) { + rv.setTargets(target, null); + addDescendantAttributes(rv, target); + return rv; + } + + XMLList matchDescendantChildren(XMLList rv, XML target) { + rv.setTargets(target, null); + addDescendantChildren(rv, target); + return rv; + } + + void addDescendants(XMLList rv, XML target) { + XMLName xmlName = this; + if (xmlName.isAttributeName()) { + matchDescendantAttributes(rv, target); + } else { + matchDescendantChildren(rv, target); + } + } + + private void addAttributes(XMLList rv, XML target) { + addMatchingAttributes(rv, target); + } + + void addMatches(XMLList rv, XML target) { + if (isDescendants()) { + addDescendants(rv, target); + } else if (isAttributeName()) { + addAttributes(rv, target); + } else { + XML[] children = target.getChildren(); + if (children != null) { + for (int i=0; i<children.length; i++) { + if (this.matches(children[i])) { + rv.addToList( children[i] ); + } + } + } + rv.setTargets(target, this.toQname()); + } + } + + XMLList getMyValueOn(XML target) { + XMLList rv = target.newXMLList(); + addMatches(rv, target); + return rv; + } + + void setMyValueOn(XML target, Object value) { + // Special-case checks for undefined and null + if (value == null) { + value = "null"; + } else if (value instanceof Undefined) { + value = "undefined"; + } + + XMLName xmlName = this; + // Get the named property + if (xmlName.isAttributeName()) { + target.setAttribute(xmlName, value); + } else if (xmlName.uri() == null && xmlName.localName().equals("*")) { + target.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).isAttribute()) { + xmlValue = target.makeXmlFromString(xmlName, xmlValue.toString()); + } + } + + if (xmlValue instanceof XMLList) { + for (int i = 0; i < xmlValue.length(); i++) { + XML xml = ((XMLList) xmlValue).item(i); + + if (xml.isAttribute()) { + ((XMLList)xmlValue).replace(i, target.makeXmlFromString(xmlName, xml.toString())); + } + } + } + } else { + xmlValue = target.makeXmlFromString(xmlName, ScriptRuntime.toString(value)); + } + + XMLList matches = target.getPropertyList(xmlName); + + if (matches.length() == 0) { + target.appendChild(xmlValue); + } else { + // Remove all other matches + for (int i = 1; i < matches.length(); i++) { + target.removeChild(matches.item(i).childIndex()); + } + + // Replace first match with new value. + XML firstMatch = matches.item(0); + target.replace(firstMatch.childIndex(), xmlValue); + } + } + } + + public boolean has(Context cx) { + if (xmlObject == null) { + return false; + } + return xmlObject.hasXMLProperty(this); + } + + public Object get(Context cx) { + if (xmlObject == null) { + throw ScriptRuntime.undefReadError(Undefined.instance, + toString()); + } + return xmlObject.getXMLProperty(this); + } + + public Object set(Context cx, Object value) { + if (xmlObject == null) { + throw ScriptRuntime.undefWriteError(Undefined.instance, + toString(), + value); + } + // Assignment to descendants causes parse error on bad reference + // and this should not be called + if (isDescendants) throw Kit.codeBug(); + xmlObject.putXMLProperty(this, value); + return value; + } + + public boolean delete(Context cx) { + if (xmlObject == null) { + return true; + } + xmlObject.deleteXMLProperty(this); + return !xmlObject.hasXMLProperty(this); + } + + 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(); + } + + final XmlNode.QName toQname() { + return this.qname; + } + + final boolean matchesLocalName(String localName) { + return localName().equals("*") || localName().equals(localName); + } + + final boolean matchesElement(XmlNode.QName qname) { + if (this.uri() == null || this.uri().equals(qname.getNamespace().getUri())) { + if (this.localName().equals("*") || this.localName().equals(qname.getLocalName())) { + return true; + } + } + return false; + } + + final boolean matches(XML node) { + XmlNode.QName qname = node.getNodeQname(); + String nodeUri = null; + if (qname.getNamespace() != null) { + nodeUri = qname.getNamespace().getUri(); + } + if (isAttributeName) { + if (node.isAttribute()) { + if (this.uri() == null || this.uri().equals(nodeUri)) { + if (this.localName().equals("*") || this.localName().equals(qname.getLocalName())) { + return true; + } + } + return false; + } else { + // TODO Could throw exception maybe, should not call this method on attribute name with arbitrary node type + // unless we traverse all attributes and children habitually + return false; + } + } else { + if ( this.uri() == null || ((node.isElement()) && this.uri().equals(nodeUri)) ) { + if (localName().equals("*")) return true; + if (node.isElement()) { + if (localName().equals(qname.getLocalName())) return true; + } + } + return false; + } + } + + /** @deprecated */ + boolean isAttributeName() { + return isAttributeName; + } + + // TODO Fix whether this is an attribute XMLName at construction? + /** @deprecated */ + void setAttributeName() { +// if (isAttributeName) throw new IllegalStateException(); + isAttributeName = true; + } + + /** @deprecated */ + boolean isDescendants() { + return isDescendants; + } + + // TODO Fix whether this is an descendant XMLName at construction? + /** @deprecated */ + void setIsDescendants() { +// if (isDescendants) throw new IllegalStateException(); + isDescendants = true; + } +} diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java new file mode 100644 index 0000000..db918f8 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java @@ -0,0 +1,812 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * Ethan Hugg + * Terry Lucas + * Milen Nankov + * David P. Caldwell <inonit@inonit.com> + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.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 + */ +abstract class XMLObjectImpl extends XMLObject { + private static final Object XMLOBJECT_TAG = new Object(); + private XMLLibImpl lib; + private boolean prototypeFlag; + + protected XMLObjectImpl(XMLLibImpl lib, Scriptable scope, + XMLObject prototype) + { + initialize(lib, scope, prototype); + } + + final void initialize(XMLLibImpl lib, Scriptable scope, + XMLObject prototype) + { + setParentScope(scope); + setPrototype(prototype); + prototypeFlag = (prototype == null); + this.lib = lib; + } + + final boolean isPrototype() { + return prototypeFlag; + } + + XMLLibImpl getLib() { + return lib; + } + + final XML newXML(XmlNode node) { + return lib.newXML(node); + } + + XML xmlFromNode(XmlNode node) { + if (node.getXml() == null) { + node.setXml( newXML(node) ); + } + return node.getXml(); + } + + final XMLList newXMLList() { + return lib.newXMLList(); + } + + final XMLList newXMLListFrom(Object o) { + return lib.newXMLListFrom(o); + } + + final XmlProcessor getProcessor() { + return lib.getProcessor(); + } + + final QName newQName(String uri, String localName, String prefix) { + return lib.newQName(uri, localName, prefix); + } + + final QName newQName(XmlNode.QName name) { + return lib.newQName(name); + } + + final Namespace createNamespace(XmlNode.Namespace declaration) { + if (declaration == null) return null; + return lib.createNamespaces( new XmlNode.Namespace[] { declaration } )[0]; + } + + final Namespace[] createNamespaces(XmlNode.Namespace[] declarations) { + return lib.createNamespaces(declarations); + } + + // + // Scriptable + // + + public final Object get(String name, Scriptable start) { + return super.get(name, start); + } + + public final boolean has(String name, Scriptable start) { + return super.has(name, start); + } + + public final void put(String name, Scriptable start, Object value) { + super.put(name, start, value); + } + + public final void delete(String name) { + // TODO I am not sure about this, but this is how I found it. DPC + throw new IllegalArgumentException("String: [" + name + "]"); + } + + public final Scriptable getPrototype() { + return super.getPrototype(); + } + + public final void setPrototype(Scriptable prototype) { + super.setPrototype(prototype); + } + + public final Scriptable getParentScope() { + return super.getParentScope(); + } + + public final void setParentScope(Scriptable parent) { + super.setParentScope(parent); + } + + public final Object getDefaultValue(Class hint) { + return this.toString(); + } + + public final boolean hasInstance(Scriptable scriptable) { + return super.hasInstance(scriptable); + } + + /** + * ecmaHas(cx, id) calls this after resolving when id to XMLName + * and checking it is not Uint32 index. + */ + abstract boolean hasXMLProperty(XMLName name); + + /** + * ecmaGet(cx, id) calls this after resolving when id to XMLName + * and checking it is not Uint32 index. + */ + abstract Object getXMLProperty(XMLName name); + + /** + * ecmaPut(cx, id, value) calls this after resolving when id to XMLName + * and checking it is not Uint32 index. + */ + 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. + */ + abstract void deleteXMLProperty(XMLName name); + + /** + * Test XML equality with target the target. + */ + abstract boolean equivalentXml(Object target); + + abstract void addMatches(XMLList rv, XMLName name); + + private XMLList getMatches(XMLName name) { + XMLList rv = newXMLList(); + addMatches(rv, name); + return rv; + } + + abstract XML getXML(); + + // Methods from section 12.4.4 in the spec + abstract XMLList child(int index); + abstract XMLList child(XMLName xmlName); + abstract XMLList children(); + abstract XMLList comments(); + abstract boolean contains(Object xml); + abstract XMLObjectImpl copy(); + abstract XMLList elements(XMLName xmlName); + abstract boolean hasOwnProperty(XMLName xmlName); + abstract boolean hasComplexContent(); + abstract boolean hasSimpleContent(); + abstract int length(); + abstract void normalize(); + abstract Object parent(); + abstract XMLList processingInstructions(XMLName xmlName); + abstract boolean propertyIsEnumerable(Object member); + abstract XMLList text(); + public abstract String toString(); + abstract String toXMLString(); + abstract Object valueOf(); + + protected abstract Object jsConstructor(Context cx, boolean inNewExpr, Object[] args); + + final Object getMethod(String id) { + return super.get(id, this); + } + + // + // + // Methods overriding ScriptableObject + // + // + + /** + * XMLObject always compare with any value and equivalentValues + * never returns {@link Scriptable#NOT_FOUND} for them but rather + * calls equivalentXml(value) and wrap the result as Boolean. + */ + protected final Object equivalentValues(Object value) { + boolean result = equivalentXml(value); + return result ? Boolean.TRUE : Boolean.FALSE; + } + + // + // + // Methods overriding XMLObject + // + // + + /** + * 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; + } + + // TODO Can this be made more strongly typed? + public Ref memberRef(Context cx, Object elem, int memberTypeFlags) { + boolean attribute = (memberTypeFlags & Node.ATTRIBUTE_FLAG) != 0; + boolean descendants = (memberTypeFlags & Node.DESCENDANTS_FLAG) != 0; + if (!attribute && !descendants) { + // Code generation would use ecma(Get|Has|Delete|Set) for + // normal name identifiers so one ATTRIBUTE_FLAG + // or DESCENDANTS_FLAG has to be set + throw Kit.codeBug(); + } + XmlNode.QName qname = lib.toNodeQName(cx, elem, attribute); + XMLName rv = XMLName.create(qname, attribute, descendants); + rv.initXMLObject(this); + return rv; + } + + /** + * Generic reference to implement x::ns, x.@ns::y, x..@ns::y etc. + */ + public Ref memberRef(Context cx, Object namespace, Object elem, int memberTypeFlags) { + boolean attribute = (memberTypeFlags & Node.ATTRIBUTE_FLAG) != 0; + boolean descendants = (memberTypeFlags & Node.DESCENDANTS_FLAG) != 0; + XMLName rv = XMLName.create(lib.toNodeQName(cx, namespace, elem), attribute, descendants); + rv.initXMLObject(this); + return rv; + } + + public NativeWith enterWith(Scriptable scope) { + return new XMLWithScope(lib, scope, this); + } + + public NativeWith enterDotQuery(Scriptable scope) { + XMLWithScope xws = new XMLWithScope(lib, scope, this); + xws.initAsDotQuery(); + return xws; + } + + public final Object addValues(Context cx, boolean thisIsLeft, + Object value) { + if (value instanceof XMLObject) { + XMLObject v1, v2; + if (thisIsLeft) { + v1 = this; + v2 = (XMLObject)value; + } else { + v1 = (XMLObject)value; + v2 = this; + } + return lib.addXMLObjects(cx, v1, v2); + } + if (value == Undefined.instance) { + // both "xml + undefined" and "undefined + xml" gives String(xml) + return ScriptRuntime.toString(this); + } + + return super.addValues(cx, thisIsLeft, value); + } + + // + // + // IdScriptableObject machinery + // + // + + final void exportAsJSClass(boolean sealed) { + prototypeFlag = true; + exportAsJSClass(MAX_PROTOTYPE_ID, getParentScope(), 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_elements = 13, + Id_inScopeNamespaces = 14, + Id_insertChildAfter = 15, + Id_insertChildBefore = 16, + Id_hasOwnProperty = 17, + Id_hasComplexContent = 18, + Id_hasSimpleContent = 19, + Id_length = 20, + Id_localName = 21, + Id_name = 22, + Id_namespace = 23, + Id_namespaceDeclarations = 24, + Id_nodeKind = 25, + Id_normalize = 26, + Id_parent = 27, + Id_prependChild = 28, + Id_processingInstructions = 29, + Id_propertyIsEnumerable = 30, + Id_removeNamespace = 31, + Id_replace = 32, + Id_setChildren = 33, + Id_setLocalName = 34, + Id_setName = 35, + Id_setNamespace = 36, + Id_text = 37, + Id_toString = 38, + Id_toXMLString = 39, + Id_valueOf = 40, + + MAX_PROTOTYPE_ID = 40; + + protected int findPrototypeId(String s) { + int id; +// #generated# Last update: 2007-08-20 09:04:06 EDT + 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 'e': X="elements";id=Id_elements; 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; + break L0; + } +// #/generated# + return id; + } +// #/string_id_map# + + protected void initPrototypeId(int id) { + String s; + int arity; + switch (id) { + case Id_constructor: { + IdFunctionObject ctor; + if (this instanceof XML) { + ctor = new XMLCtor((XML)this, XMLOBJECT_TAG, id, 1); + } else { + ctor = new IdFunctionObject(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_elements: arity=1; s="elements"; 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=1; s="toXMLString"; break; + case Id_valueOf: arity=0; s="valueOf"; break; + + default: throw new IllegalArgumentException(String.valueOf(id)); + } + initPrototypeMethod(XMLOBJECT_TAG, id, s, arity); + } + + private Object[] toObjectArray(Object[] typed) { + Object[] rv = new Object[typed.length]; + for (int i=0; i<rv.length; i++) { + rv[i] = typed[i]; + } + return rv; + } + + private void xmlMethodNotFound(Object object, String name) { + throw ScriptRuntime.notFunctionError(object, name); + } + + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + if (!f.hasTag(XMLOBJECT_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + if (id == Id_constructor) { + return jsConstructor(cx, thisObj == null, args); + } + + // All (XML|XMLList).prototype methods require thisObj to be XML + if (!(thisObj instanceof XMLObjectImpl)) + throw incompatibleCallError(f); + XMLObjectImpl realThis = (XMLObjectImpl)thisObj; + + XML xml = realThis.getXML(); + switch (id) { + case Id_appendChild: { + if (xml == null) xmlMethodNotFound(realThis, "appendChild"); + return xml.appendChild(arg(args, 0)); + } + case Id_addNamespace: { + if (xml == null) xmlMethodNotFound(realThis, "addNamespace"); + Namespace ns = lib.castToNamespace(cx, arg(args, 0)); + return xml.addNamespace(ns); + } + case Id_childIndex: { + if (xml == null) xmlMethodNotFound(realThis, "childIndex"); + return ScriptRuntime.wrapInt(xml.childIndex()); + } + case Id_inScopeNamespaces: { + if (xml == null) xmlMethodNotFound(realThis, "inScopeNamespaces"); + return cx.newArray(scope, toObjectArray(xml.inScopeNamespaces())); + } + case Id_insertChildAfter: { + if (xml == null) xmlMethodNotFound(realThis, "insertChildAfter"); + Object arg0 = arg(args, 0); + if (arg0 == null || arg0 instanceof XML) { + return xml.insertChildAfter((XML)arg0, arg(args, 1)); + } + return Undefined.instance; + } + case Id_insertChildBefore: { + if (xml == null) xmlMethodNotFound(realThis, "insertChildBefore"); + Object arg0 = arg(args, 0); + if (arg0 == null || arg0 instanceof XML) { + return xml.insertChildBefore((XML)arg0, arg(args, 1)); + } + return Undefined.instance; + } + case Id_localName: { + if (xml == null) xmlMethodNotFound(realThis, "localName"); + return xml.localName(); + } + case Id_name: { + if (xml == null) xmlMethodNotFound(realThis, "name"); + return xml.name(); + } + case Id_namespace: { + if (xml == null) xmlMethodNotFound(realThis, "namespace"); + String prefix = (args.length > 0) ? ScriptRuntime.toString(args[0]) : null; + Namespace rv = xml.namespace(prefix); + if (rv == null) { + return Undefined.instance; + } else { + return rv; + } + } + case Id_namespaceDeclarations: { + if (xml == null) xmlMethodNotFound(realThis, "namespaceDeclarations"); + Namespace[] array = xml.namespaceDeclarations(); + return cx.newArray(scope, toObjectArray(array)); + } + case Id_nodeKind: { + if (xml == null) xmlMethodNotFound(realThis, "nodeKind"); + return xml.nodeKind(); + } + case Id_prependChild: { + if (xml == null) xmlMethodNotFound(realThis, "prependChild"); + return xml.prependChild(arg(args, 0)); + } + case Id_removeNamespace: { + if (xml == null) xmlMethodNotFound(realThis, "removeNamespace"); + Namespace ns = lib.castToNamespace(cx, arg(args, 0)); + return xml.removeNamespace(ns); + } + case Id_replace: { + if (xml == null) xmlMethodNotFound(realThis, "replace"); + XMLName xmlName = lib.toXMLNameOrIndex(cx, arg(args, 0)); + Object arg1 = arg(args, 1); + if (xmlName == null) { + // I refuse to believe that this number will exceed 2^31 + int index = (int)ScriptRuntime.lastUint32Result(cx); + return xml.replace(index, arg1); + } else { + return xml.replace(xmlName, arg1); + } + } + case Id_setChildren: { + if (xml == null) xmlMethodNotFound(realThis, "setChildren"); + return xml.setChildren(arg(args, 0)); + } + case Id_setLocalName: { + if (xml == null) xmlMethodNotFound(realThis, "setLocalName"); + String localName; + Object arg = arg(args, 0); + if (arg instanceof QName) { + localName = ((QName)arg).localName(); + } else { + localName = ScriptRuntime.toString(arg); + } + xml.setLocalName(localName); + return Undefined.instance; + } + case Id_setName: { + if (xml == null) xmlMethodNotFound(realThis, "setName"); + Object arg = (args.length != 0) ? args[0] : Undefined.instance; + QName qname = lib.constructQName(cx, arg); + xml.setName(qname); + return Undefined.instance; + } + case Id_setNamespace: { + if (xml == null) xmlMethodNotFound(realThis, "setNamespace"); + Namespace ns = lib.castToNamespace(cx, arg(args, 0)); + xml.setNamespace(ns); + return Undefined.instance; + } + + case Id_attribute: { + XMLName xmlName = XMLName.create( lib.toNodeQName(cx, arg(args, 0), true), true, false ); + return realThis.getMatches(xmlName); + } + case Id_attributes: + return realThis.getMatches(XMLName.create(XmlNode.QName.create(null, null), true, false)); + case Id_child: { + XMLName xmlName = lib.toXMLNameOrIndex(cx, arg(args, 0)); + if (xmlName == null) { + // Two billion or so is a fine upper limit, so we cast to int + int index = (int)ScriptRuntime.lastUint32Result(cx); + return realThis.child(index); + } else { + return realThis.child(xmlName); + } + } + case Id_children: + return realThis.children(); + case Id_comments: + return realThis.comments(); + case Id_contains: + return ScriptRuntime.wrapBoolean( + realThis.contains(arg(args, 0))); + case Id_copy: + return realThis.copy(); + case Id_descendants: { + XmlNode.QName qname = (args.length == 0) ? XmlNode.QName.create(null, null) : lib.toNodeQName(cx, args[0], false); + return realThis.getMatches( XMLName.create(qname, false, true) ); + } + case Id_elements: { + XMLName xmlName = (args.length == 0) + ? XMLName.formStar() + : lib.toXMLName(cx, args[0]); + return realThis.elements(xmlName); + } + case Id_hasOwnProperty: { + XMLName xmlName = lib.toXMLName(cx, arg(args, 0)); + return ScriptRuntime.wrapBoolean( + realThis.hasOwnProperty(xmlName)); + } + case Id_hasComplexContent: + return ScriptRuntime.wrapBoolean(realThis.hasComplexContent()); + case Id_hasSimpleContent: + return ScriptRuntime.wrapBoolean(realThis.hasSimpleContent()); + case Id_length: + return ScriptRuntime.wrapInt(realThis.length()); + case Id_normalize: + realThis.normalize(); + return Undefined.instance; + case Id_parent: + return realThis.parent(); + case Id_processingInstructions: { + XMLName xmlName = (args.length > 0) + ? lib.toXMLName(cx, args[0]) + : XMLName.formStar(); + return realThis.processingInstructions(xmlName); + } + case Id_propertyIsEnumerable: { + return ScriptRuntime.wrapBoolean( + realThis.propertyIsEnumerable(arg(args, 0))); + } + case Id_text: + return realThis.text(); + case Id_toString: + return realThis.toString(); + case Id_toXMLString: { + return realThis.toXMLString(); + } + case Id_valueOf: + return realThis.valueOf(); + } + throw new IllegalArgumentException(String.valueOf(id)); + } + + private static Object arg(Object[] args, int i) { + return (i < args.length) ? args[i] : Undefined.instance; + } + + final XML newTextElementXML(XmlNode reference, XmlNode.QName qname, String value) { + return lib.newTextElementXML(reference, qname, value); + } + + /** @deprecated Hopefully this can be replaced with ecmaToXml below. */ + final XML newXMLFromJs(Object inputObject) { + return lib.newXMLFromJs(inputObject); + } + + final XML ecmaToXml(Object object) { + return lib.ecmaToXml(object); + } + + final String ecmaEscapeAttributeValue(String s) { + // TODO Check this + String quoted = lib.escapeAttributeValue(s); + return quoted.substring(1, quoted.length() - 1); + } + + final XML createEmptyXML() { + return newXML(XmlNode.createEmpty(getProcessor())); + } +} diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java new file mode 100644 index 0000000..fbeb45e --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java @@ -0,0 +1,125 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. 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 General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.*; +import org.mozilla.javascript.xml.*; + +final class XMLWithScope extends NativeWith +{ + private static final long serialVersionUID = -696429282095170887L; + + private XMLLibImpl lib; + private int _currIndex; + private XMLList _xmlList; + private XMLObject _dqPrototype; + + XMLWithScope(XMLLibImpl lib, Scriptable parent, XMLObject prototype) + { + super(parent, prototype); + this.lib = lib; + } + + void initAsDotQuery() + { + XMLObject prototype = (XMLObject)getPrototype(); + // 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). + _currIndex = 0; + _dqPrototype = prototype; + if (prototype instanceof XMLList) { + XMLList xl = (XMLList)prototype; + if (xl.length() > 0) { + setPrototype((Scriptable)(xl.get(0, null))); + } + } + // Always return the outer-most type of XML lValue of + // XML to left of dotQuery. + _xmlList = lib.newXMLList(); + } + + protected Object updateDotQuery(boolean value) + { + // Return null to continue looping + + XMLObject seed = _dqPrototype; + XMLList xmlL = _xmlList; + + 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 = _currIndex; + + 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. + _currIndex = 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; + } +} diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java new file mode 100644 index 0000000..06955d0 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java @@ -0,0 +1,869 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino DOM-only E4X implementation. + * + * The Initial Developer of the Original Code is + * David P. Caldwell. + * Portions created by David P. Caldwell are Copyright (C) + * 2007 David P. Caldwell. All Rights Reserved. + * + * + * Contributor(s): + * David P. Caldwell <inonit@inonit.com> + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import java.util.*; + +import org.w3c.dom.*; + +import org.mozilla.javascript.*; + +// Disambiguate with org.mozilla.javascript +import org.w3c.dom.Node; + +class XmlNode { + private static final String XML_NAMESPACES_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/"; + + private static final String USER_DATA_XMLNODE_KEY = XmlNode.class.getName(); + + private static final boolean DOM_LEVEL_3 = true; + + private static XmlNode getUserData(Node node) { + if (DOM_LEVEL_3) { + return (XmlNode)node.getUserData(USER_DATA_XMLNODE_KEY); + } + return null; + } + + private static void setUserData(Node node, XmlNode wrap) { + if (DOM_LEVEL_3) { + node.setUserData(USER_DATA_XMLNODE_KEY, wrap, wrap.events); + } + } + + private static XmlNode createImpl(Node node) { + if (node instanceof Document) throw new IllegalArgumentException(); + XmlNode rv = null; + if (getUserData(node) == null) { + rv = new XmlNode(); + rv.dom = node; + setUserData(node, rv); + } else { + rv = getUserData(node); + } + return rv; + } + + static XmlNode newElementWithText(XmlProcessor processor, XmlNode reference, XmlNode.QName qname, String value) { + if (reference instanceof org.w3c.dom.Document) throw new IllegalArgumentException("Cannot use Document node as reference"); + Document document = null; + if (reference != null) { + document = reference.dom.getOwnerDocument(); + } else { + document = processor.newDocument(); + } + Node referenceDom = (reference != null) ? reference.dom : null; + Element e = document.createElementNS(qname.getUri(), qname.qualify(referenceDom)); + if (value != null) { + e.appendChild(document.createTextNode(value)); + } + return XmlNode.createImpl(e); + } + + static XmlNode createText(XmlProcessor processor, String value) { + return createImpl( processor.newDocument().createTextNode(value) ); + } + + static XmlNode createElementFromNode(Node node) { + if (node instanceof Document) + node = ((Document) node).getDocumentElement(); + return createImpl(node); + } + + static XmlNode createElement(XmlProcessor processor, String namespaceUri, String xml) throws org.xml.sax.SAXException { + return createImpl( processor.toXml(namespaceUri, xml) ); + } + + static XmlNode createEmpty(XmlProcessor processor) { + return createText(processor, ""); + } + + private static XmlNode copy(XmlNode other) { + return createImpl( other.dom.cloneNode(true) ); + } + + private static final long serialVersionUID = 1L; + + private UserDataHandler events = new UserDataHandler() { + public void handle(short operation, String key, Object data, Node src, Node dest) { + } + }; + + private Node dom; + + private XML xml; + + private XmlNode() { + } + + String debug() { + XmlProcessor raw = new XmlProcessor(); + raw.setIgnoreComments(false); + raw.setIgnoreProcessingInstructions(false); + raw.setIgnoreWhitespace(false); + raw.setPrettyPrinting(false); + return raw.ecmaToXmlString(this.dom); + } + + public String toString() { + return "XmlNode: type=" + dom.getNodeType() + " dom=" + dom.toString(); + } + + XML getXml() { + return xml; + } + + void setXml(XML xml) { + this.xml = xml; + } + + int getChildCount() { + return this.dom.getChildNodes().getLength(); + } + + XmlNode parent() { + Node domParent = dom.getParentNode(); + if (domParent instanceof Document) return null; + if (domParent == null) return null; + return createImpl(domParent); + } + + int getChildIndex() { + if (this.isAttributeType()) return -1; + if (parent() == null) return -1; + org.w3c.dom.NodeList siblings = this.dom.getParentNode().getChildNodes(); + for (int i=0; i<siblings.getLength(); i++) { + if (siblings.item(i) == dom) { + return i; + } + } + // Either the parent is -1 or one of the this node's parent's children is this node. + throw new RuntimeException("Unreachable."); + } + + void removeChild(int index) { + this.dom.removeChild( this.dom.getChildNodes().item(index) ); + } + + String toXmlString(XmlProcessor processor) { + return processor.ecmaToXmlString(this.dom); + } + + String ecmaValue() { + // TODO See ECMA 357 Section 9.1 + if (isTextType()) { + return ((org.w3c.dom.Text)dom).getData(); + } else if (isAttributeType()) { + return ((org.w3c.dom.Attr)dom).getValue(); + } else if (isProcessingInstructionType()) { + return ((org.w3c.dom.ProcessingInstruction)dom).getData(); + } else if (isCommentType()) { + return ((org.w3c.dom.Comment)dom).getNodeValue(); + } else if (isElementType()) { + throw new RuntimeException("Unimplemented ecmaValue() for elements."); + } else { + throw new RuntimeException("Unimplemented for node " + dom); + } + } + + void deleteMe() { + if (dom instanceof Attr) { + Attr attr = (Attr)this.dom; + attr.getOwnerElement().getAttributes().removeNamedItemNS(attr.getNamespaceURI(), attr.getLocalName()); + } else { + if (this.dom.getParentNode() != null) { + this.dom.getParentNode().removeChild(this.dom); + } else { + // This case can be exercised at least when executing the regression + // tests under https://bugzilla.mozilla.org/show_bug.cgi?id=354145 + } + } + } + + void normalize() { + this.dom.normalize(); + } + + void insertChildAt(int index, XmlNode node) { + Node parent = this.dom; + Node child = parent.getOwnerDocument().importNode( node.dom, true ); + if (parent.getChildNodes().getLength() < index) { + // TODO Check ECMA for what happens here + throw new IllegalArgumentException("index=" + index + " length=" + parent.getChildNodes().getLength()); + } + if (parent.getChildNodes().getLength() == index) { + parent.appendChild(child); + } else { + parent.insertBefore(child, parent.getChildNodes().item(index)); + } + } + + void insertChildrenAt(int index, XmlNode[] nodes) { + for (int i=0; i<nodes.length; i++) { + insertChildAt(index+i, nodes[i]); + } + } + + XmlNode getChild(int index) { + Node child = dom.getChildNodes().item(index); + return createImpl(child); + } + + // Helper method for XML.hasSimpleContent() + boolean hasChildElement() { + org.w3c.dom.NodeList nodes = this.dom.getChildNodes(); + for (int i=0; i<nodes.getLength(); i++) { + if (nodes.item(i).getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) return true; + } + return false; + } + + boolean isSameNode(XmlNode other) { + // TODO May need to be changed if we allow XmlNode to refer to several Node objects + return this.dom == other.dom; + } + + private String toUri(String ns) { + return (ns == null) ? "" : ns; + } + + private void addNamespaces(Namespaces rv, Element element) { + if (element == null) throw new RuntimeException("element must not be null"); + String myDefaultNamespace = toUri(element.lookupNamespaceURI(null)); + String parentDefaultNamespace = ""; + if (element.getParentNode() != null) { + parentDefaultNamespace = toUri(element.getParentNode().lookupNamespaceURI(null)); + } + if (!myDefaultNamespace.equals(parentDefaultNamespace) || !(element.getParentNode() instanceof Element) ) { + rv.declare(Namespace.create("", myDefaultNamespace)); + } + NamedNodeMap attributes = element.getAttributes(); + for (int i=0; i<attributes.getLength(); i++) { + Attr attr = (Attr)attributes.item(i); + if (attr.getPrefix() != null && attr.getPrefix().equals("xmlns")) { + rv.declare(Namespace.create(attr.getLocalName(), attr.getValue())); + } + } + } + + private Namespaces getAllNamespaces() { + Namespaces rv = new Namespaces(); + + Node target = this.dom; + if (target instanceof Attr) { + target = ((Attr)target).getOwnerElement(); + } + while(target != null) { + if (target instanceof Element) { + addNamespaces(rv, (Element)target); + } + target = target.getParentNode(); + } + // Fallback in case no namespace was declared + rv.declare(Namespace.create("", "")); + return rv; + } + + Namespace[] getInScopeNamespaces() { + Namespaces rv = getAllNamespaces(); + return rv.getNamespaces(); + } + + Namespace[] getNamespaceDeclarations() { + // ECMA357 13.4.4.24 + if (this.dom instanceof Element) { + Namespaces rv = new Namespaces(); + addNamespaces( rv, (Element)this.dom ); + return rv.getNamespaces(); + } else { + return new Namespace[0]; + } + } + + Namespace getNamespaceDeclaration(String prefix) { + if (prefix.equals("") && dom instanceof Attr) { + // Default namespaces do not apply to attributes; see XML Namespaces section 5.2 + return Namespace.create("", ""); + } + Namespaces rv = getAllNamespaces(); + return rv.getNamespace(prefix); + } + + Namespace getNamespaceDeclaration() { + if (dom.getPrefix() == null) return getNamespaceDeclaration(""); + return getNamespaceDeclaration(dom.getPrefix()); + } + + private static class Namespaces { + private HashMap map = new HashMap(); + private HashMap uriToPrefix = new HashMap(); + + Namespaces() { + } + + void declare(Namespace n) { + if (map.get(n.prefix) == null) { + map.put(n.prefix, n.uri); + } + // TODO I think this is analogous to the other way, but have not really thought it through ... should local scope + // matter more than outer scope? + if (uriToPrefix.get(n.uri) == null) { + uriToPrefix.put(n.uri, n.prefix); + } + } + + Namespace getNamespaceByUri(String uri) { + if (uriToPrefix.get(uri) == null) return null; + return Namespace.create(uri, (String)uriToPrefix.get(uri)); + } + + Namespace getNamespace(String prefix) { + if (map.get(prefix) == null) return null; + return Namespace.create(prefix, (String)map.get(prefix)); + } + + Namespace[] getNamespaces() { + Iterator i = map.keySet().iterator(); + ArrayList rv = new ArrayList(); + while(i.hasNext()) { + String prefix = (String)i.next(); + String uri = (String)map.get(prefix); + Namespace n = Namespace.create(prefix, uri); + if (!n.isEmpty()) { + rv.add( n ); + } + } + return (Namespace[])rv.toArray(new Namespace[0]); + } + } + + final XmlNode copy() { + return copy( this ); + } + + // Returns whether this node is capable of being a parent + final boolean isParentType() { + return isElementType(); + } + + final boolean isTextType() { + return dom.getNodeType() == Node.TEXT_NODE || dom.getNodeType() == Node.CDATA_SECTION_NODE; + } + + final boolean isAttributeType() { + return dom.getNodeType() == Node.ATTRIBUTE_NODE; + } + + final boolean isProcessingInstructionType() { + return dom.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE; + } + + final boolean isCommentType() { + return dom.getNodeType() == Node.COMMENT_NODE; + } + + final boolean isElementType() { + return dom.getNodeType() == Node.ELEMENT_NODE; + } + + final void renameNode(QName qname) { + this.dom = dom.getOwnerDocument().renameNode(dom, qname.getUri(), qname.qualify(dom)); + } + + void invalidateNamespacePrefix() { + if (!(dom instanceof Element)) throw new IllegalStateException(); + String prefix = this.dom.getPrefix(); + QName after = QName.create(this.dom.getNamespaceURI(), this.dom.getLocalName(), null); + renameNode(after); + NamedNodeMap attrs = this.dom.getAttributes(); + for (int i=0; i<attrs.getLength(); i++) { + if (attrs.item(i).getPrefix().equals(prefix)) { + createImpl( attrs.item(i) ).renameNode( QName.create(attrs.item(i).getNamespaceURI(), attrs.item(i).getLocalName(), null) ); + } + } + } + + private void declareNamespace(Element e, String prefix, String uri) { + if (prefix.length() > 0) { + e.setAttributeNS(XML_NAMESPACES_NAMESPACE_URI, "xmlns:" + prefix, uri); + } else { + e.setAttribute("xmlns", uri); + } + } + + void declareNamespace(String prefix, String uri) { + if (!(dom instanceof Element)) throw new IllegalStateException(); + if (dom.lookupNamespaceURI(uri) != null && dom.lookupNamespaceURI(uri).equals(prefix)) { + // do nothing + } else { + Element e = (Element)dom; + declareNamespace(e, prefix, uri); + } + } + + private Namespace getDefaultNamespace() { + String prefix = ""; + String uri = (dom.lookupNamespaceURI(null) == null) ? "" : dom.lookupNamespaceURI(null); + return Namespace.create(prefix, uri); + } + + private String getExistingPrefixFor(Namespace namespace) { + if (getDefaultNamespace().getUri().equals(namespace.getUri())) { + return ""; + } + return dom.lookupPrefix(namespace.getUri()); + } + + private Namespace getNodeNamespace() { + String uri = dom.getNamespaceURI(); + String prefix = dom.getPrefix(); + if (uri == null) uri = ""; + if (prefix == null) prefix = ""; + return Namespace.create(prefix, uri); + } + + Namespace getNamespace() { + return getNodeNamespace(); + } + + void removeNamespace(Namespace namespace) { + Namespace current = getNodeNamespace(); + + // Do not remove in-use namespace + if (namespace.is(current)) return; + NamedNodeMap attrs = this.dom.getAttributes(); + for (int i=0; i<attrs.getLength(); i++) { + XmlNode attr = XmlNode.createImpl(attrs.item(i)); + if (namespace.is(attr.getNodeNamespace())) return; + } + + // TODO I must confess I am not sure I understand the spec fully. See ECMA357 13.4.4.31 + String existingPrefix = getExistingPrefixFor(namespace); + if (existingPrefix != null) { + if (namespace.isUnspecifiedPrefix()) { + // we should remove any namespace with this URI from scope; we do this by declaring a namespace with the same + // prefix as the existing prefix and setting its URI to the default namespace + declareNamespace(existingPrefix, getDefaultNamespace().getUri()); + } else { + if (existingPrefix.equals(namespace.getPrefix())) { + declareNamespace(existingPrefix, getDefaultNamespace().getUri()); + } + } + } else { + // the argument namespace is not declared in this scope, so do nothing. + } + } + + private void setProcessingInstructionName(String localName) { + org.w3c.dom.ProcessingInstruction pi = (ProcessingInstruction)this.dom; + // We cannot set the node name; Document.renameNode() only supports elements and attributes. So we replace it + pi.getParentNode().replaceChild( + pi, + pi.getOwnerDocument().createProcessingInstruction(localName, pi.getData()) + ); + } + + final void setLocalName(String localName) { + if (dom instanceof ProcessingInstruction) { + setProcessingInstructionName(localName); + } else { + String prefix = dom.getPrefix(); + if (prefix == null) prefix = ""; + this.dom = dom.getOwnerDocument().renameNode(dom, dom.getNamespaceURI(), QName.qualify(prefix, localName)); + } + } + + final QName getQname() { + String uri = (dom.getNamespaceURI()) == null ? "" : dom.getNamespaceURI(); + String prefix = (dom.getPrefix() == null) ? "" : dom.getPrefix(); + return QName.create( uri, dom.getLocalName(), prefix ); + } + + void addMatchingChildren(XMLList result, XmlNode.Filter filter) { + Node node = this.dom; + NodeList children = node.getChildNodes(); + for(int i=0; i<children.getLength(); i++) { + Node childnode = children.item(i); + XmlNode child = XmlNode.createImpl(childnode); + if (filter.accept(childnode)) { + result.addToList(child); + } + } + } + + XmlNode[] getMatchingChildren(Filter filter) { + ArrayList rv = new ArrayList(); + NodeList nodes = this.dom.getChildNodes(); + for (int i=0; i<nodes.getLength(); i++) { + Node node = nodes.item(i); + if (filter.accept(node)) { + rv.add( createImpl(node) ); + } + } + return (XmlNode[])rv.toArray(new XmlNode[0]); + } + + XmlNode[] getAttributes() { + NamedNodeMap attrs = this.dom.getAttributes(); + // TODO Or could make callers handle null? + if (attrs == null) throw new IllegalStateException("Must be element."); + XmlNode[] rv = new XmlNode[attrs.getLength()]; + for (int i=0; i<attrs.getLength(); i++) { + rv[i] = createImpl( attrs.item(i) ); + } + return rv; + } + + String getAttributeValue() { + return ((Attr)dom).getValue(); + } + + void setAttribute(QName name, String value) { + if (!(dom instanceof Element)) throw new IllegalStateException("Can only set attribute on elements."); + name.setAttribute( (Element)dom, value ); + } + + void replaceWith(XmlNode other) { + Node replacement = other.dom; + if (replacement.getOwnerDocument() != this.dom.getOwnerDocument()) { + replacement = this.dom.getOwnerDocument().importNode(replacement, true); + } + this.dom.getParentNode().replaceChild(replacement, this.dom); + } + + String ecmaToXMLString(XmlProcessor processor) { + if (this.isElementType()) { + Element copy = (Element)this.dom.cloneNode(true); + Namespace[] inScope = this.getInScopeNamespaces(); + for (int i=0; i<inScope.length; i++) { + declareNamespace(copy, inScope[i].getPrefix(), inScope[i].getUri()); + } + return processor.ecmaToXmlString(copy); + } else { + return processor.ecmaToXmlString(dom); + } + } + + static class Namespace { + static Namespace create(String prefix, String uri) { + if (prefix == null) throw new IllegalArgumentException("Empty string represents default namespace prefix"); + if (uri == null) throw new IllegalArgumentException("Namespace may not lack a URI"); + Namespace rv = new Namespace(); + rv.prefix = prefix; + rv.uri = uri; + return rv; + } + + static Namespace create(String uri) { + Namespace rv = new Namespace(); + rv.uri = uri; + return rv; + } + + static final Namespace GLOBAL = create("", ""); + + private String prefix; + private String uri; + + private Namespace() { + } + + public String toString() { + if (prefix == null) return "XmlNode.Namespace [" + uri + "]"; + return "XmlNode.Namespace [" + prefix + "{" + uri + "}]"; + } + + boolean isUnspecifiedPrefix() { + return prefix == null; + } + + boolean is(Namespace other) { + return this.prefix != null && other.prefix != null && this.prefix.equals(other.prefix) && this.uri.equals(other.uri); + } + + boolean isEmpty() { + return prefix != null && prefix.equals("") && uri.equals(""); + } + + boolean isDefault() { + return prefix != null && prefix.equals(""); + } + + boolean isGlobal() { + return uri != null && uri.equals(""); + } + + // Called by QName + // TODO Move functionality from QName lookupPrefix to here + private void setPrefix(String prefix) { + if (prefix == null) throw new IllegalArgumentException(); + this.prefix = prefix; + } + + String getPrefix() { + return prefix; + } + + String getUri() { + return uri; + } + } + + // TODO Where is this class used? No longer using it in QName implementation + static class QName { + static QName create(Namespace namespace, String localName) { + // A null namespace indicates a wild-card match for any namespace + // A null localName indicates "*" from the point of view of ECMA357 + if (localName != null && localName.equals("*")) throw new RuntimeException("* is not valid localName"); + QName rv = new QName(); + rv.namespace = namespace; + rv.localName = localName; + return rv; + } + + /** @deprecated */ + static QName create(String uri, String localName, String prefix) { + return create(Namespace.create(prefix, uri), localName); + } + + static String qualify(String prefix, String localName) { + if (prefix == null) throw new IllegalArgumentException("prefix must not be null"); + if (prefix.length() > 0) return prefix + ":" + localName; + return localName; + } + + private Namespace namespace; + private String localName; + + private QName() { + } + + public String toString() { + return "XmlNode.QName [" + localName + "," + namespace + "]"; + } + + private boolean equals(String one, String two) { + if (one == null && two == null) return true; + if (one == null || two == null) return false; + return one.equals(two); + } + + private boolean namespacesEqual(Namespace one, Namespace two) { + if (one == null && two == null) return true; + if (one == null || two == null) return false; + return equals(one.getUri(), two.getUri()); + } + + final boolean isEqualTo(QName other) { + if (!namespacesEqual(this.namespace, other.namespace)) return false; + if (!equals(this.localName, other.localName)) return false; + return true; + } + + void lookupPrefix(org.w3c.dom.Node node) { + if (node == null) throw new IllegalArgumentException("node must not be null"); + String prefix = node.lookupPrefix(namespace.getUri()); + if (prefix == null) { + // check to see if we match the default namespace + String defaultNamespace = node.lookupNamespaceURI(null); + if (defaultNamespace == null) defaultNamespace = ""; + String nodeNamespace = namespace.getUri(); + if (nodeNamespace.equals(defaultNamespace)) { + prefix = ""; + } + } + int i = 0; + while(prefix == null) { + String generatedPrefix = "e4x_" + i++; + String generatedUri = node.lookupNamespaceURI(generatedPrefix); + if (generatedUri == null) { + prefix = generatedPrefix; + org.w3c.dom.Node top = node; + while(top.getParentNode() != null && top.getParentNode() instanceof org.w3c.dom.Element) { + top = top.getParentNode(); + } + ((org.w3c.dom.Element)top).setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + prefix, namespace.getUri()); + } + } + namespace.setPrefix(prefix); + } + + String qualify(org.w3c.dom.Node node) { + if (namespace.getPrefix() == null) { + if (node != null) { + lookupPrefix(node); + } else { + if (namespace.getUri().equals("")) { + namespace.setPrefix(""); + } else { + // TODO I am not sure this is right, but if we are creating a standalone node, I think we can set the + // default namespace on the node itself and not worry about setting a prefix for that namespace. + namespace.setPrefix(""); + } + } + } + return qualify(namespace.getPrefix(), localName); + } + + void setAttribute(org.w3c.dom.Element element, String value) { + if (namespace.getPrefix() == null) lookupPrefix(element); + element.setAttributeNS(namespace.getUri(), qualify(namespace.getPrefix(), localName), value); + } + + /** @deprecated Use getNamespace() */ + String getUri() { + return namespace.getUri(); + } + + /** @deprecated Use getNamespace() */ + String getPrefix() { + return namespace.getPrefix(); + } + + Namespace getNamespace() { + return namespace; + } + + String getLocalName() { + return localName; + } + } + + static class List { + private java.util.Vector v; + + List() { + v = new java.util.Vector(); + } + + private void _add(XmlNode n) { + v.add(n); + } + + XmlNode item(int index) { + return (XmlNode)(v.get(index)); + } + + void remove(int index) { + v.remove(index); + } + + void add(List other) { + for (int i=0; i<other.length(); i++) { + _add(other.item(i)); + } + } + + void add(List from, int startInclusive, int endExclusive) { + for (int i=startInclusive; i<endExclusive; i++) { + _add(from.item(i)); + } + } + + void add(XmlNode node) { + _add(node); + } + + /** @deprecated */ + void add(XML xml) { + _add(xml.getAnnotation()); + } + + /** @deprecated */ + 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++) { + this._add((xmlSrc.item(i)).getAnnotation()); + } + } else if (toAdd instanceof XML) { + this._add(((XML)(toAdd)).getAnnotation()); + } else if (toAdd instanceof XmlNode) { + this._add((XmlNode)toAdd); + } + } + + int length() { + return v.size(); + } + } + + static abstract class Filter { + static final Filter COMMENT = new Filter() { + boolean accept(Node node) { + return node.getNodeType() == Node.COMMENT_NODE; + } + }; + static final Filter TEXT = new Filter() { + boolean accept(Node node) { + return node.getNodeType() == Node.TEXT_NODE; + } + }; + static Filter PROCESSING_INSTRUCTION(final XMLName name) { + return new Filter() { + boolean accept(Node node) { + if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { + ProcessingInstruction pi = (ProcessingInstruction)node; + return name.matchesLocalName(pi.getTarget()); + } + return false; + } + }; + } + static Filter ELEMENT = new Filter() { + boolean accept(Node node) { + return node.getNodeType() == Node.ELEMENT_NODE; + } + }; + static Filter TRUE = new Filter() { + boolean accept(Node node) { + return true; + } + }; + abstract boolean accept(Node node); + } + + // Support experimental Java interface + org.w3c.dom.Node toDomNode() { + return this.dom; + } +} diff --git a/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java new file mode 100644 index 0000000..d8ad495 --- /dev/null +++ b/trunk/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java @@ -0,0 +1,445 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino DOM-only E4X implementation. + * + * The Initial Developer of the Original Code is + * David P. Caldwell. + * Portions created by David P. Caldwell are Copyright (C) + * 2007 David P. Caldwell. All Rights Reserved. + * + * + * Contributor(s): + * David P. Caldwell <inonit@inonit.com> + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import org.w3c.dom.*; + +import javax.xml.parsers.DocumentBuilder; + +import org.mozilla.javascript.*; + +// Disambiguate from org.mozilla.javascript.Node +import org.w3c.dom.Node; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXParseException; + +class XmlProcessor { + private boolean ignoreComments; + private boolean ignoreProcessingInstructions; + private boolean ignoreWhitespace; + private boolean prettyPrint; + private int prettyIndent; + + private javax.xml.parsers.DocumentBuilderFactory dom; + private javax.xml.transform.TransformerFactory xform; + private DocumentBuilder documentBuilder; + private RhinoSAXErrorHandler errorHandler = new RhinoSAXErrorHandler(); + + + private static class RhinoSAXErrorHandler implements ErrorHandler { + + private void throwError(SAXParseException e) { + throw ScriptRuntime.constructError("TypeError", e.getMessage(), + e.getLineNumber() - 1); + } + + public void error(SAXParseException e) { + throwError(e); + } + + public void fatalError(SAXParseException e) { + throwError(e); + } + + public void warning(SAXParseException e) { + Context.reportWarning(e.getMessage()); + } + } + + XmlProcessor() { + setDefault(); + this.dom = javax.xml.parsers.DocumentBuilderFactory.newInstance(); + this.dom.setNamespaceAware(true); + this.dom.setIgnoringComments(false); + this.xform = javax.xml.transform.TransformerFactory.newInstance(); + } + + final void setDefault() { + this.setIgnoreComments(true); + this.setIgnoreProcessingInstructions(true); + this.setIgnoreWhitespace(true); + this.setPrettyPrinting(true); + this.setPrettyIndent(2); + } + + final void setIgnoreComments(boolean b) { + this.ignoreComments = b; + } + + final void setIgnoreWhitespace(boolean b) { + this.ignoreWhitespace = b; + } + + final void setIgnoreProcessingInstructions(boolean b) { + this.ignoreProcessingInstructions = b; + } + + final void setPrettyPrinting(boolean b) { + this.prettyPrint = b; + } + + final void setPrettyIndent(int i) { + this.prettyIndent = i; + } + + final boolean isIgnoreComments() { + return ignoreComments; + } + + final boolean isIgnoreProcessingInstructions() { + return ignoreProcessingInstructions; + } + + final boolean isIgnoreWhitespace() { + return ignoreWhitespace; + } + + final boolean isPrettyPrinting() { + return prettyPrint; + } + + final int getPrettyIndent() { + return prettyIndent; + } + + private String toXmlNewlines(String rv) { + StringBuffer nl = new StringBuffer(); + for (int i=0; i<rv.length(); i++) { + if (rv.charAt(i) == '\r') { + if (rv.charAt(i+1) == '\n') { + // DOS, do nothing and skip the \r + } else { + // Macintosh, substitute \n + nl.append('\n'); + } + } else { + nl.append(rv.charAt(i)); + } + } + return nl.toString(); + } + + private javax.xml.parsers.DocumentBuilderFactory getDomFactory() { + return dom; + } + + private synchronized DocumentBuilder getDocumentBuilderFromPool() + throws javax.xml.parsers.ParserConfigurationException + { + DocumentBuilder result; + if (documentBuilder == null) { + javax.xml.parsers.DocumentBuilderFactory factory = getDomFactory(); + result = factory.newDocumentBuilder(); + } else { + result = documentBuilder; + documentBuilder = null; + } + result.setErrorHandler(errorHandler); + return result; + } + + private synchronized void returnDocumentBuilderToPool(DocumentBuilder db) { + if (documentBuilder == null) { + documentBuilder = db; + documentBuilder.reset(); + } + } + + private void addProcessingInstructionsTo(java.util.Vector v, Node node) { + if (node instanceof ProcessingInstruction) { + v.add(node); + } + if (node.getChildNodes() != null) { + for (int i=0; i<node.getChildNodes().getLength(); i++) { + addProcessingInstructionsTo(v, node.getChildNodes().item(i)); + } + } + } + + private void addCommentsTo(java.util.Vector v, Node node) { + if (node instanceof Comment) { + v.add(node); + } + if (node.getChildNodes() != null) { + for (int i=0; i<node.getChildNodes().getLength(); i++) { + addProcessingInstructionsTo(v, node.getChildNodes().item(i)); + } + } + } + + private void addTextNodesToRemoveAndTrim(java.util.Vector toRemove, Node node) { + if (node instanceof Text) { + Text text = (Text)node; + boolean BUG_369394_IS_VALID = false; + if (!BUG_369394_IS_VALID) { + text.setData(text.getData().trim()); + } else { + if (text.getData().trim().length() == 0) { + text.setData(""); + } + } + if (text.getData().length() == 0) { + toRemove.add(node); + } + } + if (node.getChildNodes() != null) { + for (int i=0; i<node.getChildNodes().getLength(); i++) { + addTextNodesToRemoveAndTrim(toRemove, node.getChildNodes().item(i)); + } + } + } + + final Node toXml(String defaultNamespaceUri, String xml) throws org.xml.sax.SAXException { + // See ECMA357 10.3.1 + DocumentBuilder builder = null; + try { + String syntheticXml = "<parent xmlns=\"" + defaultNamespaceUri + + "\">" + xml + "</parent>"; + builder = getDocumentBuilderFromPool(); + Document document = builder.parse( new org.xml.sax.InputSource(new java.io.StringReader(syntheticXml)) ); + if (ignoreProcessingInstructions) { + java.util.Vector v = new java.util.Vector(); + addProcessingInstructionsTo(v, document); + for (int i=0; i<v.size(); i++) { + Node node = (Node)v.elementAt(i); + node.getParentNode().removeChild(node); + } + } + if (ignoreComments) { + java.util.Vector v = new java.util.Vector(); + addCommentsTo(v, document); + for (int i=0; i<v.size(); i++) { + Node node = (Node)v.elementAt(i); + node.getParentNode().removeChild(node); + } + } + if (ignoreWhitespace) { + // Apparently JAXP setIgnoringElementContentWhitespace() has a different meaning, it appears from the Javadoc + // Refers to element-only content models, which means we would need to have a validating parser and DTD or schema + // so that it would know which whitespace to ignore. + + // Instead we will try to delete it ourselves. + java.util.Vector v = new java.util.Vector(); + addTextNodesToRemoveAndTrim(v, document); + for (int i=0; i<v.size(); i++) { + Node node = (Node)v.elementAt(i); + node.getParentNode().removeChild(node); + } + } + NodeList rv = document.getDocumentElement().getChildNodes(); + if (rv.getLength() > 1) { + throw ScriptRuntime.constructError("SyntaxError", "XML objects may contain at most one node."); + } else if (rv.getLength() == 0) { + Node node = document.createTextNode(""); + return node; + } else { + Node node = rv.item(0); + document.getDocumentElement().removeChild(node); + return node; + } + } catch (java.io.IOException e) { + throw new RuntimeException("Unreachable."); + } catch (javax.xml.parsers.ParserConfigurationException e) { + throw new RuntimeException(e); + } finally { + if (builder != null) + returnDocumentBuilderToPool(builder); + } + } + + Document newDocument() { + DocumentBuilder builder = null; + try { + // TODO Should this use XML settings? + builder = getDocumentBuilderFromPool(); + return builder.newDocument(); + } catch (javax.xml.parsers.ParserConfigurationException ex) { + // TODO How to handle these runtime errors? + throw new RuntimeException(ex); + } finally { + if (builder != null) + returnDocumentBuilderToPool(builder); + } + } + + // TODO Cannot remember what this is for, so whether it should use settings or not + private String toString(Node node) { + javax.xml.transform.dom.DOMSource source = new javax.xml.transform.dom.DOMSource(node); + java.io.StringWriter writer = new java.io.StringWriter(); + javax.xml.transform.stream.StreamResult result = new javax.xml.transform.stream.StreamResult(writer); + try { + javax.xml.transform.Transformer transformer = xform.newTransformer(); + transformer.setOutputProperty(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "no"); + transformer.setOutputProperty(javax.xml.transform.OutputKeys.METHOD, "xml"); + transformer.transform(source, result); + } catch (javax.xml.transform.TransformerConfigurationException ex) { + // TODO How to handle these runtime errors? + throw new RuntimeException(ex); + } catch (javax.xml.transform.TransformerException ex) { + // TODO How to handle these runtime errors? + throw new RuntimeException(ex); + } + return toXmlNewlines(writer.toString()); + } + + String escapeAttributeValue(Object value) { + String text = ScriptRuntime.toString(value); + + if (text.length() == 0) return ""; + + Document dom = newDocument(); + Element e = dom.createElement("a"); + e.setAttribute("b", text); + String elementText = toString(e); + int begin = elementText.indexOf('"'); + int end = elementText.lastIndexOf('"'); + return elementText.substring(begin+1,end); + } + + String escapeTextValue(Object value) { + if (value instanceof XMLObjectImpl) { + return ((XMLObjectImpl)value).toXMLString(); + } + + String text = ScriptRuntime.toString(value); + + if (text.length() == 0) return text; + + Document dom = newDocument(); + Element e = dom.createElement("a"); + e.setTextContent(text); + String elementText = toString(e); + + int begin = elementText.indexOf('>') + 1; + int end = elementText.lastIndexOf('<'); + return (begin < end) ? elementText.substring(begin, end) : ""; + } + + private String escapeElementValue(String s) { + // TODO Check this + return escapeTextValue(s); + } + + private String elementToXmlString(Element element) { + // TODO My goodness ECMA is complicated (see 10.2.1). We'll try this first. + Element copy = (Element)element.cloneNode(true); + if (prettyPrint) { + beautifyElement(copy, 0); + } + return toString(copy); + } + + final String ecmaToXmlString(Node node) { + // See ECMA 357 Section 10.2.1 + StringBuffer s = new StringBuffer(); + int indentLevel = 0; + if (prettyPrint) { + for (int i=0; i<indentLevel; i++) { + s.append(' '); + } + } + if (node instanceof Text) { + String data = ((Text)node).getData(); + // TODO Does Java trim() work same as XMLWhitespace? + String v = (prettyPrint) ? data.trim() : data; + s.append(escapeElementValue(v)); + return s.toString(); + } + if (node instanceof Attr) { + String value = ((Attr)node).getValue(); + s.append(escapeAttributeValue(value)); + return s.toString(); + } + if (node instanceof Comment) { + s.append("<!--" + ((Comment)node).getNodeValue() + "-->"); + return s.toString(); + } + if (node instanceof ProcessingInstruction) { + ProcessingInstruction pi = (ProcessingInstruction)node; + s.append("<?" + pi.getTarget() + " " + pi.getData() + "?>"); + return s.toString(); + } + s.append(elementToXmlString((Element)node)); + return s.toString(); + } + + private void beautifyElement(Element e, int indent) { + StringBuffer s = new StringBuffer(); + s.append('\n'); + for (int i=0; i<indent; i++) { + s.append(' '); + } + String afterContent = s.toString(); + for (int i=0; i<prettyIndent; i++) { + s.append(' '); + } + String beforeContent = s.toString(); + + // We "mark" all the nodes first; if we tried to do this loop otherwise, it would behave unexpectedly (the inserted nodes + // would contribute to the length and it might never terminate). + java.util.Vector toIndent = new java.util.Vector(); + boolean indentChildren = false; + for (int i=0; i<e.getChildNodes().getLength(); i++) { + if (i == 1) indentChildren = true; + if (e.getChildNodes().item(i) instanceof Text) { + toIndent.add(e.getChildNodes().item(i)); + } else { + indentChildren = true; + toIndent.add(e.getChildNodes().item(i)); + } + } + if (indentChildren) { + for (int i=0; i<toIndent.size(); i++) { + e.insertBefore( e.getOwnerDocument().createTextNode(beforeContent), (Node)toIndent.elementAt(i) ); + } + } + NodeList nodes = e.getChildNodes(); + java.util.Vector v = new java.util.Vector(); + for (int i=0; i<nodes.getLength(); i++) { + if (nodes.item(i) instanceof Element) { + v.add( nodes.item(i) ); + } + } + for (int i=0; i<v.size(); i++) { + beautifyElement( (Element)v.elementAt(i), indent + prettyIndent ); + } + if (indentChildren) { + e.appendChild( e.getOwnerDocument().createTextNode(afterContent) ); + } + } +} |