/**
  * Apply the current SetCorrectResponse rule on the current state.
  * 
  * A RuleProcessingException will be thrown if:
  * 
  * * No variable corresponds to the given identifier in the current state.
  * * The target variable is not a ResponseVariable.
  * * The baseType and/or cardinality of the value to be set does not correspond to the baseType and/or cardinality of the target variable.
  * * An error occurs while processing the expression representing the value to be set.
  * 
  * @throws \qtism\runtime\rules\RuleProcessingException
  */
 public function process()
 {
     $rule = $this->getRule();
     $state = $this->getState();
     $variableIdentifier = $rule->getIdentifier();
     $var = $state->getVariable($variableIdentifier);
     if (is_null($var) === true) {
         $msg = "No variable with identifier '{$variableIdentifier}' to be set in the current state.";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::NONEXISTENT_VARIABLE);
     } elseif (!$var instanceof ResponseVariable) {
         $msg = "The variable to set '{$variableIdentifier}' is not an instance of 'ResponseVariable'.";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::WRONG_VARIABLE_TYPE);
     }
     try {
         $expressionEngine = new ExpressionEngine($rule->getExpression(), $state);
         $val = $expressionEngine->process();
         $var->setCorrectResponse($val);
     } catch (InvalidArgumentException $e) {
         $varBaseType = BaseType::getNameByConstant($var->getBaseType()) === false ? 'noBaseType' : BaseType::getNameByConstant($var->getBaseType());
         $varCardinality = Cardinality::getNameByConstant($var->getCardinality());
         // The affected value does not match the baseType of the variable $var.
         $msg = "Unable to set value {$val} to variable '{$variableIdentifier}' (cardinality = {$varCardinality}, baseType = {$varBaseType}).";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::WRONG_VARIABLE_BASETYPE, $e);
     } catch (ExpressionProcessingException $e) {
         $msg = "An error occured while processing the expression bound with the 'SetCorrectResponse' rule.";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::RUNTIME_ERROR, $e);
     }
 }
 public static function asArray()
 {
     $values = BaseType::asArray();
     $values['ANY'] = self::ANY;
     $values['SAME'] = self::SAME;
     return $values;
 }
 /**
  * Unmarshall a DOMElement object corresponding to a QTI variableDeclaration element.
  *
  * @param \DOMElement $element A DOMElement object.
  * @return \qtism\data\QtiComponent A VariableDeclaration object.
  * @throws \qtism\data\storage\xml\marshalling\UnmarshallingException
  */
 protected function unmarshall(DOMElement $element)
 {
     try {
         // identifier is a mandatory value for the variableDeclaration element.
         if (($identifier = static::getDOMElementAttributeAs($element, 'identifier')) !== null) {
             // cardinality is a mandatory value too.
             if (($cardinality = static::getDOMElementAttributeAs($element, 'cardinality')) !== null) {
                 $object = new VariableDeclaration($identifier, -1, Cardinality::getConstantByName($cardinality));
                 // deal with baseType.
                 $baseType = static::getDOMElementAttributeAs($element, 'baseType');
                 if (!empty($baseType)) {
                     $object->setBaseType(BaseType::getConstantByName($baseType));
                 }
                 // set up optional default value.
                 $defaultValueElements = $element->getElementsByTagName('defaultValue');
                 if ($defaultValueElements->length == 1) {
                     $defaultValueElement = $defaultValueElements->item(0);
                     $defaultValueMarshaller = $this->getMarshallerFactory()->createMarshaller($defaultValueElements->item(0), array($object->getBaseType()));
                     $object->setDefaultValue($defaultValueMarshaller->unmarshall($defaultValueElement));
                 }
                 return $object;
             } else {
                 $msg = "The mandatory attribute 'cardinality' is missing from element '" . $element->localName . "'.";
                 throw new UnmarshallingException($msg, $element);
             }
         } else {
             $msg = "The mandatory attribute 'identifier' is missing from element '" . $element->localName . "'.";
             throw new UnmarshallingException($msg, $element);
         }
     } catch (InvalidArgumentException $e) {
         $msg = "An unexpected error occured while unmarshalling the variableDeclaration.";
         throw new UnmarshallingException($msg, $element, $e);
     }
 }
