/** * 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 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; }
/** * 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; } }
/** * @dataProvider inferCardinalityProvider */ public function testInferCardinality($value, $expectedCardinality) { $this->assertTrue(Utils::inferCardinality($value) === $expectedCardinality); }
/** * 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; } }