Ejemplo n.º 1
0
 /**
  * Process the Delete operator.
  *
  * @return \qtism\common\collections\Container A new container derived from the second sub-expression with all instances of the first sub-expression removed, or NULL if either sub-expression is considered to be NULL.
  * @throws \qtism\runtime\expressions\operators\OperatorProcessingException
  */
 public function process()
 {
     $operands = $this->getOperands();
     if ($operands->containsNull() === true) {
         return null;
     }
     if ($operands->sameBaseType() === false) {
         $msg = "The Delete operator only accepts operands with the same baseType.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
     }
     $operand1 = $operands[0];
     if (RuntimeUtils::inferCardinality($operand1) !== Cardinality::SINGLE) {
         $msg = "The first operand of the Delete operator must have the single cardinality.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
     }
     $operand2 = $operands[1];
     $cardinality = RuntimeUtils::inferCardinality($operand2);
     if ($cardinality !== Cardinality::MULTIPLE && $cardinality !== Cardinality::ORDERED) {
         $msg = "The second operand of the Delete operator must have a cardinality or multiple or ordered.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
     }
     $returnedBaseType = RuntimeUtils::inferBaseType($operand1);
     $returnValue = $cardinality === Cardinality::MULTIPLE ? new MultipleContainer($returnedBaseType) : new OrderedContainer($returnedBaseType);
     foreach ($operand2 as $value) {
         if ($value === $operand1 || $operand1 instanceof Comparable && $operand1->equals($value) === true) {
             // This is the same value, it will not be included in the returned value.
             continue;
         } else {
             $returnValue[] = $value;
         }
     }
     return $returnValue;
 }
Ejemplo n.º 2
0
 /**
  * Process the Member operator.
  *
  * @return boolean Whether the first operand is contained by the second one as a boolean value, or NULL if any of the sub-expressions are NULL.
  * @throws \qtism\runtime\expressions\operators\OperatorProcessingException
  */
 public function process()
 {
     $operands = $this->getOperands();
     if ($operands->containsNull() === true) {
         return null;
     }
     if ($operands->sameBaseType() === false) {
         $msg = "The Member operator only accepts values with the same baseType.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
     }
     $operand1 = $operands[0];
     $operand2 = $operands[1];
     // The first expression must have single cardinality.
     if (CommonUtils::inferCardinality($operand1) !== Cardinality::SINGLE) {
         $msg = "The first operand of the Member operator must have a single cardinality.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
     }
     // The second expression must have multiple or ordered cardinality.
     $cardinality = CommonUtils::inferCardinality($operand2);
     if ($cardinality !== Cardinality::MULTIPLE && $cardinality !== Cardinality::ORDERED) {
         $msg = "The second operand of the Member operator must have a multiple or ordered cardinality.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
     }
     return new Boolean($operand2->contains($operand1));
 }
 /**
  * Process the TemplateConstraint rule. It simply throws a RuleProcessingException with
  * the special code RuleProcessingException::TEMPLATE_CONSTRAINT_UNSATISFIED to warn client
  * code that the expression related to the constraint returned false or null.
  *
  * @throws \qtism\runtime\rules\RuleProcessingException with code = RuleProcessingException::TEMPLATE_CONSTRAINT_UNSATISFIED.
  */
 public function process()
 {
     $state = $this->getState();
     $rule = $this->getRule();
     $expr = $rule->getExpression();
     $expressionEngine = new ExpressionEngine($expr, $state);
     $val = $expressionEngine->process();
     if (Utils::isNull($val) || $val->getValue() === false) {
         $msg = "Unsatisfied Template Constraint.";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::TEMPLATE_CONSTRAINT_UNSATISFIED);
     }
 }
