Package org.olat.course.condition.interpreter

Source Code of org.olat.course.condition.interpreter.ConditionInterpreter

/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <p>
*/

package org.olat.course.condition.interpreter;

import java.text.ParseException;

import org.olat.core.gui.translator.PackageTranslator;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.OLATRuntimeException;
import org.olat.core.util.Util;
import org.olat.course.condition.Condition;
import org.olat.course.condition.interpreter.score.GetPassedFunction;
import org.olat.course.condition.interpreter.score.GetPassedWithCourseIdFunction;
import org.olat.course.condition.interpreter.score.GetScoreFunction;
import org.olat.course.condition.interpreter.score.GetScoreWithCourseIdFunction;
import org.olat.course.editor.CourseEditorEnv;
import org.olat.course.run.userview.UserCourseEnvironment;

import com.neemsoft.jmep.Environment;
import com.neemsoft.jmep.Expression;
import com.neemsoft.jmep.XExpression;
import com.neemsoft.jmep.XIllegalOperation;
import com.neemsoft.jmep.XIllegalStatus;
import com.neemsoft.jmep.XUndefinedFunction;
import com.neemsoft.jmep.XUndefinedUnit;
import com.neemsoft.jmep.XUndefinedVariable;

/**
* Initial Date:  Jan 27, 2004
* @author gnaegi
* Comment: 
*/
public class ConditionInterpreter {
  protected static final String PACKAGE = Util.getPackageName(ConditionInterpreter.class);
  /** static Integer(1) object */
  public static final Integer INT_TRUE = new Integer(1);
  /** static Integer(0) object */
  public static final Integer INT_FALSE = new Integer(0);
  protected Environment env;
  protected PackageTranslator translator = null;
  protected UserCourseEnvironment uce;

  protected ConditionInterpreter() {
   
  }

  /**
   * ConditionInterpreter interpretes course conditions.
   *
   * @param userCourseEnv
   */
  public ConditionInterpreter(UserCourseEnvironment userCourseEnv) {
    uce = userCourseEnv;
    //
    CourseEditorEnv cev = uce.getCourseEditorEnv();
    if (cev != null) {
      translator = new PackageTranslator(PACKAGE, cev.getEditorEnvLocale());
    }

    env = new Environment();

    // constants: add for user convenience
    env.addConstant("true", 1);
    env.addConstant("false", 0);

    // variables
    env.addVariable(NowVariable.name, new NowVariable(userCourseEnv));

    // functions
    env.addFunction(DateFunction.name, new DateFunction(userCourseEnv));
    env.addFunction("inGroup", new InLearningGroupFunction(userCourseEnv, "inGroup")); // legacy
    env.addFunction("inLearningGroup", new InLearningGroupFunction(userCourseEnv, "inLearningGroup"));
    env.addFunction(InRightGroupFunction.name, new InRightGroupFunction(userCourseEnv));
    env.addFunction(InLearningAreaFunction.name, new InLearningAreaFunction(userCourseEnv));
    env.addFunction(IsUserFunction.name, new IsUserFunction(userCourseEnv));
    env.addFunction(IsGuestFunction.name, new IsGuestFunction(userCourseEnv));
    env.addFunction(IsGlobalAuthorFunction.name, new IsGlobalAuthorFunction(userCourseEnv));
    EvalAttributeFunction eaf;
    eaf = new EvalAttributeFunction(userCourseEnv, EvalAttributeFunction.FUNCTION_TYPE_HAS_ATTRIBUTE);
    env.addFunction(eaf.name, eaf);
    eaf = new EvalAttributeFunction(userCourseEnv, EvalAttributeFunction.FUNCTION_TYPE_IS_IN_ATTRIBUTE);
    env.addFunction(eaf.name, eaf);
    eaf = new EvalAttributeFunction(userCourseEnv, EvalAttributeFunction.FUNCTION_TYPE_HAS_NOT_ATTRIBUTE);
    env.addFunction(eaf.name, eaf);
    eaf = new EvalAttributeFunction(userCourseEnv, EvalAttributeFunction.FUNCTION_TYPE_IS_NOT_IN_ATTRIBUTE);
    env.addFunction(eaf.name, eaf);
    eaf = new EvalAttributeFunction(userCourseEnv, EvalAttributeFunction.FUNCTION_TYPE_ATTRIBUTE_ENDS_WITH);
    env.addFunction(eaf.name, eaf);
    eaf = new EvalAttributeFunction(userCourseEnv, EvalAttributeFunction.FUNCTION_TYPE_ATTRIBUTE_STARTS_WITH);
    env.addFunction(eaf.name, eaf);
    env.addFunction(GetUserPropertyFunction.name, new GetUserPropertyFunction(userCourseEnv));
    env.addFunction(HasLanguageFunction.name, new HasLanguageFunction(userCourseEnv));
    env.addFunction(InInstitutionFunction.name, new InInstitutionFunction(userCourseEnv));
    env.addFunction(IsCourseCoachFunction.name, new IsCourseCoachFunction(userCourseEnv));
    env.addFunction(IsCourseAdministratorFunction.name, new IsCourseAdministratorFunction(userCourseEnv));

    env.addFunction(GetAttemptsFunction.name, new GetAttemptsFunction(userCourseEnv));

    // enrollment building block specific functions
    env.addFunction(GetInitialEnrollmentDateFunction.name, new GetInitialEnrollmentDateFunction(userCourseEnv));
    env.addFunction(GetRecentEnrollmentDateFunction.name, new GetRecentEnrollmentDateFunction(userCourseEnv));

    // functions to calculate score
    env.addFunction(GetPassedFunction.name, new GetPassedFunction(userCourseEnv));
    env.addFunction(GetScoreFunction.name, new GetScoreFunction(userCourseEnv));
    env.addFunction(GetPassedWithCourseIdFunction.name, new GetPassedWithCourseIdFunction(userCourseEnv));
    env.addFunction(GetScoreWithCourseIdFunction.name, new GetScoreWithCourseIdFunction(userCourseEnv));

    // units
    env.addUnit("min", new MinuteUnit());
    env.addUnit("h", new HourUnit());
    env.addUnit("d", new DayUnit());
    env.addUnit("w", new WeekUnit());
    env.addUnit("m", new MonthUnit());
  }

