001: /*
002: $Header: /cvsroot/xorm/xorm/src/org/xorm/query/ExpressionValidator.java,v 1.9 2003/12/20 00:43:27 wbiggs Exp $
003:
004: This file is part of XORM.
005:
006: XORM is free software; you can redistribute it and/or modify
007: it under the terms of the GNU General Public License as published by
008: the Free Software Foundation; either version 2 of the License, or
009: (at your option) any later version.
010:
011: XORM is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU General Public License for more details.
015:
016: You should have received a copy of the GNU General Public License
017: along with XORM; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package org.xorm.query;
021:
022: import java.util.Arrays;
023: import java.util.List;
024:
025: import org.xorm.XORM;
026: import org.xorm.ClassMapping;
027: import org.xorm.ModelMapping;
028:
029: /**
030: * Validates and normalizes expressions.
031: */
032: public class ExpressionValidator extends ExpressionVisitor.NoOp {
033: private static List ALLOWED_METHODS = Arrays.asList(new String[] {
034: "contains", "isEmpty", "startsWith", "endsWith", "strstr",
035: "indexOf" });
036:
037: private QueryImpl query;
038: private boolean valid = true;
039: private ModelMapping modelMapping;
040:
041: public ExpressionValidator(QueryImpl query) {
042: this .query = query;
043: }
044:
045: public boolean isValid() {
046: query.getExpression().accept(this );
047: return valid;
048: }
049:
050: /** Lazy initialization of modelMapping. */
051: private ModelMapping getModelMapping() {
052: if (modelMapping == null) {
053: modelMapping = XORM.getModelMapping(query
054: .getPersistenceManager());
055: }
056: return modelMapping;
057: }
058:
059: /**
060: * Ensures that both sides of a comparison are valid.
061: */
062: public boolean visitComparison(Expression.Comparison exp) {
063: exp.getLHS().accept(this );
064: if (valid) {
065: exp.getRHS().accept(this );
066: }
067: return true;
068: }
069:
070: /**
071: * Ensures that the method is acceptable as per the JDO spec.
072: */
073: public boolean visitMethodCall(Expression.MethodCall exp) {
074: exp.getOwner().accept(this );
075:
076: valid &= ALLOWED_METHODS.contains(exp.getName());
077: // TODO validate if owner is of correct type
078:
079: Expression[] params = exp.getParameters();
080: for (int i = 0; i < params.length; i++) {
081: params[i].accept(this );
082: }
083: return true;
084: }
085:
086: /**
087: * Ensures that the field is mapped for the owning class
088: * and sets the Type field to the correct class.
089: */
090: public boolean visitFieldAccess(Expression.FieldAccess exp) {
091: if (exp == Expression.FieldAccess.THIS) {
092: return true;
093: }
094: Expression owner = exp.getOwner();
095: ClassMapping mapping;
096: if (owner != Expression.FieldAccess.THIS) {
097: owner.accept(this );
098: mapping = getModelMapping().getClassMapping(
099: exp.getOwner().getType());
100: } else {
101: mapping = getModelMapping().getClassMapping(
102: query.getCandidateClass());
103: }
104: //System.out.println("Setting type of field " + exp.getName() + " to " + mapping.getFieldDescriptor(exp.getName()).type + " using mapping for " + mapping.getMappedClass());
105: exp.setType(mapping.getFieldDescriptor(exp.getName()).type);
106: return true;
107: }
108:
109: /**
110: * Ensures that the named parameter has been declared.
111: */
112: public boolean visitParameter(Expression.Parameter exp) {
113: valid &= query.getParameterType(exp.getName()) != null;
114: return true;
115: }
116:
117: /**
118: * Ensures that the named variable has been declared.
119: */
120: public boolean visitVariable(Expression.Variable exp) {
121: valid &= query.getVariableType(exp.getName()) != null;
122: return true;
123: }
124:
125: public boolean visitNot(Expression.Not exp) {
126: // For AND/OR clauses, push the negation down the chain
127: Expression inside = exp.getOperand();
128: if (inside instanceof Expression.ConditionalAnd) {
129: Expression.ConditionalAnd and = (Expression.ConditionalAnd) inside;
130: exp.setOperand(new Expression.Not(
131: new Expression.ConditionalOr(new Expression.Not(and
132: .getLHS()),
133: new Expression.Not(and.getRHS()))));
134: inside = ((Expression.Unary) exp.getOperand()).getOperand();
135: } else if (inside instanceof Expression.ConditionalOr) {
136: Expression.ConditionalOr or = (Expression.ConditionalOr) inside;
137: exp
138: .setOperand(new Expression.Not(
139: new Expression.ConditionalAnd(
140: new Expression.Not(or.getLHS()),
141: new Expression.Not(or.getRHS()))));
142: inside = ((Expression.Unary) exp.getOperand()).getOperand();
143: }
144: inside.accept(this );
145: return true;
146: }
147:
148: public boolean visitUnary(Expression.Unary exp) {
149: exp.getOperand().accept(this );
150: return true;
151: }
152: }
|