Java tutorial
/******************************************************************************* * Educational Online Test Delivery System Copyright (c) 2014 American * Institutes for Research * * Distributed under the AIR Open Source License, Version 1.0 See accompanying * file AIR-License-1_0.txt or at http://www.smarterapp.org/documents/ * American_Institutes_for_Research_Open_Source_Software_License.pdf ******************************************************************************/ package qtiscoringengine; import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.regex.Pattern; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.Predicate; import org.apache.commons.lang3.StringUtils; import org.jdom2.Element; import AIR.Common.Helpers._Ref; import AIR.Common.Utilities.JavaPrimitiveUtils; import AIR.Common.Utilities.MathUtils; import AIR.Common.Utilities.TDSStringUtils; class ExprNot extends Expression { ExprNot(Element node) { super(node, 1, 1, BaseType.Boolean, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(-1, Cardinality.Single, BaseType.Boolean, true, true)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { // Shiva: What if it is not instance of DEBoolean ? DEBoolean result = (DEBoolean) paramValues.get(0); return new DEBoolean(!result.getBooleanValue()); } }// end class ExprNot class ExprAnd extends Expression { ExprAnd(Element node) { super(node, 1, Integer.MAX_VALUE, BaseType.Boolean, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(-1, Cardinality.Single, BaseType.Boolean, true, true)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { boolean hadNull = false; for (DataElement param : paramValues) { if (param == null) { hadNull = true; continue; } // Shiva: What if it is not instance of DEBoolean ? if (((DEBoolean) param).getBooleanValue() == false) return new DEBoolean(false); } return hadNull ? null : new DEBoolean(true); } }// end class ExprAnd class ExprOr extends Expression { ExprOr(Element node) { super(node, 1, Integer.MAX_VALUE, BaseType.Boolean, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(-1, Cardinality.Single, BaseType.Boolean, true, true)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { for (DataElement param : paramValues) { // Shiva: What if it is not instance of DEBoolean? if (((DEBoolean) param).getBooleanValue()) return new DEBoolean(true); } return new DEBoolean(false); } }// end class ExprOR class ExprAnyN extends Expression { ExprAnyN(Element node) { super(node, 1, Integer.MAX_VALUE, BaseType.Boolean, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(-1, Cardinality.Single, BaseType.Boolean, true, true)); addAttribute(new ExpressionAttributeSpec("min", BaseType.Integer)); addAttribute(new ExpressionAttributeSpec("max", BaseType.Integer)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { int count = 0; int min = ((DEInteger) getAttributeValue("min")).getValue(); int max = ((DEInteger) getAttributeValue("max")).getValue(); for (DataElement param : paramValues) { if (param instanceof DEBoolean) { if (((DEBoolean) param).getBooleanValue()) ++count; } else { // Shiva: What if not instance of DEBoolean ? } } if ((count >= min) && (count <= max)) return new DEBoolean(true); else return new DEBoolean(false); } }// end class ExprAny class ExprMatch extends Expression { ExprMatch(Element node) { super(node, 2, 2, BaseType.Boolean, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.File, BaseType.Identifier, BaseType.Integer, BaseType.String, BaseType.Pair, BaseType.Point, BaseType.URI); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.None, types, true, true)); } @Override public DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if ((paramValues.get(0) == null) || (paramValues.get(1) == null)) return null; return new DEBoolean(paramValues.get(0).equals(paramValues.get(1))); } }// end class ExprMatch class ExprStringMatch extends Expression { ExprStringMatch(Element node) { super(node, 2, 2, BaseType.Boolean, Cardinality.Single); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("caseSensitive", BaseType.Boolean); addAttribute(eac); addParameterConstraint( new ExpressionParameterConstraint(-1, Cardinality.Single, BaseType.String, true, true)); } @Override public DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { DEBoolean cs = (DEBoolean) getAttributeValue("caseSensitive"); boolean caseSensitive = false; if (cs != null) caseSensitive = cs.getBooleanValue(); // handle when paramValues.count == 0? String string1 = ((DEString) paramValues.get(0)).getValue(); // jdc. Sept 6, 2014. This was paramValues[0]. fixed to paramValues[1] String string2 = ((DEString) paramValues.get(1)).getValue(); if (caseSensitive) { string1 = string1.toLowerCase(); string2 = string2.toLowerCase(); } return new DEBoolean(StringUtils.equals(string1, string2)); } }// end class ExprStringMatch class ExprPatternMatch extends Expression { ExprPatternMatch(Element node) { super(node, 1, 1, BaseType.Boolean, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(0, Cardinality.Single, BaseType.String, true, true)); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("pattern", BaseType.String); addAttribute(eac); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { DEString val = (DEString) getAttributeValue("pattern"); Pattern pattern = Pattern.compile(val.getValue()); return new DEBoolean(Pattern.matches(val.getValue(), ((DEString) paramValues.get(0)).getValue())); } }// end class ExprPatternMatch class ExprEqual extends Expression { private enum ToleranceMode { Exact, Absolute, Relative }; private _Ref<ToleranceMode> _mode = new _Ref<>(ToleranceMode.Exact); private double _allowLow = 0.0; private double _allowHi = 0.0; private boolean _includeLowerBound = true; private boolean _includeUpperBound = true; ExprEqual(Element node) { super(node, 2, 2, BaseType.Boolean, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.Integer, BaseType.Float, BaseType.Boolean); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, types, false, false)); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("toleranceMode", Arrays.asList("exact", "absolute", "relative"), true); addAttribute(eac); eac = new ExpressionAttributeSpec("tolerance", BaseType.String, true);// they // stuff // to // floats // into // this // attribute. // Does // not // implement // template // processing--this // is // spec'd // as // floatOrTemplateRef addAttribute(eac); eac = new ExpressionAttributeSpec("includeUpperBound", BaseType.Boolean, true); addAttribute(eac); eac = new ExpressionAttributeSpec("includeLowerBound", BaseType.Boolean, true); addAttribute(eac); } @Override protected boolean validate(ValidationLog log, QTIRubric rubric) { boolean ok = super.validate(log, rubric); DEIdentifier toleranceMode = (DEIdentifier) getAttributeValue("toleranceMode"); DEString tolerance = (DEString) getAttributeValue("tolerance"); DEBoolean includeUpper = (DEBoolean) getAttributeValue("includeUpperBound"); DEBoolean includeLower = (DEBoolean) getAttributeValue("includeLowerBound"); if (toleranceMode == null) { _mode = new _Ref<>(ToleranceMode.Exact); } else { ok = validateTolerance(ok, log, toleranceMode, tolerance, includeLower, includeUpper); } return ok; } private boolean validateTolerance(boolean ok, ValidationLog log, DEIdentifier toleranceMode, DEString tolerance, DEBoolean includeLower, DEBoolean includeUpper) { boolean status = JavaPrimitiveUtils.enumTryParse(ToleranceMode.class, toleranceMode.getValue(), true, _mode); if (status == false) { log.addMessage(_node, TDSStringUtils.format( "Tolerance mode, if provided, must be one of 'exact', 'absolute','relative'. Got {0}", toleranceMode.getValue())); return false; } DEContainer tolerances = null; try { tolerances = (DEContainer) DataElement.createContainer(tolerance.getValue(), BaseType.Float, Cardinality.Ordered); } catch (Exception e) { if (status) { if (_mode.get() != ToleranceMode.Exact) { log.addMessage(_node, "Tolerances must be specified if the tolerance mode is not 'exact' and must be one or two valid floats"); return false; } } } if ((tolerances == null) || (tolerances.getMemberCount() == 0) || (tolerances.getMember(0) == null)) { if (_mode.get() != ToleranceMode.Exact) { log.addMessage(_node, "Tolerances must be specified if the tolerance mode is not 'exact' and must be one or two valid floats"); return false; } } else { _allowLow = ((_DEFloat) tolerances.getMember(0)).getValue().doubleValue(); if (tolerances.getMemberCount() < 2 || tolerances.getMember(1) == null) { _allowHi = _allowLow; } else _allowHi = ((_DEFloat) tolerances.getMember(1)).getValue().doubleValue(); } DEBoolean includeLow = (DEBoolean) getAttributeValue("includeLowerBound"); DEBoolean includeHi = (DEBoolean) getAttributeValue("includeUpperBound"); // jdc. Sept 9, 2014. Removed this validation because defaults are provided // and documented in the spec. // if (_mode != ToleranceMode.Exact) //removed this /* Start Removed */ // if (_mode.get () != ToleranceMode.Exact) { // if ((includeLow == null) || (includeHi == null)) { // log.addMessage (_node, // "When the tolerance mode is not 'exact' you must specify includeLowerBound and includeUpperBound"); // return false; // } // } /* End Removed */ if (includeLow != null) _includeLowerBound = includeLow.getBooleanValue(); if (includeHi != null) _includeUpperBound = includeHi.getBooleanValue(); return ok; } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) throws QTIScoringException { _DEFloat val1 = (_DEFloat) paramValues.get(0); _DEFloat val2 = (_DEFloat) paramValues.get(1); if ((val1 == null) || (val2 == null)) return new DEBoolean(false); switch (_mode.get()) { case Exact: return new DEBoolean(val1.equals(val2)); case Absolute: // In absolute mode the result of the // comparison is true if the value of the // second expression, y is within the following // range defined by the first value, x. // x-t0,x+t1 return new DEBoolean((_includeLowerBound ? val2.gte(val1.getValue().doubleValue() - _allowLow) : val2.gt(val1.getValue().doubleValue() - _allowLow)) && (_includeUpperBound ? val2.lte(val1.getValue().doubleValue() + _allowHi) : val2.lt(val1.getValue().doubleValue() + _allowHi))); case Relative: // In relative mode, t0 and t1 are treated as // percentages and the following range is used // instead. x*(1-t0/100),x*(1+t1/100) return new DEBoolean((_includeLowerBound ? val2.gte(val1.getValue().doubleValue() * (1.0 - _allowLow / 100.0)) : val2.gt(val1.getValue().doubleValue() * (1.0 - _allowLow / 100.0))) && (_includeUpperBound ? val2.lte(val1.getValue().doubleValue() * (1.0 + _allowHi / 100.0)) : val2.lt(val1.getValue().doubleValue() * (1.0 + _allowHi / 100.0)))); default: throw new QTIScoringException("ExprEquals got an unknown tolerance mode"); } } }// end class ExprEqual class ExprEqualRounded extends Expression { // this is a very stupid default. I am going to require the mode attribute private _Ref<DEFloat.RoundingMode> _roundingMode = new _Ref<>(DEFloat.RoundingMode.SignificantFigures); private int _figures = 1; ExprEqualRounded(Element node) { super(node, 2, 2, BaseType.Boolean, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.Integer, BaseType.Float, BaseType.Boolean); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, types, false, false)); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("roundingMode", Arrays.asList("significantFigures", "decimalPlaces")); addAttribute(eac); eac = new ExpressionAttributeSpec("figures", BaseType.Integer);// does not // implement // template // ref addAttribute(eac); } @Override protected boolean validate(ValidationLog log, QTIRubric rubric) { boolean ok = super.validate(log, rubric); DEIdentifier roundingMode = (DEIdentifier) getAttributeValue("roundingMode"); DEInteger figures = (DEInteger) getAttributeValue("figures"); boolean status = JavaPrimitiveUtils.enumTryParse(DEFloat.RoundingMode.class, roundingMode.getValue(), true, _roundingMode); if (status == false) { log.addMessage(_node, "Invalid rounding mode, must be 'significantFigures' or 'decimalPlaces'"); ok = false; } _figures = figures.getValue(); return ok; } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if ((paramValues.get(0) == null) || (paramValues.get(1) == null)) return null; _DEFloat val1 = (_DEFloat) paramValues.get(0); _DEFloat val2 = (_DEFloat) paramValues.get(1); double val1Rounded = val1.round(_roundingMode.get(), _figures); double val2Rounded = val2.round(_roundingMode.get(), _figures); return new DEBoolean(val1Rounded == val2Rounded); } }// end class ExprEqualRounded class ExprInside extends Expression { private Area _area = null; ExprInside(Element node) { super(node, 1, 1, BaseType.Boolean, Cardinality.Single); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, BaseType.Point)); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("shape", Arrays.asList("default", "rect", "circle", "poly")); addAttribute(eac); eac = new ExpressionAttributeSpec("coords", BaseType.String);// this string // contains a // comma-separated // list of // integers. // This is how // the // examples // read, // despite the // spec that // calls this // out as an // XTML // ordered // list. addAttribute(eac); } @Override protected boolean validate(ValidationLog log, QTIRubric rubric) { boolean ok = super.validate(log, rubric); DEIdentifier shapeName = (DEIdentifier) getAttributeValue("shape"); DEString coords = (DEString) getAttributeValue("coords"); if (shapeName == null) { ok = false; log.addMessage(_node, "Expression 'inside' must specify a shape"); } _Ref<Shape> shape = new _Ref<>(); boolean status = JavaPrimitiveUtils.enumTryParse(Shape.class, shapeName.getValue(), true, shape); if (!status) { log.addMessage(_node, TDSStringUtils.format( "expression 'inside' requires a shape to be 'default', 'rect', 'circle' or 'poly', but got {0}", shapeName.getValue())); ok = false; } if (coords == null) { if (shape.get() != Shape.Default) { log.addMessage(_node, "expression 'inside' requires coordinates for its shape, unless the shape is default, covering the entire area."); ok = false; } } if (ok) { try { _area = Area.create(shape.get(), coords.getValue()); } catch (Exception e) { log.addMessage(_node, TDSStringUtils.format("Unable to create area object: {0}", e.getMessage())); ok = false; } } return ok; } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if (paramValues.get(0) == null) // Shiva: this was a return null before and I have now changed it to // return DEBoolean(false). return new DEBoolean(false); return _area.getIsInside((DEPoint) paramValues.get(0)); } }// end class ExprInside class ExprLt extends Expression { ExprLt(Element node) { super(node, 2, 2, BaseType.Boolean, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.Integer, BaseType.Float, BaseType.Boolean); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, types, true, true)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { return new DEBoolean(((_DEFloat) paramValues.get(0)).getValue() .doubleValue() < ((_DEFloat) paramValues.get(1)).getValue().doubleValue()); } }// end class ExprLt class ExprGt extends Expression { ExprGt(Element node) { super(node, 2, 2, BaseType.Boolean, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.Integer, BaseType.Float, BaseType.Boolean); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, types, true, true)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { return new DEBoolean(((_DEFloat) paramValues.get(0)).getValue() .doubleValue() > ((_DEFloat) paramValues.get(1)).getValue().doubleValue()); } }// end class ExprGt class ExprLte extends Expression { ExprLte(Element node) { super(node, 2, 2, BaseType.Boolean, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.Integer, BaseType.Float, BaseType.Boolean); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, types, true, true)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { return new DEBoolean(((_DEFloat) paramValues.get(0)).getValue() .doubleValue() <= ((_DEFloat) paramValues.get(1)).getValue().doubleValue()); } }// end class ExprLte class ExprGte extends Expression { ExprGte(Element node) { super(node, 2, 2, BaseType.Boolean, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.Integer, BaseType.Float, BaseType.Boolean); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, types, true, true)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { return new DEBoolean(((_DEFloat) paramValues.get(0)).getValue() .doubleValue() >= ((_DEFloat) paramValues.get(1)).getValue().doubleValue()); } }// end class ExprGte class ExprSum extends Expression { ExprSum(Element node) { super(node, 1, Integer.MAX_VALUE, BaseType.Float, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.Integer, BaseType.Float, BaseType.Boolean); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, types, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { double val = 0.0; for (DataElement datum : paramValues) { if (datum == null) return null; val += ((_DEFloat) datum).getValue().doubleValue(); } return new DEFloat(val); } }// end class ExprSum class ExprProduct extends Expression { ExprProduct(Element node) { super(node, 1, Integer.MAX_VALUE, BaseType.Float, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.Integer, BaseType.Float, BaseType.Boolean); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, types, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if (CollectionUtils.exists(paramValues, new Predicate() { @Override public boolean evaluate(Object arg0) { if (arg0 == null) return true; return false; } })) return null; double val = ((_DEFloat) paramValues.get(0)).getValue().doubleValue(); for (int i = 1; i < paramValues.size(); i++) { val *= ((_DEFloat) paramValues.get(i)).getValue().doubleValue(); } return new DEFloat(val); } }// end class ExprProduct class ExprSubtract extends Expression { ExprSubtract(Element node) { super(node, 2, 2, BaseType.Float, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.Integer, BaseType.Float, BaseType.Boolean); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, types, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if (CollectionUtils.exists(paramValues, new Predicate() { @Override public boolean evaluate(Object arg0) { if (arg0 == null) return true; return false; } })) return null; return new DEFloat(((_DEFloat) paramValues.get(0)).getValue().doubleValue() - ((_DEFloat) paramValues.get(1)).getValue().doubleValue()); } }// end class ExprSubtract class ExprDivide extends Expression { ExprDivide(Element node) { super(node, 2, 2, BaseType.Float, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.Integer, BaseType.Float, BaseType.Boolean); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, types, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if (CollectionUtils.exists(paramValues, new Predicate() { @Override public boolean evaluate(Object arg0) { if (arg0 == null) return true; return false; } })) return null; return new DEFloat(((_DEFloat) paramValues.get(0)).getValue().doubleValue() / ((_DEFloat) paramValues.get(1)).getValue().doubleValue()); } }// end class ExprDivide class ExprPower extends Expression { ExprPower(Element node) { super(node, 2, 2, BaseType.Float, Cardinality.Single); List<BaseType> types = Arrays.asList(BaseType.Integer, BaseType.Float, BaseType.Boolean); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.Single, types, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { for (DataElement datum : paramValues) { if (datum == null) return null; } return new DEFloat(Math.pow(((_DEFloat) paramValues.get(0)).getValue().doubleValue(), ((_DEFloat) paramValues.get(1)).getValue().doubleValue())); } }// end class ExprPower class ExprIntegerDivide extends Expression { ExprIntegerDivide(Element node) { super(node, 2, 2, BaseType.Integer, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(-1, Cardinality.Single, BaseType.Integer, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { for (DataElement datum : paramValues) { if (datum == null) return null; } return new DEInteger(((DEInteger) paramValues.get(0)).getValue().intValue() / ((DEInteger) paramValues.get(1)).getValue().intValue()); } }// end class ExprIntegerDivide class ExprIntegerModulus extends Expression { ExprIntegerModulus(Element node) { super(node, 2, 2, BaseType.Integer, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(-1, Cardinality.Single, BaseType.Integer, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { for (DataElement datum : paramValues) { if (datum == null) return null; } return new DEInteger(((DEInteger) paramValues.get(0)).getValue().intValue() % ((DEInteger) paramValues.get(1)).getValue().intValue()); } }// end class ExprModulus class ExprTruncate extends Expression { ExprTruncate(Element node) { super(node, 1, 1, BaseType.Integer, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(0, Cardinality.Single, BaseType.Float, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if ((paramValues.size() == 0) || (paramValues.get(0) == null)) return null; return new DEInteger((int) MathUtils.truncate(((_DEFloat) paramValues.get(0)).getValue().doubleValue())); } }// end class ExprTruncate class ExprRound extends Expression { ExprRound(Element node) { super(node, 1, 1, BaseType.Integer, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(0, Cardinality.Single, BaseType.Float, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if (paramValues.get(0) == null) return null; return new DEInteger((int) ((_DEFloat) paramValues.get(0)).round(DEFloat.RoundingMode.DecimalPlaces, 0)); } }// end class ExprRound class ExprIntegerToFloat extends Expression { ExprIntegerToFloat(Element node) { super(node, 1, 1, BaseType.Float, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(0, Cardinality.Single, BaseType.Integer, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { return (_DEFloat) paramValues.get(0); } }// end class ExprIntegerToFloat class ExprBaseValue extends Expression { private String _value; private DataElement _dataValue; ExprBaseValue(Element node) { super(node, 0, 0, BaseType.String, Cardinality.None); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("baseType", BaseType.BaseType); addAttribute(eac); _value = node.getText();// node.InnerText; } // todo: should probably move processing of the constant value to the // validation section @Override protected boolean validate(ValidationLog log, QTIRubric rubric) { boolean ok = super.validate(log, rubric); resolveType(rubric); if (ok) { DataElement de = DataElement.create(_value, _returnType); if (de.getType() == BaseType.Null) { log.addMessage(_node, TDSStringUtils.format("Unable to interpret element content ({0}) as {1}", _value, _returnType.toString())); ok = false; } if (ok) { if (_returnCardinality == Cardinality.Single) { _dataValue = DataElement.create(_value, _returnType); if (_dataValue.getType() != _returnType) { ok = false; log.addMessage(_node, "Unable to turn the given string into the requested base type and cardinality"); } } else { _dataValue = DataElement.createContainer(_value, _returnType, _returnCardinality); if (_dataValue.getType() == BaseType.Null) { ok = false; log.addMessage(_node, "Unable to turn the given string into the requested base type and cardinality"); } } } } return ok; } void resolveType(QTIRubric rubric) { DEBaseType de = (DEBaseType) getAttributeValue("baseType"); if (de != null) { _returnType = de.getValue(); } _returnCardinality = Cardinality.Single; if (_returnType != BaseType.String) // this is a kludge, because baseValues // are always supposed to be // single-valued, but examples use them // as { if (_value.trim().contains(" ")) _returnCardinality = Cardinality.None; } } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { return _dataValue; } }// end class ExprBaseType class ExprVariable extends Expression { ExprVariable(Element node) { super(node, 0, 0, BaseType.Null, Cardinality.None); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("identifier", BaseType.Identifier); addAttribute(eac); } @Override protected boolean validate(ValidationLog log, QTIRubric rubric) { resolveIdentifierCardinality(rubric); resolveIdentifierType(rubric); return super.validate(log, rubric); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { DEIdentifier identifier = (DEIdentifier) getAttributeValue("identifier"); return vb.getVariable(identifier); // if (_result == null) //the spec is unclear. GetVariable will return the // default value if another value has not been provided. // _result = rubric.GetDefaultValue(identifier); } }// end class ExprVariable class ExprDefault extends Expression { ExprDefault(Element node) { super(node, 0, 0, BaseType.Null, Cardinality.None); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("identifier", BaseType.Identifier); addAttribute(eac); } @Override protected boolean validate(ValidationLog log, QTIRubric rubric) { resolveIdentifierCardinality(rubric); resolveIdentifierType(rubric); return super.validate(log, rubric); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { DEIdentifier identifier = (DEIdentifier) getAttributeValue("identifier"); return rubric.getDefaultValue(identifier); } }// end class ExprDefault class ExprCorrect extends Expression { ExprCorrect(Element node) { super(node, 0, 0, BaseType.Null, Cardinality.None); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("identifier", BaseType.Identifier); addAttribute(eac); } @Override protected boolean validate(ValidationLog log, QTIRubric rubric) { resolveIdentifierCardinality(rubric); resolveIdentifierType(rubric); return super.validate(log, rubric); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { DEIdentifier identifier = (DEIdentifier) getAttributeValue("identifier"); return rubric.getCorrectValue(identifier); } }// end class ExprCorrect class ExprMapResponse extends Expression { ExprMapResponse(Element node) { super(node, 0, 0, BaseType.Float, Cardinality.Single); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("identifier", BaseType.Identifier); addAttribute(eac); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { DEIdentifier varname = (DEIdentifier) getAttributeValue("identifier"); VariableMapping map = rubric.getResponseMapping(varname); if (map == null) return null; double val = map.mapResponse(vb.getVariable(varname)); return new DEFloat(val); } }// end class ExprMapResponse class ExprMapResponsePoint extends Expression { ExprMapResponsePoint(Element node) { super(node, 0, 0, BaseType.Float, Cardinality.Single); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("identifier", BaseType.Identifier); addAttribute(eac); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { DEIdentifier varname = (DEIdentifier) getAttributeValue("identifier"); AreaMapping map = rubric.getAreaMapping(varname); if (map == null) return null; double val = map.mapResponse(vb.getVariable(varname)); return new DEFloat(val); } }// end class ExprMapResponsePoint class ExprNull extends Expression { ExprNull(Element node) { super(node, 0, 0, BaseType.Null, Cardinality.None); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { return null; } }// end class ExprNull class ExprRandomInteger extends Expression { ExprRandomInteger(Element node) { super(node, 0, 0, BaseType.Integer, Cardinality.Single); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("min", BaseType.Integer, true); addAttribute(eac); eac = new ExpressionAttributeSpec("max", BaseType.Integer); addAttribute(eac); eac = new ExpressionAttributeSpec("step", BaseType.Integer, true); addAttribute(eac); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { DEInteger minx = (DEInteger) getAttributeValue("min"); DEInteger maxx = (DEInteger) getAttributeValue("max"); DEInteger stepx = (DEInteger) getAttributeValue("step"); int min = (minx == null) ? 0 : minx.getValue(); int step = (stepx == null) ? 0 : stepx.getValue(); int max = maxx.getValue(); int valCount = (max - min) / step; Random randomizer = new Random(); double rand = randomizer.nextDouble() * 1000000; int val = (int) (rand % valCount); return new DEInteger((int) val * step + min); } }// end class ExprRandomInteger class ExprRandomFloat extends Expression { ExprRandomFloat(Element node) { super(node, 0, 0, BaseType.Float, Cardinality.Single); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("min", BaseType.Float); addAttribute(eac); eac = new ExpressionAttributeSpec("max", BaseType.Float); addAttribute(eac); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { _DEFloat min = (_DEFloat) getAttributeValue("min"); _DEFloat max = (_DEFloat) getAttributeValue("max"); Random randomizer = new Random(); double rand = randomizer.nextDouble(); return new DEFloat(min.getValue().doubleValue() + rand * (max.getValue().doubleValue() - min.getValue().doubleValue())); } }// end class ExprRandomFloat class ExprMultiple extends Expression { ExprMultiple(Element node) { super(node, 0, Integer.MAX_VALUE, BaseType.Null, Cardinality.Multiple); List<Cardinality> cards = Arrays.asList(Cardinality.Single, Cardinality.Multiple); List<BaseType> types = Arrays.asList(BaseType.Null); addParameterConstraint(new ExpressionParameterConstraint(-1, cards, types, false, true)); } @Override protected boolean validate(ValidationLog log, QTIRubric rubric) { return resolveType(super.validate(log, rubric)); } private boolean resolveType(boolean ok) { for (Expression exp : _parameters) { if (exp != null) { _returnType = exp.getReturnType(); } return ok; } return ok; } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { BaseType bt = BaseType.Null; DEContainer rslt = null; for (DataElement de : paramValues) { if (de == null) continue; if (bt == BaseType.Null) { bt = de.getType(); rslt = new DEContainer(bt, Cardinality.Multiple); } if (de.getIsContainer()) { DEContainer container = (DEContainer) de; for (int i = 0; i < container.getMemberCount(); i++) { if (container.getMember(i) != null) rslt.add(container.getMember(i)); } } else rslt.add(de); } return rslt; } }// end class ExprMultiple class ExprOrdered extends Expression { ExprOrdered(Element node) { super(node, 0, Integer.MAX_VALUE, BaseType.Null, Cardinality.Ordered); List<Cardinality> cards = Arrays.asList(Cardinality.Single, Cardinality.Ordered); List<BaseType> types = Arrays.asList(BaseType.Null); addParameterConstraint(new ExpressionParameterConstraint(-1, cards, types, false, true)); } @Override protected boolean validate(ValidationLog log, QTIRubric rubric) { boolean ok = super.validate(log, rubric); return resolveType(ok); } private boolean resolveType(boolean ok) { for (Expression exp : _parameters) { if (exp != null) { _returnType = exp.getReturnType(); } return ok; } return ok; } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { BaseType bt = BaseType.Null; DEContainer rslt = null; for (DataElement de : paramValues) { if (de == null) continue; if (bt == BaseType.Null) { bt = de.getType(); rslt = new DEContainer(bt, Cardinality.Ordered); } if (de.getIsContainer()) { DEContainer container = (DEContainer) de; for (int i = 0; i < container.getMemberCount(); i++) { if (container.getMember(i) != null) rslt.add(container.getMember(i)); } } else rslt.add(de); } return rslt; } }// end class ExprOrdered class ExprContainerSize extends Expression { ExprContainerSize(Element node) { super(node, 1, 1, BaseType.Integer, Cardinality.Single); List<Cardinality> cards = Arrays.asList(Cardinality.Multiple, Cardinality.Ordered); List<BaseType> types = Arrays.asList(BaseType.Null); addParameterConstraint(new ExpressionParameterConstraint(0, cards, types, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if (paramValues.get(0) == null) return new DEInteger(0); return new DEInteger(((DEContainer) paramValues.get(0)).getMemberCount()); } }// end class ExprContainerSize class ExprIsNull extends Expression { ExprIsNull(Element node) { super(node, 1, 1, BaseType.Boolean, Cardinality.Single); addParameterConstraint(new ExpressionParameterConstraint(0, Cardinality.None, BaseType.Null, false, false)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if ((paramValues.get(0) == null) || (paramValues.get(0).getType() == BaseType.Null)) return new DEBoolean(true); else return new DEBoolean(false); } }// end class ExprIsNull class ExprIndex extends Expression { ExprIndex(Element node) { super(node, 1, 1, BaseType.Null, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(0, Cardinality.Ordered, BaseType.Null, false, false)); addAttribute(new ExpressionAttributeSpec("n", BaseType.Integer, false)); // jdc. // sept // 7 // 2014. // Changed // to // "n" // which // matches // spec. // made // it // mandatory. } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { DEInteger idx = (DEInteger) getAttributeValue("n"); if (idx == null) return null; DataElement param = paramValues.get(0); if (param == null) return null; if (!param.getIsContainer()) { // convert to a single element container DEContainer container = new DEContainer(param.getType(), Cardinality.Ordered); container.add(param); param = container; } DEContainer vals = (DEContainer) param; if (vals.getMemberCount() < idx.getValue()) return null; if (idx.getValue() < 1) return null; return vals.getMember(idx.getValue() - 1); } }// end class ExprIndex class ExprRandom extends Expression { ExprRandom(Element node) { super(node, 1, 1, BaseType.Null, Cardinality.Single); List<Cardinality> cards = Arrays.asList(Cardinality.Multiple, Cardinality.Ordered); List<BaseType> types = Arrays.asList(BaseType.Null); addParameterConstraint(new ExpressionParameterConstraint(0, cards, types, false, false)); } @Override protected boolean validate(ValidationLog log, QTIRubric rubric) { boolean ok = super.validate(log, rubric); for (Expression exp : _parameters) { if (exp != null) { _returnType = exp.getReturnType(); return ok; } } return ok; } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { Random randomizer = new Random(); int randSet = (int) MathUtils.truncate(randomizer.nextDouble() * 1000000); DEContainer param = (DEContainer) paramValues.get(0); if (param == null) return null; if (param.getMemberCount() == 0) return null; int idx = randSet % param.getMemberCount(); return param.getMember(idx); } }// end class ExprRandom class ExprMember extends Expression { ExprMember(Element node) { super(node, 2, 2, BaseType.Boolean, Cardinality.Single); List<Cardinality> cards = Arrays.asList(Cardinality.Multiple, Cardinality.Ordered); List<BaseType> types = Arrays.asList(BaseType.Null); addParameterConstraint(new ExpressionParameterConstraint(0, Cardinality.Single, BaseType.Null)); addParameterConstraint(new ExpressionParameterConstraint(1, cards, types, false, false)); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.None, BaseType.Null, false, true)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if ((paramValues.get(0) == null) || (paramValues.get(1) == null)) return null; return new DEBoolean(((DEContainer) paramValues.get(1)).contains(paramValues.get(0))); } }// end class ExprMember class ExprDelete extends Expression { ExprDelete(Element node) { // this could be a problem--if the original was orderded super(node, 2, 2, BaseType.Null, Cardinality.Multiple); List<Cardinality> cards = Arrays.asList(Cardinality.Multiple, Cardinality.Ordered); List<BaseType> types = Arrays.asList(BaseType.Null); addParameterConstraint(new ExpressionParameterConstraint(0, Cardinality.Single, BaseType.Null)); addParameterConstraint(new ExpressionParameterConstraint(1, cards, types, false, false)); addParameterConstraint(new ExpressionParameterConstraint(-1, Cardinality.None, BaseType.Null, false, true)); } @Override protected boolean validate(ValidationLog log, QTIRubric rubric) { return (resolveType(super.validate(log, rubric))); } private boolean resolveType(boolean ok) { for (Expression exp : _parameters) { if (exp != null) { _returnType = exp.getReturnType(); } return ok; } return ok; } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if ((paramValues.get(0) == null) || (paramValues.get(1) == null)) return null; return (((DEContainer) paramValues.get(1)).delete(paramValues.get(0))); } }// end class ExprDelete class ExprContains extends Expression { ExprContains(Element node) { super(node, 2, 2, BaseType.Boolean, Cardinality.Single); List<Cardinality> cards = Arrays.asList(Cardinality.Multiple, Cardinality.Ordered); List<BaseType> types = Arrays.asList(BaseType.Null); addParameterConstraint(new ExpressionParameterConstraint(-1, cards, types, true, true)); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { if ((paramValues.get(0) == null) || (paramValues.get(1) == null)) return null; return new DEBoolean(((DEContainer) paramValues.get(1)).contains(paramValues.get(0))); } }// end class ExprContains class ExprSubstring extends Expression { ExprSubstring(Element node) { super(node, 2, 2, BaseType.String, Cardinality.Single); addParameterConstraint( new ExpressionParameterConstraint(-1, Cardinality.Single, BaseType.String, true, true)); ExpressionAttributeSpec eac = new ExpressionAttributeSpec("caseSensitive", BaseType.Boolean); addAttribute(eac); } @Override protected DataElement exprEvaluate(VariableBindings vb, QTIRubric rubric, List<DataElement> paramValues) { boolean caseSensitive = true; if ((paramValues.get(0) == null) || (paramValues.get(1) == null)) return null; DEBoolean cs = (DEBoolean) getAttributeValue("caseSensitive"); if (cs != null) caseSensitive = cs.getBooleanValue(); if (caseSensitive) return new DEBoolean(((DEString) paramValues.get(1)).getValue() .contains(((DEString) paramValues.get(0)).getValue())); else return new DEBoolean(((DEString) paramValues.get(1)).getValue().toLowerCase() .contains(((DEString) paramValues.get(0)).getValue().toLowerCase())); } }// end class ExprSubstring