org.springmodules.validation.valang.parser.valang.jj Maven / Gradle / Ivy
The newest version!
options {
STATIC = false;
DEBUG_PARSER = false;
}
PARSER_BEGIN(ValangParser)
/*
* Copyright 2004-2005 the original author or authors.
*
* 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.springmodules.validation.valang.parser;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.io.StringReader;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.functors.AndPredicate;
import org.apache.commons.collections.functors.NotPredicate;
import org.apache.commons.collections.functors.OrPredicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.Assert;
import org.springmodules.validation.util.date.DateParseException;
import org.springmodules.validation.util.date.DefaultDateParser;
import org.springmodules.validation.valang.functions.AddFunction;
import org.springmodules.validation.valang.functions.BeanPropertyFunction;
import org.springmodules.validation.valang.functions.DateLiteralFunction;
import org.springmodules.validation.valang.functions.DivideFunction;
import org.springmodules.validation.valang.functions.Function;
import org.springmodules.validation.valang.functions.LiteralFunction;
import org.springmodules.validation.valang.functions.ModuloFunction;
import org.springmodules.validation.valang.functions.MultiplyFunction;
import org.springmodules.validation.valang.functions.SubtractFunction;
import org.springmodules.validation.valang.functions.TargetBeanFunction;
import org.springmodules.validation.valang.predicates.BasicValidationRule;
import org.springmodules.validation.valang.predicates.Operator;
import org.springmodules.validation.valang.predicates.OperatorConstants;
public class ValangParser {
private static Log log = LogFactory.getLog(ValangParser.class);
private DefaultVisitor visitor = new DefaultVisitor();
public ValangParser(String expression) {
this(new StringReader(expression));
}
public ValangParser(String expression, Map customFunctions, Map dateParsers) {
this(new StringReader(expression));
setDateParsersByRegexp(dateParsers);
setFunctionsByName(customFunctions);
}
public DefaultVisitor getVisitor() {
if (this.visitor == null) {
throw new IllegalStateException("Visistor is not set on parser");
}
return this.visitor;
}
public void setVisitor(DefaultVisitor visitor) {
Assert.notNull(visitor, "ValangParser cannot accept a 'null' visitor");
this.visitor = visitor;
}
public void setDateParsersByRegexp(Map dateParsersByRegexp) {
if (dateParsersByRegexp == null) {
return;
}
for (Iterator iter = dateParsersByRegexp.keySet().iterator(); iter.hasNext();) {
String regexp = (String)iter.next();
Object value = dateParsersByRegexp.get(regexp);
if (value instanceof String) {
getVisitor().getDateParser().register(regexp, (String)value);
}
else if (value instanceof DefaultDateParser.DateModifier) {
getVisitor().getDateParser().register(regexp, (DefaultDateParser.DateModifier)value);
}
else {
throw new ClassCastException("Could not register instance [" + value + "] with date parser!");
}
}
}
public void setFunctionsByName(Map functionsByName) {
if (functionsByName == null) {
return;
}
final Map constructorMap = new HashMap();
for (Iterator iter = functionsByName.keySet().iterator(); iter.hasNext();) {
Object stringNameObject = iter.next();
if (!(stringNameObject instanceof String)) {
throw new IllegalArgumentException("Key for custom functions map must be a string value!");
}
String functionName = (String)stringNameObject;
Object functionClassNameObject = functionsByName.get(functionName);
if (!(functionClassNameObject instanceof String)) {
throw new IllegalArgumentException("Value for custom function map must be a string!");
}
String functionClassName = (String)functionClassNameObject;
Class functionClass = loadClass(functionClassName);
if (!(Function.class.isAssignableFrom(functionClass))) {
throw new IllegalArgumentException(
"Custom function classes must implement org.springmodules.validation.valang.functions.Function!");
}
Constructor constructor = null;
try {
constructor = functionClass.getConstructor(new Class[] {Function[].class, int.class, int.class});
}
catch (NoSuchMethodException e) {
throw new IllegalArgumentException(
"Class ["
+ functionClass.getName()
+ "] has no constructor with one org.springmodules.validation.valang.functions.Function parameter!");
}
constructorMap.put(functionName, constructor);
}
getVisitor().setVisitor(new ValangVisitor() {
public Function getFunction(String name, Function[] functions, int line, int column) {
if (constructorMap.containsKey(name)) {
Constructor functionConstructor = (Constructor)constructorMap.get(name);
return (Function) BeanUtils.instantiateClass(functionConstructor, new Object[] {functions,
new Integer(line), new Integer(column)});
}
return null;
}
});
}
private Class loadClass(String className) {
try {
return ClassUtils.forName(className);
} catch (ClassNotFoundException cnfe) {
log.error("Could not load class '" + className + "'", cnfe);
throw new IllegalArgumentException("Could not load class '" + className + "'");
}
}
private String replace(String s) {
String tmpS = s;
tmpS = StringUtils.replace(tmpS, "\\'", "'");
tmpS = StringUtils.replace(tmpS, "\\n", "\n");
tmpS = StringUtils.replace(tmpS, "\\r", "\r");
tmpS = StringUtils.replace(tmpS, "\\t", "\t");
tmpS = StringUtils.replace(tmpS, "\\b", "\b");
tmpS = StringUtils.replace(tmpS, "\\f", "\f");
tmpS = StringUtils.replace(tmpS, "\\\\", "\\");
return tmpS;
}
}
PARSER_END(ValangParser)
SKIP :
{
" "
| "\t"
| "\n"
| "\r"
}
TOKEN [IGNORE_CASE] :
{
< AND : "AND" >
| < OR : "OR" >
| < NOT : "NOT" >
| < BETWEEN : "BETWEEN" | "IS BETWEEN" >
| < NOT_BETWEEN: "NOT BETWEEN" | "IS NOT BETWEEN" >
| < IN : "IN" | "IS IN">
| < NOT_IN : "NOT IN" | "IS NOT IN">
| < IS_NULL : "IS NULL" | "NULL" >
| < IS_NOT_NULL : "IS NOT NULL" | "NOT NULL" >
| < HAS_TEXT : "HAS TEXT" >
| < HAS_NO_TEXT : "HAS NO TEXT" >
| < HAS_LENGTH : "HAS LENGTH" >
| < HAS_NO_LENGTH : "HAS NO LENGTH" >
| < IS_BLANK : "IS BLANK" >
| < IS_NOT_BLANK : "IS NOT BLANK" >
| < IS_UPPER_CASE : "IS UPPERCASE" | "IS UPPER CASE" | "IS UPPER" >
| < IS_NOT_UPPER_CASE : "IS NOT UPPERCASE" | "IS NOT UPPER CASE" | "IS NOT UPPER" >
| < IS_LOWER_CASE : "IS LOWERCASE" | "IS LOWER CASE" | "IS LOWER" >
| < IS_NOT_LOWER_CASE : "IS NOT LOWERCASE" | "IS NOT LOWER CASE" | "IS NOT LOWER" >
| < IS_WORD : "IS WORD" >
| < IS_NOT_WORD : "IS NOT WORD" >
| < TRUE : "TRUE" | "YES" >
| < FALSE : "FALSE" | "NO" >
| < MORE_THAN_OR_EQUAL : ">=" | "=>" | "IS GREATER THAN OR EQUALS" | "GREATER THAN OR EQUALS">
| < MORE_THAN : ">" | "IS GREATER THAN" | "GREATER THAN">
| < LESS_THAN_OR_EQUAL : "<=" | "=<" | "IS LESS THAN OR EQUALS" | "LESS THAN OR EQUALS" >
| < LESS_THAN : "<" | "IS LESS THAN" | "LESS THAN">
| < NOT_EQUAL : "!=" | "<>" | "><" | "IS NOT" | "NOT EQUALS" >
| < EQUALS : "=" | "==" | "IS" | "EQUALS" >
| < ADD : "+" >
| < SUBTRACT : "-" >
| < MULTIPLY : "*" >
| < DIVIDE : "/" | "div" >
| < MOD : "%" | "mod" >
}
TOKEN :
{
< NUM : ("-")?(["0"-"9"])+ | ("-")?(["0"-"9"])+"." (["0"-"9"])* >
| < STRING : "'" ( ~["'", "\\", "\n", "\r"] | ( "\\" ( "'" | "\\" | "\n" | "\r" | "\t" | "\b" | "\f" ) ) )* "'" >
| < DATE : "[" (~["[", "]"])* "]" >
| < #DOT : "." >
| < #UNDERSCORE : "_" >
| < #DIGIT : ["0"-"9"] >
| < #POSITIVE_INTEGER : "0" | ["1"-"9"] (["0"-"9"])* >
| < #LOWERLETTER : ["a"-"z"] >
| < #UPPERLETTER : ["A"-"Z"] >
| < #PLAIN_PATH_ELEMENT : ( | ) ( | | | )* >
| < #INDEXED_PATH_ELEMENT : "[" ( ~[ "]" ] )+ "]" >
| < FUNCTION_NAME : >
| < PATH : ( ( ) | )* >
}
Predicate parseExpression() :
{
Predicate predicate = null;
Function targetBeanFunction = new TargetBeanFunction();
}
{
predicate = predicates(targetBeanFunction) { return predicate; }
}
Collection parseValidation() :
{
Predicate predicate = null;
Collection rules = new ArrayList();
String message = null;
String field = null;
String errorKey = null;
Collection errorArgs = null;
Function function = null;
Function fieldFunction = null;
}
{
(
(
"{"
( | )
{
field = token.image;
fieldFunction = new BeanPropertyFunction(field);
/* MOD-26: re-initiliaze error key and arguments to null for every rule.
kudos to Cesar Ordinana for reporting this bug. */
errorKey = null;
errorArgs = new ArrayList();
}
":"
predicate = predicates(fieldFunction)
":"
{ message = token.image.substring(1, token.image.length() - 1); }
[
":"
{ errorKey = token.image.substring(1, token.image.length() - 1); }
[
":"
(
LOOKAHEAD(2)
function = function(fieldFunction) { errorArgs.add(function); }
","
)*
function = function(fieldFunction) { errorArgs.add(function); }
]
]
"}"
)
{
/* take into account error key and error args for localization */
rules.add(new BasicValidationRule(field, predicate, errorKey, message, errorArgs));
}
)+
{
return rules;
}
}
Predicate not(Function fieldFunction) :
{
Predicate predicate = null;
}
{
predicate = expression(fieldFunction) { return NotPredicate.getInstance(predicate); }
}
Predicate predicates(Function fieldFunction) :
{
Predicate predicate1 = null;
Predicate predicate2 = null;
boolean andTest = false;
boolean orTest = false;
}
{
(
predicate1 = expression(fieldFunction)
(
(
{ andTest = true; }
| { orTest = true; }
)
predicate2 = expression(fieldFunction)
{
if (andTest) {
predicate1 = AndPredicate.getInstance(predicate1, predicate2);
andTest = false;
} else if (orTest) {
predicate1 = OrPredicate.getInstance(predicate1, predicate2);
orTest = false;
}
}
)*
)
{
return predicate1;
}
}
Predicate expression(Function fieldFunction) :
{
Predicate predicate = null;
}
{
(
LOOKAHEAD("(" predicates(fieldFunction) ")")
"(" predicate = predicates(fieldFunction) ")"
| predicate = not(fieldFunction)
| predicate = predicate(fieldFunction)
)
{
return predicate;
}
}
Predicate predicate(Function fieldFunction) :
{
Function leftFunction = null;
Function rightFunction1 = null;
Function rightFunction2 = null;
Function tmpFunction = null;
Operator operator = null;
boolean notBetween = false;
boolean notIn = false;
Collection functions = new ArrayList();
boolean collectionProperty = false;
}
{
(
leftFunction = additiveExpr(fieldFunction)
(
(
operator = binaryOperator() rightFunction1 = additiveExpr(fieldFunction)
| operator = unaryOperator()
)
{
return getVisitor().getPredicate(leftFunction, operator, rightFunction1, 0, 0);
}
| (
( | { notBetween = true; } )
(
rightFunction1 = additiveExpr(fieldFunction)
rightFunction2 = additiveExpr(fieldFunction)
)
{
if (notBetween) {
return getVisitor().getPredicate(leftFunction, OperatorConstants.NOT_BETWEEN_OPERATOR, new LiteralFunction(new Function[] { rightFunction1, rightFunction2 }), 0, 0);
} else {
return getVisitor().getPredicate(leftFunction, OperatorConstants.BETWEEN_OPERATOR, new LiteralFunction(new Function[] { rightFunction1, rightFunction2 }), 0, 0);
}
}
)
| (
( | { notIn = true; } )
(
(
LOOKAHEAD(2)
tmpFunction = additiveExpr(fieldFunction) { functions.add(tmpFunction); }
","
)*
tmpFunction = additiveExpr(fieldFunction) { functions.add(tmpFunction); }
| "@" tmpFunction = beanProperty(fieldFunction) { collectionProperty = true; }
)
{
if (!collectionProperty) {
tmpFunction = new LiteralFunction(functions);
}
if (notIn) {
return getVisitor().getPredicate(leftFunction, OperatorConstants.NOT_IN_OPERATOR, tmpFunction, 0, 0);
} else {
return getVisitor().getPredicate(leftFunction, OperatorConstants.IN_OPERATOR, tmpFunction, 0, 0);
}
}
)
)
)
}
Function function(Function fieldFunction) :
{
String function = null;
Function leftFunction = null;
Function rightFunction = null;
int line = 0;
int column = 0;
Collection functionArguments = new ArrayList();
}
{
(
LOOKAHEAD(2)
( "!" | )
{ function = token.image; line = token.beginLine; column = token.beginColumn; }
"("
leftFunction = function(fieldFunction) { functionArguments.add(leftFunction); }
(
","
leftFunction = function(fieldFunction) { functionArguments.add(leftFunction); }
)*
")"
{ leftFunction = getVisitor().getFunction(function, (Function[])functionArguments.toArray(new Function[functionArguments.size()]), line, column); }
| leftFunction = beanPropertyOrLiteral(fieldFunction)
| "?" { leftFunction = fieldFunction; }
| "(" leftFunction = additiveExpr(fieldFunction) ")"
)
{
return leftFunction;
}
}
Function additiveExpr(Function fieldFunction) :
{
Function leftFunction = null;
Function rightFunction = null;
int line = 0;
int column = 0;
}
{
leftFunction = subtractiveExpr(fieldFunction)
(
{ line = token.beginLine; column = token.beginColumn; } rightFunction = subtractiveExpr(fieldFunction) { leftFunction = new AddFunction(leftFunction, rightFunction, line, column); }
)*
{
return leftFunction;
}
}
Function subtractiveExpr(Function fieldFunction) :
{
Function leftFunction = null;
Function rightFunction = null;
int line = 0;
int column = 0;
}
{
leftFunction = multiplicativeExpr(fieldFunction)
(
{ line = token.beginLine; column = token.beginColumn; } rightFunction = multiplicativeExpr(fieldFunction) { leftFunction = new SubtractFunction(leftFunction, rightFunction, line, column); }
)*
{
return leftFunction;
}
}
Function multiplicativeExpr(Function fieldFunction) :
{
Function leftFunction = null;
Function rightFunction = null;
int line = 0;
int column = 0;
}
{
leftFunction = function(fieldFunction)
(
{ line = token.beginLine; column = token.beginColumn; } rightFunction = function(fieldFunction) { leftFunction = new MultiplyFunction(leftFunction, rightFunction, line, column); }
| { line = token.beginLine; column = token.beginColumn; } rightFunction = function(fieldFunction) { leftFunction = new DivideFunction(leftFunction, rightFunction, line, column); }
| { line = token.beginLine; column = token.beginColumn; } rightFunction = function(fieldFunction) { leftFunction = new ModuloFunction(leftFunction, rightFunction, line, column); }
)*
{
return leftFunction;
}
}
Function beanPropertyOrLiteral(Function fieldFunction) :
{
Function function = null;
}
{
(
function = literal() { return function; }
| function = beanProperty(fieldFunction) { return function; }
)
}
Function literal() :
{}
{
(
{ return new LiteralFunction(Boolean.TRUE); }
| { return new LiteralFunction(Boolean.FALSE); }
| { return new LiteralFunction(replace(token.image.substring(1, token.image.length() - 1))); }
| { return new LiteralFunction(new BigDecimal(token.image)); }
| {
try {
getVisitor().getDateParser().parse(token.image.substring(1, token.image.length() - 1));
} catch (DateParseException e) {
throw new ParseException("Could not parse date [" + token.image.substring(1, token.image.length() - 1) + "] at line " + token.beginLine + ", column " + token.beginColumn + ".");
}
return new DateLiteralFunction(token.image.substring(1, token.image.length() - 1), getVisitor().getDateParser(), token.beginLine, token.beginColumn);
}
)
}
Function beanProperty(Function fieldFunction) :
{
Function function = null;
}
{
function = path() { return function; }
}
Function path() :
{
String path = null;
}
{
(
{ path = token.image; }
| { path = token.image; }
)
{
return new BeanPropertyFunction(path);
}
}
Operator binaryOperator() :
{
Operator operator = null;
}
{
(
{ operator = OperatorConstants.NOT_EQUAL_OPERATOR; }
| { operator = OperatorConstants.MORE_THAN_OR_EQUAL_OPERATOR; }
| { operator = OperatorConstants.MORE_THAN_OPERATOR; }
| { operator = OperatorConstants.LESS_THAN_OR_EQUAL_OPERATOR; }
| { operator = OperatorConstants.LESS_THAN_OPERATOR; }
| { operator = OperatorConstants.EQUALS_OPERATOR; }
)
{
return operator;
}
}
Operator unaryOperator() :
{
Operator operator = null;
}
{
(
{ operator = OperatorConstants.NULL_OPERATOR; }
| { operator = OperatorConstants.NOT_NULL_OPERATOR; }
| { operator = OperatorConstants.HAS_TEXT_OPERATOR; }
| { operator = OperatorConstants.HAS_NO_TEXT_OPERATOR; }
| { operator = OperatorConstants.HAS_LENGTH_OPERATOR; }
| { operator = OperatorConstants.HAS_NO_LENGTH_OPERATOR; }
| { operator = OperatorConstants.IS_BLANK_OPERATOR; }
| { operator = OperatorConstants.IS_NOT_BLANK_OPERATOR; }
| { operator = OperatorConstants.IS_WORD_OPERATOR; }
| { operator = OperatorConstants.IS_NOT_WORD_OPERATOR; }
| { operator = OperatorConstants.IS_UPPER_CASE_OPERATOR; }
| { operator = OperatorConstants.IS_NOT_UPPER_CASE_OPERATOR; }
| { operator = OperatorConstants.IS_LOWER_CASE_OPERATOR; }
| { operator = OperatorConstants.IS_NOT_LOWER_CASE_OPERATOR; }
)
{
return operator;
}
}