Exemple #4
0
 /**
  * Checks whether or not $value:
  *
  * * is an instance of OutcomeVariable
  * * has a 'duration' QTI baseType.
  * * has 'single' QTI cardinality.
  *
  * @throws \InvalidArgumentException If one or more of the conditions above are not respected.
  */
 protected function checkType($value)
 {
     parent::checkType($value);
     if (!$value instanceof OutcomeVariable) {
         $className = get_class($value);
         $msg = "The DurationStore only aims at storing OutcomeVariable objects, {$className} object given.";
         throw new InvalidArgumentException($msg);
     }
     if (($bt = $value->getBaseType()) !== BaseType::DURATION) {
         $baseTypeName = BaseType::getNameByConstant($bt);
         $msg = "The DurationStore only aims at storing OutcomeVariable objects with a 'duration' baseType, ";
         $msg .= "'{$baseTypeName}' baseType given ";
         $id = $value->getIdentifier();
         $msg .= "for variable '{$id}'.";
         throw new InvalidArgumentException($msg);
     }
     if (($bt = $value->getCardinality()) !== Cardinality::SINGLE) {
         $cardinalityName = Cardinality::getNameByConstant($bt);
         $msg = "The DurationStore only aims at storing OutcomeVariable objects with a 'single' cardinality, ";
         $msg .= "'{$cardinalityName}' cardinality given ";
         $id = $value->getIdentifier();
         $msg .= "for variable '{$id}'.";
         throw new InvalidArgumentException($msg);
     }
 }
 public function setBaseType($baseType = -1)
 {
     if (in_array($baseType, BaseType::asArray()) || $baseType == -1) {
         $this->baseType = $baseType;
     } else {
         $msg = "The baseType argument must be a value from the BaseType enumeration.";
         throw new InvalidArgumentException($msg);
     }
 }
 /**
  * Set the base type of the expected targetValue. 
  * 
  * @param integer $baseType A value from the BaseType enumeration.
  * @throws InvalidArgumentException If $baseType is not a value from the BaseType enumeration.
  */
 protected function setBaseType($baseType)
 {
     if (in_array($baseType, BaseType::asArray())) {
         $this->baseType = $baseType;
     } else {
         $msg = "BaseType must be a value from the BaseType enumeration.";
         throw new InvalidArgumentException($msg);
     }
 }
 /**
  * Unmarshall a DOMElement object corresponding to a QTI baseValue element.
  *
  * @param \DOMElement $element A DOMElement object.
  * @return \qtism\data\QtiComponent A BaseValue object.
  * @throws \qtism\data\storage\xml\marshalling\UnmarshallingException
  */
 protected function unmarshall(DOMElement $element)
 {
     if (($baseType = static::getDOMElementAttributeAs($element, 'baseType', 'string')) !== null) {
         $value = $element->nodeValue;
         $baseTypeCst = BaseType::getConstantByName($baseType);
         $object = new BaseValue($baseTypeCst, Utils::stringToDatatype($value, $baseTypeCst));
         return $object;
     } else {
         $msg = "The mandatory attribute 'baseType' is missing from element '" . $element->localName . "'.";
         throw new UnmarshallingException($msg, $element);
     }
 }
 /**
  * Marshall a testVariable QTI element in its TestVariable object equivalent.
  *
  * @param \DOMElement A DOMElement object.
  * @return \qtism\data\QtiComponent The corresponding TestVariable object.
  */
 protected function unmarshall(DOMElement $element)
 {
     $baseComponent = parent::unmarshall($element);
     if (($variableIdentifier = static::getDOMElementAttributeAs($element, 'variableIdentifier')) !== null) {
         $object = new TestVariables($variableIdentifier);
         $object->setSectionIdentifier($baseComponent->getSectionIdentifier());
         $object->setIncludeCategories($baseComponent->getIncludeCategories());
         $object->setExcludeCategories($baseComponent->getExcludeCategories());
         if (($baseType = static::getDOMElementAttributeAs($element, 'baseType')) !== null) {
             $object->setBaseType(BaseType::getConstantByName($baseType));
         }
         if (($weightIdentifier = static::getDOMElementAttributeAs($element, 'weightIdentifier')) !== null) {
             $object->setWeightIdentifier($weightIdentifier);
         }
         return $object;
     } else {
         $msg = "The mandatory attribute 'variableIdentifier' is missing from element '" . $element->localName . "'.";
         throw new UnmarshallingException($msg, $element);
     }
 }
 /**
  * Process the setOutcomeValue/setTemplateValue rule.
  *
  * A RuleProcessingException will be thrown if:
  *
  * * The variable does not exist.
  * * The requested variable is not an OutcomeVariable/TemplateVariable.
  * * The variable's baseType does not match the baseType of the affected value.
  * * An error occurs while processing 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 = "No variable with identifier '{$identifier}' to be set in the current state.";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::NONEXISTENT_VARIABLE);
     } elseif (Reflection::isInstanceOf($var, $this->getVariableType()) === false) {
         $msg = "The variable to set '{$identifier}' is not an instance of '" . $this->getVariableType() . "'.";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::WRONG_VARIABLE_TYPE);
     }
     // Process the expression.
     // Its result will be the value to set to the target variable.
     try {
         $expressionEngine = new ExpressionEngine($rule->getExpression(), $state);
         $val = $expressionEngine->process();
     } catch (ExpressionProcessingException $e) {
         $msg = "An error occured while processing the expression bound with the '" . Reflection::shortClassName($rule) . "' rule.";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::RUNTIME_ERROR, $e);
     }
     // The variable exists, its new value is processed.
     try {
         // juggle a little bit (int -> float, float -> int)
         if ($val !== null && $var->getCardinality() === Cardinality::SINGLE) {
             $baseType = $var->getBaseType();
             if ($baseType === BaseType::INTEGER && $val instanceof Float) {
                 $val = new Integer(intval($val->getValue()));
             } elseif ($baseType === BaseType::FLOAT && $val instanceof Integer) {
                 $val = new Float(floatval($val->getValue()));
             }
         }
         $var->setValue($val);
     } catch (InvalidArgumentException $e) {
         $varBaseType = BaseType::getNameByConstant($var->getBaseType()) === false ? 'noBaseType' : BaseType::getNameByConstant($var->getBaseType());
         $varCardinality = Cardinality::getNameByConstant($var->getCardinality());
         // The affected value does not match the baseType of the variable $var.
         $msg = "Unable to set value {$val} to variable '{$identifier}' (cardinality = {$varCardinality}, baseType = {$varBaseType}).";
         throw new RuleProcessingException($msg, $this, RuleProcessingException::WRONG_VARIABLE_BASETYPE, $e);
     }
 }
 /**
  * Unmarshall a DOMElement object corresponding to a QTI MatchTable element.
  * 
  * @param DOMElement $element A DOMElement object.
  * @return QtiComponent A MatchTable object.
  * @throws UnmarshallingException If the $element to unmarshall has no matchTableEntry children.
  */
 protected function unmarshall(DOMElement $element)
 {
     $matchTableEntryElements = $element->getElementsByTagName('matchTableEntry');
     if ($matchTableEntryElements->length > 0) {
         $matchTableEntries = new MatchTableEntryCollection();
         for ($i = 0; $i < $matchTableEntryElements->length; $i++) {
             $marshaller = $this->getMarshallerFactory()->createMarshaller($matchTableEntryElements->item($i), array($this->getBaseType()));
             $matchTableEntries[] = $marshaller->unmarshall($matchTableEntryElements->item($i));
         }
         $object = new MatchTable($matchTableEntries);
         if (($defaultValue = static::getDOMElementAttributeAs($element, 'defaultValue')) !== null) {
             try {
                 $defaultValue = Utils::stringToDatatype($defaultValue, $this->getBaseType());
                 $object->setDefaultValue($defaultValue);
             } catch (InvalidArgumentException $e) {
                 $strType = BaseType::getNameByConstant($this->getBaseType());
                 $msg = "Unable to transform '{$defaultValue}' in a {$strType}.";
                 throw new UnmarshallingException($msg, $element, $e);
             }
         }
         return $object;
     } else {
         $msg = "A QTI matchTable element must contain at least one matchTableEntry element.";
         throw new UnmarshallingException($msg, $element);
     }
 }
 /**
  * Transform a PCI JSON representation of QTI data into the QTISM runtime model.
  * 
  * @param string|array $json The json data to be transformed.
  * @throws UnmarshallingException If an error occurs while processing $json.
  * @return null|qtism\common\datatypes\QtiDataType|array
  */
 public function unmarshall($json)
 {
     if (is_string($json) === true) {
         $tmpJson = @json_decode($json, true);
         if ($tmpJson === null) {
             // An error occured while decoding.
             $msg = "An error occured while decoding the following JSON data '" . mb_substr($json, 0, 30, 'UTF-8') . "...'.";
             $code = UnmarshallingException::JSON_DECODE;
             throw new UnmarshallingException($msg, $code);
         }
         $json = $tmpJson;
     }
     if (is_array($json) === false || count($json) === 0) {
         $msg = "The '" . get_class($this) . "::unmarshall' method only accepts a JSON string or a non-empty array as argument, '";
         if (is_object($json) === true) {
             $msg .= get_class($json);
         } else {
             $msg .= gettype($json);
         }
         $msg .= "' given.";
         $code = UnmarshallingException::NOT_SUPPORTED;
         throw new UnmarshallingException($msg, $code);
     }
     if (Arrays::isAssoc($json) === false) {
         $msg = "The '" . get_class($this) . "::unmarshall' does not accepts non-associative arrays.";
         $code = UnmarshallingException::NOT_SUPPORTED;
         throw new UnmarshallingException($msg, $code);
     }
     // Check whether or not $json is a state (no 'base' nor 'list' keys found),
     // a base, a list or a record.
     $keys = array_keys($json);
     if (in_array('base', $keys) === true) {
         // This is a base.
         return $this->unmarshallUnit($json);
     } else {
         if (in_array('list', $keys) === true) {
             $keys = array_keys($json['list']);
             if (isset($keys[0]) === false) {
                 $msg = "No baseType provided for list.";
                 throw new UnmarshallingException($msg, UnmarshallingException::NOT_PCI);
             }
             $baseType = BaseType::getConstantByName($keys[0]);
             if ($baseType === false) {
                 $msg = "Unknown QTI baseType '" . $keys[0] . "'.";
                 $code = UnmarshallingException::NOT_PCI;
                 throw new UnmarshallingException($msg, $code);
             }
             $returnValue = new MultipleContainer($baseType);
             // This is a list.
             foreach ($json['list'][$keys[0]] as $v) {
                 try {
                     if ($v === null) {
                         $returnValue[] = $this->unmarshallUnit(array('base' => $v));
                     } else {
                         $returnValue[] = $this->unmarshallUnit(array('base' => array($keys[0] => $v)));
                     }
                 } catch (InvalidArgumentException $e) {
                     $strBaseType = BaseType::getNameByConstant($baseType);
                     $msg = "A value is not compliant with the '{$strBaseType}' baseType.";
                     $code = UnmarshallingException::NOT_PCI;
                     throw new UnmarshallingException($msg, $code);
                 }
             }
             return $returnValue;
         } else {
             if (in_array('record', $keys) === true) {
                 // This is a record.
                 $returnValue = new RecordContainer();
                 if (count($json['record']) === 0) {
                     return $returnValue;
                 }
                 foreach ($json['record'] as $v) {
                     if (isset($v['name']) === false) {
                         $msg = "No 'name' key found in record field.";
                         $code = UnmarshallingException::NOT_PCI;
                         throw new UnmarshallingException($msg, $code);
                     }
                     if (isset($v['base']) === true || array_key_exists('base', $v) && $v['base'] === null) {
                         $unit = array('base' => $v['base']);
                     } else {
                         // No value found, let's go for a null value.
                         $unit = array('base' => null);
                     }
                     $returnValue[$v['name']] = $this->unmarshallUnit($unit);
                 }
                 return $returnValue;
             } else {
                 // This is a state.
                 $state = array();
                 foreach ($json as $k => $j) {
                     $state[$k] = $this->unmarshall($j);
                 }
                 return $state;
             }
         }
     }
 }
 /**
  * Process the MapResponsePoint Expression.
  *
  * An ExpressionProcessingException is throw if:
  *
  * * The expression's identifier attribute does not point a variable in the current State object.
  * * The targeted variable is not a ResponseVariable object.
  * * The targeted variable has no areaMapping.
  * * The target variable has the RECORD cardinality.
  *
  * @return float A transformed float value according to the areaMapping of the target variable.
  * @throws \qtism\runtime\expressions\ExpressionProcessingException
  */
 public function process()
 {
     $expr = $this->getExpression();
     $identifier = $expr->getIdentifier();
     $state = $this->getState();
     $var = $state->getVariable($identifier);
     if (!is_null($var)) {
         if ($var instanceof ResponseVariable) {
             $areaMapping = $var->getAreaMapping();
             if (!is_null($areaMapping)) {
                 // Correct cardinality ?
                 if ($var->getBaseType() === BaseType::POINT && ($var->isSingle() || $var->isMultiple())) {
                     // We can begin!
                     // -- Null value, nothing will match
                     if ($var->isNull()) {
                         return new Float($areaMapping->getDefaultValue());
                     }
                     if ($var->isSingle()) {
                         $val = new MultipleContainer(BaseType::POINT, array($state[$identifier]));
                     } else {
                         $val = $state[$identifier];
                     }
                     $result = 0;
                     $mapped = array();
                     foreach ($val as $point) {
                         foreach ($areaMapping->getAreaMapEntries() as $areaMapEntry) {
                             $coords = $areaMapEntry->getCoords();
                             if (!in_array($coords, $mapped) && $coords->inside($point)) {
                                 $mapped[] = $coords;
                                 $result += $areaMapEntry->getMappedValue();
                             }
                         }
                     }
                     // If no relevant mapping found, return the default.
                     if (count($mapped) === 0) {
                         return new Float($areaMapping->getDefaultValue());
                     } else {
                         // Check upper and lower bound.
                         if ($areaMapping->hasLowerBound() && $result < $areaMapping->getLowerBound()) {
                             return new Float($areaMapping->getLowerBound());
                         } elseif ($areaMapping->hasUpperBound() && $result > $areaMapping->getUpperBound()) {
                             return new Float($areaMapping->getUpperBound());
                         } else {
                             return new Float(floatval($result));
                         }
                     }
                 } else {
                     if ($var->isRecord()) {
                         $msg = "The MapResponsePoint expression cannot be applied to RECORD variables.";
                         throw new ExpressionProcessingException($msg, $this, ExpressionProcessingException::WRONG_VARIABLE_CARDINALITY);
                     } else {
                         $strBaseType = BaseType::getNameByConstant($var->getBaseType());
                         $msg = "The MapResponsePoint expression applies only on variables with baseType 'point', baseType '{$strBaseType}' given.";
                         throw new ExpressionProcessingException($msg, $this, ExpressionProcessingException::WRONG_VARIABLE_BASETYPE);
                     }
                 }
             } else {
                 $msg = "Variable with identifier '{$identifier}' has no areaMapping.";
                 throw new ExpressionProcessingException($msg, $this, ExpressionProcessingException::INCONSISTENT_VARIABLE);
             }
         } else {
             $msg = "The variable with identifier '{$identifier}' is not a ResponseVariable.";
             throw new ExpressionProcessingException($msg, $this, ExpressionProcessingException::WRONG_VARIABLE_TYPE);
         }
     } else {
         $msg = "No variable with identifier '{$identifier}' could be found in the current State object.";
         throw new ExpressionProcessingException($msg, $this, ExpressionProcessingException::NONEXISTENT_VARIABLE);
     }
 }
 /**
  * Transmit a test-level QtiSm Runtime Variable to the target Result Server as a test result.
  * 
  * @param mixed $variable An OutcomeVariable object to be transmitted to the target Result Server.
  * @param string $transmissionId A unique identifier that identifies uniquely the visited test.
  * @param string $testUri An optional URL that identifies uniquely the test the $variable comes from.
  */
 public function transmitTestVariable($variable, $transmissionId, $testUri = '')
 {
     $resultVariable = new taoResultServer_models_classes_OutcomeVariable();
     $resultVariable->setIdentifier($variable->getIdentifier());
     $resultVariable->setBaseType(BaseType::getNameByConstant($variable->getBaseType()));
     $resultVariable->setCardinality(Cardinality::getNameByConstant($variable->getCardinality()));
     $value = $variable->getValue();
     $resultVariable->setValue(self::transformValue($value));
     try {
         $this->getResultServer()->storeTestVariable($testUri, $resultVariable, $transmissionId);
     } catch (Exception $e) {
         $msg = "An error occured while transmitting a Response Variable to the target result server.";
         $code = taoQtiCommon_helpers_ResultTransmissionException::OUTCOME;
         throw new taoQtiCommon_helpers_ResultTransmissionException($msg, $code);
     }
 }