Ejemplo n.º 4
0
 /**
  * Process the current expression.
  *
  * @return \qtism\runtime\common\OrderedContainer|null An OrderedContainer object or NULL.
  * @throws \qtism\runtime\expressions\operators\OperatorProcessingException
  */
 public function process()
 {
     $operands = $this->getOperands();
     if (count($operands) === 0) {
         return null;
     }
     if ($operands->exclusivelySingleOrOrdered() === false) {
         $msg = "The Ordered operator only accepts operands with single or ordered cardinality.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
     }
     $refType = null;
     $returnValue = null;
     foreach ($operands as $operand) {
         if (is_null($operand) || $operand instanceof OrderedContainer && $operand->isNull()) {
             // As per specs, ignore.
             continue;
         } else {
             if ($refType !== null) {
                 // A reference type as already been identifier.
                 if (CommonUtils::inferBaseType($operand) === $refType) {
                     // $operand can be added to $returnValue.
                     static::appendValue($returnValue, $operand);
                 } else {
                     // baseType mismatch.
                     $msg = "The Ordered operator only accepts values with a similar baseType.";
                     throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
                 }
             } elseif (($discoveryType = CommonUtils::inferBaseType($operand)) !== false) {
                 // First value being identified as non-null.
                 $refType = $discoveryType;
                 $returnValue = new OrderedContainer($refType);
                 static::appendValue($returnValue, $operand);
             }
         }
     }
     return $returnValue;
 }
Ejemplo n.º 5
0
 /**
  * Create a QTI Runtime value from Data Model ValueCollection
  *
  * @param \qtism\data\state\ValueCollection $valueCollection A collection of qtism\data\state\Value objects.
  * @param integer $baseType The baseType the Value objects in the ValueCollection must respect.
  * @param integer $cardinality The cardinality the Value objects in the ValueCollection must respect.
  * @throws \UnexpectedValueException If $baseType or/and $cardinality are not respected by the Value objects in the ValueCollection.
  * @return mixed The resulting QTI Runtime value (primitive or container depending on baseType/cardinality).
  */
 protected static function dataModelValuesToRuntime(ValueCollection $valueCollection, $baseType, $cardinality)
 {
     // Cardinality?
     // -> Single? Multiple? Ordered? Record?
     if ($cardinality === Cardinality::SINGLE) {
         // We should find a single value in the DefaultValue's values.
         if (count($valueCollection) == 1) {
             $dataModelValue = RuntimeUtils::valueToRuntime($valueCollection[0]->getValue(), $baseType);
             return $dataModelValue;
         } else {
             // The Data Model is in an inconsistent state.
             // This should be handled by the Data Model but
             // I prefer to be defensive.
             $msg = "A Data Model VariableDeclaration with 'single' cardinality must contain a single value, ";
             $msg .= count($valueCollection) . " value(s) found.";
             throw new UnexpectedValueException($msg);
         }
     } else {
         // Multiple|Ordered|Record, use a container.
         $container = null;
         try {
             // Create the appropriate Container object.
             $className = ucfirst(Cardinality::getNameByConstant($cardinality)) . 'Container';
             $nsClassName = 'qtism\\runtime\\common\\' . $className;
             $callback = array($nsClassName, 'createFromDataModel');
             $container = call_user_func_array($callback, array($valueCollection, $baseType));
             return $container;
             // return container.
         } catch (InvalidArgumentException $e) {
             $msg = "The default value found in the Data Model Variable Declaration is not consistent. ";
             $msg .= "The values must have a baseType compliant with the baseType of the VariableDeclaration.";
             $msg .= "If the VariableDeclaration's cardinality is 'record', make sure the values it contains have ";
             $msg .= "fieldIdentifiers.";
             throw new UnexpectedValueException($msg, 0, $e);
         }
     }
 }
Ejemplo n.º 6
0
 /**
  * Set the default value of the Variable.
  *
  * @param \qtism\common\datatypes\QtiDatatype|null $defaultValue A QtiDatatype object or null.
  * @throws \InvalidArgumentException If $defaultValue's type is not compliant with the qti:baseType of the Variable.
  */
 public function setDefaultValue(QtiDatatype $defaultValue = null)
 {
     if (Utils::isBaseTypeCompliant($this->getBaseType(), $defaultValue) && Utils::isCardinalityCompliant($this->getCardinality(), $defaultValue)) {
         $this->defaultValue = $defaultValue;
         return;
     } else {
         Utils::throwBaseTypeTypingError($this->getBaseType(), $defaultValue);
     }
 }
 /**
  * @dataProvider isValidVariableIdentifierProvider
  * 
  * @param string $string
  * @param boolean $expected
  */
 public function testIsValidVariableIdentifier($string, $expected)
 {
     $this->assertSame($expected, Utils::isValidVariableIdentifier($string));
 }