  /**
   * @param expression
   * @return null if no error, else the error msg
   */
  public ConditionErrorMessage[] syntaxTestExpression(ConditionExpression condExpr) {
    try {
      /*
       * the functions, units, variables from the condition expression access
       * the active condition expression through the
       * CourseEditorEnv.getActiveConditionExpression(). Whereas the active
       * condition expression is determined by calling
       * CourseEditorEnv.validateConditionExpression(). Hence
       * syntaxTestExpression should never be called directly or in a non editor
       * environment.
       */
      String conditionString = condExpr.getExptressionString();
      Expression exp = new Expression(conditionString, env);
      exp.evaluate();
      Exception[] condExceptions = condExpr.getExceptions();
      ConditionErrorMessage[] cems = null;
      if (condExceptions != null && condExceptions.length > 0) {
        // create an error message from the first error in the expression
        cems=new ConditionErrorMessage[condExceptions.length];
        for (int i = 0; i < condExceptions.length; i++) {
          cems[i]=handleExpressionExceptions(condExceptions[i]);
        }
        return cems;
      }// else return null, at the end of the method!
    } catch (Exception e) {
      // catches every non ArgumentParseException
      return new ConditionErrorMessage[] {handleExpressionExceptions(e)};
    }
    // if no exception was found, thus no condition error message to report.
    return null;
  }

  /**
   * Check an expression on syntactical errors.
   *
   * @param expression
   * @return Null if syntactically correct, error message otherwise.
   * @deprecated TODO: remove as it is no longer referenced, except test?
   */
  public ConditionErrorMessage syntaxTestCalculation(String expression) {
    try {
      Expression exp = new Expression(expression, env);
      exp.evaluate();
    } catch (Exception e) {
      return handleExpressionExceptions(e);
    }
    return null;
  }

