/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
* Free SoftwareFoundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.xpath;
import com.caucho.util.CharBuffer;
import com.caucho.xml.XmlChar;
import com.caucho.xml.XmlUtil;
import com.caucho.xpath.expr.ObjectVar;
import com.caucho.xpath.expr.Var;
import com.caucho.xpath.pattern.AbstractPattern;
import com.caucho.xpath.pattern.FromExpr;
import com.caucho.xpath.pattern.NodeArrayListIterator;
import com.caucho.xpath.pattern.NodeIterator;
import com.caucho.xpath.pattern.NodeListIterator;
import com.caucho.xpath.pattern.SingleNodeIterator;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.ArrayList;
import java.util.Iterator;
/**
* Compute values from nodes. Because the expressions themselves are
* untyped, the class provides methods for creating the type of the
* desired result.
*/
abstract public class Expr {
protected static final int CONST = 0;
protected static final int NODE_SET = CONST + 1;
protected static final int ID = NODE_SET + 1;
protected static final int OR = ID + 1;
protected static final int AND = OR + 1;
protected static final int EQ = AND + 1;
protected static final int NEQ = EQ + 1;
protected static final int LT = NEQ + 1;
protected static final int LE = LT + 1;
protected static final int GT = LE + 1;
protected static final int GE = GT + 1;
protected static final int BOOLEAN_EQ = GE + 1;
protected static final int BOOLEAN_NEQ = BOOLEAN_EQ + 1;
protected static final int NUMBER_EQ = BOOLEAN_NEQ + 1;
protected static final int NUMBER_NEQ = NUMBER_EQ + 1;
protected static final int NUMBER_LT = NUMBER_NEQ + 1;
protected static final int NUMBER_LE = NUMBER_LT + 1;
protected static final int NUMBER_GT = NUMBER_LE + 1;
protected static final int NUMBER_GE = NUMBER_GT + 1;
protected static final int STRING_EQ = NUMBER_GE + 1;
protected static final int STRING_NEQ = STRING_EQ + 1;
protected static final int NEG = STRING_NEQ + 1;
protected static final int ADD = NEG + 1;
protected static final int SUB = ADD + 1;
protected static final int MUL = SUB + 1;
protected static final int DIV = MUL + 1;
protected static final int QUO = DIV + 1;
protected static final int MOD = QUO + 1;
protected static final int TRUE = MOD + 1;
protected static final int FALSE = TRUE + 1;
protected static final int NOT = FALSE + 1;
protected static final int BOOLEAN = NOT + 1;
protected static final int LANG = BOOLEAN + 1;
protected static final int NUMBER = LANG + 1;
protected static final int SUM = NUMBER + 1;
protected static final int FLOOR = SUM + 1;
protected static final int CEILING = FLOOR + 1;
protected static final int ROUND = CEILING + 1;
public static final int POSITION = ROUND + 1;
protected static final int COUNT = POSITION + 1;
protected static final int LAST = COUNT + 1;
protected static final int STRING = LAST + 1;
protected static final int CONCAT = STRING + 1;
protected static final int STARTS_WITH = CONCAT + 1;
protected static final int CONTAINS = STARTS_WITH + 1;
protected static final int SUBSTRING = CONTAINS + 1;
protected static final int SUBSTRING_BEFORE = SUBSTRING + 1;
protected static final int SUBSTRING_AFTER = SUBSTRING_BEFORE + 1;
protected static final int STRING_LENGTH = SUBSTRING_AFTER + 1;
protected static final int NORMALIZE = STRING_LENGTH + 1;
protected static final int TRANSLATE = NORMALIZE + 1;
protected static final int FORMAT_NUMBER = TRANSLATE + 1;
protected static final int LOCAL_PART = FORMAT_NUMBER + 1;
protected static final int NAMESPACE = LOCAL_PART + 1;
protected static final int QNAME = NAMESPACE + 1;
protected static final int GENERATE_ID = QNAME + 1;
protected static final int FUNCTION_AVAILABLE = GENERATE_ID + 1;
protected static final int SYSTEM_PROPERTY = FUNCTION_AVAILABLE + 1;
protected static final int IF = SYSTEM_PROPERTY + 1;
protected static final int SELF = IF + 1;
protected static final int SELF_NAME = SELF + 1;
protected static final int ATTRIBUTE = SELF_NAME + 1;
protected static final int ELEMENT = ATTRIBUTE + 1;
protected static final int BASE_URI = ELEMENT + 1;
protected static final int LAST_FUN = BASE_URI + 1;
private AbstractPattern listContext;
protected Expr() {}
public void setListContext(AbstractPattern listContext)
{
this.listContext = listContext;
}
public AbstractPattern getListContext()
{
return listContext;
}
/**
* true if the expression prefers to return a number.
*/
public boolean isNumber()
{
return false;
}
/**
* Evaluates the expression as a double using the node as a context.
*
* @param node the node to evaluate and use as a context
*
* @return the numeric value.
*/
public double evalNumber(Node node)
throws XPathException
{
Env env = XPath.createEnv();
env.setCurrentNode(node);
env.setContextNode(node);
double result = evalNumber(node, env);
XPath.freeEnv(env);
return result;
}
/**
* Evaluates the expression as a number.
*
* @param node the current node.
* @param env variable environment.
*
* @return the numeric value.
*/
public abstract double evalNumber(Node node, ExprEnvironment env)
throws XPathException;
/**
* true if the expression prefers to return a boolean.
*/
public boolean isBoolean()
{
return false;
}
/**
* Returns the boolean value of the node.
*
* @param node the node to evaluate and use as a context
*
* @return the boolean value
*/
public boolean evalBoolean(Node node)
throws XPathException
{
Env env = XPath.createEnv();
env.setCurrentNode(node);
env.setContextNode(node);
boolean result = evalBoolean(node, env);
XPath.freeEnv(env);
return result;
}
/**
* Returns the boolean value of the node.
*
* @param node the node to evaluate and use as a context
* @param env variable environment.
*
* @return the boolean value.
*/
public abstract boolean evalBoolean(Node node, ExprEnvironment env)
throws XPathException;
/**
* Returns the expression evaluated as a string.
*
* @param node the node to evaluate and use as a context
*
* @return the string value of the expression.
*/
public String evalString(Node node)
throws XPathException
{
Env env = XPath.createEnv();
env.setCurrentNode(node);
env.setContextNode(node);
String result = evalString(node, env);
XPath.freeEnv(env);
return result;
}
/**
* true if the expression prefers to return a string.
*/
public boolean isString()
{
return false;
}
/**
* Returns the string value of the node.
*
* @param node the node to evaluate and use as a context
* @param env variable environment.
*/
public abstract String evalString(Node node, ExprEnvironment env)
throws XPathException;
/**
* Fills a char buffer with the evaluated string results.
*
* @param cb the buffer containing the results.
* @param node the node to evaluate and use as a context
*/
public void evalString(CharBuffer cb, Node node)
throws XPathException
{
Env env = XPath.createEnv();
env.setCurrentNode(node);
env.setContextNode(node);
evalString(cb, node, env);
XPath.freeEnv(env);
}
/**
* Fills a char buffer with the evaluated string results.
*
* @param cb the buffer containing the results.
* @param node the node to evaluate and use as a context
* @param env the variable environment
*/
public void evalString(CharBuffer cb, Node node, ExprEnvironment env)
throws XPathException
{
cb.append(evalString(node, env));
}
/**
* true if the expression prefers to return a node set.
*/
public boolean isNodeSet()
{
return false;
}
/**
* Returns an iterator of matching nodes
*
* @param node the node to evaluate and use as a context
*
* @return the value as a node iterator.
*/
public NodeIterator evalNodeSet(Node node)
throws XPathException
{
Env env = XPath.createEnv();
env.setCurrentNode(node);
env.setContextNode(node);
NodeIterator result = evalNodeSet(node, env);
XPath.freeEnv(env);
return result;
}
/**
* Returns an iterator of matching nodes
*
* @param node the node to evaluate and use as a context
* @param env variable environment.
*
* @return the value as a node iterator.
*/
public NodeIterator evalNodeSet(Node node, ExprEnvironment env)
throws XPathException
{
Object obj = evalObject(node, env);
if (obj instanceof Node)
return new SingleNodeIterator(env, (Node) obj);
else if (obj instanceof NodeList)
return new NodeListIterator(env, (NodeList) obj);
else if (obj instanceof NodeIterator)
return (NodeIterator) obj;
else if (obj instanceof ArrayList)
return new NodeArrayListIterator(env, (ArrayList) obj);
else {
return new SingleNodeIterator(env, null);
}
}
/**
* Returns the object value of the node.
*
* @param node the node to evaluate and use as a context
*/
public Object evalObject(Node node)
throws XPathException
{
Env env = XPath.createEnv();
env.setCurrentNode(node);
env.setContextNode(node);
Object result = evalObject(node, env);
XPath.freeEnv(env);
return result;
}
/**
* Returns the object value of the node.
*
* @param node the node to evaluate and use as a context
* @param env variable environment.
*/
public abstract Object evalObject(Node node, ExprEnvironment env)
throws XPathException;
/**
* Evaluates to a variable.
*
* @param node the node to evaluate and use as a context.
* @param env the variable environment.
*
* @return a variable containing the value.
*/
public Var evalVar(Node node, ExprEnvironment env)
throws XPathException
{
Object obj = evalObject(node, env);
return new ObjectVar(obj);
}
/**
* Adds a variable with the expression's value.
*/
public void addVar(Env newEnv, String name, Node node, Env env)
throws XPathException
{
Var var = evalVar(node, env);
newEnv.addVar(name, var);
}
/**
* Sets a variable with the expression's value.
*/
public void setVar(String name, Node node, Env env)
throws XPathException
{
env.setVar(name, evalVar(node, env));
}
/**
* Adds a param with the expression's value.
*/
public void addParam(Env newEnv, String name,
Node node, Env env)
throws XPathException
{
Var var = env.getVar(name);
if (var == null)
newEnv.addVar(name, evalVar(node, env));
else
newEnv.addVar(name, var);
}
/**
* Convert a Java object to a boolean using the XPath rules.
*/
public static boolean toBoolean(Object value)
throws XPathException
{
if (value instanceof Node)
value = XmlUtil.textValue((Node) value);
else if (value instanceof NodeList) {
NodeList list = (NodeList) value;
return list.item(0) != null;
}
else if (value instanceof ArrayList) {
ArrayList list = (ArrayList) value;
return list.size() > 0;
}
else if (value instanceof Iterator) {
return ((Iterator) value).hasNext();
}
if (value == null)
return false;
else if (value instanceof Double) {
Double d = (Double) value;
return d.doubleValue() != 0;
}
else if (value instanceof Boolean) {
Boolean b = (Boolean) value;
return b.booleanValue();
}
else if (value instanceof String) {
String string = (String) value;
return string != null && string.length() > 0;
}
else
return true;
}
/**
* Convert a Java object to a double using the XPath rules.
*/
public static double toDouble(Object value)
throws XPathException
{
if (value instanceof Node) {
String string = XmlUtil.textValue((Node) value);
if (string == null)
return 0;
else
return stringToNumber(string);
}
else if (value instanceof NodeList) {
NodeList list = (NodeList) value;
value = list.item(0);
}
else if (value instanceof ArrayList) {
ArrayList list = (ArrayList) value;
if (list.size() > 0)
value = list.get(0);
else
value = null;
}
else if (value instanceof NodeIterator) {
value = ((NodeIterator) value).nextNode();
}
if (value instanceof Node)
value = XmlUtil.textValue((Node) value);
if (value == null)
return 0;
if (value instanceof Number) {
Number d = (Number) value;
return d.doubleValue();
}
else if (value instanceof Boolean) {
Boolean b = (Boolean) value;
return b.booleanValue() ? 1 : 0;
}
else if (value instanceof String) {
return stringToNumber((String) value);
}
else
return 0;
}
/**
* Convert a Java object to a string using the XPath rules.
*/
public static String toString(Object value)
throws XPathException
{
if (value instanceof Node) {
String s = XmlUtil.textValue((Node) value);
if (s == null)
return "";
else
return s;
}
else if (value instanceof NodeList) {
NodeList list = (NodeList) value;
value = list.item(0);
}
else if (value instanceof ArrayList) {
ArrayList list = (ArrayList) value;
if (list.size() > 0)
value = list.get(0);
else
value = null;
}
else if (value instanceof Iterator) {
value = ((Iterator) value).next();
}
if (value instanceof Node)
value = XmlUtil.textValue((Node) value);
else if (value instanceof Double) {
double d = ((Double) value).doubleValue();
if ((int) d == d)
return String.valueOf((int) d);
else
return String.valueOf(d);
}
if (value == null)
return "";
else
return value.toString();
}
/**
* Convert a Java object to a node using the XPath rules.
*/
public static Node toNode(Object value)
throws XPathException
{
if (value instanceof Node)
return (Node) value;
else if (value instanceof NodeList) {
NodeList list = (NodeList) value;
value = list.item(0);
}
else if (value instanceof ArrayList) {
ArrayList list = (ArrayList) value;
if (list.size() > 0)
value = list.get(0);
else
value = null;
}
else if (value instanceof NodeIterator) {
value = ((NodeIterator) value).nextNode();
}
if (value instanceof Node)
return (Node) value;
else
return null;
}
/**
* Convert a string to a double following XPath.
*
* @param string string to be treated as a double.
* @return the double value.
*/
static protected double stringToNumber(String string)
throws XPathException
{
int i = 0;
int length = string.length();
boolean isNumber = false;
for (; i < length && XmlChar.isWhitespace(string.charAt(i)); i++) {
}
if (i >= length)
return 0;
int ch = string.charAt(i);;
int sign = 1;
if (ch == '-') {
sign = -1;
for (i++; i < length && XmlChar.isWhitespace(string.charAt(i)); i++) {
}
}
double value = 0;
double exp = 1;
for (; i < length && (ch = string.charAt(i)) >= '0' && ch <= '9'; i++) {
value = 10 * value + ch - '0';
isNumber = true;
}
if (ch == '.') {
for (i++;
i < length && (ch = string.charAt(i)) >= '0' && ch <= '9';
i++) {
value = 10 * value + ch - '0';
isNumber = true;
exp = 10 * exp;
}
}
double pexp = 1.0;
if (ch == 'e' || ch == 'E') {
int eSign = 1;
i++;
if (i >= length)
return Double.NaN;
if (string.charAt(i) == '-') {
eSign = -1;
i++;
}
else if (string.charAt(i) == '+') {
i++;
}
int v = 0;
for (; i < length && (ch = string.charAt(i)) >= '0' && ch <= '9'; i++) {
v = v * 10 + ch - '0';
}
pexp = Math.pow(10, eSign * v);
}
for (; i < length && XmlChar.isWhitespace(string.charAt(i)); i++) {
}
if (i < length || ! isNumber)
return Double.NaN;
else
return sign * value * pexp / exp;
}
/**
* Convert from an expression to a pattern.
*/
protected AbstractPattern toNodeList()
{
return new FromExpr(null, this);
}
}