Ejemplo n.º 8
0
 /**
  * Set the identifier string.
  *
  * @param string $identifier A prefixed identifier.
  * @throws \InvalidArgumentException If $identifier is not a valid prefixed identifier.
  */
 protected function setIdentifier($identifier)
 {
     if (Utils::isValidVariableIdentifier($identifier)) {
         $this->identifier = $identifier;
     } else {
         $msg = "The identifier '{$identifier}' is not a valid QTI Variable Name Identifier.";
         throw new InvalidArgumentException($msg);
     }
 }
Ejemplo n.º 9
0
 /**
  * Set the correct response.
  *
  * @param \qtism\common\datatypes\QtiDatatype|null $correctResponse A QtiDatatype object or null.
  * @throws \InvalidArgumentException If $correctResponse does not match baseType and/or cardinality of the variable.
  */
 public function setCorrectResponse(QtiDatatype $correctResponse = null)
 {
     if ($correctResponse === null) {
         $this->correctResponse = null;
     } elseif (Utils::isBaseTypeCompliant($this->getBaseType(), $correctResponse) === true && Utils::isCardinalityCompliant($this->getCardinality(), $correctResponse) === true) {
         $this->correctResponse = $correctResponse;
     } else {
         $msg = "The given correct response is not compliant with the associated response variable.";
         throw new InvalidArgumentException($msg);
     }
 }
Ejemplo n.º 10
0
 /**
  * @see \qtism\runtime\common\Processable::process()
  */
 public function process()
 {
     $expression = $this->getExpression();
     return RuntimeUtils::valueToRuntime($expression->getValue(), $expression->getBaseType());
 }
Ejemplo n.º 11
0
 /**
  * Whether the item of the session has been attempted (at least once) and for which at least one response was given.
  *
  * @return boolean
  */
 public function isResponded()
 {
     if ($this->isPresented() === false) {
         return false;
     }
     $excludedResponseVariables = array('numAttempts', 'duration');
     foreach ($this->getKeys() as $k) {
         $var = $this->getVariable($k);
         if ($var instanceof ResponseVariable && in_array($k, $excludedResponseVariables) === false) {
             $value = $var->getValue();
             $defaultValue = $var->getDefaultValue();
             if (Utils::isNull($value) === true) {
                 if (Utils::isNull($defaultValue) === false) {
                     return true;
                 }
             } else {
                 if ($value->equals($defaultValue) === false) {
                     return true;
                 }
             }
         }
     }
     return false;
 }
Ejemplo n.º 12
0
 /**
  * @dataProvider equalsProvider
  * 
  * @param QtiDatatype $a
  * @param QtiDatatype $b
  * @param boolean $expected
  */
 public function testEquals(QtiDatatype $a = null, QtiDatatype $b = null, $expected)
 {
     $this->assertSame($expected, Utils::equals($a, $b));
 }
Ejemplo n.º 13
0
 /**
  * Create a RecordContainer object from a Data Model ValueCollection object.
  *
  * @param \qtism\data\state\ValueCollection $valueCollection A collection of qtism\data\state\Value objects.
  * @return \qtism\runtime\common\RecordContainer A Container object populated with the values found in $valueCollection.
  * @throws \InvalidArgumentException If a value from $valueCollection is not compliant with the QTI Runtime Model or the container type or if a value has no fieldIdentifier.
  */
 public static function createFromDataModel(ValueCollection $valueCollection)
 {
     $container = new static();
     foreach ($valueCollection as $value) {
         $fieldIdentifier = $value->getFieldIdentifier();
         if (!empty($fieldIdentifier)) {
             $container[$value->getFieldIdentifier()] = RuntimeUtils::valueToRuntime($value->getValue(), $value->getBaseType());
         } else {
             $msg = "Cannot include qtism\\data\\state\\Value '" . $value->getValue() . "' in the RecordContainer ";
             $msg .= "because it has no fieldIdentifier specified.";
             throw new InvalidArgumentException($msg);
         }
     }
     return $container;
 }
 /**
  * Wether the collection is composed of values with the same cardinality. Please
  * note that:
  * 
  * * If the OperandsCollection is empty, false is returned.
  * * If the OperandsCollection contains a NULL value or a NULL container (empty), false is returned
  * 
  * @return boolean
  */
 public function sameCardinality()
 {
     $operandsCount = count($this);
     if ($operandsCount > 0 && !$this->containsNull()) {
         $refType = RuntimeUtils::inferCardinality($this[0]);
         for ($i = 1; $i < $operandsCount; $i++) {
             if ($refType !== RuntimeUtils::inferCardinality($this[$i])) {
                 return false;
             }
         }
         return true;
     } else {
         return false;
     }
 }