  /**
   * evaluation of expression may throw exceptions, the handling of these is
   * done here.
   *
   * @param e
   * @return
   */
  private ConditionErrorMessage handleExpressionExceptions(Exception e) {
    String msg = "";
    String[] params = null;
    String solutionMsg = "";
    try {
      throw e;
      //TODO:pb:b do no rethrow, but test for instanceof
    } catch (XIllegalOperation xe) {
      msg = "error.illegal.operation.at";
      params = new String[] { Integer.toString(xe.getPosition()) };
    } catch (XUndefinedFunction xe) {
      msg = "error.undefined.function.at";
      params = new String[] { Integer.toString(xe.getPosition()) };
    } catch (XUndefinedUnit xe) {
      msg = "error.undefined.unit.at";
      params = new String[] { Integer.toString(xe.getPosition()) };
    } catch (XUndefinedVariable xe) {
      msg = "error.undefined.variable.at";
      params = new String[] { Integer.toString(xe.getPosition()) };
      solutionMsg = "solution.error.undefvariable";
    } catch (XIllegalStatus xe) {
      // illegal status in the condition interpreter, this must not happen!
      throw new OLATRuntimeException(xe.getMessage(), xe);
    } catch (XExpression xe) {
      msg = "error.inexpression.at";
      params = new String[] { Integer.toString(xe.getPosition()) };
      solutionMsg = "solution.error.inexpression";
    } catch (ArgumentParseException apex) {
      // the function, units etc, are responsible to provide reasonable error
      // messages (translation keys)
      msg = apex.getWhatsWrong();
      params = new String[] { apex.getFunctionName(), apex.getWrongArgs() };
      solutionMsg = apex.getSolutionProposal();
    } catch (Exception ex) {
      // this must not happen!
      throw new OLATRuntimeException(ex.getMessage(), ex);
    }
    return new ConditionErrorMessage(msg, solutionMsg, params);
  }

  /**
   * Evaluates a condition.
   *
   * @param c
   * @return True if evaluation successfull.
   */
  public boolean evaluateCondition(Condition c) {
    return evaluateCondition(c.getConditionExpression());
  }

  /**
   * Evaluates a condition.
   *
   * @param condition
   * @return True if evaluation successfull.
   */
  public boolean evaluateCondition(String condition) {
    boolean ok = false;
    try {
      // TODO: lookup in Map: key = c -> cached Expression
      // if not null then: ok = evaluateCondition(Expression cachedExpression)
      ok = doEvaluateCondition(condition);
    } catch (ParseException e) {
      throw new AssertException("parse error in:: " + condition);
    }
    return ok;
  }

  /**
   * Evaluates a calculation.
   *
   * @param calculation
   * @return True if evaluation successfull.
   */
  public float evaluateCalculation(String calculation) {
    float res = -100000000000000000000000000000000f;
    try {
      res = doEvaluateCalculation(calculation);
    } catch (ParseException e) {
      throw new AssertException("parse or execute error in calculation:" + calculation);
    }
    return res;
  }

  private float doEvaluateCalculation(String calculation) throws ParseException {
    try {
      Expression exp = new Expression(calculation, env);
      Object result = exp.evaluate();
      if (result instanceof Double) {
        return ((Double) result).floatValue();
      } else if (result instanceof Integer) {
        return ((Integer) result).floatValue();
      } else throw new ArgumentParseException("Parse exception: expected Double or Integer, but got:"
          + (result == null ? "no object(null)" : result.getClass().getName()));
    } catch (XExpression xe) {
      throw new ParseException("Parse exception for calculation: " + calculation + ". " + xe.getMessage(), xe.getPosition());
    }
  }

  /**
   * Evaluate a condition using the jmep expression parser
   *
   * @param condition The condition as a java script text
   * @return true if condition matches, false otherwhise
   */
  private boolean doEvaluateCondition(String condition) throws ParseException {
    try {
      Expression exp = new Expression(condition, env);
      Object result = exp.evaluate();
      if (result instanceof Double) {
        return (((Double) result).doubleValue() == 1.0) ? true : false;
      } else if (result instanceof Integer) {
        return (((Integer) result).intValue() == 1) ? true : false;
      } else return false;
    } catch (XExpression xe) {
      throw new ParseException("Parse exception for condition: " + condition + ". " + xe.getMessage(), xe.getPosition());
    }
  }

  private boolean evaluateCondition(Expression exp) throws ParseException {
    try {
      Object result = exp.evaluate();
      if (result instanceof Double) {
        return (((Double) result).doubleValue() == 1.0) ? true : false;
      } else if (result instanceof Integer) {
        return (((Integer) result).intValue() == 1) ? true : false;
      } else return false;
    } catch (XExpression xe) {
      throw new ParseException("Parse exception" + xe.getMessage(), xe.getPosition());
    }
  }

