webwork/src/main/webwork/util ComplexException.java,NONE,1.1 SimpleTest.java,NONE,1.1
[email protected] Sun, 16 Nov 2003 15:40:33 -0800
| Newsgroups | gmane.comp.java.open-symphony.cvs |
|---|---|
| Message-ID | <[email protected]> |
Update of /cvsroot/opensymphony/webwork/src/main/webwork/util In directory sc8-pr-cvs1:/tmp/cvs-serv2823 Added Files: ComplexException.java SimpleTest.java Log Message: New class that is used to evaluate if expressions. It is much faster than the javacc generated parser. --- NEW FILE: ComplexException.java --- /* * WebWork, Web Application Framework * * Distributable under Apache license. * See terms of license at opensource.org */ package webwork.util; /** * This exception is thrown from the constructor of the * SimpleTest class when it notices that an expression * is too complex for it to handle. * * @see webwork.util.SimpleTest * @author Dick Zetterberg ([email protected]) * @version $Revision: 1.1 $ */ public class ComplexException extends java.lang.Exception { public ComplexException() { } public ComplexException(String msg) { super(msg); } } --- NEW FILE: SimpleTest.java --- /* * WebWork, Web Application Framework * * Distributable under Apache license. * See terms of license at opensource.org */ package webwork.util; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * This class is used to handle test expressions in an efficient way. * It is more than 5 times faster than the javacc generated Parser.java * Instances of the class are retrieved through the static method * getSimpleTest. If the method returns null it means the expression was * too complex for this class too handle. Complex expressions are * those using parenthesis to control the order of evaluation. * Those expressions will instead be handled by the old * javacc Parser.java class. * * @see webwork.util.ValueStack * @see webwork.util.ComplexException * @see webwork.expr.Parser * @author Dick Zetterberg ([email protected]) * @version $Revision: 1.1 $ */ public class SimpleTest { private static Log log = LogFactory.getLog(SimpleTest.class); private static Map simpleTestMap = new ConcurrentReaderHashMap(); /** * This marker object is stored in the simpleTestMap to show * that the expression has been found to be too complex for * this class to handle */ private static final SimpleTest COMPLEX_TEST = new SimpleTest(); private static final Object NO_CONSTANT = new Object(); // The possible values for the operator protected static final int NONE = 0; protected static final int AND = 1; protected static final int OR = 2; // The condition characters used protected static final char AND_CHAR = '&'; protected static final char OR_CHAR = '|'; protected static final char QUOTE_CHAR = '\''; protected static final char EQUAL_CHAR = '='; protected static final char NOT_CHAR = '!'; protected static final char GREATER_CHAR = '>'; protected static final char LESS_CHAR = '<'; // The possible values for the condition protected static final int CHECK_NULL = 0; protected static final int EQUAL = 1; protected static final int NOT_EQUAL = 2; protected static final int GREATER = 3; protected static final int GREATER_EQUAL = 4; protected static final int LESS = 5; protected static final int LESS_EQUAL = 6; protected int condition; protected boolean neg1; protected boolean neg2; protected Query q1; protected Query q2; protected String exp1; protected String exp2; protected Object value1; protected Object value2; protected SimpleTest nextTest; protected int operator = NONE; protected int sameVal1; protected int sameVal2; /** * Get a SimpleTest object for the expression. * If the expression is too complex then null is returned * If the expression has been handled before then a cached SimpleTest * object is returned. Otherwise a new one is created */ public static SimpleTest getSimpleTest(String expression) { SimpleTest test = (SimpleTest) simpleTestMap.get(expression); if (test != null) { if (test != COMPLEX_TEST) return test; else return null; } // The expression has not been handled before, try creating a SimpleTest now try { // log.debug("Creating SimpleTest for expression: " + expression); test = new SimpleTest(expression); // Put the expression in the map simpleTestMap.put(expression, test); return test; } catch(ComplexException e) { // log.debug("SimpleTest not created because expression is too complex: " + expression, e); // Put the expression in the map so we do not try to handle it again simpleTestMap.put(expression, COMPLEX_TEST); // Return null to show that no SimpleTest could be created return null; } // Consider catching throwable to give better error message /* catch(Throwable t) { log.error("SimpleTest not created because unexpected throwable, expression: " + expression, t); // Put the expression in the map so we do not try to handle it again simpleTestMap.put(expression, COMPLEX_TEST); // Return null to show that no SimpleTest could be created return null; } */ } public SimpleTest() {} /** * Create a SimpleTest for the expression exp, and make use of the values * found in the previously evaluated expression prevText if possible */ public SimpleTest(SimpleTest prevTest, String exp) throws ComplexException { // First evaluate the expression exp this(exp); // If the first expression is not a constant then // check if it already exists in the previous test if (value1 == NO_CONSTANT) { if (exp1.equals(prevTest.exp1)) sameVal1 = 1; else if (exp1.equals(prevTest.exp2)) sameVal1 = 2; } // If we have a second non constant expression check if it exists in the previous test if (exp2 != null && value2 == NO_CONSTANT) { if (exp2.equals(prevTest.exp1)) sameVal2 = 1; else if (exp2.equals(prevTest.exp2)) sameVal2 = 2; } } public SimpleTest(String exp) throws ComplexException { exp = exp.trim(); String nextExp = null; // Get the index of any && or || operators // The method also check for conditions like == and != and // if found, sets the exp1 and exp2 variables int opIndex = checkOperator(exp); // If there is an operator it means that the expression consists of // several expressions. Get the index of the next one if (operator != NONE) { nextExp = exp.substring(opIndex+2).trim(); exp = exp.substring(0, opIndex).trim(); } if (condition == CHECK_NULL) { // The expression is only to check for null value if (exp.charAt(0)=='!') { neg1 = true; exp1 = exp.substring(1).trim(); } else exp1 = exp; // Check that expression does not start with ( if (exp1.charAt(0) == '(') throw new ComplexException("Expression too complex because it starts with ( : " + exp); value1 = getConstant(exp1); } else { // The expression consists of 2 parts with a condition: ==, !=, <, > etc // Check that the expressions do not start with negation, in that case it is an // illegal expression if (exp1.charAt(0)=='!' || exp2.charAt(0)=='!') throw new IllegalArgumentException("Invalid expression, misplaced \'!\' : " + exp); // Check that expression does not start with ( if (exp1.charAt(0) == '(' || exp2.charAt(0) == '(') throw new ComplexException("Expression too complex because it starts with ( : " + exp); value1 = getConstant(exp1); value2 = getConstant(exp2); } if (value1 == NO_CONSTANT) q1 = Query.getQuery(exp1); if (condition != CHECK_NULL && value2 == NO_CONSTANT) q2 = Query.getQuery(exp2); if (nextExp != null) nextTest = new SimpleTest(this, nextExp); } /** * Look through the expression to find any operators like && or ||, <, > etc. * If found then the operator variable is updated. */ protected int checkOperator(String str) throws ComplexException { int length = str.length(); int i = 0; int exp2Index = -1; while(i < length) { char c = str.charAt(i++); // Check if the current characters is the && expression if (c == AND_CHAR && i < length && str.charAt(i) == AND_CHAR) { operator = AND; // If exp2Index is set we must truncate the exp2 expression now if (exp2Index >= 0) { exp2 = exp2.substring(0, i-exp2Index-1).trim(); } return i-1; } // Check if the current characters is the || expression if (c == OR_CHAR && i < length && str.charAt(i) == OR_CHAR) { operator = OR; if (exp2Index >= 0) { exp2 = exp2.substring(0, i-exp2Index-1).trim(); } return i-1; } // Check if the current character starts a quote if (c == QUOTE_CHAR && i < length) { // Find the next quote character and skip forward to it i = str.indexOf(QUOTE_CHAR, i); // If no other quote found do not handle it if (i < 0) throw new IllegalArgumentException("Invalid expression, no matching quote found: " + str); // Set i to the next character i++; continue; } // Perhaps add check if condition is already set and in that case throw exception if (c == EQUAL_CHAR && i < length && str.charAt(i) == EQUAL_CHAR) { condition = EQUAL; // Set exp1 to the first part of the expression and exp2 to the second exp1 = str.substring(0, i-1).trim(); // Do not trim exp2 now, because we might have to truncate it later exp2 = str.substring(++i); exp2Index = i; continue; } if (c == NOT_CHAR && i < length && str.charAt(i) == EQUAL_CHAR) { condition = NOT_EQUAL; // Set exp1 to the first part of the expression and exp2 to the second exp1 = str.substring(0, i-1).trim(); // Do not trim exp2 now, because we might have to truncate it later exp2 = str.substring(++i); exp2Index = i; continue; } if (c == LESS_CHAR && i < length && str.charAt(i) == EQUAL_CHAR) { condition = LESS_EQUAL; // Set exp1 to the first part of the expression and exp2 to the second exp1 = str.substring(0, i-1).trim(); // Do not trim exp2 now, because we might have to truncate it later exp2 = str.substring(++i); exp2Index = i; continue; } if (c == LESS_CHAR) { condition = LESS; // Set exp1 to the first part of the expression and exp2 to the second exp1 = str.substring(0, i-1).trim(); // Do not trim exp2 now, because we might have to truncate it later exp2 = str.substring(i); exp2Index = i; continue; } if (c == GREATER_CHAR && i < length && str.charAt(i) == EQUAL_CHAR) { condition = GREATER_EQUAL; // Set exp1 to the first part of the expression and exp2 to the second exp1 = str.substring(0, i-1).trim(); // Do not trim exp2 now, because we might have to truncate it later exp2 = str.substring(++i); exp2Index = i; continue; } if (c == GREATER_CHAR) { condition = GREATER; // Set exp1 to the first part of the expression and exp2 to the second exp1 = str.substring(0, i-1).trim(); // Do not trim exp2 now, because we might have to truncate it later exp2 = str.substring(i); exp2Index = i; continue; } } if (exp2 != null) exp2 = exp2.trim(); return -1; } /** * This method checks if the expression is a constant value. * If it is not a constant then the method returns the object NO_CONSTANT * If it is a constant then it returns the constant value which may be null */ protected Object getConstant(String exp) { Query q = Query.getQuery(exp); QuerySegment[] segments = q.getSegments(); QuerySegment segment = segments[0]; switch (segment.getType()) { case QuerySegment.STRING: return segment.getId(); // the integer is the first value and only value in the segment case QuerySegment.NUMBER: return segment.getValues().get(0); // the reserved keyword "true" case QuerySegment.TRUE: return Boolean.TRUE; // the reserved keyword "false" case QuerySegment.FALSE: return Boolean.FALSE; // the reserved keyword "null" case QuerySegment.NULL: return null; default: return NO_CONSTANT; } } public boolean test(ValueStack stack) { return test(stack, null, null); } /** * The values in prevVal1 and prevVal2 will always be sent in. They can be null * because they are not used or because the value found was null. This does not matter. * The expression after this one will only care about the value IF the same expression * existed in this test as in the next test. */ protected boolean test(ValueStack stack, Object prevVal1, Object prevVal2) { Object val1; Object val2 = null; boolean result = false; // Check if the first expression is not a constant if (value1 == NO_CONSTANT) { // Check if it is the same value as one already evaluated. Otherwise look // it up on the value stack if (sameVal1 == 0) val1 = stack.findValue(q1); else if (sameVal1 == 1) val1 = prevVal1; else val1 = prevVal2; } // The value is a constant so just assign it else { val1 = value1; } if (condition == CHECK_NULL) { // No equals expression. Just check if the value is null or not result = (val1 == null ? false : true); if (neg1) result = !result; } // The expression contains a condition like == or != else { if (value2 == NO_CONSTANT) { if (sameVal2 == 0) val2 = stack.findValue(q2); else if (sameVal2 == 1) val2 = prevVal1; else val2 = prevVal2; } else val2 = value2; int comparison = -1; // less than // We resolve nulls by hand if ( val1 == null || val2 == null ) { if ( val1 == null && val2 == null ) // equal to { comparison = 0; // equal to } else if (val2 == null) // greater than comparison = 1; } else if ( val1 instanceof Number && val2 instanceof Number ) { double number1 = ((Number)val1).doubleValue(); double number2 = ((Number)val2).doubleValue(); if (number1 > number2) comparison = 1; // greater than else if ( number1 == number2 ) { long longBits1 = Double.doubleToLongBits(number1); long longBits2 = Double.doubleToLongBits(number2); if ( longBits1 > longBits2 ) comparison = 1; else if ( longBits1 == longBits2 ) comparison = 0; } } // If operands are of the same type, do a direct comparison else if ( val1.getClass().equals(val2.getClass()) && (val1 instanceof Comparable)) { comparison = ((Comparable)val1).compareTo((Comparable)val2); } else { // If the operands aren't the same type or aren't comparible, then // convert them to strings and do a comparison // Mo: I think that this could lead to some difficult to predict or unpredictable behavior comparison = ((Comparable)val1.toString()).compareTo((Comparable)val2.toString()); } // Now call resolve to determine the resulting value result = resolve(comparison, condition); } switch(operator) { case NONE: return result; case AND: if (result) return nextTest.test(stack, val1, val2); else return false; case OR: if (result) return true; else return nextTest.test(stack, val1, val2); default: return false; } } /** * determine true or false by comparing the comparison result * with the operator. * * @param comp the comparison result * @param operatr the operator * @return the boolean result * */ protected boolean resolve(int comp, int condition) { //log.debug( "comp: " + comp + ", operatr: " + operatr); if ( comp == 0 ) { switch (condition) { case EQUAL: case GREATER_EQUAL: case LESS_EQUAL: return true; default: return false; } } if ( comp > 0 ) { switch (condition) { case GREATER: case GREATER_EQUAL: case NOT_EQUAL: return true; default: return false; } } switch (condition) { case LESS: case LESS_EQUAL: case NOT_EQUAL: return true; } return false; } } ------------------------------------------------------- This SF. Net email is sponsored by: GoToMyPC GoToMyPC is the fast, easy and secure way to access your computer from any Web browser or wireless device. Click here to Try it Free! https://www.gotomypc.com/tr/OSDN/AW/Q4_2003/t/g22lp?Target=mm/g22lp.tmpl