ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
class.ilAssOrderingElementList.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3 
4 require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssOrderingElement.php';
5 
13 {
14  public static $objectInstanceCounter = 0;
16 
20 
24 
28 
29  const IDENTIFIER_TYPE_SOLUTION = 'SolutionIds';
30  const IDENTIFIER_TYPE_RANDOM = 'RandomIds';
31 
35  protected static $identifierRegistry = array(
36  self::IDENTIFIER_TYPE_SOLUTION => array(),
37  self::IDENTIFIER_TYPE_RANDOM => array()
38  );
39 
43  protected $questionId;
44 
48  protected $elements;
49 
53  public function __construct()
54  {
55  $this->objectInstanceId = ++self::$objectInstanceCounter;
56 
57  $this->questionId = null;
58  $this->resetElements();
59  }
60 
64  public function __clone()
65  {
66  $this->objectInstanceId = ++self::$objectInstanceCounter;
67 
68  $elements = array();
69 
70  foreach ($this as $key => $element) {
71  $elements[$key] = clone $element;
72  }
73 
74  $this->elements = $elements;
75  }
76 
80  public function getClone()
81  {
82  $that = clone $this;
83  return $that;
84  }
85 
89  public function getQuestionId()
90  {
91  return $this->questionId;
92  }
93 
97  public function setQuestionId($questionId)
98  {
99  $this->questionId = $questionId;
100  }
101 
105  public function loadFromDb()
106  {
107  $ilDB = isset($GLOBALS['DIC']) ? $GLOBALS['DIC']['ilDB'] : $GLOBALS['ilDB'];
108 
109  $result = $ilDB->queryF(
110  "SELECT * FROM qpl_a_ordering WHERE question_fi = %s ORDER BY position ASC",
111  array('integer'),
112  array($this->getQuestionId())
113  );
114 
115  while ($row = $ilDB->fetchAssoc($result)) {
116  $element = new ilAssOrderingElement();
117 
118  $element->setRandomIdentifier($row['random_id']);
119  $element->setSolutionIdentifier($row['solution_key']);
120 
121  $element->setPosition($row['position']);
122  $element->setIndentation($row["depth"]);
123 
124  $element->setContent($row['answertext']);
125 
126  $this->addElement($element);
127  $this->registerIdentifiers($element);
128  }
129  }
130 
134  public function saveToDb()
135  {
137  $ilDB = isset($GLOBALS['DIC']) ? $GLOBALS['DIC']['ilDB'] : $GLOBALS['DIC']['ilDB'];
138 
139  $ilDB->manipulateF(
140  "DELETE FROM qpl_a_ordering WHERE question_fi = %s",
141  array( 'integer' ),
142  array( $this->getQuestionId() )
143  );
144 
145  foreach ($this as $orderElement) {
146  $this->ensureValidIdentifiers($orderElement);
147 
148  $ilDB->insert('qpl_a_ordering', array(
149  'answer_id' => array( 'integer', $ilDB->nextId('qpl_a_ordering') ),
150  'question_fi' => array( 'integer', $this->getQuestionId() ),
151  'answertext' => array( 'text', $orderElement->getContent()),
152  'solution_key' => array( 'integer', $orderElement->getSolutionIdentifier() ),
153  'random_id' => array( 'integer', $orderElement->getRandomIdentifier() ),
154  'position' => array( 'integer', $orderElement->getPosition() ),
155  'depth' => array( 'integer', $orderElement->getIndentation() ),
156  'tstamp' => array( 'integer', time() )
157  ));
158  }
159  }
160 
164  public function clearElementContents()
165  {
166  foreach ($this as $orderingElement) {
167  $orderingElement->setContent('');
168  }
169  }
170 
171  public function countElements()
172  {
173  return count($this->elements);
174  }
175 
176  public function isFirstElementPosition($position)
177  {
178  return $position == 0;
179  }
180 
181  public function isLastElementPosition($position)
182  {
183  return $position == ($this->countElements() - 1);
184  }
185 
186  public function moveElementByPositions($currentPosition, $targetPosition)
187  {
188  $movingElement = $this->getElementByPosition($currentPosition);
189  $dodgingElement = $this->getElementByPosition($targetPosition);
190 
191  $elementList = new self();
192  $elementList->setQuestionId($this->getQuestionId());
193 
194  foreach ($this as $element) {
195  if ($element->getPosition() == $currentPosition) {
196  $elementList->addElement($dodgingElement);
197  continue;
198  }
199 
200  if ($element->getPosition() == $targetPosition) {
201  $elementList->addElement($movingElement);
202  continue;
203  }
204 
205  $elementList->addElement($element);
206  }
207 
208  $dodgingElement->setPosition($currentPosition);
209  $movingElement->setPosition($targetPosition);
210 
211  $this->setElements($elementList->getElements());
212  }
213 
214  public function removeElement(ilAssOrderingElement $removeElement)
215  {
216  $elementList = new self();
217  $elementList->setQuestionId($this->getQuestionId());
218 
219  $positionCounter = 0;
220 
221  foreach ($this as $element) {
222  if ($element->isSameElement($removeElement)) {
223  continue;
224  }
225 
226  $element->setPosition($positionCounter++);
227  $elementList->addElement($element);
228  }
229  }
230 
234  public function resetElements()
235  {
236  $this->elements = array();
237  }
238 
242  public function setElements($elements)
243  {
244  $this->resetElements();
245 
246  foreach ($elements as $element) {
247  $this->addElement($element);
248  }
249  }
250 
254  public function getElements()
255  {
256  return $this->elements;
257  }
258 
263  {
264  return $this->getIndexedElements(self::IDENTIFIER_TYPE_RANDOM);
265  }
266 
270  public function getRandomIdentifierIndex()
271  {
272  return array_keys($this->getRandomIdentifierIndexedElements());
273  }
274 
279  {
280  return $this->getIndexedElements(self::IDENTIFIER_TYPE_SOLUTION);
281  }
282 
286  public function getSolutionIdentifierIndex()
287  {
288  return array_keys($this->getSolutionIdentifierIndexedElements());
289  }
290 
294  protected function getIndexedElements($identifierType)
295  {
296  $elements = array();
297 
298  foreach ($this as $element) {
299  $elements[$this->fetchIdentifier($element, $identifierType)] = $element;
300  }
301 
302  return $elements;
303  }
304 
308  public function addElement(ilAssOrderingElement $element)
309  {
310  if ($this->hasValidIdentifiers($element)) {
311  $this->registerIdentifiers($element);
312  }
313 
314  $this->elements[] = $element;
315  }
316 
321  public function getElementByPosition($position)
322  {
323  if (isset($this->elements[$position])) {
324  return $this->elements[$position];
325  }
326 
327  return null;
328  }
329 
334  public function elementExistByPosition($position)
335  {
336  return ($this->getElementByPosition($position) !== null);
337  }
338 
343  public function getElementByRandomIdentifier($randomIdentifier)
344  {
345  foreach ($this as $element) {
346  if ($element->getRandomIdentifier() != $randomIdentifier) {
347  continue;
348  }
349 
350  return $element;
351  }
352 
353  return null;
354  }
355 
360  public function elementExistByRandomIdentifier($randomIdentifier)
361  {
362  return ($this->getElementByRandomIdentifier($randomIdentifier) !== null);
363  }
364 
369  public function getElementBySolutionIdentifier($solutionIdentifier)
370  {
371  foreach ($this as $element) {
372  if ($element->getSolutionIdentifier() != $solutionIdentifier) {
373  continue;
374  }
375 
376  return $element;
377  }
378  return null;
379  }
380 
385  public function elementExistBySolutionIdentifier($solutionIdentifier)
386  {
387  return ($this->getElementBySolutionIdentifier($solutionIdentifier) !== null);
388  }
389 
393  protected function getRegisteredSolutionIdentifiers()
394  {
395  return $this->getRegisteredIdentifiers(self::IDENTIFIER_TYPE_SOLUTION);
396  }
397 
401  protected function getRegisteredRandomIdentifiers()
402  {
403  return $this->getRegisteredIdentifiers(self::IDENTIFIER_TYPE_RANDOM);
404  }
405 
410  protected function getRegisteredIdentifiers($identifierType)
411  {
412  if (!isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
413  return array();
414  }
415 
416  return self::$identifierRegistry[$identifierType][$this->getQuestionId()];
417  }
418 
423  protected function hasValidIdentifiers(ilAssOrderingElement $element)
424  {
425  $identifier = $this->fetchIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
426 
427  if (!$this->isValidIdentifier(self::IDENTIFIER_TYPE_SOLUTION, $identifier)) {
428  return false;
429  }
430 
431  $identifier = $this->fetchIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
432 
433  if (!$this->isValidIdentifier(self::IDENTIFIER_TYPE_RANDOM, $identifier)) {
434  return false;
435  }
436 
437  return true;
438  }
439 
443  protected function ensureValidIdentifiers(ilAssOrderingElement $element)
444  {
445  $this->ensureValidIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
446  $this->ensureValidIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
447  }
448 
453  protected function ensureValidIdentifier(ilAssOrderingElement $element, $identifierType)
454  {
455  $identifier = $this->fetchIdentifier($element, $identifierType);
456 
457  if (!$this->isValidIdentifier($identifierType, $identifier)) {
458  $identifier = $this->buildIdentifier($identifierType);
459  $this->populateIdentifier($element, $identifierType, $identifier);
460  $this->registerIdentifier($element, $identifierType);
461  }
462  }
463 
467  protected function registerIdentifiers(ilAssOrderingElement $element)
468  {
469  $this->registerIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
470  $this->registerIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
471  }
472 
478  protected function registerIdentifier(ilAssOrderingElement $element, $identifierType)
479  {
480  if (!isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
481  self::$identifierRegistry[$identifierType][$this->getQuestionId()] = array();
482  }
483 
484  $identifier = $this->fetchIdentifier($element, $identifierType);
485 
486  if (!in_array($identifier, self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
487  self::$identifierRegistry[$identifierType][$this->getQuestionId()][] = $identifier;
488  }
489  }
490 
497  protected function isIdentifierRegistered(ilAssOrderingElement $element, $identifierType)
498  {
499  if (!isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
500  return false;
501  }
502 
503  $identifier = $this->fetchIdentifier($element, $identifierType);
504 
505  if (!in_array($identifier, self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
506  return false;
507  }
508 
509  return true;
510  }
511 
518  protected function fetchIdentifier(ilAssOrderingElement $element, $identifierType)
519  {
520  switch ($identifierType) {
521  case self::IDENTIFIER_TYPE_SOLUTION: return $element->getSolutionIdentifier();
522  case self::IDENTIFIER_TYPE_RANDOM: return $element->getRandomIdentifier();
523  }
524 
525  $this->throwUnknownIdentifierTypeException($identifierType);
526  }
527 
534  protected function populateIdentifier(ilAssOrderingElement $element, $identifierType, $identifier)
535  {
536  switch ($identifierType) {
537  case self::IDENTIFIER_TYPE_SOLUTION: $element->setSolutionIdentifier($identifier); break;
538  case self::IDENTIFIER_TYPE_RANDOM: $element->setRandomIdentifier($identifier); break;
539  default: $this->throwUnknownIdentifierTypeException($identifierType);
540  }
541  }
542 
549  protected function isValidIdentifier($identifierType, $identifier)
550  {
551  switch ($identifierType) {
552  case self::IDENTIFIER_TYPE_SOLUTION:
553  return self::isValidSolutionIdentifier($identifier);
554 
555  case self::IDENTIFIER_TYPE_RANDOM:
556  return self::isValidRandomIdentifier($identifier);
557  }
558 
559  $this->throwUnknownIdentifierTypeException($identifierType);
560  }
561 
567  protected function buildIdentifier($identifierType)
568  {
569  switch ($identifierType) {
570  case self::IDENTIFIER_TYPE_SOLUTION: return $this->buildSolutionIdentifier();
571  case self::IDENTIFIER_TYPE_RANDOM: return $this->buildRandomIdentifier();
572  }
573 
574  $this->throwUnknownIdentifierTypeException($identifierType);
575  }
576 
581  protected function throwUnknownIdentifierTypeException($identifierType)
582  {
583  throw new ilTestQuestionPoolException(
584  "unknown identifier type given (type: $identifierType)"
585  );
586  }
587 
592  protected function throwCouldNotBuildRandomIdentifierException($maxTries)
593  {
594  throw new ilTestQuestionPoolException(
595  "could not build random identifier (max tries: $maxTries)"
596  );
597  }
598 
603  protected function throwMissingReorderPositionException($randomIdentifier)
604  {
605  throw new ilTestQuestionPoolException(
606  "cannot reorder element due to missing position (random identifier: $randomIdentifier)"
607  );
608  }
609 
614  protected function throwUnknownRandomIdentifiersException($randomIdentifiers)
615  {
616  throw new ilTestQuestionPoolException(
617  'cannot reorder element due to one or more unknown random identifiers ' .
618  '(' . implode(', ', $randomIdentifiers) . ')'
619  );
620  }
621 
625  protected function getLastSolutionIdentifier()
626  {
627  $lastSolutionIdentifier = null;
628 
629  foreach ($this->getRegisteredSolutionIdentifiers() as $registeredIdentifier) {
630  if ($lastSolutionIdentifier > $registeredIdentifier) {
631  continue;
632  }
633 
634  $lastSolutionIdentifier = $registeredIdentifier;
635  }
636 
637  return $lastSolutionIdentifier;
638  }
639 
643  protected function buildSolutionIdentifier()
644  {
645  $lastSolutionIdentifier = $this->getLastSolutionIdentifier();
646 
647  if ($lastSolutionIdentifier === null) {
648  return 0;
649  }
650 
651  $nextSolutionIdentifier = $lastSolutionIdentifier + self::SOLUTION_IDENTIFIER_VALUE_INTERVAL;
652 
653  return $nextSolutionIdentifier;
654  }
655 
660  protected function buildRandomIdentifier()
661  {
662  $usedTriesCounter = 0;
663 
664  do {
665  if ($usedTriesCounter >= self::RANDOM_IDENTIFIER_BUILD_MAX_TRIES) {
666  $this->throwCouldNotBuildRandomIdentifierException(self::RANDOM_IDENTIFIER_BUILD_MAX_TRIES);
667  }
668 
669  $usedTriesCounter++;
670 
671  $lowerBound = self::RANDOM_IDENTIFIER_RANGE_LOWER_BOUND;
672  $upperBound = self::RANDOM_IDENTIFIER_RANGE_UPPER_BOUND;
673  $randomIdentifier = mt_rand($lowerBound, $upperBound);
674 
675  $testElement = new ilAssOrderingElement();
676  $testElement->setRandomIdentifier($randomIdentifier);
677  } while ($this->isIdentifierRegistered($testElement, self::IDENTIFIER_TYPE_RANDOM));
678 
679  return $randomIdentifier;
680  }
681 
682  public static function isValidSolutionIdentifier($identifier)
683  {
684  if (!is_numeric($identifier)) {
685  return false;
686  }
687 
688  if ($identifier != (int) $identifier) {
689  return false;
690  }
691 
692  if ($identifier < 0) {
693  return false;
694  }
695 
696  return true;
697  }
698 
699  public static function isValidRandomIdentifier($identifier)
700  {
701  if (!is_numeric($identifier)) {
702  return false;
703  }
704 
705  if ($identifier != (int) $identifier) {
706  return false;
707  }
708 
709  if ($identifier < self::RANDOM_IDENTIFIER_RANGE_LOWER_BOUND) {
710  return false;
711  }
712 
713  if ($identifier > self::RANDOM_IDENTIFIER_RANGE_UPPER_BOUND) {
714  return false;
715  }
716 
717  return true;
718  }
719 
720  public static function isValidPosition($position)
721  {
722  return self::isValidSolutionIdentifier($position); // this was the position earlier
723  }
724 
725  public static function isValidIndentation($indentation)
726  {
727  return self::isValidPosition($indentation); // horizontal position ^^
728  }
729 
734  {
735  foreach ($this as $element) {
736  $element->setRandomIdentifier($this->buildRandomIdentifier());
737  }
738  }
739 
744  public function hasSameElementSetByRandomIdentifiers(self $otherList)
745  {
746  $numIntersectingElements = count(array_intersect(
747  $otherList->getRandomIdentifierIndex(),
748  $this->getRandomIdentifierIndex()
749  ));
750 
751  if ($numIntersectingElements != $this->countElements()) {
752  return false;
753  }
754 
755  if ($numIntersectingElements != $otherList->countElements()) {
756  return false;
757  }
758 
759  return true; // faster ;-)
760 
761  $otherListRandomIdentifierIndex = $otherList->getRandomIdentifierIndex();
762 
763  foreach ($this as $orderingElement) {
764  if (!in_array($orderingElement->getRandomIdentifier(), $otherListRandomIdentifierIndex)) {
765  return false;
766  }
767 
768  $randomIdentifierIndexMatchingsCount = count(array_keys(
769  $otherListRandomIdentifierIndex,
770  $orderingElement->getRandomIdentifier(),
771  false
772  ));
773 
774  if ($randomIdentifierIndexMatchingsCount != 1) {
775  return false;
776  }
777  }
778 
779  return $this->countElements() == $otherList->countElements();
780  }
781 
782  public function getParityTrueElementList(self $otherList)
783  {
784  if (!$this->hasSameElementSetByRandomIdentifiers($otherList)) {
785  throw new ilTestQuestionPoolException('cannot compare lists having different element sets');
786  }
787 
788  $parityTrueElementList = new self();
789  $parityTrueElementList->setQuestionId($this->getQuestionId());
790 
791  foreach ($this as $thisElement) {
792  $otherElement = $otherList->getElementByRandomIdentifier(
793  $thisElement->getRandomIdentifier()
794  );
795 
796  if ($otherElement->getPosition() != $thisElement->getPosition()) {
797  continue;
798  }
799 
800  if ($otherElement->getIndentation() != $thisElement->getIndentation()) {
801  continue;
802  }
803 
804  $parityTrueElementList->addElement($thisElement);
805  }
806 
807  return $parityTrueElementList;
808  }
809 
815  public function reorderByRandomIdentifiers($randomIdentifiers)
816  {
817  $positionsMap = array_flip(array_values($randomIdentifiers));
818 
819  $orderedElements = array();
820 
821  foreach ($this as $element) {
822  if (!isset($positionsMap[$element->getRandomIdentifier()])) {
823  $this->throwMissingReorderPositionException($element->getRandomIdentifier());
824  }
825 
826  $position = $positionsMap[$element->getRandomIdentifier()];
827  unset($positionsMap[$element->getRandomIdentifier()]);
828 
829  $element->setPosition($position);
830  $orderedElements[$position] = $element;
831  }
832 
833  if (count($positionsMap)) {
834  $this->throwUnknownRandomIdentifiersException(array_keys($positionsMap));
835  }
836 
837  ksort($orderedElements);
838 
839  $this->setElements(array_values($orderedElements));
840  }
841 
845  public function resetElementsIndentations()
846  {
847  foreach ($this as $element) {
848  $element->setIndentation(0);
849  }
850  }
851 
856  public function getDifferenceElementList(self $otherElementList)
857  {
858  $differenceRandomIdentifierIndex = $this->getDifferenceRandomIdentifierIndex($otherElementList);
859 
860  $differenceElementList = new self();
861  $differenceElementList->setQuestionId($this->getQuestionId());
862 
863  foreach ($differenceRandomIdentifierIndex as $randomIdentifier) {
864  $element = $this->getElementByRandomIdentifier($randomIdentifier);
865  $differenceElementList->addElement($element);
866  }
867 
868  return $differenceElementList;
869  }
870 
875  protected function getDifferenceRandomIdentifierIndex(self $otherElementList)
876  {
877  $differenceRandomIdentifierIndex = array_diff(
878  $this->getRandomIdentifierIndex(),
879  $otherElementList->getRandomIdentifierIndex()
880  );
881 
882  return $differenceRandomIdentifierIndex;
883  }
884 
888  public function completeContentsFromElementList(self $otherList)
889  {
890  foreach ($this as $thisElement) {
891  if (!$otherList->elementExistByRandomIdentifier($thisElement->getRandomIdentifier())) {
892  continue;
893  }
894 
895  $otherElement = $otherList->getElementByRandomIdentifier(
896  $thisElement->getRandomIdentifier()
897  );
898 
899  $thisElement->setContent($otherElement->getContent());
900  }
901  }
902 
906  public function current()
907  {
908  return current($this->elements);
909  }
910 
914  public function next()
915  {
916  return next($this->elements);
917  }
918 
922  public function key()
923  {
924  return key($this->elements);
925  }
926 
930  public function valid()
931  {
932  return ($this->key() !== null);
933  }
934 
938  public function rewind()
939  {
940  return reset($this->elements);
941  }
942 
946  public static function getFallbackDefaultElement()
947  {
948  $element = new ilAssOrderingElement();
949  $element->setRandomIdentifier(self::FALLBACK_DEFAULT_ELEMENT_RANDOM_IDENTIFIER);
950 
951  return $element;
952  }
953 
959  public static function buildInstance($questionId, $orderingElements = array())
960  {
961  $elementList = new self();
962 
963  $elementList->setQuestionId($questionId);
964  $elementList->setElements($orderingElements);
965 
966  return $elementList;
967  }
968 }
__clone()
clone list by additionally cloning the element objects
registerIdentifiers(ilAssOrderingElement $element)
elementExistBySolutionIdentifier($solutionIdentifier)
isValidIdentifier($identifierType, $identifier)
$result
__construct()
ilAssOrderingElementList constructor.
populateIdentifier(ilAssOrderingElement $element, $identifierType, $identifier)
removeElement(ilAssOrderingElement $removeElement)
setSolutionIdentifier($solutionIdentifier)
moveElementByPositions($currentPosition, $targetPosition)
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
static buildInstance($questionId, $orderingElements=array())
throwMissingReorderPositionException($randomIdentifier)
loadFromDb()
load elements from database
throwUnknownRandomIdentifiersException($randomIdentifiers)
getElementBySolutionIdentifier($solutionIdentifier)
ensureValidIdentifier(ilAssOrderingElement $element, $identifierType)
addElement(ilAssOrderingElement $element)
setRandomIdentifier($randomIdentifier)
getDifferenceRandomIdentifierIndex(self $otherElementList)
isIdentifierRegistered(ilAssOrderingElement $element, $identifierType)
Create styles array
The data for the language used.
fetchIdentifier(ilAssOrderingElement $element, $identifierType)
resetElementsIndentations()
resets the indentation to level 0 for all elements in list
global $ilDB
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
$key
Definition: croninfo.php:18
hasValidIdentifiers(ilAssOrderingElement $element)
ensureValidIdentifiers(ilAssOrderingElement $element)
clearElementContents()
clears the contents of all elements
registerIdentifier(ilAssOrderingElement $element, $identifierType)
getDifferenceElementList(self $otherElementList)