  /**
   * Test method for condition interpreter using the dummy lu callback
   *
   * @param args
   */
  public static void main(String[] args) {
    /*
     * --- this method is not working, but left for the docu ---
     */
   
    // CourseEnvironment ce = new CourseEnvironmentImpl(null);
    ConditionInterpreter interpreter = null; //new ConditionInterpreter(new TestUserCourseEnvironmentImpl());// ce);

    long start = System.currentTimeMillis();
    try {
      Expression exp = new Expression("(inGroup(\"blue\")|!inGroup(\"red\")) & now > date(\"09.03.2004 15:00\")", interpreter.env);
      Expression exp2 = new Expression("(9.99999 < 10) & (1000/2 > 200)", interpreter.env);

      for (int i = 0; i < 1000; i++) {
        exp = new Expression("(inGroup(\"blue\")|!inGroup(\"red\")) & now > date(\"09.03.2004 15:00\")", interpreter.env);
        interpreter.evaluateCondition(exp);
        exp2 = new Expression("(9.99999 < 10) & (1000/2 > 200)", interpreter.env);
        interpreter.evaluateCondition(exp2);
      }

    } catch (Exception e) {
      // just a timing test
    }
    long stop = System.currentTimeMillis();
    System.out.println("time:" + ((stop - start)));

    // TODO refine tests with new interpreter

    // number tests
    /*
     * interpreter.printTestCase("is in group red", "inGroup(\"red\")");
     * interpreter.printTestCase("is not in group red", "!inGroup(\"red\")");
     * interpreter.printTestCase("is in group blue", "inGroup(\"blue\")");
     * interpreter.printTestCase("is in group blue or red", "(inGroup(\"blue\")) |
     * (inGroup(\"red\"))"); interpreter.printTestCase("is in group red or blue
     * (no cond eval)", "(inGroup(\"red\")) | (inGroup(\"blue\"))");
     * interpreter.printTestCase("is in group red and blue", "(inGroup(\"red\")) &
     * (inGroup(\"blue\"))"); interpreter.printTestCase("is not in group blue
     * and in group red", "(inGroup(\"blue\")) & (inGroup(\"red\"))");
     */
    interpreter.printTestCase("(inGroup(\"blue\")|inGroup(\"red\")) & now > date(\"09.03.2004 15:00\")");

    interpreter.printTestCase("1 < 10");
    interpreter.printTestCase("(9.99999 < 10) & (1000/2 > 200)");
    interpreter.printTestCase("(90.99999 < 10) | (1000/2 > 200)");
    // interpreter.printTestCase("100/0");
    // interpreter.printTestCase("100/0 < 2"); // hm, interresting
    // interpreter.printTestCase("100/0 > 2");

    // property tests using exposed java object as property callback
    // PropertyManager pm = new PropertyManager();
    // pm.setCourseProperty(new IntProperty("integer-prop", 5));
    /*
     * interpreter.printTestCase("courseProperty(\"integer-prop\") > 4");
     * interpreter.printTestCase("courseProperty(\"integer-prop\") < 4");
     * interpreter.printTestCase("courseProperty(\"integer-prop\") = 5"); //
     * might be confusing to beginners that = must be written as == (will throw
     * exception otherwhise) // property tests with non existing properties
     * interpreter.printTestCase("courseProperty(\"gibts-nöd\") < 4"); // hm...
     * user must understand that a property exists by default and has a default
     * value interpreter.printTestCase("courseProperty(\"gibts-nöd\") > 4");
     * interpreter.printTestCase("courseProperty(\"gibts-nöd\") = 4");
     */
    // Property tests with date properties
    /*
     * pm.setUserProperty(new DateProperty("date-prop", new Date())); // set
     * property to a tesing value
     * interpreter.printTestCase("userProperty(\"date-prop\") < now");
     * interpreter.printTestCase("userProperty(\"date-prop\") = now");
     * interpreter.printTestCase("userProperty(\"date-prop\") > now");
     */
    // Now a test with a fake date (e.g. for runtime simulation-preview-mode)
    interpreter.printTestCase("date(\"01.01.2004 12:00\") < now");

    interpreter.printTestCase("date(\"01.01.2004 12:00\") > now");
    interpreter.printTestCase("date(\"01.01.2004 12:00\") + 3m > now");
    interpreter.printTestCase("now > date(\"09.03.2004 15:00\")");
    interpreter.printTestCase("1 & 1 & 0 | 1");
    //
    // System.out.println("testsyntax ok:" +
    // interpreter.syntaxTestExpression("now > date(\"28.02.2004 15:00\")"));
    // System.out.println("testsyntax nok:" +
    // interpreter.syntaxTestExpression("now > date(\"31.02.2004 15:00\")"));

  }

  /**
   * helper for main method
   *
   * @param testCondition
   */
  private void printTestCase(String testCondition) {
    try {
      System.out.println(testCondition + ":  " + doEvaluateCondition(testCondition) + "\n-----\n");
    } catch (ParseException e) {
      System.out.println(e.toString());
    }
  }

}
TOP

Related Classes of org.olat.course.condition.interpreter.ConditionInterpreter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.