/*
* Copyright 1999-2004 The Apache Software Foundation
*
* Licensed 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.commons.jxpath.ri.model.jdom;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.jxpath.AbstractFactory;
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathException;
import org.apache.commons.jxpath.ri.Compiler;
import org.apache.commons.jxpath.ri.QName;
import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
import org.apache.commons.jxpath.ri.compiler.NodeTest;
import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
import org.apache.commons.jxpath.ri.compiler.ProcessingInstructionTest;
import org.apache.commons.jxpath.ri.model.NodeIterator;
import org.apache.commons.jxpath.ri.model.NodePointer;
import org.apache.commons.jxpath.util.TypeUtils;
import org.jdom.Attribute;
import org.jdom.CDATA;
import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.ProcessingInstruction;
import org.jdom.Text;
/**
* A Pointer that points to a DOM node.
*
* @author Dmitri Plotnikov
* @version $Revision: 1.17 $ $Date: 2004/06/29 22:58:18 $
*/
public class JDOMNodePointer extends NodePointer {
private Object node;
private Map namespaces;
private String defaultNamespace;
private String id;
public static final String XML_NAMESPACE_URI =
"http://www.w3.org/XML/1998/namespace";
public static final String XMLNS_NAMESPACE_URI =
"http://www.w3.org/2000/xmlns/";
public JDOMNodePointer(Object node, Locale locale) {
super(null, locale);
this.node = node;
}
public JDOMNodePointer(Object node, Locale locale, String id) {
super(null, locale);
this.node = node;
this.id = id;
}
public JDOMNodePointer(NodePointer parent, Object node) {
super(parent);
this.node = node;
}
public NodeIterator childIterator(
NodeTest test,
boolean reverse,
NodePointer startWith)
{
return new JDOMNodeIterator(this, test, reverse, startWith);
}
public NodeIterator attributeIterator(QName name) {
return new JDOMAttributeIterator(this, name);
}
public NodeIterator namespaceIterator() {
return new JDOMNamespaceIterator(this);
}
public NodePointer namespacePointer(String prefix) {
return new JDOMNamespacePointer(this, prefix);
}
public String getNamespaceURI() {
return getNamespaceURI(node);
}
private static String getNamespaceURI(Object node) {
if (node instanceof Element) {
Element element = (Element) node;
String ns = element.getNamespaceURI();
if (ns != null && ns.equals("")) {
ns = null;
}
return ns;
}
return null;
}
public String getNamespaceURI(String prefix) {
if (node instanceof Document) {
Element element = ((Document)node).getRootElement();
Namespace ns = element.getNamespace(prefix);
if (ns != null) {
return ns.getURI();
}
}
else if (node instanceof Element) {
Element element = (Element) node;
Namespace ns = element.getNamespace(prefix);
if (ns != null) {
return ns.getURI();
}
}
return null;
}
public int compareChildNodePointers(
NodePointer pointer1,
NodePointer pointer2)
{
Object node1 = pointer1.getBaseValue();
Object node2 = pointer2.getBaseValue();
if (node1 == node2) {
return 0;
}
if ((node1 instanceof Attribute) && !(node2 instanceof Attribute)) {
return -1;
}
else if (
!(node1 instanceof Attribute) && (node2 instanceof Attribute)) {
return 1;
}
else if (
(node1 instanceof Attribute) && (node2 instanceof Attribute)) {
List list = ((Element) getNode()).getAttributes();
int length = list.size();
for (int i = 0; i < length; i++) {
Object n = list.get(i);
if (n == node1) {
return -1;
}
else if (n == node2) {
return 1;
}
}
return 0; // Should not happen
}
if (!(node instanceof Element)) {
throw new RuntimeException(
"JXPath internal error: "
+ "compareChildNodes called for "
+ node);
}
List children = ((Element) node).getContent();
int length = children.size();
for (int i = 0; i < length; i++) {
Object n = children.get(i);
if (n == node1) {
return -1;
}
else if (n == node2) {
return 1;
}
}
return 0;
}
/**
* @see org.apache.commons.jxpath.ri.model.NodePointer#getBaseValue()
*/
public Object getBaseValue() {
return node;
}
public boolean isCollection() {
return false;
}
public int getLength() {
return 1;
}
public boolean isLeaf() {
if (node instanceof Element) {
return ((Element) node).getContent().size() == 0;
}
else if (node instanceof Document) {
return ((Document) node).getContent().size() == 0;
}
return true;
}
/**
* @see org.apache.commons.jxpath.ri.model.NodePointer#getName()
*/
public QName getName() {
String ns = null;
String ln = null;
if (node instanceof Element) {
ns = ((Element) node).getNamespacePrefix();
if (ns != null && ns.equals("")) {
ns = null;
}
ln = ((Element) node).getName();
}
else if (node instanceof ProcessingInstruction) {
ln = ((ProcessingInstruction) node).getTarget();
}
return new QName(ns, ln);
}
/**
* @see org.apache.commons.jxpath.ri.model.NodePointer#getNode()
*/
public Object getImmediateNode() {
return node;
}
public Object getValue() {
if (node instanceof Element) {
return ((Element) node).getTextTrim();
}
else if (node instanceof Comment) {
String text = ((Comment) node).getText();
if (text != null) {
text = text.trim();
}
return text;
}
else if (node instanceof Text) {
return ((Text) node).getTextTrim();
}
else if (node instanceof CDATA) {
return ((CDATA) node).getTextTrim();
}
else if (node instanceof ProcessingInstruction) {
String text = ((ProcessingInstruction) node).getData();
if (text != null) {
text = text.trim();
}
return text;
}
return null;
}
public void setValue(Object value) {
if (node instanceof Text) {
String string = (String) TypeUtils.convert(value, String.class);
if (string != null && !string.equals("")) {
((Text) node).setText(string);
}
else {
nodeParent(node).removeContent((Text) node);
}
}
else {
Element element = (Element) node;
element.getContent().clear();
if (value instanceof Element) {
Element valueElement = (Element) value;
addContent(valueElement.getContent());
}
else if (value instanceof Document) {
Document valueDocument = (Document) value;
addContent(valueDocument.getContent());
}
else if (value instanceof Text || value instanceof CDATA) {
String string = ((Text) value).getText();
element.addContent(new Text(string));
}
else if (value instanceof ProcessingInstruction) {
ProcessingInstruction pi =
(ProcessingInstruction) ((ProcessingInstruction) value)
.clone();
element.addContent(pi);
}
else if (value instanceof Comment) {
Comment comment = (Comment) ((Comment) value).clone();
element.addContent(comment);
}
else {
String string = (String) TypeUtils.convert(value, String.class);
if (string != null && !string.equals("")) {
element.addContent(new Text(string));
}
}
}
}
private void addContent(List content) {
Element element = (Element) node;
int count = content.size();
for (int i = 0; i < count; i++) {
Object child = content.get(i);
if (child instanceof Element) {
child = ((Element) child).clone();
element.addContent((Element) child);
}
else if (child instanceof Text) {
child = ((Text) child).clone();
element.addContent((Text) child);
}
else if (node instanceof CDATA) {
child = ((CDATA) child).clone();
element.addContent((CDATA) child);
}
else if (node instanceof ProcessingInstruction) {
child = ((ProcessingInstruction) child).clone();
element.addContent((ProcessingInstruction) child);
}
else if (node instanceof Comment) {
child = ((Comment) child).clone();
element.addContent((Comment) child);
}
}
}
public boolean testNode(NodeTest test) {
return testNode(this, node, test);
}
public static boolean testNode(
NodePointer pointer,
Object node,
NodeTest test)
{
if (test == null) {
return true;
}
else if (test instanceof NodeNameTest) {
if (!(node instanceof Element)) {
return false;
}
NodeNameTest nodeNameTest = (NodeNameTest) test;
QName testName = nodeNameTest.getNodeName();
String namespaceURI = nodeNameTest.getNamespaceURI();
boolean wildcard = nodeNameTest.isWildcard();
String testPrefix = testName.getPrefix();
if (wildcard && testPrefix == null) {
return true;
}
if (wildcard
|| testName.getName()
.equals(JDOMNodePointer.getLocalName(node))) {
String nodeNS = JDOMNodePointer.getNamespaceURI(node);
return equalStrings(namespaceURI, nodeNS);
}
}
else if (test instanceof NodeTypeTest) {
switch (((NodeTypeTest) test).getNodeType()) {
case Compiler.NODE_TYPE_NODE :
return node instanceof Element;
case Compiler.NODE_TYPE_TEXT :
return (node instanceof Text) || (node instanceof CDATA);
case Compiler.NODE_TYPE_COMMENT :
return node instanceof Comment;
case Compiler.NODE_TYPE_PI :
return node instanceof ProcessingInstruction;
}
return false;
}
else if (test instanceof ProcessingInstructionTest) {
if (node instanceof ProcessingInstruction) {
String testPI = ((ProcessingInstructionTest) test).getTarget();
String nodePI = ((ProcessingInstruction) node).getTarget();
return testPI.equals(nodePI);
}
}
return false;
}
private static boolean equalStrings(String s1, String s2) {
if (s1 == null && s2 != null) {
return false;
}
if (s1 != null && s2 == null) {
return false;
}
if (s1 != null && !s1.trim().equals(s2.trim())) {
return false;
}
return true;
}
public static String getPrefix(Object node) {
if (node instanceof Element) {
String prefix = ((Element) node).getNamespacePrefix();
return (prefix == null || prefix.equals("")) ? null : prefix;
}
else if (node instanceof Attribute) {
String prefix = ((Attribute) node).getNamespacePrefix();
return (prefix == null || prefix.equals("")) ? null : prefix;
}
return null;
}
public static String getLocalName(Object node) {
if (node instanceof Element) {
return ((Element) node).getName();
}
else if (node instanceof Attribute) {
return ((Attribute) node).getName();
}
return null;
}
/**
* Returns true if the xml:lang attribute for the current node
* or its parent has the specified prefix <i>lang</i>.
* If no node has this prefix, calls <code>super.isLanguage(lang)</code>.
*/
public boolean isLanguage(String lang) {
String current = getLanguage();
if (current == null) {
return super.isLanguage(lang);
}
return current.toUpperCase().startsWith(lang.toUpperCase());
}
protected String getLanguage() {
Object n = node;
while (n != null) {
if (n instanceof Element) {
Element e = (Element) n;
String attr =
e.getAttributeValue("lang", Namespace.XML_NAMESPACE);
if (attr != null && !attr.equals("")) {
return attr;
}
}
n = nodeParent(n);
}
return null;
}
private Element nodeParent(Object node) {
if (node instanceof Element) {
Object parent = ((Element) node).getParent();
if (parent instanceof Element) {
return (Element) parent;
}
}
else if (node instanceof Text) {
return (Element) ((Text) node).getParent();
}
else if (node instanceof CDATA) {
return (Element) ((CDATA) node).getParent();
}
else if (node instanceof ProcessingInstruction) {
return (Element) ((ProcessingInstruction) node).getParent();
}
else if (node instanceof Comment) {
return (Element) ((Comment) node).getParent();
}
return null;
}
public NodePointer createChild(
JXPathContext context,
QName name,
int index)
{
if (index == WHOLE_COLLECTION) {
index = 0;
}
boolean success =
getAbstractFactory(context).createObject(
context,
this,
node,
name.toString(),
index);
if (success) {
NodeTest nodeTest;
String prefix = name.getPrefix();
if (prefix != null) {
String namespaceURI = context.getNamespaceURI(prefix);
nodeTest = new NodeNameTest(name, namespaceURI);
}
else {
nodeTest = new NodeNameTest(name);
}
NodeIterator it =
childIterator(nodeTest, false, null);
if (it != null && it.setPosition(index + 1)) {
return it.getNodePointer();
}
}
throw new JXPathException(
"Factory could not create "
+ "a child node for path: "
+ asPath()
+ "/"
+ name
+ "["
+ (index + 1)
+ "]");
}
public NodePointer createChild(
JXPathContext context, QName name, int index, Object value)
{
NodePointer ptr = createChild(context, name, index);
ptr.setValue(value);
return ptr;
}
public NodePointer createAttribute(JXPathContext context, QName name) {
if (!(node instanceof Element)) {
return super.createAttribute(context, name);
}
Element element = (Element) node;
String prefix = name.getPrefix();
if (prefix != null) {
Namespace ns = element.getNamespace(prefix);
if (ns == null) {
throw new JXPathException(
"Unknown namespace prefix: " + prefix);
}
Attribute attr = element.getAttribute(name.getName(), ns);
if (attr == null) {
element.setAttribute(name.getName(), "", ns);
}
}
else {
Attribute attr = element.getAttribute(name.getName());
if (attr == null) {
element.setAttribute(name.getName(), "");
}
}
NodeIterator it = attributeIterator(name);
it.setPosition(1);
return it.getNodePointer();
}
public void remove() {
Element parent = nodeParent(node);
if (parent == null) {
throw new JXPathException("Cannot remove root JDOM node");
}
parent.getContent().remove(node);
}
public String asPath() {
if (id != null) {
return "id('" + escape(id) + "')";
}
StringBuffer buffer = new StringBuffer();
if (parent != null) {
buffer.append(parent.asPath());
}
if (node instanceof Element) {
// If the parent pointer is not a JDOMNodePointer, it is
// the parent's responsibility to produce the node test part
// of the path
if (parent instanceof JDOMNodePointer) {
if (buffer.length() == 0
|| buffer.charAt(buffer.length() - 1) != '/') {
buffer.append('/');
}
String nsURI = getNamespaceURI();
String ln = JDOMNodePointer.getLocalName(node);
if (nsURI == null) {
buffer.append(ln);
buffer.append('[');
buffer.append(getRelativePositionByName()).append(']');
}
else {
String prefix = getNamespaceResolver().getPrefix(nsURI);
if (prefix != null) {
buffer.append(prefix);
buffer.append(':');
buffer.append(ln);
buffer.append('[');
buffer.append(getRelativePositionByName());
buffer.append(']');
}
else {
buffer.append("node()");
buffer.append('[');
buffer.append(getRelativePositionOfElement());
buffer.append(']');
}
}
}
}
else if (node instanceof Text || node instanceof CDATA) {
buffer.append("/text()");
buffer.append('[').append(getRelativePositionOfTextNode()).append(
']');
}
else if (node instanceof ProcessingInstruction) {
String target = ((ProcessingInstruction) node).getTarget();
buffer.append("/processing-instruction(\'").append(target).append(
"')");
buffer.append('[').append(getRelativePositionOfPI(target)).append(
']');
}
return buffer.toString();
}
private String escape(String string) {
int index = string.indexOf('\'');
while (index != -1) {
string =
string.substring(0, index)
+ "'"
+ string.substring(index + 1);
index = string.indexOf('\'');
}
index = string.indexOf('\"');
while (index != -1) {
string =
string.substring(0, index)
+ """
+ string.substring(index + 1);
index = string.indexOf('\"');
}
return string;
}
private int getRelativePositionByName() {
if (node instanceof Element) {
Object parent = ((Element) node).getParent();
if (!(parent instanceof Element)) {
return 1;
}
List children = ((Element)parent).getContent();
int count = 0;
String name = ((Element) node).getQualifiedName();
for (int i = 0; i < children.size(); i++) {
Object child = children.get(i);
if ((child instanceof Element)
&& ((Element) child).getQualifiedName().equals(name)) {
count++;
}
if (child == node) {
break;
}
}
return count;
}
return 1;
}
private int getRelativePositionOfElement() {
Element parent = (Element) ((Element) node).getParent();
if (parent == null) {
return 1;
}
List children = parent.getContent();
int count = 0;
for (int i = 0; i < children.size(); i++) {
Object child = children.get(i);
if (child instanceof Element) {
count++;
}
if (child == node) {
break;
}
}
return count;
}
private int getRelativePositionOfTextNode() {
Element parent;
if (node instanceof Text) {
parent = (Element) ((Text) node).getParent();
}
else {
parent = (Element) ((CDATA) node).getParent();
}
if (parent == null) {
return 1;
}
List children = parent.getContent();
int count = 0;
for (int i = 0; i < children.size(); i++) {
Object child = children.get(i);
if (child instanceof Text || child instanceof CDATA) {
count++;
}
if (child == node) {
break;
}
}
return count;
}
private int getRelativePositionOfPI(String target) {
Element parent = (Element) ((ProcessingInstruction) node).getParent();
if (parent == null) {
return 1;
}
List children = parent.getContent();
int count = 0;
for (int i = 0; i < children.size(); i++) {
Object child = children.get(i);
if (child instanceof ProcessingInstruction
&& (target == null
|| target.equals(
((ProcessingInstruction) child).getTarget()))) {
count++;
}
if (child == node) {
break;
}
}
return count;
}
public int hashCode() {
return System.identityHashCode(node);
}
public boolean equals(Object object) {
if (object == this) {
return true;
}
if (!(object instanceof JDOMNodePointer)) {
return false;
}
JDOMNodePointer other = (JDOMNodePointer) object;
return node == other.node;
}
private AbstractFactory getAbstractFactory(JXPathContext context) {
AbstractFactory factory = context.getFactory();
if (factory == null) {
throw new JXPathException(
"Factory is not set on the JXPathContext - cannot create path: "
+ asPath());
}
return factory;
}
}