/**
  * Fill the variable $variableName with a correctly transformed $clientSideValue.
  *
  * @param string $variableName The variable identifier you want to fill.
  * @param array $clientSideValue The value received from the client-side representing the value of the variable with identifier $variableName.
  * @return Variable A Variable object filled with a correctly transformed $clientSideValue.
  * @throws OutOfBoundsException If no variable with $variableName is described in the item.
  * @throws OutOfRangeException If the $clientSideValue does not fit the target variable's baseType.
  */
 public function fill($variableName, $clientSideValue)
 {
     $variableDeclaration = $this->findVariableDeclaration($variableName);
     if ($variableDeclaration === false) {
         $itemId = $this->getItemRef()->getIdentifier();
         $msg = "Variable declaration with identifier '{$variableName}' not found in item '{$itemId}'.";
         throw new OutOfBoundsException($msg);
     }
     // Create Runtime Variable from Data Model.
     $runtimeVar = $variableDeclaration instanceof ResponseDeclaration ? ResponseVariable::createFromDataModel($variableDeclaration) : OutcomeVariable::createFromDataModel($variableDeclaration);
     // Set the data into the runtime variable thanks to the PCI JSON Unmarshaller
     // from QTISM.
     try {
         $unmarshaller = new PciJsonUnmarshaller(taoQtiCommon_helpers_Utils::getFileDatatypeManager());
         $value = $unmarshaller->unmarshall($clientSideValue);
         // Dev's note:
         // The PCI JSON Representation format does make the difference between multiple and ordered containers.
         // We then have to juggle a bit if the target variable has ordered cardinality.
         if ($value !== null && $value->getCardinality() === Cardinality::MULTIPLE && $variableDeclaration->getCardinality() === Cardinality::ORDERED) {
             $value = new OrderedContainer($value->getBaseType(), $value->getArrayCopy());
         }
         $runtimeVar->setValue($value);
     } catch (PciJsonUnmarshallingException $e) {
         $strClientSideValue = mb_substr(var_export($clientSideValue, true), 0, 50, TAO_DEFAULT_ENCODING);
         $msg = "Unable to put value '{$strClientSideValue}' into variable '{$variableName}'.";
         throw new OutOfRangeException($msg, 0, $e);
     }
     return $runtimeVar;
 }
예제 #2
0
 /**
  * @depends testSimple
  */
 public function testEmptyStringValue()
 {
     $baseValue = new BaseValue(BaseType::STRING, '');
     $customOperator = new CustomOperator(new ExpressionCollection(array($baseValue)), '<customOperator class="qti.customOperators.csvToOrdered"><baseValue baseType="string"></baseValue></customOperator>');
     $operands = new OperandsCollection(array(new QtiString('')));
     $operator = new CsvToOrdered($customOperator, $operands);
     $result = $operator->process();
     $expected = new OrderedContainer(BaseType::STRING, array(new QtiString('')));
     $this->assertInstanceOf('qtism\\runtime\\common\\OrderedContainer', $result);
     $this->assertTrue($expected->equals($result));
 }
예제 #3
0
 public function testMixed()
 {
     $expression = $this->createFakeExpression(2);
     $operands = new OperandsCollection();
     $operands[] = new QtiPoint(0, 0);
     $operands[] = new OrderedContainer(BaseType::POINT, array(new QtiPoint(1, 2), new QtiPoint(2, 3), new QtiPoint(3, 4)));
     $operands[] = new QtiPoint(10, 10);
     $operands[] = new OrderedContainer(BaseType::POINT, array(new QtiPoint(4, 5)));
     $processor = new RepeatProcessor($expression, $operands);
     $result = $processor->process();
     $comparison = new OrderedContainer(BaseType::POINT, array(new QtiPoint(0, 0), new QtiPoint(1, 2), new QtiPoint(2, 3), new QtiPoint(3, 4), new QtiPoint(10, 10), new QtiPoint(4, 5), new QtiPoint(0, 0), new QtiPoint(1, 2), new QtiPoint(2, 3), new QtiPoint(3, 4), new QtiPoint(10, 10), new QtiPoint(4, 5)));
     $this->assertTrue($comparison->equals($result));
 }
예제 #4
0
    public function testMultipleCardinality()
    {
        $variableDeclaration = $this->createComponentFromXml('
			<responseDeclaration identifier="response1" baseType="duration" cardinality="ordered">
				<defaultValue>
					<value>P2D</value>
					<value>P3D</value>
					<value>P4D</value>
				</defaultValue>
			</responseDeclaration>
		');
        $expr = $this->createComponentFromXml('<default identifier="response1"/>');
        $variable = ResponseVariable::createFromDataModel($variableDeclaration);
        $processor = new DefaultProcessor($expr);
        $processor->setState(new State(array($variable)));
        $comparable = new OrderedContainer(BaseType::DURATION);
        $comparable[] = new QtiDuration('P2D');
        $comparable[] = new QtiDuration('P3D');
        $comparable[] = new QtiDuration('P4D');
        $this->assertTrue($comparable->equals($processor->process()));
    }
예제 #5
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;
     }
 }