@Override public FloatOrVariableRef parseDomAttributeValue(final String domAttributeValue) { return FloatOrVariableRef.parseString(domAttributeValue); }
@Override protected String itemToQtiString(final FloatOrVariableRef item) { return item.toString(); } }
@Override protected void validateThis(final ValidationContext context) { super.validateThis(context); final FloatOrVariableRef maxComputer = getMax(); final FloatOrVariableRef minComputer = getMin(); if (maxComputer.isConstantFloat() && minComputer.isConstantFloat()) { final double max = maxComputer.getConstantFloatValue().doubleValue(); final double min = minComputer.getConstantFloatValue().doubleValue(); if (max < min) { context.fireAttributeValidationError(getAttributes().get(ATTR_MAX_NAME), "Attribute " + ATTR_MAX_NAME + " (" + max + ") cannot be lower than " + ATTR_MIN_NAME + " (" + min + ")"); } } }
@Override public void validateBasic(final ValidationContext context) { super.validateBasic(context); /* If variable reference, make sure it refers to the right type of variable */ if (value!=null && value.isVariableRef()) { final Identifier variableReferenceIdentifier = value.getIdentifier(); final VariableDeclaration variableDeclaration = context.checkLocalVariableReference(owner, variableReferenceIdentifier); if (variableDeclaration!=null) { context.checkVariableType(owner, variableDeclaration, VariableType.TEMPLATE, VariableType.OUTCOME); context.checkSignature(owner, variableDeclaration, Signature.SINGLE_FLOAT); } } }
/** * Wrapper for {@link #evaluate(Expression, ProcessingContext)} that substitutes a replacement value and emits a * runtime warning if the result was NULL. */ public double evaluateNotNull(final Expression expression, final ProcessingContext context, final String messageOnNull, final double replacementOnNull) { final Value evaluated = evaluate(expression, context); double result; if (evaluated.isNull()) { context.fireRuntimeWarning(expression, messageOnNull); result = replacementOnNull; } else { result = ((FloatValue) evaluated).doubleValue(); } return result; } }
@Override protected Value evaluateValidSelf(final ProcessingContext context, final Value[] childValues, final int depth) { final double min = getMin().evaluateNotNull(this, context, "Computed value of min was NULL. Replacing with 0", 0); final double max = getMax().evaluateNotNull(this, context, "Computed value of max was NULL. Replacing with min+1", min+1); if (min > max) { /* Bad computed values */ context.fireRuntimeWarning(this, "Computed value of min (" + min + ") was greater than (" + max + "). Returning NULL"); return NullValue.INSTANCE; } final Random randomGenerator = context.getRandomGenerator(); final double randomNumber = randomGenerator.nextDouble(); final double randomFloat = min + (max - min) * randomNumber; return new FloatValue(randomFloat); } }
/** * Parses a new floatOrVariableRef from the given String, as defined in the QTI spec. * * @throws QtiParseException */ public static FloatOrVariableRef parseString(final String string) { Assert.notNull(string); if (string.isEmpty()) { throw new QtiParseException("floatOrVariableRef must not be empty"); } try { /* Try to parse as a float */ final double floatValue = DataTypeBinder.parseFloat(string); return new FloatOrVariableRef(floatValue); } catch (final QtiParseException e) { /* Try to parse as a variable reference */ final Identifier variableReferenceIdentifier = Identifier.parseString(string); return new FloatOrVariableRef(variableReferenceIdentifier); } }
/** * Evaluates this holder. If this holds an explicit float then its value is returned as-is. * Otherwise, the given {@link ProcessingContext} is used to look up the value of the variable * that this type refers to. The result will either be an {@link FloatValue} or {@link NullValue} * <p> * If the variable cannot be successfully resolved or is of the wrong type then a runtime error * is recorded and a {@link NullValue} will be returned. */ public Value evaluate(final Expression expression, final ProcessingContext context) { if (isConstantFloat()) { return constantFloatValue; } else { final Value result = context.evaluateVariableValue(variableReferenceValue, VariableType.TEMPLATE, VariableType.OUTCOME); if (result.hasSignature(Signature.SINGLE_FLOAT)) { return result; } else { context.fireRuntimeError(expression, "Variable referenced by " + variableReferenceValue + " was expected to be a single float - returning NULL"); return NullValue.INSTANCE; } } }
@Override protected void validateThis(final ValidationContext context) { super.validateThis(context); final Attribute<?> tolerancesAttr = getAttributes().get(ATTR_TOLERANCES_NAME); final List<FloatOrVariableRef> tolerances = getTolerances(); if (tolerances!=null && (tolerances.size()==0 || tolerances.size()>2)) { context.fireAttributeValidationError(tolerancesAttr, "Attribute " + ATTR_TOLERANCES_NAME + " must contain 1 or 2 floatOrVariableRefs when specified"); } final FloatOrVariableRef firstToleranceComputer = getFirstTolerance(); if (firstToleranceComputer!=null && firstToleranceComputer.isConstantFloat()) { final double firstToleranceValue = firstToleranceComputer.getConstantFloatValue().doubleValue(); if (firstToleranceValue <= 0) { context.fireAttributeValidationError(tolerancesAttr, "Attribute " + ATTR_TOLERANCES_NAME + " (" + firstToleranceValue + ") must be positive"); } } final FloatOrVariableRef secondToleranceComputer = getSecondTolerance(); if (secondToleranceComputer!=null && secondToleranceComputer.isConstantFloat()) { final double secondToleranceValue = secondToleranceComputer.getConstantFloatValue().doubleValue(); if (secondToleranceValue < 0) { context.fireAttributeValidationError(tolerancesAttr, "Attribute " + ATTR_TOLERANCES_NAME + " (" + getSecondTolerance() + ") must be positive if specified"); } } if (getToleranceMode() != null && getToleranceMode() != ToleranceMode.EXACT && tolerances==null) { context.fireAttributeValidationError(tolerancesAttr, "Attribute " + ATTR_TOLERANCES_NAME + " must be specified when toleranceMode is absolute or relative"); } }
final Value firstToleranceValue = firstToleranceComputer.evaluate(this, context); if (firstToleranceValue.isNull()) { context.fireRuntimeWarning(this, "Computed value of first tolerance is NULL. Returning NULL"); final Value secondToleranceValue = secondToleranceComputer.evaluate(this, context); if (secondToleranceValue.isNull()) { context.fireRuntimeWarning(this, "Computed value of second tolerance is NULL. Returning NULL");
@Override public String toDomAttributeValue(final FloatOrVariableRef value) { return value.toString(); }
@Override protected FloatOrVariableRef parseItemValue(final String value) { return FloatOrVariableRef.parseString(value); }