Ejemplo n.º 15
0
 /**
  * Process the Repeat operator.
  *
  * Note: NULL values are simply ignored. If all sub-expressions are NULL, NULL is
  * returned.
  *
  * @return \qtism\runtime\common\OrderedContainer An ordered container filled sequentially by evaluating each sub-expressions, repeated a 'numberRepeats' of times. NULL is returned if all sub-expressions are NULL or numberRepeats < 1.
  * @throws \qtism\runtime\expressions\operators\OperatorProcessingException
  */
 public function process()
 {
     $operands = $this->getOperands();
     // get the value of numberRepeats
     $expression = $this->getExpression();
     $numberRepeats = $expression->getNumberRepeats();
     if (gettype($numberRepeats) === 'string') {
         // Variable reference found.
         $state = $this->getState();
         $varName = Utils::sanitizeVariableRef($numberRepeats);
         $varValue = $state[$varName];
         if (is_null($varValue) === true) {
             $msg = "The variable with name '{$varName}' could not be resolved.";
             throw new OperatorProcessingException($msg, $this);
         } elseif ($varValue instanceof Integer) {
             $msg = "The variable with name '{$varName}' is not an integer value.";
             throw new OperatorProcessingException($msg, $this);
         }
         $numberRepeats = $varValue->getValue();
     }
     if ($numberRepeats < 1) {
         return null;
     }
     $result = null;
     for ($i = 0; $i < $numberRepeats; $i++) {
         $refType = null;
         foreach ($operands as $operand) {
             // If null, ignore
             if (is_null($operand) || $operand instanceof Container && $operand->isNull()) {
                 continue;
             }
             // Check cardinality.
             if ($operand->getCardinality() !== Cardinality::SINGLE && $operand->getCardinality() !== Cardinality::ORDERED) {
                 $msg = "The Repeat operator only accepts operands with a single or ordered cardinality.";
                 throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
             }
             // Check baseType.
             $currentType = RuntimeUtils::inferBaseType($operand);
             if ($refType !== null && $currentType !== $refType) {
                 $msg = "The Repeat operator only accepts operands with the same baseType.";
                 throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
             } elseif (is_null($result)) {
                 $refType = $currentType;
                 $result = new OrderedContainer($refType);
             }
             // Okay we are good...
             $operandCardinality = RuntimeUtils::inferCardinality($operand);
             if ($operandCardinality !== Cardinality::ORDERED) {
                 $operand = new OrderedContainer($currentType, array($operand));
             }
             foreach ($operand as $o) {
                 $result[] = $o instanceof QtiDatatype ? clone $o : $o;
             }
         }
     }
     if (isset($result) && $result->isNull() !== true) {
         return $result;
     } else {
         return null;
     }
 }
 /**
  * Write a record field value in the current binary stream. A record field is composed of a key string and a value.
  * 
  * @param array $recordField An array where index 0 is the key string, and the index 1 is the value.
  * @throws QtiBinaryStreamAccessException
  */
 public function writeRecordField(array $recordField, $isNull = false)
 {
     try {
         $this->writeBoolean($isNull);
         $key = $recordField[0];
         $this->writeString($key);
         if ($isNull === false) {
             $value = $recordField[1];
             $baseType = Utils::inferBaseType($value);
             $this->writeTinyInt($baseType);
             $toCall = 'write' . ucfirst(BaseType::getNameByConstant($baseType));
             call_user_func(array($this, $toCall), $value instanceof Scalar ? $value->getValue() : $value);
         }
     } catch (BinaryStreamAccessException $e) {
         $msg = "An error occured while reading a Record Field.";
         throw new QtiBinaryStreamAccessException($msg, $this, QtiBinaryStreamAccessException::RECORDFIELD);
     }
 }
