package gnu.xml.xpath;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.StringTokenizer;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public abstract class Expr
implements XPathExpression
{
protected static final Comparator documentOrderComparator =
new DocumentOrderComparator();
protected static final DecimalFormat decimalFormat =
new DecimalFormat("####################################################" +
".####################################################",
new DecimalFormatSymbols(Locale.US));
public Object evaluate(Object item, QName returnType)
throws XPathExpressionException
{
Object ret = null;
Node context = null;
if (item instanceof Node)
{
context = (Node) item;
ret = evaluate(context, 1, 1);
if (XPathConstants.STRING == returnType &&
!(ret instanceof String))
{
ret = _string(context, ret);
}
else if (XPathConstants.NUMBER == returnType &&
!(ret instanceof Double))
{
ret = new Double(_number(context, ret));
}
else if (XPathConstants.BOOLEAN == returnType &&
!(ret instanceof Boolean))
{
ret = _boolean(context, ret) ? Boolean.TRUE : Boolean.FALSE;
}
else if (XPathConstants.NODE == returnType)
{
if (ret instanceof Collection)
{
Collection ns = (Collection) ret;
switch (ns.size())
{
case 0:
ret = null;
break;
case 1:
ret = (Node) ns.iterator().next();
break;
default:
throw new XPathExpressionException("multiple nodes in node-set");
}
}
else if (ret != null)
{
throw new XPathExpressionException("return value is not a node-set");
}
}
else if (XPathConstants.NODESET == returnType)
{
if (ret != null && !(ret instanceof Collection))
{
throw new XPathExpressionException("return value is not a node-set");
}
}
}
return ret;
}
public String evaluate(Object item)
throws XPathExpressionException
{
return (String) evaluate(item, XPathConstants.STRING);
}
public Object evaluate(InputSource source, QName returnType)
throws XPathExpressionException
{
try
{
DocumentBuilderFactory factory =
new gnu.xml.dom.JAXPFactory();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(source);
return evaluate(doc, returnType);
}
catch (ParserConfigurationException e)
{
throw new XPathExpressionException(e);
}
catch (SAXException e)
{
throw new XPathExpressionException(e);
}
catch (IOException e)
{
throw new XPathExpressionException(e);
}
}
public String evaluate(InputSource source)
throws XPathExpressionException
{
return (String) evaluate(source, XPathConstants.STRING);
}
public abstract Object evaluate(Node context, int pos, int len);
public abstract Expr clone(Object context);
public static Collection _id(Node context, Object object)
{
Set ret = new HashSet();
if (object instanceof Collection)
{
Collection nodeSet = (Collection) object;
for (Iterator i = nodeSet.iterator(); i.hasNext(); )
{
String string = stringValue((Node) i.next());
ret.addAll(_id (context, string));
}
}
else
{
Document doc = (context instanceof Document) ? (Document) context :
context.getOwnerDocument();
String string = _string(context, object);
StringTokenizer st = new StringTokenizer(string, " \t\r\n");
while (st.hasMoreTokens())
{
Node element = doc.getElementById(st.nextToken());
if (element != null)
{
ret.add(element);
}
}
}
return ret;
}
public static String _local_name(Node context, Collection nodeSet)
{
Node node = (nodeSet == null || nodeSet.size() == 0) ? context :
firstNode(nodeSet);
return node.getLocalName();
}
public static String _namespace_uri(Node context, Collection nodeSet)
{
Node node = (nodeSet == null || nodeSet.size() == 0) ? context :
firstNode(nodeSet);
return node.getNamespaceURI();
}
public static String _name(Node context, Collection nodeSet)
{
Node node = (nodeSet == null || nodeSet.size() == 0) ? context :
firstNode(nodeSet);
switch (node.getNodeType())
{
case Node.ATTRIBUTE_NODE:
case Node.ELEMENT_NODE:
case Node.PROCESSING_INSTRUCTION_NODE:
return node.getNodeName();
default:
return "";
}
}
static Node firstNode(Collection nodeSet)
{
List list = new ArrayList(nodeSet);
Collections.sort(list, documentOrderComparator);
return (Node) list.get(0);
}
public static String _string(Node context, Object object)
{
if (object == null)
{
return stringValue(context);
}
if (object instanceof String)
{
return (String) object;
}
if (object instanceof Boolean)
{
return object.toString();
}
if (object instanceof Double)
{
double d = ((Double) object).doubleValue();
if (Double.isNaN(d))
{
return "NaN";
}
else if (d == 0.0d)
{
return "0";
}
else if (Double.isInfinite(d))
{
if (d < 0)
{
return "-Infinity";
}
else
{
return "Infinity";
}
}
else
{
String ret = decimalFormat.format(d);
if (ret.endsWith (".0"))
{
ret = ret.substring(0, ret.length() - 2);
}
return ret;
}
}
if (object instanceof Collection)
{
Collection nodeSet = (Collection) object;
if (nodeSet.isEmpty())
{
return "";
}
Node node = firstNode(nodeSet);
return stringValue(node);
}
throw new IllegalArgumentException(object.toString());
}
public static boolean _boolean(Node context, Object object)
{
if (object instanceof Boolean)
{
return ((Boolean) object).booleanValue();
}
if (object instanceof Double)
{
return ((Double) object).doubleValue() != 0.0;
}
if (object instanceof String)
{
return ((String) object).length() != 0;
}
if (object instanceof Collection)
{
return ((Collection) object).size() != 0;
}
return false; }
public static double _number(Node context, Object object)
{
if (object == null)
{
object = Collections.singleton(context);
}
if (object instanceof Double)
{
return ((Double) object).doubleValue();
}
if (object instanceof Boolean)
{
return ((Boolean) object).booleanValue() ? 1.0 : 0.0;
}
if (object instanceof Collection)
{
object = stringValue((Collection) object);
}
if (object instanceof String)
{
String string = ((String) object).trim();
try
{
return Double.parseDouble(string);
}
catch (NumberFormatException e)
{
return Double.NaN;
}
}
return Double.NaN; }
public static String stringValue(Collection nodeSet)
{
StringBuffer buf = new StringBuffer();
for (Iterator i = nodeSet.iterator(); i.hasNext(); )
{
buf.append(stringValue((Node) i.next()));
}
return buf.toString();
}
public static String stringValue(Node node)
{
return stringValue(node, false);
}
static String stringValue(Node node, boolean elementMode)
{
switch (node.getNodeType())
{
case Node.DOCUMENT_NODE: case Node.DOCUMENT_FRAGMENT_NODE:
case Node.ELEMENT_NODE: StringBuffer buf = new StringBuffer();
for (Node ctx = node.getFirstChild(); ctx != null;
ctx = ctx.getNextSibling())
{
buf.append(stringValue(ctx, true));
}
return buf.toString();
case Node.TEXT_NODE: case Node.CDATA_SECTION_NODE:
return node.getNodeValue();
case Node.ATTRIBUTE_NODE: case Node.PROCESSING_INSTRUCTION_NODE: case Node.COMMENT_NODE: if (!elementMode)
{
return node.getNodeValue();
}
default:
return "";
}
}
}