/**
* Copyright (C) 2001-2004 France Telecom R&D
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.objectweb.speedo.generation.jorm.rdb;
import org.objectweb.speedo.query.jdo.parser.SpeedoQLVisitor;
import org.objectweb.speedo.query.jdo.parser.SimpleNode;
import org.objectweb.speedo.query.jdo.parser.ASTSpeedoPrimary;
import org.objectweb.speedo.query.jdo.parser.ASTSpeedoQL;
import org.objectweb.speedo.query.jdo.parser.ASTPrimary;
import org.objectweb.speedo.query.jdo.parser.ASTRelationalExpression;
import org.objectweb.speedo.query.jdo.parser.ASTAdditiveExpression;
import org.objectweb.speedo.query.jdo.parser.ASTUnaryExpression;
import org.objectweb.speedo.query.jdo.parser.ASTCastExpression;
import org.objectweb.speedo.query.jdo.parser.ASTArgumentList;
import org.objectweb.speedo.query.jdo.parser.ASTLiteral;
import org.objectweb.speedo.query.jdo.parser.ASTType;
import org.objectweb.speedo.query.jdo.parser.ASTQualifiedName;
import org.objectweb.speedo.query.jdo.parser.SpeedoQLConstants;
import org.objectweb.speedo.query.jdo.parser.SpeedoQL;
import org.objectweb.speedo.query.jdo.parser.ParseException;
import org.objectweb.speedo.api.SpeedoException;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.medor.expression.api.Expression;
import org.objectweb.medor.expression.api.Operator;
import org.objectweb.medor.expression.api.Operand;
import org.objectweb.medor.expression.lib.ConditionalOr;
import org.objectweb.medor.expression.lib.ConditionalAnd;
import org.objectweb.medor.expression.lib.Or;
import org.objectweb.medor.expression.lib.And;
import org.objectweb.medor.expression.lib.Equal;
import org.objectweb.medor.expression.lib.NotEqual;
import org.objectweb.medor.expression.lib.Lower;
import org.objectweb.medor.expression.lib.Greater;
import org.objectweb.medor.expression.lib.GreaterEqual;
import org.objectweb.medor.expression.lib.LowerEqual;
import org.objectweb.medor.expression.lib.Plus;
import org.objectweb.medor.expression.lib.Minus;
import org.objectweb.medor.expression.lib.Mult;
import org.objectweb.medor.expression.lib.DivideBy;
import org.objectweb.medor.expression.lib.UMinus;
import org.objectweb.medor.expression.lib.Bitwize;
import org.objectweb.medor.expression.lib.Not;
import org.objectweb.medor.expression.lib.BasicOperand;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.jorm.mapper.rdb.metainfo.RdbFilter;
import org.objectweb.jorm.type.api.PTypeSpace;
import java.util.Stack;
import java.io.CharArrayReader;
/**
* It manages the filter dedicated to the filtered inheritance mapping.
*
* @author S.Chassande-Barrioz
*/
public class FilterManager {
private Logger logger;
private boolean debug = false;
private FilterParser parser = new FilterParser();
public FilterManager(Logger logger) {
this.logger = logger;
}
public FilterParser getParser() {
return parser;
}
public void assignFilter(String filter,
RdbFilter rdbFilter,
SpeedoClass sc,
String projectName,
String mapperName) throws SpeedoException {
debug = logger != null && logger.isLoggable(BasicLevel.DEBUG);
Expression medorExpression = parser.parse(filter);
if (!isJormPredicate(medorExpression)) {
throw new SpeedoException(
"The specified filter is not supported, \n\tclass"
+ sc.getFQName() + "\n\tfilter=" + filter);
}
rdbFilter.setExpression(medorExpression);
}
public class FilterParser implements SpeedoQLVisitor {
private Expression expression;
private String tab = "";
public synchronized Expression parse(String filter) throws SpeedoException {
try {
visit(new SpeedoQL(new CharArrayReader(filter.toCharArray())).SpeedoQL());
return expression;
} catch (ParseException e) {
throw new SpeedoException(
"Impossible to parse the filter and to create AST", e);
} catch (SpeedoException e) {
throw e;
} catch (Exception e) {
throw new SpeedoException("Impossible to parse the filter", e);
}
}
// IMPLEMENTATION OF THE SpeedoQLVisitor INTERFACE //
//-------------------------------------------------//
public Object visit(ASTPrimary node, Object data) {
visit((SimpleNode) node, data);
return null;
}
public Object visit(ASTSpeedoPrimary node, Object data) {
visit((SimpleNode) node, data);
Stack stack = (Stack) data;
expression = (Expression) stack.pop();
return null;
}
public Object visit(ASTRelationalExpression node, Object data) {
logger.log(BasicLevel.DEBUG, tab + "Visit RelationalExpression: " + node);
tab += '\t';
visit((SimpleNode) node, data);
tab = tab.substring(1);
Stack stack = (Stack) data;
if (stack.size() > 0) {
if (debug) {
logger.log(BasicLevel.DEBUG, "manage relational expression: "
+ "(children: " + node.jjtGetNumChildren()
+ ", stack: " + stack.size()
+ ", peek:" + stack.peek() + ")");
}
for (int i = 0; (i < (node.jjtGetNumChildren() - 1) && stack.size() > 0); i++) {
int op = ((Integer) node.ops.get(node.jjtGetNumChildren() - 2 - i)).intValue();
if (usedInRelationalExpresssion(op)) {
Object child2 = stack.pop();
Object child1 = stack.pop();
if (debug) {
logger.log(BasicLevel.DEBUG, "pop child1: " + child1);
logger.log(BasicLevel.DEBUG, "pop child2: " + child2);
}
Expression ret = null;
switch (op) {
case SpeedoQLConstants.OR:
ret = new ConditionalOr((Expression) child1, (Expression) child2);
break;
case SpeedoQLConstants.AND:
ret = new ConditionalAnd((Expression) child1, (Expression) child2);
break;
case SpeedoQLConstants.BITWISEOR:
ret = new Or((Expression) child1, (Expression) child2);
break;
case SpeedoQLConstants.BITWISEXOR:
ret = null;
break;
case SpeedoQLConstants.BITWISEAND:
ret = new And((Expression) child1, (Expression) child2);
break;
case SpeedoQLConstants.EQ:
ret = new Equal((Expression) child1, (Expression) child2);
break;
case SpeedoQLConstants.NEQ:
ret = new NotEqual((Expression) child1, (Expression) child2);
break;
case SpeedoQLConstants.LT:
ret = new Lower((Expression) child1, (Expression) child2);
break;
case SpeedoQLConstants.GT:
ret = new Greater((Expression) child1, (Expression) child2);
break;
case SpeedoQLConstants.GTE:
ret = new GreaterEqual((Expression) child1, (Expression) child2);
break;
case SpeedoQLConstants.LTE:
ret = new LowerEqual((Expression) child1, (Expression) child2);
break;
}
if (debug) {
logger.log(BasicLevel.DEBUG, "push(" + ret + ")");
}
stack.push(ret);
}
}
}
if (debug) {
logger.log(BasicLevel.DEBUG, "children:" + node.jjtGetNumChildren()
+ " / stack: " + stack.size());
logger.log(BasicLevel.DEBUG, tab + "End of Visit RelationalExpression: " + node);
}
return null;
}
public Object visit(ASTAdditiveExpression node, Object data) {
logger.log(BasicLevel.DEBUG, tab + "Visit AdditiveExpression: " + node);
tab += '\t';
visit((SimpleNode) node, data);
tab = tab.substring(1);
Stack stack = (Stack) data;
if (stack.size() > 0) {
Expression ret = (Expression) stack.pop();
for (int i = 0; i < node.jjtGetNumChildren() - 1; i++) {
logger.log(BasicLevel.DEBUG, "Visit AdditiveExpression... children...[" + i + "]");
switch (((Integer) node.ops.get(node.jjtGetNumChildren() - 2 - i)).intValue()) {
case SpeedoQLConstants.PLUS:
ret = new Plus((Expression) stack.pop(), ret);
break;
case SpeedoQLConstants.MINUS:
ret = new Minus((Expression) stack.pop(), ret);
break;
case SpeedoQLConstants.MULT:
ret = new Mult((Expression) stack.pop(), ret);
break;
case SpeedoQLConstants.DIV:
ret = new DivideBy((Expression) stack.pop(), ret);
break;
default:
}
}
((Stack) data).push(ret);
}
logger.log(BasicLevel.DEBUG, tab + "End of Visit AdditiveExpression: " + node);
return null;
}
public Object visit(ASTUnaryExpression node, Object data) {
logger.log(BasicLevel.DEBUG, tab + "Visit UnaryExpression" + node);
tab += '\t';
visit((SimpleNode) node, data);
tab = tab.substring(1);
Stack stack = (Stack) data;
if (stack.size() > 0) {
Object o = stack.pop();
if (node.ops.size() > 0) {
switch (((Integer) node.ops.get(0)).intValue()) {
case SpeedoQLConstants.MINUS:
o = new UMinus((Expression) o);
break;
case SpeedoQLConstants.BITWISECOMPL:
o = new Bitwize((Expression) o);
break;
case SpeedoQLConstants.NOT:
logger.log(BasicLevel.DEBUG, "NOT(" + o + "): " + node);
o = new Not((Expression) o);
break;
}
}
((Stack) data).push(o);
}
logger.log(BasicLevel.DEBUG, tab + "End of Visit UnaryExpression: " + node);
return null;
}
public Object visit(ASTCastExpression node, Object data) {
return null;
}
public Object visit(ASTArgumentList node, Object data) {
return null;
}
public Object visit(ASTLiteral node, Object data) {
if (node.value instanceof Integer) {
((Stack) data).push(new BasicOperand(((Integer) node.value).intValue()));
} else if (node.value instanceof Float) {
((Stack) data).push(new BasicOperand(((Float) node.value).floatValue()));
} else if (node.value instanceof Character) {
((Stack) data).push(new BasicOperand(((Character) node.value).charValue()));
} else if (node.value instanceof String) {
String s = (String) (node.value);
s = s.substring(1, s.length() - 1);
((Stack) data).push(new BasicOperand(s));
} else if (node.value instanceof Boolean) {
((Stack) data).push(new BasicOperand(((Boolean) node.value).booleanValue()));
}
return null;
}
public Object visit(ASTType node, Object data) {
logger.log(BasicLevel.DEBUG, "Visit Type: " + node);
return null;
}
public Object visit(ASTQualifiedName node, Object data) {
((Stack) data).push(new BasicOperand((String) node.value));
return null;
}
/**
* Visit method to call from constructor.
* Child node visitors get a <code>java.util.Stack</code> as data parameter.
* @throws java.lang.Exception any nested exception thrown from other visit method
*/
public Object visit(SimpleNode node) throws Exception {
return visit(node, new Stack());
}
/**
* Generic visit method that traverses all child nodes
*/
public Object visit(SimpleNode node, Object data) {
return node.childrenAccept(this, data);
}
public Object visit(ASTSpeedoQL node, Object data) {
return null;
}
private boolean usedInRelationalExpresssion(int op) {
switch (op) {
case SpeedoQLConstants.OR:
case SpeedoQLConstants.AND:
case SpeedoQLConstants.BITWISEOR:
case SpeedoQLConstants.BITWISEXOR:
case SpeedoQLConstants.BITWISEAND:
case SpeedoQLConstants.EQ:
case SpeedoQLConstants.NEQ:
case SpeedoQLConstants.LT:
case SpeedoQLConstants.GT:
case SpeedoQLConstants.GTE:
case SpeedoQLConstants.LTE:
logger.log(BasicLevel.DEBUG, "node useful");
return true;
default:
logger.log(BasicLevel.DEBUG, "node useless");
return false;
}
}
}
// OTHER METHODS //
//---------------//
/**
* Calculates if an expression can be used as Jorm predicate as inheritance
* filter.
* @param e is the medor Expression to analyze.
* @return true if the expression is compatible.
*/
private boolean isJormPredicate(Expression e) {
return isSimpleOperator(e)
|| (e instanceof ConditionalAnd
&& isJormPredicate(((Operator) e).getExpression(0))
&& isJormPredicate(((Operator) e).getExpression(1)));
}
private boolean isSimpleOperator(Expression e) {
boolean res = e instanceof Equal
|| e instanceof Greater
|| e instanceof GreaterEqual
|| e instanceof Lower
|| e instanceof LowerEqual;
if (res == false) {
return false;
}
Operator op = (Operator) e;
if (!(op.getExpression(0) instanceof Operand
&& op.getExpression(1) instanceof Operand)) {
return false;
}
Operand o0 = (Operand) op.getExpression(0);
Operand o1 = (Operand) op.getExpression(1);
if (o0.getType().getTypeCode() == PTypeSpace.STRING.getTypeCode()) {
return true;
} else if (o1.getType().getTypeCode() == PTypeSpace.STRING.getTypeCode()) {
//reverse the order: column name at left
op.setExpression(0, o1);
op.setExpression(1, o0);
return true;
} else {
return false;
}
}
}