Ejemplo n.º 17
0
 protected function checkType($value)
 {
     if (!Utils::isRuntimeCompliant($value)) {
         Utils::throwTypingError($value);
     }
 }
 /**
  * Set the correct response.
  * 
  * @param mixed $correctResponse A QTI Runtime compliant object.
  */
 public function setCorrectResponse($correctResponse)
 {
     if (Utils::isBaseTypeCompliant($this->getBaseType(), $correctResponse) === true) {
         $this->correctResponse = $correctResponse;
         return;
     } else {
         if ($correctResponse instanceof Container) {
             if ($correctResponse->getCardinality() === $this->getCardinality()) {
                 if (get_class($correctResponse) === 'qtism\\runtime\\common\\Container' || $correctResponse->getBaseType() === $this->getBaseType()) {
                     // This is a simple container with no baseType restriction
                     // or a Multiple|Record|Ordered container with a compliant
                     // baseType.
                     $this->correctResponse = $correctResponse;
                     return;
                 } else {
                     $msg = "The baseType of the given container ('" . BaseType::getNameByConstant($correctResponse->getBaseType()) . "') ";
                     $msg .= "is not compliant with ";
                     $msg .= "the baseType of the variable ('" . BaseType::getNameByConstant($this->getBaseType()) . "').";
                     throw new InvalidArgumentException($msg);
                 }
             } else {
                 $msg = "The cardinality of the given container ('" . Cardinality::getNameByConstant($value->getCardinality()) . "') ";
                 $msg .= "is not compliant with ";
                 $msg .= "the cardinality of the variable ('" . Cardinality::getNameByConstant($this->getCardinality()) . "').";
                 throw new InvalidArgumentException($msg);
             }
         }
     }
     $msg = "The provided value is not compliant with the baseType of the ResponseVariable.";
     throw new InvalidArgumentException($msg);
 }
