/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-09 The eXist Project
* http://exist-db.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package org.exist.xquery.functions.util;
import org.apache.log4j.Logger;
import org.exist.dom.QName;
import org.exist.xquery.*;
import org.exist.xquery.value.*;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
public class NodeXPath extends Function
{
protected static final Logger logger = Logger.getLogger(NodeXPath.class);
public final static FunctionSignature signature =
new FunctionSignature(
new QName("node-xpath", UtilModule.NAMESPACE_URI, UtilModule.PREFIX),
"Returns the XPath for a Node.",
new SequenceType[] {
new FunctionParameterSequenceType("node", Type.NODE, Cardinality.EXACTLY_ONE, "The node to retrieve the XPath to"),
},
new FunctionReturnSequenceType(Type.STRING, Cardinality.ZERO_OR_ONE, "the XPath expression of the node"));
public NodeXPath(XQueryContext context)
{
super(context, signature);
}
public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException
{
Sequence seq = getArgument(0).eval(contextSequence, contextItem);
if(seq.isEmpty()) {
return Sequence.EMPTY_SEQUENCE;
}
NodeValue nv = (NodeValue)seq.itemAt(0);
Node n = nv.getNode();
//if at the document level just return /
if(n.getNodeType() == Node.DOCUMENT_NODE) {
return new StringValue("/");
}
/* walk up the node hierarchy
* - node names become path names
* - attributes become predicates
*/
StringBuilder buf = new StringBuilder(nodeToXPath(n));
while((n = n.getParentNode()) != null)
{
if(n.getNodeType() == Node.ELEMENT_NODE)
{
buf.insert(0, nodeToXPath(n));
}
}
return new StringValue(buf.toString());
}
/**
* Creates an XPath for a Node
* The nodes attribute's become predicates
*
* @param n The Node to generate an XPath for
* @return StringBuilder containing the XPath
*/
public StringBuilder nodeToXPath(Node n)
{
StringBuilder xpath = new StringBuilder("/" + getFullNodeName(n));
NamedNodeMap attrs = n.getAttributes();
for(int i = 0; i < attrs.getLength(); i++)
{
Node attr = attrs.item(i);
xpath.append("[@" + getFullNodeName(attr) + " eq \"" + attr.getNodeValue() + "\"]");
}
return xpath;
}
/**
* Returns the full node name including the prefix if present
*
* @param n The node to get the name for
* @return The full name of the node
*/
public String getFullNodeName(Node n)
{
return n.getPrefix() != null && !n.getPrefix().equals("") ? n.getPrefix() + ":" + n.getLocalName() : n.getLocalName();
}
}