/*==========================================================================*\ | $Id: OgnlQualifierUtils.java,v 1.5 2010/09/27 00:59:42 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2006-2008 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT 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 Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package ognl; import com.webobjects.eocontrol.EOAndQualifier; import com.webobjects.eocontrol.EOKeyComparisonQualifier; import com.webobjects.eocontrol.EOKeyValueQualifier; import com.webobjects.eocontrol.EONotQualifier; import com.webobjects.eocontrol.EOOrQualifier; import com.webobjects.eocontrol.EOQualifier; import com.webobjects.eocontrol.EOQualifierVariable; import com.webobjects.foundation.NSKeyValueCoding; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSMutableDictionary; import com.webobjects.foundation.NSSelector; //------------------------------------------------------------------------- /** * The fact that this class is in this package is a major hack. We need access * to the ognl.AST* classes, but they only have package visibility. Dumping it * in the ognl package is easier than using reflection everywhere. * * @author Tony Allevato * @author Last changed by $Author: stedwar2 $ * @version $Revision: 1.5 $, $Date: 2010/09/27 00:59:42 $ */ public class OgnlQualifierUtils { //~ Public Methods ........................................................ // ---------------------------------------------------------- public static EOQualifier qualifierFromOgnlExpression(String expression) throws OgnlException { Node node = (Node)Ognl.parseExpression(expression); return qualifierFromOgnlAST(node); } // ---------------------------------------------------------- public static void printOgnlAST(Node node, int depth) { for (int j = 0; j < depth; j++) { System.out.print(" "); } System.out.print(node.getClass().getSimpleName()); System.out.println(":" + node.toString()); for (int i = 0; i < node.jjtGetNumChildren(); i++) { Node child = node.jjtGetChild(i); printOgnlAST(child, depth + 1); } } //~ Private Methods ....................................................... // ---------------------------------------------------------- private static EOQualifier qualifierFromOgnlAST(Node node) { NodeHandler handler = astHandlers.objectForKey(node.getClass()); if (handler != null) { return handler.handle(node); } else { generateException("Expression is not a valid qualifier"); return null; } } // ---------------------------------------------------------- private static EOQualifier processRelational( NSSelector<?> selector, Node lhs, Node rhs) { String lhsString = null, rhsString = null; Object rhsObject = null; if (lhs instanceof ASTProperty) { lhsString = nameOfPropertyOrVariable(lhs); } else if (lhs instanceof ASTChain) { lhsString = stringForChain( (ASTChain)lhs, true, lhs.jjtGetNumChildren()); } else { generateException("Only properties are allowed on the " + "left-hand side of an expression in a qualifier"); } if (rhs instanceof ASTProperty || rhs instanceof ASTVarRef) { rhsString = nameOfPropertyOrVariable(rhs); if (rhsString.equals("nil")) { return new EOKeyValueQualifier( lhsString, selector, NSKeyValueCoding.NullValue); } else if (rhsString.startsWith("#")) { String varName = rhsString.substring(1); return new EOKeyValueQualifier( lhsString, selector, new EOQualifierVariable(varName)); } else { return new EOKeyComparisonQualifier( lhsString, selector, rhsString); } } else if (rhs instanceof ASTChain) { rhsString = stringForChain( (ASTChain)rhs, false, rhs.jjtGetNumChildren()); if (rhsString.startsWith("$")) { String varName = rhsString.substring(1); return new EOKeyValueQualifier( lhsString, selector, new EOQualifierVariable(varName)); } else { return new EOKeyComparisonQualifier( lhsString, selector, rhsString); } } else if (rhs instanceof ASTConst) { rhsObject = ((ASTConst)rhs).getValue(); return new EOKeyValueQualifier(lhsString, selector, rhsObject); } else { generateException( "Only properties, variable references, and constant values " + "are allowed on the right-hand side of an expression in a " + "qualifier"); return null; } } // ---------------------------------------------------------- private static EOQualifier processChain(ASTChain chain) { // A chain is a potential standalone method call, so it could be // isLike or isLikeNoCase. int methodIndex = 0; while (methodIndex < chain.jjtGetNumChildren() && !(chain.jjtGetChild(methodIndex) instanceof ASTMethod)) { methodIndex++; } if (methodIndex == chain.jjtGetNumChildren()) { generateException( "A standalone keypath cannot be used in a qualifier " + "expression"); return null; } ASTMethod method = (ASTMethod)chain.jjtGetChild(methodIndex); String methodName = method.getMethodName(); if (!methodName.equals("isLike") && !methodName.equals("isLikeNoCase")) { generateException("The only methods supported in a qualifier " + "expression are 'isLike' and 'isLikeNoCase'"); return null; } NSSelector<?> selector = methodName.equals("isLike") ? EOQualifier.QualifierOperatorLike : EOQualifier.QualifierOperatorCaseInsensitiveLike; if (methodIndex == 0) { generateException("'" + methodName + "' needs a property " + "reference preceding it"); return null; } String lhsString = stringForChain(chain, true, methodIndex); if (method.jjtGetNumChildren() != 1) { generateException("'" + methodName + "' takes 1 argument"); return null; } Node rhs = method.jjtGetChild(0); if (rhs instanceof ASTProperty || rhs instanceof ASTVarRef) { String rhsString = nameOfPropertyOrVariable(rhs); if (rhsString.equals("nil")) { return new EOKeyValueQualifier( lhsString, selector, NSKeyValueCoding.NullValue); } else if (rhsString.startsWith("$")) { String varName = rhsString.substring(1); return new EOKeyValueQualifier( lhsString, selector, new EOQualifierVariable(varName)); } else { return new EOKeyComparisonQualifier( lhsString, selector, rhsString); } } else if (rhs instanceof ASTChain) { String rhsString = stringForChain( (ASTChain)rhs, false, rhs.jjtGetNumChildren()); return new EOKeyComparisonQualifier(lhsString, selector, rhsString); } else if (rhs instanceof ASTConst) { Object rhsObject = ((ASTConst)rhs).getValue().toString(); return new EOKeyValueQualifier(lhsString, selector, rhsObject); } else { generateException( "Only properties, variable references, and constant values " + "are allowed on the right-hand side of an expression in a " + "qualifier"); return null; } } // ---------------------------------------------------------- private static String stringForChain( ASTChain chain, boolean allowPropertiesOnly, int end) { StringBuilder str = new StringBuilder(); for (int i = 0; i < end; i++) { Node node = chain.jjtGetChild(i); if (i > 0) { str.append('.'); } if (allowPropertiesOnly && !(node instanceof ASTProperty)) { generateException("Only properties are allowed on the " + "left-hand side of an expression in a qualifier"); } str.append(nameOfPropertyOrVariable(node)); } return str.toString(); } // ---------------------------------------------------------- private static String nameOfPropertyOrVariable(Node node) { if (node instanceof ASTProperty) { ASTProperty property = (ASTProperty)node; if (!property.isIndexedAccess()) { return property.toString(); } else { generateException("Indexed properties cannot be used in an " + "entity qualifier"); return null; } } else // if(node instanceof ASTVarRef) { // First character of the ASTVarRef is always '#' return "$" + node.toString().substring(1); } } // ---------------------------------------------------------- public static String computeDependenciesFromOgnlAST( Node node, NSMutableArray<String> dependentBindings) { int startChild = 0; if (node instanceof ASTChain) { Node firstChild = node.jjtGetChild(0); Node secondChild = node.jjtGetChild(1); String firstName = null; if (firstChild instanceof ASTProperty) { ASTProperty property = (ASTProperty)firstChild; if (!property.isIndexedAccess()) { firstName = property.toString(); } } else if (firstChild instanceof ASTVarRef) { firstName = firstChild.toString().substring(1); } if ("selected".equals(firstName)) { if (secondChild instanceof ASTProperty) { ASTProperty property = (ASTProperty)secondChild; if (!property.isIndexedAccess()) { dependentBindings.addObject(property.toString()); startChild = 2; } else { return "The 'selected' property/variable may only be " + "followed by a non-indexed property name: "; } } else { return "The 'selected' property/variable may only be " + "followed by a non-indexed property name: "; } } } for (int i = startChild; i < node.jjtGetNumChildren(); i++) { String msg = computeDependenciesFromOgnlAST( node.jjtGetChild(i), dependentBindings); if (msg != null) { return msg; } } return null; } // ---------------------------------------------------------- private static void generateException(String msg) { throw new IllegalArgumentException(msg); } //~ Instance/static variables ............................................. private static NSMutableDictionary<Class<?>, NodeHandler> astHandlers; // ---------------------------------------------------------- private interface NodeHandler { EOQualifier handle(Node node); } // ---------------------------------------------------------- private static NodeHandler andHandler = new NodeHandler() { public EOQualifier handle(Node node) { NSMutableArray<EOQualifier> children = new NSMutableArray<EOQualifier>(); for (int i = 0; i < node.jjtGetNumChildren(); i++) { children.addObject(qualifierFromOgnlAST(node.jjtGetChild(i))); } return new EOAndQualifier(children); } }; // ---------------------------------------------------------- private static NodeHandler orHandler = new NodeHandler() { public EOQualifier handle(Node node) { NSMutableArray<EOQualifier> children = new NSMutableArray<EOQualifier>(); for (int i = 0; i < node.jjtGetNumChildren(); i++) { children.addObject(qualifierFromOgnlAST(node.jjtGetChild(i))); } return new EOOrQualifier(children); } }; // ---------------------------------------------------------- private static NodeHandler notHandler = new NodeHandler() { public EOQualifier handle(Node node) { return new EONotQualifier( qualifierFromOgnlAST(node.jjtGetChild(0))); } }; // ---------------------------------------------------------- private static NodeHandler eqHandler = new NodeHandler() { public EOQualifier handle(Node node) { return processRelational(EOQualifier.QualifierOperatorEqual, node.jjtGetChild(0), node.jjtGetChild(1)); } }; // ---------------------------------------------------------- private static NodeHandler greaterHandler = new NodeHandler() { public EOQualifier handle(Node node) { return processRelational(EOQualifier.QualifierOperatorGreaterThan, node.jjtGetChild(0), node.jjtGetChild(1)); } }; // ---------------------------------------------------------- private static NodeHandler greaterEqHandler = new NodeHandler() { public EOQualifier handle(Node node) { return processRelational( EOQualifier.QualifierOperatorGreaterThanOrEqualTo, node.jjtGetChild(0), node.jjtGetChild(1)); } }; // ---------------------------------------------------------- private static NodeHandler inHandler = new NodeHandler() { public EOQualifier handle(Node node) { return processRelational(EOQualifier.QualifierOperatorContains, node.jjtGetChild(1), node.jjtGetChild(0)); } }; // ---------------------------------------------------------- private static NodeHandler lessHandler = new NodeHandler() { public EOQualifier handle(Node node) { return processRelational(EOQualifier.QualifierOperatorLessThan, node.jjtGetChild(0), node.jjtGetChild(1)); } }; // ---------------------------------------------------------- private static NodeHandler lessEqHandler = new NodeHandler() { public EOQualifier handle(Node node) { return processRelational( EOQualifier.QualifierOperatorLessThanOrEqualTo, node.jjtGetChild(0), node.jjtGetChild(1)); } }; // ---------------------------------------------------------- private static NodeHandler notEqHandler = new NodeHandler() { public EOQualifier handle(Node node) { return processRelational(EOQualifier.QualifierOperatorNotEqual, node.jjtGetChild(0), node.jjtGetChild(1)); } }; // ---------------------------------------------------------- private static NodeHandler notInHandler = new NodeHandler() { public EOQualifier handle(Node node) { return new EONotQualifier( processRelational(EOQualifier.QualifierOperatorContains, node.jjtGetChild(1), node.jjtGetChild(0))); } }; // ---------------------------------------------------------- private static NodeHandler chainHandler = new NodeHandler() { public EOQualifier handle(Node node) { return processChain((ASTChain)node); } }; // ---------------------------------------------------------- // static initializer for astHandlers static { astHandlers = new NSMutableDictionary<Class<?>, NodeHandler>(); astHandlers.setObjectForKey(andHandler, ASTAnd.class); astHandlers.setObjectForKey(orHandler, ASTOr.class); astHandlers.setObjectForKey(notHandler, ASTNot.class); astHandlers.setObjectForKey(eqHandler, ASTEq.class); astHandlers.setObjectForKey(greaterHandler, ASTGreater.class); astHandlers.setObjectForKey(greaterEqHandler, ASTGreaterEq.class); astHandlers.setObjectForKey(inHandler, ASTIn.class); astHandlers.setObjectForKey(lessHandler, ASTLess.class); astHandlers.setObjectForKey(lessEqHandler, ASTLessEq.class); astHandlers.setObjectForKey(notEqHandler, ASTNotEq.class); astHandlers.setObjectForKey(notInHandler, ASTNotIn.class); astHandlers.setObjectForKey(chainHandler, ASTChain.class); } }