ILIAS  release_7 Revision v7.30-3-g800a261c036
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
4require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssOrderingElement.php';
5
12class ilAssOrderingElementList implements Iterator
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 $question_id;
44
48 protected $elements;
49
54 public function __construct(
55 int $question_id = null,
56 array $elements = []
57 ) {
58 $this->objectInstanceId = ++self::$objectInstanceCounter;
59
60 $this->question_id = $question_id;
61 $this->elements = $elements;
62 }
63
67 public function __clone()
68 {
69 $this->objectInstanceId = ++self::$objectInstanceCounter;
70
71 $elements = array();
72
73 foreach ($this as $key => $element) {
74 $elements[$key] = clone $element;
75 }
76
77 $this->elements = $elements;
78 }
79
83 public function getClone()
84 {
85 $that = clone $this;
86 return $that;
87 }
88
92 public function getQuestionId()
93 {
94 return $this->question_id;
95 }
96
101 {
102 $this->question_id = $question_id;
103 }
104
105 public function countElements()
106 {
107 return count($this->elements);
108 }
109
110 public function hasElements()
111 {
112 return (bool) $this->countElements();
113 }
114
115 public function isFirstElementPosition($position)
116 {
117 return $position == 0;
118 }
119
120 public function isLastElementPosition($position)
121 {
122 return $position == ($this->countElements() - 1);
123 }
124
125 public function moveElementByPositions($currentPosition, $targetPosition)
126 {
127 $movingElement = $this->getElementByPosition($currentPosition);
128 $dodgingElement = $this->getElementByPosition($targetPosition);
129
130 $elementList = new self();
131 $elementList->setQuestionId($this->getQuestionId());
132
133 foreach ($this as $element) {
134 if ($element->getPosition() == $currentPosition) {
135 $elementList->addElement($dodgingElement);
136 continue;
137 }
138
139 if ($element->getPosition() == $targetPosition) {
140 $elementList->addElement($movingElement);
141 continue;
142 }
143
144 $elementList->addElement($element);
145 }
146
147 $dodgingElement->setPosition($currentPosition);
148 $movingElement->setPosition($targetPosition);
149
150 $this->setElements($elementList->getElements());
151 }
152
153 public function removeElement(ilAssOrderingElement $removeElement)
154 {
155 $elementList = new self();
156 $elementList->setQuestionId($this->getQuestionId());
157
158 $positionCounter = 0;
159
160 foreach ($this as $element) {
161 if ($element->isSameElement($removeElement)) {
162 continue;
163 }
164
165 $element->setPosition($positionCounter++);
166 $elementList->addElement($element);
167 }
168 }
169
173 public function resetElements()
174 {
175 $this->elements = array();
176 }
177
181 public function setElements($elements)
182 {
183 $this->resetElements();
184
185 foreach ($elements as $element) {
186 $this->addElement($element);
187 }
188 }
189
193 public function getElements()
194 {
195 return $this->elements;
196 }
197
202 {
203 return $this->getIndexedElements(self::IDENTIFIER_TYPE_RANDOM);
204 }
205
209 public function getRandomIdentifierIndex()
210 {
211 return array_keys($this->getRandomIdentifierIndexedElements());
212 }
213
218 {
219 return $this->getIndexedElements(self::IDENTIFIER_TYPE_SOLUTION);
220 }
221
226 {
227 return array_keys($this->getSolutionIdentifierIndexedElements());
228 }
229
233 protected function getIndexedElements($identifierType)
234 {
235 $elements = array();
236
237 foreach ($this as $element) {
238 $elements[$this->fetchIdentifier($element, $identifierType)] = $element;
239 }
240
241 return $elements;
242 }
243
247 public function addElement(ilAssOrderingElement $element)
248 {
249 if ($this->hasValidIdentifiers($element)) {
250 $this->registerIdentifiers($element);
251 }
252
253 $this->elements[] = $element;
254 }
255
260 public function getElementByPosition($position)
261 {
262 if (isset($this->elements[$position])) {
263 return $this->elements[$position];
264 }
265
266 return null;
267 }
268
273 public function elementExistByPosition($position)
274 {
275 return ($this->getElementByPosition($position) !== null);
276 }
277
282 public function getElementByRandomIdentifier($randomIdentifier)
283 {
284 foreach ($this as $element) {
285 if ($element->getRandomIdentifier() === intval($randomIdentifier)) {
286 return $element;
287 }
288 }
289 return null;
290 }
291
296 public function elementExistByRandomIdentifier($randomIdentifier)
297 {
298 return ($this->getElementByRandomIdentifier($randomIdentifier) !== null);
299 }
300
305 public function getElementBySolutionIdentifier($solutionIdentifier)
306 {
307 foreach ($this as $element) {
308 if ($element->getSolutionIdentifier() != $solutionIdentifier) {
309 continue;
310 }
311
312 return $element;
313 }
314 return null;
315 }
316
321 public function elementExistBySolutionIdentifier($solutionIdentifier)
322 {
323 return ($this->getElementBySolutionIdentifier($solutionIdentifier) !== null);
324 }
325
330 {
331 return $this->getRegisteredIdentifiers(self::IDENTIFIER_TYPE_SOLUTION);
332 }
333
338 {
339 return $this->getRegisteredIdentifiers(self::IDENTIFIER_TYPE_RANDOM);
340 }
341
346 protected function getRegisteredIdentifiers($identifierType)
347 {
348 if (!isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
349 return array();
350 }
351
352 return self::$identifierRegistry[$identifierType][$this->getQuestionId()];
353 }
354
359 protected function hasValidIdentifiers(ilAssOrderingElement $element)
360 {
361 $identifier = $this->fetchIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
362
363 if (!$this->isValidIdentifier(self::IDENTIFIER_TYPE_SOLUTION, $identifier)) {
364 return false;
365 }
366
367 $identifier = $this->fetchIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
368
369 if (!$this->isValidIdentifier(self::IDENTIFIER_TYPE_RANDOM, $identifier)) {
370 return false;
371 }
372
373 return true;
374 }
375
380 {
381 //TODO: remove!
382 $this->ensureValidIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
383 $this->ensureValidIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
384 return $element;
385 }
386
391 protected function ensureValidIdentifier(ilAssOrderingElement $element, $identifierType)
392 {
393 $identifier = $this->fetchIdentifier($element, $identifierType);
394
395 if (!$this->isValidIdentifier($identifierType, $identifier)) {
396 $identifier = $this->buildIdentifier($identifierType);
397 $this->populateIdentifier($element, $identifierType, $identifier);
398 $this->registerIdentifier($element, $identifierType);
399 }
400 }
401
405 protected function registerIdentifiers(ilAssOrderingElement $element)
406 {
407 $this->registerIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
408 $this->registerIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
409 }
410
416 protected function registerIdentifier(ilAssOrderingElement $element, $identifierType)
417 {
418 if (!isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
419 self::$identifierRegistry[$identifierType][$this->getQuestionId()] = array();
420 }
421
422 $identifier = $this->fetchIdentifier($element, $identifierType);
423
424 if (!in_array($identifier, self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
425 self::$identifierRegistry[$identifierType][$this->getQuestionId()][] = $identifier;
426 }
427 }
428
435 protected function isIdentifierRegistered(ilAssOrderingElement $element, $identifierType)
436 {
437 if (!isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
438 return false;
439 }
440
441 $identifier = $this->fetchIdentifier($element, $identifierType);
442
443 if (!in_array($identifier, self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
444 return false;
445 }
446
447 return true;
448 }
449
456 protected function fetchIdentifier(ilAssOrderingElement $element, $identifierType)
457 {
458 switch ($identifierType) {
460 case self::IDENTIFIER_TYPE_RANDOM: return $element->getRandomIdentifier();
461 }
462
463 $this->throwUnknownIdentifierTypeException($identifierType);
464 }
465
472 protected function populateIdentifier(ilAssOrderingElement $element, $identifierType, $identifier)
473 {
474 switch ($identifierType) {
475 case self::IDENTIFIER_TYPE_SOLUTION: $element->setSolutionIdentifier($identifier); break;
476 case self::IDENTIFIER_TYPE_RANDOM: $element->setRandomIdentifier($identifier); break;
477 default: $this->throwUnknownIdentifierTypeException($identifierType);
478 }
479 }
480
487 protected function isValidIdentifier($identifierType, $identifier)
488 {
489 switch ($identifierType) {
491 return self::isValidSolutionIdentifier($identifier);
492
494 return self::isValidRandomIdentifier($identifier);
495 }
496
497 $this->throwUnknownIdentifierTypeException($identifierType);
498 }
499
505 protected function buildIdentifier($identifierType)
506 {
507 switch ($identifierType) {
510 }
511
512 $this->throwUnknownIdentifierTypeException($identifierType);
513 }
514
519 protected function throwUnknownIdentifierTypeException($identifierType)
520 {
522 "unknown identifier type given (type: $identifierType)"
523 );
524 }
525
531 {
533 "could not build random identifier (max tries: $maxTries)"
534 );
535 }
536
541 protected function throwMissingReorderPositionException($randomIdentifier)
542 {
544 "cannot reorder element due to missing position (random identifier: $randomIdentifier)"
545 );
546 }
547
552 protected function throwUnknownRandomIdentifiersException($randomIdentifiers)
553 {
555 'cannot reorder element due to one or more unknown random identifiers ' .
556 '(' . implode(', ', $randomIdentifiers) . ')'
557 );
558 }
559
563 protected function getLastSolutionIdentifier()
564 {
565 $lastSolutionIdentifier = null;
566
567 foreach ($this->getRegisteredSolutionIdentifiers() as $registeredIdentifier) {
568 if ($lastSolutionIdentifier > $registeredIdentifier) {
569 continue;
570 }
571
572 $lastSolutionIdentifier = $registeredIdentifier;
573 }
574
575 return $lastSolutionIdentifier;
576 }
577
581 protected function buildSolutionIdentifier()
582 {
583 $lastSolutionIdentifier = $this->getLastSolutionIdentifier();
584
585 if ($lastSolutionIdentifier === null) {
586 return 0;
587 }
588
589 $nextSolutionIdentifier = $lastSolutionIdentifier + self::SOLUTION_IDENTIFIER_VALUE_INTERVAL;
590
591 return $nextSolutionIdentifier;
592 }
593
598 protected function buildRandomIdentifier()
599 {
600 $usedTriesCounter = 0;
601
602 do {
603 if ($usedTriesCounter >= self::RANDOM_IDENTIFIER_BUILD_MAX_TRIES) {
604 $this->throwCouldNotBuildRandomIdentifierException(self::RANDOM_IDENTIFIER_BUILD_MAX_TRIES);
605 }
606
607 $usedTriesCounter++;
608
611 $randomIdentifier = mt_rand($lowerBound, $upperBound);
612
613 $testElement = new ilAssOrderingElement();
614 $testElement->setRandomIdentifier($randomIdentifier);
615 } while ($this->isIdentifierRegistered($testElement, self::IDENTIFIER_TYPE_RANDOM));
616
617 return $randomIdentifier;
618 }
619
620 public static function isValidSolutionIdentifier($identifier) : bool
621 {
622 return is_numeric($identifier)
623 && $identifier == (int) $identifier
624 && (int) $identifier >= 0;
625 }
626
627 public static function isValidRandomIdentifier($identifier) : bool
628 {
629 return is_numeric($identifier)
630 && $identifier == (int) $identifier
631 && (int) $identifier >= self::RANDOM_IDENTIFIER_RANGE_LOWER_BOUND
632 && (int) $identifier <= self::RANDOM_IDENTIFIER_RANGE_UPPER_BOUND;
633 }
634
635 public static function isValidPosition($position)
636 {
637 return self::isValidSolutionIdentifier($position); // this was the position earlier
638 }
639
640 public static function isValidIndentation($indentation)
641 {
642 return self::isValidPosition($indentation); // horizontal position ^^
643 }
644
649 {
650 foreach ($this as $element) {
651 $element->setRandomIdentifier($this->buildRandomIdentifier());
652 }
653 }
654
658 public function hasSameElementSetByRandomIdentifiers(self $otherList) : bool
659 {
660 $numIntersectingElements = count(array_intersect(
661 $otherList->getRandomIdentifierIndex(),
662 $this->getRandomIdentifierIndex()
663 ));
664
665 return $numIntersectingElements == $this->countElements()
666 && $numIntersectingElements == $otherList->countElements();
667 }
668
669 public function getParityTrueElementList(self $otherList)
670 {
671 if (!$this->hasSameElementSetByRandomIdentifiers($otherList)) {
672 throw new ilTestQuestionPoolException('cannot compare lists having different element sets');
673 }
674
675 $parityTrueElementList = new self();
676 $parityTrueElementList->setQuestionId($this->getQuestionId());
677
678 foreach ($this as $thisElement) {
679 $otherElement = $otherList->getElementByRandomIdentifier(
680 $thisElement->getRandomIdentifier()
681 );
682
683 if ($otherElement->getPosition() != $thisElement->getPosition()) {
684 continue;
685 }
686
687 if ($otherElement->getIndentation() != $thisElement->getIndentation()) {
688 continue;
689 }
690
691 $parityTrueElementList->addElement($thisElement);
692 }
693
694 return $parityTrueElementList;
695 }
696
702 public function reorderByRandomIdentifiers($randomIdentifiers)
703 {
704 $positionsMap = array_flip(array_values($randomIdentifiers));
705
706 $orderedElements = array();
707
708 foreach ($this as $element) {
709 if (!isset($positionsMap[$element->getRandomIdentifier()])) {
710 $this->throwMissingReorderPositionException($element->getRandomIdentifier());
711 }
712
713 $position = $positionsMap[$element->getRandomIdentifier()];
714 unset($positionsMap[$element->getRandomIdentifier()]);
715
716 $element->setPosition($position);
717 $orderedElements[$position] = $element;
718 }
719
720 if (count($positionsMap)) {
721 $this->throwUnknownRandomIdentifiersException(array_keys($positionsMap));
722 }
723
724 ksort($orderedElements);
725
726 $this->setElements(array_values($orderedElements));
727 }
728
733 {
734 foreach ($this as $element) {
735 $element->setIndentation(0);
736 }
737 }
738
743 public function getDifferenceElementList(self $otherElementList)
744 {
745 $differenceRandomIdentifierIndex = $this->getDifferenceRandomIdentifierIndex($otherElementList);
746
747 $differenceElementList = new self();
748 $differenceElementList->setQuestionId($this->getQuestionId());
749
750 foreach ($differenceRandomIdentifierIndex as $randomIdentifier) {
751 $element = $this->getElementByRandomIdentifier($randomIdentifier);
752 $differenceElementList->addElement($element);
753 }
754
755 return $differenceElementList;
756 }
757
762 protected function getDifferenceRandomIdentifierIndex(self $otherElementList)
763 {
764 $differenceRandomIdentifierIndex = array_diff(
766 $otherElementList->getRandomIdentifierIndex()
767 );
768
769 return $differenceRandomIdentifierIndex;
770 }
771
775 public function completeContentsFromElementList(self $otherList)
776 {
777 foreach ($this as $thisElement) {
778 if (!$otherList->elementExistByRandomIdentifier($thisElement->getRandomIdentifier())) {
779 continue;
780 }
781
782 $otherElement = $otherList->getElementByRandomIdentifier(
783 $thisElement->getRandomIdentifier()
784 );
785
786 $thisElement->setContent($otherElement->getContent());
787 }
788 }
789
793 public function current()
794 {
795 return current($this->elements);
796 }
797
801 public function next()
802 {
803 return next($this->elements);
804 }
805
809 public function key()
810 {
811 return key($this->elements);
812 }
813
817 public function valid()
818 {
819 return ($this->key() !== null);
820 }
821
825 public function rewind()
826 {
827 return reset($this->elements);
828 }
829
833 public static function getFallbackDefaultElement()
834 {
835 $element = new ilAssOrderingElement();
836 $element->setRandomIdentifier(self::FALLBACK_DEFAULT_ELEMENT_RANDOM_IDENTIFIER);
837
838 return $element;
839 }
840
841 //TODO: remove this (it's __construct, actually...)
842 public static function buildInstance(int $question_id, array $elements = []) : ilAssOrderingElementList
843 {
844 $elementList = new self();
845
846 $elementList->setQuestionId($question_id);
847 $elementList->setElements($elements);
848
849 return $elementList;
850 }
851
852 public function getHash()
853 {
854 $items = array();
855
856 foreach ($this as $element) {
857 $items[] = implode(':', array(
858 $element->getSolutionIdentifier(),
859 $element->getRandomIdentifier(),
860 $element->getIndentation()
861 ));
862 }
863
864 return md5(serialize($items));
865 }
866
870 public function withElements(array $elements) : self
871 {
872 $clone = clone $this;
873 $clone->elements = $elements;
874 return $clone;
875 }
876
877 public function withQuestionId(int $question_id) : self
878 {
879 $clone = clone $this;
880 $clone->question_id = $question_id;
881 return $clone;
882 }
883}
An exception for terminatinating execution or to throw for unit testing.
isIdentifierRegistered(ilAssOrderingElement $element, $identifierType)
registerIdentifiers(ilAssOrderingElement $element)
elementExistBySolutionIdentifier($solutionIdentifier)
getDifferenceElementList(self $otherElementList)
static buildInstance(int $question_id, array $elements=[])
getElementBySolutionIdentifier($solutionIdentifier)
registerIdentifier(ilAssOrderingElement $element, $identifierType)
getDifferenceRandomIdentifierIndex(self $otherElementList)
throwMissingReorderPositionException($randomIdentifier)
populateIdentifier(ilAssOrderingElement $element, $identifierType, $identifier)
removeElement(ilAssOrderingElement $removeElement)
isValidIdentifier($identifierType, $identifier)
hasValidIdentifiers(ilAssOrderingElement $element)
ensureValidIdentifiers(ilAssOrderingElement $element)
__construct(int $question_id=null, array $elements=[])
ilAssOrderingElementList constructor.
fetchIdentifier(ilAssOrderingElement $element, $identifierType)
moveElementByPositions($currentPosition, $targetPosition)
addElement(ilAssOrderingElement $element)
__clone()
clone list by additionally cloning the element objects
ensureValidIdentifier(ilAssOrderingElement $element, $identifierType)
throwUnknownRandomIdentifiersException($randomIdentifiers)
resetElementsIndentations()
resets the indentation to level 0 for all elements in list
setSolutionIdentifier($solution_identifier)
setRandomIdentifier(int $random_identifier)