Example #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;
 }
Example #2
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;
 }
 /**
  * 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 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());
 }
Example #5
0
 /**
  * @dataProvider inferBaseTypeProvider
  */
 public function testInferBaseType($value, $expectedBaseType)
 {
     $this->assertTrue(Utils::inferBaseType($value) === $expectedBaseType);
 }
 /**
  * Whether the collection is composed of values with the same baseType.
  * 
  * * If any of the values has not the same baseType than other values in the collection, false is returned.
  * * If the OperandsCollection is an empty collection, false is returned.
  * * If the OperandsCollection contains a value considered to be null, false is returned.
  * * If the OperandsCollection is composed exclusively by non-null RecordContainer objects, true is returned.
  * 
  * @return boolean
  */
 public function sameBaseType()
 {
     $operandsCount = count($this);
     if ($operandsCount > 0 && !$this->containsNull()) {
         // take the first value of the collection as a referer.
         $refValue = $this[0];
         $refType = RuntimeUtils::inferBaseType($refValue);
         for ($i = 1; $i < $operandsCount; $i++) {
             $value = $this[$i];
             $testType = RuntimeUtils::inferBaseType($value);
             if ($testType !== $refType) {
                 return false;
             }
         }
         // In any other case, return true.
         return true;
     } else {
         return false;
     }
 }
Example #7
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);
     }
 }
 /**
  * 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;
 }