All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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;
	}
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy