/** * 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); } }
/** * 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); } }
/** * 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; } } } }
/** * 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; }
/** * 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; }
/** * @dataProvider invalidBaseTypeProvider */ public function testGetConstantByNameInvalidBaseType($baseType) { $this->assertFalse(BaseType::getConstantByName($baseType)); }