/*
$Header: /cvsroot/xorm/xorm/src/org/xorm/query/ExpressionValidator.java,v 1.9 2003/12/20 00:43:27 wbiggs Exp $
This file is part of XORM.
XORM 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.
XORM 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with XORM; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.xorm.query;
import java.util.Arrays;
import java.util.List;
import org.xorm.XORM;
import org.xorm.ClassMapping;
import org.xorm.ModelMapping;
/**
* Validates and normalizes expressions.
*/
public class ExpressionValidator extends ExpressionVisitor.NoOp {
private static List ALLOWED_METHODS = Arrays.asList(new String[] {
"contains", "isEmpty", "startsWith", "endsWith", "strstr",
"indexOf" });
private QueryImpl query;
private boolean valid = true;
private ModelMapping modelMapping;
public ExpressionValidator(QueryImpl query) {
this.query = query;
}
public boolean isValid() {
query.getExpression().accept(this);
return valid;
}
/** Lazy initialization of modelMapping. */
private ModelMapping getModelMapping() {
if (modelMapping == null) {
modelMapping = XORM.getModelMapping(query.getPersistenceManager());
}
return modelMapping;
}
/**
* Ensures that both sides of a comparison are valid.
*/
public boolean visitComparison(Expression.Comparison exp) {
exp.getLHS().accept(this);
if (valid) {
exp.getRHS().accept(this);
}
return true;
}
/**
* Ensures that the method is acceptable as per the JDO spec.
*/
public boolean visitMethodCall(Expression.MethodCall exp) {
exp.getOwner().accept(this);
valid &= ALLOWED_METHODS.contains(exp.getName());
// TODO validate if owner is of correct type
Expression[] params = exp.getParameters();
for (int i = 0; i < params.length; i++) {
params[i].accept(this);
}
return true;
}
/**
* Ensures that the field is mapped for the owning class
* and sets the Type field to the correct class.
*/
public boolean visitFieldAccess(Expression.FieldAccess exp) {
if (exp == Expression.FieldAccess.THIS) { return true; }
Expression owner = exp.getOwner();
ClassMapping mapping;
if (owner != Expression.FieldAccess.THIS) {
owner.accept(this);
mapping = getModelMapping()
.getClassMapping(exp.getOwner().getType());
} else {
mapping = getModelMapping()
.getClassMapping(query.getCandidateClass());
}
//System.out.println("Setting type of field " + exp.getName() + " to " + mapping.getFieldDescriptor(exp.getName()).type + " using mapping for " + mapping.getMappedClass());
exp.setType(mapping.getFieldDescriptor(exp.getName()).type);
return true;
}
/**
* Ensures that the named parameter has been declared.
*/
public boolean visitParameter(Expression.Parameter exp) {
valid &= query.getParameterType(exp.getName()) != null;
return true;
}
/**
* Ensures that the named variable has been declared.
*/
public boolean visitVariable(Expression.Variable exp) {
valid &= query.getVariableType(exp.getName()) != null;
return true;
}
public boolean visitNot(Expression.Not exp) {
// For AND/OR clauses, push the negation down the chain
Expression inside = exp.getOperand();
if (inside instanceof Expression.ConditionalAnd) {
Expression.ConditionalAnd and = (Expression.ConditionalAnd) inside;
exp.setOperand(new Expression.Not
(new Expression.ConditionalOr
(new Expression.Not(and.getLHS()),
new Expression.Not(and.getRHS()))));
inside = ((Expression.Unary) exp.getOperand()).getOperand();
} else if (inside instanceof Expression.ConditionalOr) {
Expression.ConditionalOr or = (Expression.ConditionalOr) inside;
exp.setOperand(new Expression.Not
(new Expression.ConditionalAnd
(new Expression.Not(or.getLHS()),
new Expression.Not(or.getRHS()))));
inside = ((Expression.Unary) exp.getOperand()).getOperand();
}
inside.accept(this);
return true;
}
public boolean visitUnary(Expression.Unary exp) {
exp.getOperand().accept(this);
return true;
}
}