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