Exemple #14
0
 /**
  * Unmarshall a DOMElement object corresponding to a QTI Value element.
  *
  * @param \DOMElement $element A DOMElement object.
  * @return \qtism\data\QtiComponent A Value object.
  * @throws \qtism\data\storage\xml\marshalling\UnmarshallingException If the 'baseType' attribute is not a valid QTI baseType.
  */
 protected function unmarshall(DOMElement $element)
 {
     $object = null;
     if (($baseType = static::getDOMElementAttributeAs($element, 'baseType', 'string')) !== null) {
         // baseType attribute is set -> part of a record.
         $baseTypeCst = BaseType::getConstantByName($baseType);
         if ($baseTypeCst !== false) {
             $object = new Value(Utils::stringToDatatype(trim($element->nodeValue), $baseTypeCst), $baseTypeCst);
             $object->setPartOfRecord(true);
         } else {
             $msg = "The 'baseType' attribute value ('{$value}') is not a valid QTI baseType in element '" . $element->localName . "'.";
             throw new UnmarshallingException($msg, $element);
         }
     } else {
         // baseType attribute not set -> not part of a record.
         $nodeValue = trim($element->nodeValue);
         if ($nodeValue !== '') {
             // Try to use the marshaller as parametric to know how to unserialize the value.
             if ($this->getBaseType() != -1) {
                 $object = new Value(Utils::stringToDatatype($nodeValue, $this->getBaseType()), $this->getBaseType());
             } else {
                 // value used as plain string (at your own risks).
                 $object = new Value($nodeValue);
             }
         } else {
             $msg = "The element '" . $element->localName . "' has no value.";
             throw new UnmarshallingException($msg, $element);
         }
     }
     if (($value = static::getDOMElementAttributeAs($element, 'fieldIdentifier', 'string')) !== null) {
         $object->setFieldIdentifier($value);
     }
     return $object;
 }
Exemple #15
0
 /**
  * Transform a TAO serialized result value to its QtiDatatype equivalent.
  * 
  * This method transforms a TAO serialized result $value to a the equivalent QtiDatatype depending
  * on result's $cardinality and $baseType.
  * 
  * Note: at the present time, this implementation only supports 'single', 'multiple', and 'ordered' cardinality in conjunction
  * with the 'identifier', 'boolean', 'pair' or 'directedPair' baseType.
  * 
  * @param string $cardinality i.e. 'ordered', 'multiple' or 'single'.
  * @param string $basetype i.e. 'identifier'
  * @param string $value A TAO serialized result value e.g. '<choice1 choice2 choice3>'
  * @return mixed A QtiDatatype object or null in case of no possibility to transform $value in the appropriate target $cardinality and $basetype.
  */
 public static function toQtiDatatype($cardinality, $basetype, $value)
 {
     // @todo support all baseTypes
     $datatype = null;
     if (is_string($value) && empty($value) === false && $cardinality !== 'record' && ($basetype === 'identifier' || $basetype === 'pair' || $basetype === 'directedPair' || $basetype === 'boolean')) {
         if ($cardinality !== 'simple') {
             $value = trim($value, "<>[]");
             $value = explode(';', $value);
         } else {
             $value = array($value);
         }
         if (count($value) === 1 && empty($value[0]) === true) {
             $value = array();
         }
         $value = array_map(function ($val) {
             return trim($val);
         }, $value);
         $qtiBasetype = BaseType::getConstantByName($basetype);
         $datatype = $cardinality === 'ordered' ? new OrderedContainer($qtiBasetype) : new MultipleContainer($qtiBasetype);
         foreach ($value as $val) {
             try {
                 switch ($basetype) {
                     case 'identifier':
                         $datatype[] = new QtiIdentifier($val);
                         break;
                     case 'pair':
                         $pair = explode(" ", $val);
                         if (count($pair) === 2) {
                             $datatype[] = new QtiPair($pair[0], $pair[1]);
                         }
                         break;
                     case 'directedPair':
                         $pair = explode(" ", $val);
                         if (count($pair) === 2) {
                             $datatype[] = new QtiDirectedPair($pair[0], $pair[1]);
                         }
                         break;
                     case 'boolean':
                         if ($val === 'true') {
                             $datatype[] = new QtiBoolean(true);
                         } elseif ($val === 'false') {
                             $datatype[] = new QtiBoolean(false);
                         } else {
                             $datatype[] = new QtiBoolean($val);
                         }
                         break;
                 }
             } catch (InvalidArgumentException $e) {
                 $datatype = null;
                 break;
             }
         }
         $datatype = $cardinality === 'single' ? isset($datatype[0]) ? $datatype[0] : null : $datatype;
     }
     return $datatype;
 }
Exemple #16
0
 /**
  * Marshall a single unit of QTI data.
  *
  * @param \qtism\runtime\common\State|\qtism\common\datatypes\QtiDatatype|null $unit
  * @return array An array representing the JSON data to be encoded later on.
  */
 protected function marshallUnit($unit)
 {
     if (is_null($unit) === true) {
         $json = array('base' => null);
     } elseif ($unit instanceof QtiScalar) {
         $json = $this->marshallScalar($unit);
     } elseif ($unit instanceof MultipleContainer) {
         $json = array();
         $strBaseType = BaseType::getNameByConstant($unit->getBaseType());
         $json['list'] = array($strBaseType => array());
         foreach ($unit as $u) {
             $data = $this->marshallUnit($u);
             $json['list'][$strBaseType][] = $data['base'][$strBaseType];
         }
     } elseif ($unit instanceof RecordContainer) {
         $json = array();
         $json['record'] = array();
         foreach ($unit as $k => $u) {
             $data = $this->marshallUnit($u);
             $jsonEntry = array();
             $jsonEntry['name'] = $k;
             if (isset($data['base']) === true || $data['base'] === null) {
                 // Primitive base type.
                 $jsonEntry['base'] = $data['base'];
             } else {
                 // A nested list.
                 $jsonEntry['list'] = $data['list'];
             }
             $json['record'][] = $jsonEntry;
         }
     } else {
         $json = $this->marshallComplex($unit);
     }
     return $json;
 }
