org.mozilla.javascript.xmlimpl.XMLLibImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhino Show documentation
Show all versions of rhino Show documentation
Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically
embedded into Java applications to provide scripting to end users.
The newest version!
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript.xmlimpl;
import java.io.Serializable;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Kit;
import org.mozilla.javascript.Node;
import org.mozilla.javascript.Ref;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.Wrapper;
import org.mozilla.javascript.xml.XMLLib;
import org.mozilla.javascript.xml.XMLObject;
import org.xml.sax.SAXException;
public final class XMLLibImpl extends XMLLib implements Serializable {
//
// EXPERIMENTAL Java interface
//
private static final long serialVersionUID = 1L;
/** 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);
}
}
@Override
public void setIgnoreComments(boolean b) {
options.setIgnoreComments(b);
}
@Override
public void setIgnoreWhitespace(boolean b) {
options.setIgnoreWhitespace(b);
}
@Override
public void setIgnoreProcessingInstructions(boolean b) {
options.setIgnoreProcessingInstructions(b);
}
@Override
public void setPrettyPrinting(boolean b) {
options.setPrettyPrinting(b);
}
@Override
public void setPrettyIndent(int i) {
options.setPrettyIndent(i);
}
@Override
public boolean isIgnoreComments() {
return options.isIgnoreComments();
}
@Override
public boolean isIgnoreProcessingInstructions() {
return options.isIgnoreProcessingInstructions();
}
@Override
public boolean isIgnoreWhitespace() {
return options.isIgnoreWhitespace();
}
@Override
public boolean isPrettyPrinting() {
return options.isPrettyPrinting();
}
@Override
public int getPrettyIndent() {
return options.getPrettyIndent();
}
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 */
@Deprecated
QName qnamePrototype() {
return qnamePrototype;
}
/** @deprecated */
@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 */
@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);
}
/* TODO: Marked deprecated by original author */
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);
}
/* TODO: Can 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 (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 = "" + frag.substring(2);
if (!frag.endsWith(">")) {
throw ScriptRuntime.typeError("XML with anonymous tag missing end anonymous tag");
}
frag = frag.substring(0, frag.length() - 3) + " ";
XML orgXML = 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);
}
}
}
/*
TODO: 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
//
@Override
public boolean isXMLName(Context _cx, Object nameObj) {
return XMLName.accept(nameObj);
}
@Override
public Object toDefaultXmlNamespace(Context cx, Object uriValue) {
return this.namespacePrototype.constructNamespace(uriValue);
}
@Override
public String escapeTextValue(Object o) {
return options.escapeTextValue(o);
}
@Override
public String escapeAttributeValue(Object o) {
return options.escapeAttributeValue(o);
}
@Override
public Ref nameRef(Context cx, Object name, Scriptable scope, int memberTypeFlags) {
if ((memberTypeFlags & Node.ATTRIBUTE_FLAG) == 0) {
// should only be called for cases like @name or @[expr]
throw Kit.codeBug();
}
XMLName xmlName = toAttributeName(cx, name);
return xmlPrimaryReference(cx, xmlName, scope);
}
@Override
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);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy