blob: d575cbfdf49f51cae49866ed960cd1142d0000ac [file] [log] [blame]
/* 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.codehaus.groovy.grails.orm.hibernate.metaclass;
import groovy.lang.MissingMethodException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.grails.commons.GrailsApplication;
import org.codehaus.groovy.grails.commons.GrailsDomainClass;
import org.codehaus.groovy.grails.commons.GrailsDomainClassProperty;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Expression;
/**
* @author Graeme Rocher
* @since 31-Aug-2005
*
*/
public abstract class AbstractClausedStaticPersistentMethod extends
AbstractStaticPersistentMethod {
private static final Log LOG = LogFactory.getLog(AbstractClausedStaticPersistentMethod.class);
/**
*
* @author Graeme Rocher
*
*/
protected abstract static class GrailsMethodExpression {
private static final String LESS_THAN = "LessThan";
private static final String LESS_THAN_OR_EQUAL = "LessThanOrEqual";
private static final String GREATER_THAN = "GreaterThan";
private static final String GREATER_THAN_OR_EQUAL = "GreaterThanOrEqual";
private static final String LIKE = "Like";
private static final String BETWEEN = "Between";
private static final String IS_NOT_NULL = "IsNotNull";
private static final String IS_NULL = "IsNull";
private static final String NOT = "Not";
private static final String EQUAL = "Equal";
private static final String NOT_EQUAL = "NotEqual";
protected String propertyName;
protected Object[] arguments;
protected int argumentsRequired;
protected boolean negation;
protected String type;
protected Class targetClass;
private GrailsApplication application;
GrailsMethodExpression(GrailsApplication application,Class targetClass,String propertyName, String type,int argumentsRequired,boolean negation) {
this.application = application;
this.targetClass = targetClass;
this.propertyName = propertyName;
this.type = type;
this.argumentsRequired = argumentsRequired;
this.negation = negation;
}
public String toString() {
StringBuffer buf = new StringBuffer("[GrailsMethodExpression] ");
buf.append(propertyName)
.append(" ")
.append(type)
.append(" ");
for (int i = 0; i < arguments.length; i++) {
buf.append(arguments[i]);
if(i != arguments.length)
buf.append(" and ");
}
return buf.toString();
}
void setArguments(Object[] args)
throws IllegalArgumentException {
if(args.length != argumentsRequired)
throw new IllegalArgumentException("Method expression '"+this.type+"' requires " + argumentsRequired + " arguments");
GrailsDomainClass dc = application.getGrailsDomainClass(targetClass.getName());
GrailsDomainClassProperty prop = dc.getPropertyByName(propertyName);
if(prop == null)
throw new IllegalArgumentException("Property "+propertyName+" doesn't exist for method expression '"+this.type+"'");
for (int i = 0; i < args.length; i++) {
if(args[i] == null)
throw new IllegalArgumentException("Argument " + args[0] + " cannot be null");
if(!prop.getType().isAssignableFrom( args[i].getClass() ))
throw new IllegalArgumentException("Argument " + args[0] + " does not match property '"+propertyName+"' of type " + prop.getType());
}
this.arguments = args;
}
abstract Criterion createCriterion();
protected Criterion getCriterion() {
if(arguments == null)
throw new IllegalStateException("Parameters array must be set before retrieving Criterion");
if(negation) {
return Expression.not( createCriterion() );
}
else {
return createCriterion();
}
}
protected static GrailsMethodExpression create(final GrailsApplication application,Class clazz, String queryParameter) {
if(queryParameter.endsWith( LESS_THAN_OR_EQUAL )) {
return new GrailsMethodExpression(
application,
clazz,
calcPropertyName(queryParameter, LESS_THAN_OR_EQUAL),
LESS_THAN_OR_EQUAL,
1,
isNegation(queryParameter, LESS_THAN_OR_EQUAL) ) {
Criterion createCriterion() {
return Expression.le( this.propertyName, arguments[0] );
}
};
}
else if(queryParameter.endsWith( LESS_THAN )) {
return new GrailsMethodExpression(
application,
clazz,
calcPropertyName(queryParameter, LESS_THAN),
LESS_THAN,
1, // argument count
isNegation(queryParameter, LESS_THAN) ) {
Criterion createCriterion() {
return Expression.lt( this.propertyName, arguments[0] );
}
};
}
else if(queryParameter.endsWith( GREATER_THAN_OR_EQUAL )) {
return new GrailsMethodExpression(
application,
clazz,
calcPropertyName(queryParameter, GREATER_THAN_OR_EQUAL),
GREATER_THAN_OR_EQUAL,
1,
isNegation(queryParameter, GREATER_THAN_OR_EQUAL) ) {
Criterion createCriterion() {
return Expression.ge( this.propertyName, arguments[0] );
}
};
}
else if(queryParameter.endsWith( GREATER_THAN )) {
return new GrailsMethodExpression(
application,
clazz,
calcPropertyName(queryParameter, GREATER_THAN),
GREATER_THAN,
1,
isNegation(queryParameter, GREATER_THAN) ) {
Criterion createCriterion() {
return Expression.gt( this.propertyName, arguments[0] );
}
};
}
else if(queryParameter.endsWith( LIKE )) {
return new GrailsMethodExpression(
application,
clazz,
calcPropertyName(queryParameter, LIKE),
LIKE,
1,
isNegation(queryParameter, LIKE) ) {
Criterion createCriterion() {
return Expression.like( this.propertyName, arguments[0] );
}
};
}
else if(queryParameter.endsWith( IS_NOT_NULL )) {
return new GrailsMethodExpression(
application,
clazz,
calcPropertyName(queryParameter, IS_NOT_NULL),
IS_NOT_NULL,
0,
isNegation(queryParameter, IS_NOT_NULL) ) {
Criterion createCriterion() {
return Expression.isNotNull( this.propertyName );
}
};
}
else if(queryParameter.endsWith( IS_NULL )) {
return new GrailsMethodExpression(
application,
clazz,
calcPropertyName(queryParameter, IS_NULL),
IS_NULL,
0,
isNegation(queryParameter, IS_NULL) ) {
Criterion createCriterion() {
return Expression.isNull( this.propertyName );
}
};
}
else if(queryParameter.endsWith( BETWEEN )) {
return new GrailsMethodExpression(
application,
clazz,
calcPropertyName(queryParameter, BETWEEN),
BETWEEN,
2,
isNegation(queryParameter, BETWEEN) ) {
Criterion createCriterion() {
return Expression.between( this.propertyName,this.arguments[0], this.arguments[1] );
}
};
}
else if(queryParameter.endsWith( NOT_EQUAL )) {
return new GrailsMethodExpression(
application,
clazz,
calcPropertyName(queryParameter, NOT_EQUAL),
NOT_EQUAL,
1,
isNegation(queryParameter, NOT_EQUAL) ) {
Criterion createCriterion() {
return Expression.ne( this.propertyName,this.arguments[0]);
}
};
}
else {
return new GrailsMethodExpression(
application,
clazz,
calcPropertyName(queryParameter, null),
EQUAL,
1,
isNegation(queryParameter, EQUAL) ) {
Criterion createCriterion() {
return Expression.eq( this.propertyName,this.arguments[0]);
}
};
}
}
private static boolean isNegation(String queryParameter, String clause) {
String propName;
if(clause != null && !clause.equals( EQUAL )) {
int i = queryParameter.indexOf(clause);
propName = queryParameter.substring(0,i);
}
else {
propName = queryParameter;
}
if(propName.endsWith(NOT)) {
return true;
}
return false;
}
private static String calcPropertyName(String queryParameter, String clause) {
String propName;
if(clause != null && !clause.equals( EQUAL )) {
int i = queryParameter.indexOf(clause);
propName = queryParameter.substring(0,i);
}
else {
propName = queryParameter;
}
if(propName.endsWith(NOT)) {
int i = propName.lastIndexOf(NOT);
propName = propName.substring(0, i);
}
return propName.substring(0,1).toLowerCase()
+ propName.substring(1);
}
}
private String[] operators;
private Pattern[] operatorPatterns;
protected String operatorInUse;
protected GrailsApplication application;
public AbstractClausedStaticPersistentMethod(GrailsApplication application, SessionFactory sessionFactory, ClassLoader classLoader, Pattern pattern, String[] operators) {
super(sessionFactory, classLoader, pattern);
this.application = application;
this.operators = operators;
this.operatorPatterns = new Pattern[this.operators.length];
for (int i = 0; i < operators.length; i++) {
this.operatorPatterns[i] = Pattern.compile("(\\w+)("+this.operators[i]+")(\\p{Upper})(\\w+)");
}
}
/* (non-Javadoc)
* @see org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractStaticPersistentMethod#doInvokeInternal(java.lang.Class, java.lang.String, java.lang.Object[])
*/
protected Object doInvokeInternal(final Class clazz, String methodName,
Object[] arguments) {
List expressions = new ArrayList();
Matcher match = super.getPattern().matcher( methodName );
// find match
match.find();
String[] queryParameters = null;
int totalRequiredArguments = 0;
// get the sequence clauses
String querySequence = match.group(2);
// if it contains operator and split
boolean containsOperator = false;
for (int i = 0; i < operators.length; i++) {
Matcher currentMatcher = operatorPatterns[i].matcher( querySequence );
if(currentMatcher.find()) {
containsOperator = true;
operatorInUse = this.operators[i];
queryParameters = new String[2];
queryParameters[0] = currentMatcher.group(1);
queryParameters[1] = currentMatcher.group(3) + currentMatcher.group(4);
// loop through query parameters and create expressions
// calculating the numBer of arguments required for the expression
int argumentCursor = 0;
for (int j = 0; j < queryParameters.length; j++) {
GrailsMethodExpression currentExpression = GrailsMethodExpression.create(this.application,clazz,queryParameters[j]);
totalRequiredArguments += currentExpression.argumentsRequired;
// populate the arguments into the GrailsExpression from the argument list
Object[] currentArguments = new Object[currentExpression.argumentsRequired];
if((argumentCursor + currentExpression.argumentsRequired) > arguments.length)
throw new MissingMethodException(methodName,clazz,arguments);
for (int k = 0; k < currentExpression.argumentsRequired; k++,argumentCursor++) {
currentArguments[k] = arguments[argumentCursor];
}
try {
currentExpression.setArguments(currentArguments);
}catch(IllegalArgumentException iae) {
LOG.debug(iae.getMessage(),iae);
throw new MissingMethodException(methodName,clazz,arguments);
}
// add to list of expressions
expressions.add(currentExpression);
}
break;
}
}
// otherwise there is only one expression
if(!containsOperator) {
GrailsMethodExpression solo = GrailsMethodExpression.create(this.application, clazz,querySequence );
if(solo.argumentsRequired != arguments.length)
throw new MissingMethodException(methodName,clazz,arguments);
totalRequiredArguments += solo.argumentsRequired;
Object[] soloArgs = new Object[solo.argumentsRequired];
for (int i = 0; i < solo.argumentsRequired; i++) {
soloArgs[i] = arguments[i];
}
try {
solo.setArguments(soloArgs);
}
catch(IllegalArgumentException iae) {
LOG.debug(iae.getMessage(),iae);
throw new MissingMethodException(methodName,clazz,arguments);
}
expressions.add(solo);
}
// if the total of all the arguments necessary does not equal the number of arguments
// throw exception
if(totalRequiredArguments != arguments.length)
throw new MissingMethodException(methodName,clazz,arguments);
LOG.debug("Calculated expressions: " + expressions);
return doInvokeInternalWithExpressions(clazz, methodName, arguments, expressions);
}
protected abstract Object doInvokeInternalWithExpressions(Class clazz, String methodName, Object[] arguments, List expressions);
}