Exemple #17
0
 /**
  * Transform a string representing a QTI valueType value in a
  * the correct datatype.
  *
  * @param string $string The QTI valueType value as a string.
  * @param integer $baseType The QTI baseType that defines the datatype of $string.
  * @return mixed A converted object/primitive type.
  * @throws \InvalidArgumentException If $baseType is not a value from the BaseType enumeration.
  * @throws \UnexpectedValueException If $string cannot be transformed in a Value expression with the given $baseType.
  */
 public static function stringToDatatype($string, $baseType)
 {
     if (in_array($baseType, BaseType::asArray())) {
         $value = null;
         switch ($baseType) {
             case BaseType::BOOLEAN:
                 if (Format::isBoolean($string)) {
                     $value = Format::toLowerTrim($string) == 'true' ? true : false;
                     return $value;
                 } else {
                     $msg = "'{$string}' cannot be transformed into boolean.";
                     throw new UnexpectedValueException($msg);
                 }
                 break;
             case BaseType::INTEGER:
                 if (Format::isInteger($string)) {
                     $value = intval($string);
                     return $value;
                 } else {
                     $msg = "'{$string}' cannot be transformed into integer.";
                     throw new UnexpectedValueException($msg);
                 }
                 break;
             case BaseType::FLOAT:
                 if (Format::isFloat($string)) {
                     $value = floatval($string);
                     return $value;
                 } else {
                     $msg = "'{$string}' cannot be transformed into float.";
                     throw new UnexpectedValueException($msg);
                 }
                 break;
             case BaseType::URI:
                 if (Format::isUri($string)) {
                     return $string;
                 } else {
                     $msg = "'{$string}' is not a valid URI.";
                     throw new UnexpectedValueException($msg);
                 }
                 break;
             case BaseType::IDENTIFIER:
                 if (Format::isIdentifier($string)) {
                     return $string;
                 } else {
                     $msg = "'{$string}' is not a valid QTI Identifier.";
                     throw new UnexpectedValueException($msg);
                 }
                 break;
             case BaseType::INT_OR_IDENTIFIER:
                 if (Format::isIdentifier($string)) {
                     return $string;
                 } elseif (Format::isInteger($string)) {
                     return intval($string);
                 } else {
                     $msg = "'{$string}' is not a valid QTI Identifier nor a valid integer.";
                     throw new UnexpectedValueException($msg);
                 }
                 break;
             case BaseType::PAIR:
                 if (Format::isPair($string)) {
                     $pair = explode(" ", $string);
                     return new Pair($pair[0], $pair[1]);
                 } else {
                     $msg = "'{$string}' is not a valid pair.";
                     throw new UnexpectedValueException($msg);
                 }
                 break;
             case BaseType::DIRECTED_PAIR:
                 if (Format::isDirectedPair($string)) {
                     $pair = explode(" ", $string);
                     return new DirectedPair($pair[0], $pair[1]);
                 } else {
                     $msg = "'{$string}' is not a valid directed pair.";
                     throw new UnexpectedValueException($msg);
                 }
                 break;
             case BaseType::DURATION:
                 if (Format::isDuration($string)) {
                     return new Duration($string);
                 } else {
                     $msg = "'{$string}' is not a valid duration.";
                     throw new UnexpectedValueException($msg);
                 }
                 break;
             case BaseType::FILE:
                 throw new \RuntimeException("Unsupported baseType: file.");
                 break;
             case BaseType::STRING:
                 return '' . $string;
                 break;
             case BaseType::POINT:
                 if (Format::isPoint($string)) {
                     $parts = explode(" ", $string);
                     return new Point(intval($parts[0]), intval($parts[1]));
                 } else {
                     $msg = "'{$string}' is not valid point.";
                     throw new UnexpectedValueException($msg);
                 }
                 break;
             default:
                 throw new \RuntimeException("Unknown baseType.");
                 break;
         }
     } else {
         $msg = "BaseType must be a value from the BaseType enumeration.";
         throw new InvalidArgumentException($msg);
     }
 }
 /**
  * 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);
     }
 }
Exemple #19
0
 /**
  * Throw an InvalidArgumentException depending on a given qti:baseType
  * and an in-memory PHP value.
  *
  * @param int $baseType A value from the BaseType enumeration.
  * @param mixed $value A given PHP primitive value.
  * @throws \InvalidArgumentException In any case.
  */
 public static function throwBaseTypeTypingError($baseType, $value)
 {
     $givenValue = gettype($value) == 'object' ? get_class($value) : gettype($value) . ':' . $value;
     $acceptedTypes = BaseType::getNameByConstant($baseType);
     $msg = "The value '{$givenValue}' is not compliant with the '{$acceptedTypes}' baseType.";
     throw new InvalidArgumentException($msg);
 }
Exemple #20
0
 /**
  * @dataProvider invalidBaseTypeConstantProvider
  */
 public function testGetNameByConstantInvalidBaseType($constant)
 {
     $this->assertFalse(BaseType::getNameByConstant($constant));
 }