/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.javascript;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
/**
* A Rhino wrapper around org.w3c.dom.Node. Not comprehensive, but enough to test CXF JavaScript.
*/
public class JsSimpleDomNode extends ScriptableObject {
private static final long serialVersionUID = -299162863985870752L;
private Node wrappedNode;
private boolean childrenWrapped;
private boolean attributesWrapped;
private JsSimpleDomNode previousSibling;
private JsSimpleDomNode nextSibling;
private List<JsSimpleDomNode> children;
private JsNamedNodeMap attributes;
/**
* Only exists to make Rhino happy. Should never be used.
*/
public JsSimpleDomNode() {
}
public static void register(ScriptableObject scope) {
try {
ScriptableObject.defineClass(scope, JsSimpleDomNode.class);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
@Override
public String getClassName() {
return "Node";
}
public Node getWrappedNode() {
return wrappedNode;
}
// CHECKSTYLE:OFF
public String jsGet_localName() {
return wrappedNode.getLocalName();
}
public String jsGet_namespaceURI() {
return wrappedNode.getNamespaceURI();
}
public Object jsGet_firstChild() {
establishChildren();
if (!children.isEmpty())
return children.get(0);
else
return null;
}
public Object jsGet_nextSibling() {
return nextSibling;
}
public Object jsGet_previousSibling() {
return previousSibling;
}
public Object jsGet_parentNode() {
// risk errors in object equality ...
return wrapNode(this, wrappedNode.getParentNode());
}
public int jsGet_nodeType() {
return wrappedNode.getNodeType();
}
public String jsGet_nodeValue() {
return wrappedNode.getNodeValue();
}
public String jsGet_nodeName() {
return wrappedNode.getNodeName();
}
// in a more complete version of this, we'd use a different object type to wrap documents.
public Object jsGet_documentElement() {
if (9 /* Document */!= wrappedNode.getNodeType()) {
return null;
} else {
establishChildren();
return children.get(0); // it is, after all, just a convenience feature.
}
}
public Object[] jsGet_childNodes() {
establishChildren();
return children.toArray();
}
public Object jsGet_attributes() {
establishAttributes();
return attributes;
}
public String jsFunction_getAttributeNS(String namespaceURI, String localName) {
NamedNodeMap attributes = wrappedNode.getAttributes();
Node attrNode = attributes.getNamedItemNS(namespaceURI, localName);
if (attrNode == null) {
return null;
} else {
Attr attribute = (Attr)attrNode;
return attribute.getValue();
}
}
public String jsFunction_getAttribute(String localName) {
NamedNodeMap attributes = wrappedNode.getAttributes();
Node attrNode = attributes.getNamedItem(localName);
if (attrNode == null) {
return null;
} else {
Attr attribute = (Attr)attrNode;
return attribute.getValue();
}
}
// CHECKSTYLE:ON
public static JsSimpleDomNode wrapNode(Scriptable scope, Node node) {
if (node == null) {
return null;
}
Context cx = ContextFactory.getGlobal().enterContext();
try {
JsSimpleDomNode newObject = (JsSimpleDomNode)cx.newObject(scope, "Node");
newObject.initialize(node, null);
return newObject;
} finally {
Context.exit();
}
}
private JsSimpleDomNode newObject(Node node, JsSimpleDomNode prev) {
Context cx = ContextFactory.getGlobal().enterContext();
try {
JsSimpleDomNode newObject = (JsSimpleDomNode)cx.newObject(getParentScope(), "Node");
newObject.initialize(node, prev);
return newObject;
} finally {
Context.exit();
}
}
private void establishChildren() {
if (!childrenWrapped) {
if (wrappedNode.hasChildNodes()) {
children = new ArrayList<>();
Node node = wrappedNode.getFirstChild();
int x = 0;
while (node != null) {
JsSimpleDomNode prev = null;
if (x > 0) {
prev = children.get(x - 1);
}
children.add(x, newObject(node, prev));
if (x > 0) {
children.get(x - 1).setNext(children.get(x));
}
node = node.getNextSibling();
x++;
}
} else {
children = new ArrayList<>();
}
childrenWrapped = true;
}
}
private void establishAttributes() {
if (!attributesWrapped) {
NamedNodeMap nodeAttributes = wrappedNode.getAttributes();
attributes = JsNamedNodeMap.wrapMap(getParentScope(), nodeAttributes);
attributesWrapped = true;
}
}
// rhino won't let us use a constructor.
void initialize(Node node, JsSimpleDomNode prev) {
wrappedNode = node;
childrenWrapped = false;
previousSibling = prev;
}
void setNext(JsSimpleDomNode next) {
nextSibling = next;
}
}