Ejemplo n.º 19
0
 /**
  * Makes $value compliant with baseType $targetBaseType, if $value is compliant. Otherwise,
  * the original $value is returned.
  * 
  * @param mixed $value A QTI Runtime compliant value.
  * @param integer $targetBaseType The target baseType.
  * @return mixed The juggled value if needed, otherwise the original value of $value.
  */
 public static function juggle($value, $targetBaseType)
 {
     // A lot of people designing QTI items want to put float values
     // in integer baseType'd variables... So let's go for type juggling!
     $valueBaseType = RuntimeUtils::inferBaseType($value);
     if ($valueBaseType !== $targetBaseType && ($value instanceof MultipleContainer || $value instanceof OrderedContainer)) {
         $class = get_class($value);
         if ($valueBaseType === BaseType::FLOAT && $targetBaseType === BaseType::INTEGER) {
             $value = new $class($targetBaseType, self::floatArrayToInteger($value->getArrayCopy()));
         } else {
             if ($valueBaseType === BaseType::INTEGER && $targetBaseType === BaseType::FLOAT) {
                 $value = new $class($targetBaseType, self::integerArrayToFloat($value->getArrayCopy()));
             } else {
                 if ($valueBaseType === BaseType::IDENTIFIER && $targetBaseType === BaseType::STRING) {
                     $value = new $class($targetBaseType, $value->getArrayCopy());
                 } else {
                     if ($valueBaseType === BaseType::STRING && $targetBaseType === BaseType::IDENTIFIER) {
                         $value = new $class($targetBaseType, $value->getArrayCopy());
                     } else {
                         if ($valueBaseType === BaseType::URI && $targetBaseType === BaseType::STRING) {
                             $value = new $class($targetBaseType, $value->getArrayCopy());
                         } else {
                             if ($valueBaseType === BaseType::STRING && $targetBaseType === BaseType::URI) {
                                 $value = new $class($targetBaseType, $value->getArrayCopy());
                             } else {
                                 if ($valueBaseType === BaseType::URI && $targetBaseType === BaseType::IDENTIFIER) {
                                     $value = new $class($targetBaseType, $value->getArrayCopy());
                                 } else {
                                     if ($valueBaseType === BaseType::IDENTIFIER && $targetBaseType === BaseType::URI) {
                                         $value = new $class($targetBaseType, $value->getArrayCopy());
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
     } else {
         if ($valueBaseType !== $targetBaseType) {
             // Scalar value.
             if ($valueBaseType === BaseType::FLOAT && $targetBaseType === BaseType::INTEGER) {
                 $value = intval($value);
             } else {
                 if ($valueBaseType === BaseType::INTEGER && $targetBaseType === BaseType::FLOAT) {
                     $value = floatval($value);
                 }
             }
         }
     }
     return $value;
 }
Ejemplo n.º 20
0
 /**
  * Create a Container object from a Data Model ValueCollection object.
  *
  * @param \qtism\data\state\ValueCollection $valueCollection A collection of qtism\data\state\Value objects.
  * @return \qtism\common\collections\Container A Container object populated with the values found in $valueCollection.
  * @throws \InvalidArgumentException If a value from $valueCollection is not compliant with the QTI Runtime Model or the container type.
  */
 public static function createFromDataModel(ValueCollection $valueCollection)
 {
     $container = new static();
     foreach ($valueCollection as $value) {
         $container[] = RuntimeUtils::valueToRuntime($value->getValue(), $value->getBaseType());
     }
     return $container;
 }
Ejemplo n.º 21
0
 /**
  * Process the EqualRounded operator.
  *
  * @return boolean|null A boolean with a value of true if the two expressions are numerically equal after rounding and false if they are not. If either sub-expression is NULL, the operator results in NULL.
  * @throws \qtism\runtime\expressions\operators\OperatorProcessingException
  */
 public function process()
 {
     $operands = $this->getOperands();
     if ($operands->containsNull()) {
         return null;
     }
     if ($operands->exclusivelySingle() === false) {
         $msg = "The EqualRounded operator only accepts operands with a single cardinality.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
     }
     if ($operands->exclusivelyNumeric() === false) {
         $msg = "The EqualRounded operator only accepts operands with an integer or float baseType.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
     }
     // delegate the rounding to the RoundTo operator.
     $expression = $this->getExpression();
     $roundingMode = $expression->getRoundingMode();
     $figures = $expression->getFigures();
     if (gettype($figures) === 'string') {
         // Variable reference to deal with.
         $state = $this->getState();
         $varName = Utils::sanitizeVariableRef($figures);
         $varValue = $state[$varName];
         if (is_null($varValue) === true) {
             $msg = "The variable with name '{$varName}' could not be resolved.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::NONEXISTENT_VARIABLE);
         } elseif (!$varValue instanceof Integer) {
             $msg = "The variable with name '{$varName}' is not an integer.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_VARIABLE_BASETYPE);
         }
         $figures = $varValue->getValue();
     }
     $rounded = new OperandsCollection();
     // will contain the rounded operands.
     foreach ($operands as $operand) {
         $baseType = RuntimeUtils::inferBaseType($operand);
         $subExpression = new BaseValue($baseType, $operand);
         $roundToExpression = new RoundTo(new ExpressionCollection(array($subExpression)), $figures, $roundingMode);
         $roundToProcessor = new RoundToProcessor($roundToExpression, new OperandsCollection(array($operand)));
         try {
             $rounded[] = $roundToProcessor->process();
         } catch (OperatorProcessingException $e) {
             $msg = "An error occured while rounding '{$operand}'.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::LOGIC_ERROR, $e);
         }
     }
     return new Boolean($rounded[0]->getValue() == $rounded[1]->getValue());
 }
 /**
  * Processes all values of a record container and merge them into
  * a single string.
  * 
  * @param RecordContainer $variable The record to process.
  * @return string All the key/values delimited by printedVariable->delimiter. Indicator between keys and values is defined by printedVariable->mappingIndicator.
  */
 private function processRecord(Variable $variable)
 {
     $processedValues = array();
     $baseType = $variable->getBaseType();
     $mappingIndicator = $this->getComponent()->getMappingIndicator();
     foreach ($variable->getValue() as $k => $v) {
         $processedValues[] = "{$k}{$mappingIndicator}" . $this->processValue(Utils::inferBaseType($v), $v);
     }
     return implode($this->getComponent()->getDelimiter(), $processedValues);
 }
 /**
  * Process the LookupOutcomeValue rule.
  *
  * A RuleProcessingException will be thrown if:
  *
  * * The outcome variable to set does not exist.
  * * The variable to set is not an OutcomeVariable
  * * The outcome variable's baseType does not match the baseType of the affected value (the result of the bound expression).
  * * The outcome variable's declaration has no associated lookup table.
  * * The variable's declaration contains a matchTable but the result of the bound expression is not an integer.
  * * The variable's declaration contains an interpolationTable but the result of the bound expression is not an integer, nor a float.
  * * There is no associated table in the variable's declaration.
  * * An error occurs during the processing of the related expression.
  *
  * @throws \qtism\runtime\rules\RuleProcessingException If one of the error described above arise.
  */
 public function process()
 {
     $state = $this->getState();
     $rule = $this->getRule();
     $identifier = $rule->getIdentifier();
     $var = $state->getVariable($identifier);
     if (is_null($var) === true) {
         $msg = "The variable to set '{$identifier}' does not exist in the current state.";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::NONEXISTENT_VARIABLE);
     } elseif (!$var instanceof OutcomeVariable) {
         $msg = "The variable to set '{$identifier}' is not an OutcomeVariable.";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::WRONG_VARIABLE_TYPE);
     }
     $expression = $rule->getExpression();
     $expressionEngine = new ExpressionEngine($expression, $state);
     try {
         $val = $expressionEngine->process();
         // Let's lookup the associated table.
         $table = $var->getLookupTable();
         if (is_null($table) === true) {
             $msg = "No lookupTable in declaration of variable '{$identifier}'.";
             throw new RuleProcessingException($msg, $this, RuleProcessingException::LOGIC_ERROR);
         }
         // $targetVal = The value that will be set to the target variable.
         //
         // As per specs:
         // The default outcome value to be used when no matching table
         // entry is found. If omitted, the NULL value is used.
         $targetVal = $table->getDefaultValue();
         if ($table instanceof InterpolationTable) {
             if (!$val instanceof Float && !$val instanceof Integer && !$val instanceof Duration) {
                 $msg = "The value of variable '{$identifier}' must be integer, float or duration when used with an interpolationTable";
                 throw new RuleProcessingException($msg, $this, RuleProcessingException::LOGIC_ERROR);
             }
             foreach ($table->getInterpolationTableEntries() as $entry) {
                 $lowerBound = $entry->getSourceValue();
                 $includeBoundary = $entry->doesIncludeBoundary();
                 if ($includeBoundary === true && $val->getValue() <= $lowerBound) {
                     $targetVal = $entry->getTargetValue();
                     break;
                 } elseif ($includeBoundary === false && $val->getValue() < $lowerBound) {
                     $targetVal = $entry->getTargetValue();
                     break;
                 }
             }
         } else {
             // $table instanceof MatchTable
             if (!$val instanceof Integer) {
                 $msg = "The value of the variable '{$identifier}' must be integer when used with a matchTable.";
                 throw new RuleProcessingException($msg, $this, RuleProcessingException::LOGIC_ERROR);
             }
             foreach ($table->getMatchTableEntries() as $entry) {
                 if ($entry->getSourceValue() === $val->getValue()) {
                     $targetVal = $entry->getTargetValue();
                     break;
                 }
             }
         }
         // assign target value
         try {
             $finalVal = RuntimeUtils::valueToRuntime($targetVal, $var->getBaseType());
             $state[$identifier] = $finalVal;
         } catch (InvalidArgumentException $e) {
             // $targetVal's baseType not compliant with target variable's baseType.
             $msg = "The looked up value's baseType is not compliant with the baseType of variable '{$identifier}'.";
             throw new RuleProcessingException($msg, $this, RuleProcessingException::RUNTIME_ERROR);
         }
     } catch (ExpressionProcessingException $e) {
         $msg = "An error occured while processing the expression bound to the lookupOutcomeValue rule.";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::RUNTIME_ERROR, $e);
     }
 }
 protected function checkType($value)
 {
     parent::checkType($value);
     if (!Utils::isBaseTypeCompliant($this->getBaseType(), $value)) {
         Utils::throwBaseTypeTypingError($this->getBaseType(), $value);
     }
 }
Ejemplo n.º 25
0
 /**
  * Write an AssessmetnItemSession from the current binary stream.
  *
  * @param \qtism\runtime\storage\common\AssessmentTestSeeker $seeker The AssessmentTestSeeker object from where the position of components will be pulled out.
  * @param \qtism\runtime\tests\AssessmentItemSession $session An AssessmentItemSession object.
  * @throws \qtism\runtime\storage\binary\QtiBinaryStreamAccessException
  */
 public function writeAssessmentItemSession(AssessmentTestSeeker $seeker, AssessmentItemSession $session)
 {
     try {
         $this->writeShort($seeker->seekPosition($session->getAssessmentItem()));
         $this->writeTinyInt($session->getState());
         $this->writeTinyInt($session->getNavigationMode());
         $this->writeTinyInt($session->getSubmissionMode());
         $this->writeBoolean($session->isAttempting());
         $isItemSessionControlDefault = $session->getItemSessionControl()->isDefault();
         if ($isItemSessionControlDefault === true) {
             $this->writeBoolean(false);
         } else {
             $this->writeBoolean(true);
             $this->writeShort($seeker->seekPosition($session->getItemSessionControl()));
         }
         $this->writeTinyInt($session['numAttempts']->getValue());
         $this->writeDuration($session['duration']);
         $this->writeString($session['completionStatus']->getValue());
         $timeReference = $session->getTimeReference();
         if (is_null($timeReference) === true) {
             // Describe that we have no time reference for the session.
             $this->writeBoolean(false);
         } else {
             // Describe that we have a time reference for the session.
             $this->writeBoolean(true);
             // Write the time reference.
             $this->writeDateTime($timeReference);
         }
         // Write the session variables.
         // (minus the 3 built-in variables)
         $varCount = count($session) - 3;
         $this->writeTinyInt($varCount);
         $itemOutcomes = $session->getAssessmentItem()->getOutcomeDeclarations();
         $itemResponses = $session->getAssessmentItem()->getResponseDeclarations();
         $itemTemplates = $session->getAssessmentItem()->getTemplateDeclarations();
         foreach ($session->getKeys() as $varId) {
             if (in_array($varId, array('numAttempts', 'duration', 'completionStatus')) === false) {
                 $var = $session->getVariable($varId);
                 if ($var instanceof OutcomeVariable) {
                     $variableDeclaration = $itemOutcomes[$varId];
                     $variable = OutcomeVariable::createFromDataModel($variableDeclaration);
                     $varNature = 0;
                 } elseif ($var instanceof ResponseVariable) {
                     $variableDeclaration = $itemResponses[$varId];
                     $variable = ResponseVariable::createFromDataModel($variableDeclaration);
                     $varNature = 1;
                 } elseif ($var instanceof TemplateVariable) {
                     $variableDeclaration = $itemTemplates[$varId];
                     $variable = TemplateVariable::createFromDataModel($variableDeclaration);
                     $varNature = 2;
                 }
                 try {
                     $this->writeShort($varNature);
                     $this->writeShort($seeker->seekPosition($variableDeclaration));
                     // If defaultValue or correct response is different from what's inside
                     // the variable declaration, just write it.
                     $hasDefaultValue = !Utils::equals($variable->getDefaultValue(), $var->getDefaultValue());
                     $hasCorrectResponse = false;
                     if ($varNature === 1 && !Utils::equals($variable->getCorrectResponse(), $var->getCorrectResponse())) {
                         $hasCorrectResponse = true;
                     }
                     $this->writeBoolean($hasDefaultValue);
                     $this->writeBoolean($hasCorrectResponse);
                     $this->writeVariableValue($var, self::RW_VALUE);
                     if ($hasDefaultValue === true) {
                         $this->writeVariableValue($var, self::RW_DEFAULTVALUE);
                     }
                     if ($hasCorrectResponse === true) {
                         $this->writeVariableValue($var, self::RW_CORRECTRESPONSE);
                     }
                 } catch (OutOfBoundsException $e) {
                     $msg = "No variable found in the assessmentTest tree structure.";
                     throw new QtiBinaryStreamAccessException($msg, $this, QtiBinaryStreamAccessException::ITEM_SESSION, $e);
                 }
             }
         }
         // Write shuffling states if any.
         $shufflingStates = $session->getShufflingStates();
         $this->writeTinyInt(count($shufflingStates));
         foreach ($shufflingStates as $shufflingState) {
             $this->writeShufflingState($shufflingState);
         }
     } catch (BinaryStreamAccessException $e) {
         $msg = "An error occured while writing an assessment item session.";
         throw new QtiBinaryStreamAccessException($msg, $this, QtiBinaryStreamAccessException::ITEM_SESSION, $e);
     } catch (OutOfBoundsException $e) {
         $msg = "No assessmentItemRef found in the assessmentTest tree structure.";
         throw new QtiBinaryStreamAccessException($msg, $this, QtiBinaryStreamAccessException::ITEM_SESSION, $e);
     }
 }