ILIAS  release_8 Revision v8.24
class.ilAssOrderingElementList.php
Go to the documentation of this file.
1<?php
2
25class ilAssOrderingElementList implements Iterator
26{
27 public static $objectInstanceCounter = 0;
29
33
37
41
42 public const IDENTIFIER_TYPE_SOLUTION = 'SolutionIds';
43 public const IDENTIFIER_TYPE_RANDOM = 'RandomIds';
44
48 protected static $identifierRegistry = array(
49 self::IDENTIFIER_TYPE_SOLUTION => array(),
50 self::IDENTIFIER_TYPE_RANDOM => array()
51 );
52
56 protected $question_id;
57
61 protected $elements = array();
62
67 public function __construct(
68 int $question_id = null,
69 array $elements = []
70 ) {
71 $this->objectInstanceId = ++self::$objectInstanceCounter;
72
73 $this->question_id = $question_id;
74 $this->elements = $elements;
75 }
76
80 public function __clone()
81 {
82 $this->objectInstanceId = ++self::$objectInstanceCounter;
83
84 $elements = array();
85
86 foreach ($this as $key => $element) {
87 $elements[$key] = clone $element;
88 }
89
90 $this->elements = $elements;
91 }
92
97 {
98 $that = clone $this;
99 return $that;
100 }
101
105 public function getQuestionId(): ?int
106 {
107 return $this->question_id;
108 }
109
113 public function setQuestionId($question_id): void
114 {
115 $this->question_id = $question_id;
116 }
117
118 public function countElements(): int
119 {
120 return count($this->elements);
121 }
122
123 public function hasElements(): bool
124 {
125 return (bool) $this->countElements();
126 }
127
128 public function isFirstElementPosition($position): bool
129 {
130 return $position == 0;
131 }
132
133 public function isLastElementPosition($position): bool
134 {
135 return $position == ($this->countElements() - 1);
136 }
137
138 public function moveElementByPositions($currentPosition, $targetPosition): void
139 {
140 $movingElement = $this->getElementByPosition($currentPosition);
141 $dodgingElement = $this->getElementByPosition($targetPosition);
142
143 $elementList = new self();
144 $elementList->setQuestionId($this->getQuestionId());
145
146 foreach ($this as $element) {
147 if ($element->getPosition() == $currentPosition) {
148 $elementList->addElement($dodgingElement);
149 continue;
150 }
151
152 if ($element->getPosition() == $targetPosition) {
153 $elementList->addElement($movingElement);
154 continue;
155 }
156
157 $elementList->addElement($element);
158 }
159
160 $dodgingElement->setPosition($currentPosition);
161 $movingElement->setPosition($targetPosition);
162
163 $this->setElements($elementList->getElements());
164 }
165
166 public function removeElement(ilAssOrderingElement $removeElement): void
167 {
168 $elementList = new self();
169 $elementList->setQuestionId($this->getQuestionId());
170
171 $positionCounter = 0;
172
173 foreach ($this as $element) {
174 if ($element->isSameElement($removeElement)) {
175 continue;
176 }
177
178 $element->setPosition($positionCounter++);
179 $elementList->addElement($element);
180 }
181 }
182
186 public function resetElements(): void
187 {
188 $this->elements = array();
189 }
190
194 public function setElements($elements): void
195 {
196 $this->resetElements();
197
198 foreach ($elements as $element) {
199 $this->addElement($element);
200 }
201 }
202
206 public function getElements(): array
207 {
208 return $this->elements;
209 }
210
214 public function getRandomIdentifierIndexedElements(): array
215 {
216 return $this->getIndexedElements(self::IDENTIFIER_TYPE_RANDOM);
217 }
218
222 public function getRandomIdentifierIndex(): array
223 {
224 return array_keys($this->getRandomIdentifierIndexedElements());
225 }
226
231 {
232 return $this->getIndexedElements(self::IDENTIFIER_TYPE_SOLUTION);
233 }
234
238 public function getSolutionIdentifierIndex(): array
239 {
240 return array_keys($this->getSolutionIdentifierIndexedElements());
241 }
242
246 protected function getIndexedElements($identifierType): array
247 {
248 $elements = array();
249
250 foreach ($this as $element) {
251 $elements[$this->fetchIdentifier($element, $identifierType)] = $element;
252 }
253
254 return $elements;
255 }
256
260 public function addElement(ilAssOrderingElement $element): void
261 {
262 if ($this->hasValidIdentifiers($element)) {
263 $this->registerIdentifiers($element);
264 }
265
266 $this->elements[] = $element;
267 }
268
273 public function getElementByPosition($position): ?ilAssOrderingElement
274 {
275 if (isset($this->elements[$position])) {
276 return $this->elements[$position];
277 }
278
279 return null;
280 }
281
286 public function elementExistByPosition($position): bool
287 {
288 return ($this->getElementByPosition($position) !== null);
289 }
290
295 public function getElementByRandomIdentifier($randomIdentifier): ?ilAssOrderingElement
296 {
297 foreach ($this as $element) {
298 if ($element->getRandomIdentifier() === intval($randomIdentifier)) {
299 return $element;
300 }
301 }
302
303 return null;
304 }
305
310 public function elementExistByRandomIdentifier($randomIdentifier): bool
311 {
312 return ($this->getElementByRandomIdentifier($randomIdentifier) !== null);
313 }
314
319 public function getElementBySolutionIdentifier($solutionIdentifier): ?ilAssOrderingElement
320 {
321 foreach ($this as $element) {
322 if ($element->getSolutionIdentifier() != $solutionIdentifier) {
323 continue;
324 }
325
326 return $element;
327 }
328 return null;
329 }
330
335 public function elementExistBySolutionIdentifier($solutionIdentifier): bool
336 {
337 return ($this->getElementBySolutionIdentifier($solutionIdentifier) !== null);
338 }
339
343 protected function getRegisteredSolutionIdentifiers(): array
344 {
345 return $this->getRegisteredIdentifiers(self::IDENTIFIER_TYPE_SOLUTION);
346 }
347
351 protected function getRegisteredRandomIdentifiers(): array
352 {
353 return $this->getRegisteredIdentifiers(self::IDENTIFIER_TYPE_RANDOM);
354 }
355
360 protected function getRegisteredIdentifiers($identifierType): array
361 {
362 if (!isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
363 return array();
364 }
365
366 return self::$identifierRegistry[$identifierType][$this->getQuestionId()];
367 }
368
373 protected function hasValidIdentifiers(ilAssOrderingElement $element): bool
374 {
375 $identifier = $this->fetchIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
376
377 if (!$this->isValidIdentifier(self::IDENTIFIER_TYPE_SOLUTION, $identifier)) {
378 return false;
379 }
380
381 $identifier = $this->fetchIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
382
383 if (!$this->isValidIdentifier(self::IDENTIFIER_TYPE_RANDOM, $identifier)) {
384 return false;
385 }
386
387 return true;
388 }
389
394 {
395 //TODO: remove!
396 $this->ensureValidIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
397 $this->ensureValidIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
398 return $element;
399 }
400
405 protected function ensureValidIdentifier(ilAssOrderingElement $element, $identifierType): void
406 {
407 $identifier = $this->fetchIdentifier($element, $identifierType);
408
409 if (!$this->isValidIdentifier($identifierType, $identifier)) {
410 $identifier = $this->buildIdentifier($identifierType);
411 $this->populateIdentifier($element, $identifierType, $identifier);
412 $this->registerIdentifier($element, $identifierType);
413 }
414 }
415
419 protected function registerIdentifiers(ilAssOrderingElement $element): void
420 {
421 $this->registerIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
422 $this->registerIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
423 }
424
430 protected function registerIdentifier(ilAssOrderingElement $element, $identifierType): void
431 {
432 if (!isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
433 self::$identifierRegistry[$identifierType][$this->getQuestionId()] = array();
434 }
435
436 $identifier = $this->fetchIdentifier($element, $identifierType);
437
438 if (!in_array($identifier, self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
439 self::$identifierRegistry[$identifierType][$this->getQuestionId()][] = $identifier;
440 }
441 }
442
449 protected function isIdentifierRegistered(ilAssOrderingElement $element, $identifierType): bool
450 {
451 if (!isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
452 return false;
453 }
454
455 $identifier = $this->fetchIdentifier($element, $identifierType);
456
457 if (!in_array($identifier, self::$identifierRegistry[$identifierType][$this->getQuestionId()])) {
458 return false;
459 }
460
461 return true;
462 }
463
467 protected function fetchIdentifier(ilAssOrderingElement $element, string $identifierType): ?int
468 {
469 if ($identifierType == self::IDENTIFIER_TYPE_SOLUTION) {
470 return $element->getSolutionIdentifier();
471 } else {
472 return $element->getRandomIdentifier();
473 }
474
475 $this->throwUnknownIdentifierTypeException($identifierType);
476 }
477
484 protected function populateIdentifier(ilAssOrderingElement $element, $identifierType, $identifier): void
485 {
486 switch ($identifierType) {
487 case self::IDENTIFIER_TYPE_SOLUTION: $element->setSolutionIdentifier($identifier);
488 break;
489 case self::IDENTIFIER_TYPE_RANDOM: $element->setRandomIdentifier($identifier);
490 break;
491 default: $this->throwUnknownIdentifierTypeException($identifierType);
492 }
493 }
494
496 protected function isValidIdentifier($identifierType, $identifier)
497 {
498 switch ($identifierType) {
500 return self::isValidSolutionIdentifier($identifier);
501
503 return self::isValidRandomIdentifier($identifier);
504 }
505
506 $this->throwUnknownIdentifierTypeException($identifierType);
507 }
508
509 protected function buildIdentifier($identifierType): int
510 {
511 switch ($identifierType) {
513 return $this->buildSolutionIdentifier();
514 default:
516 return $this->buildRandomIdentifier();
517 }
518
519 $this->throwUnknownIdentifierTypeException($identifierType);
520 }
521
526 protected function throwUnknownIdentifierTypeException($identifierType): void
527 {
529 "unknown identifier type given (type: $identifierType)"
530 );
531 }
532
537 protected function throwCouldNotBuildRandomIdentifierException($maxTries): void
538 {
540 "could not build random identifier (max tries: $maxTries)"
541 );
542 }
543
548 protected function throwMissingReorderPositionException($randomIdentifier): void
549 {
551 "cannot reorder element due to missing position (random identifier: $randomIdentifier)"
552 );
553 }
554
559 protected function throwUnknownRandomIdentifiersException($randomIdentifiers): void
560 {
562 'cannot reorder element due to one or more unknown random identifiers ' .
563 '(' . implode(', ', $randomIdentifiers) . ')'
564 );
565 }
566
570 protected function getLastSolutionIdentifier(): ?int
571 {
572 $lastSolutionIdentifier = null;
573
574 foreach ($this->getRegisteredSolutionIdentifiers() as $registeredIdentifier) {
575 if ($lastSolutionIdentifier > $registeredIdentifier) {
576 continue;
577 }
578
579 $lastSolutionIdentifier = $registeredIdentifier;
580 }
581
582 return $lastSolutionIdentifier;
583 }
584
588 protected function buildSolutionIdentifier(): ?int
589 {
590 $lastSolutionIdentifier = $this->getLastSolutionIdentifier();
591
592 if ($lastSolutionIdentifier === null) {
593 return 0;
594 }
595
596 $nextSolutionIdentifier = $lastSolutionIdentifier + self::SOLUTION_IDENTIFIER_VALUE_INTERVAL;
597
598 return $nextSolutionIdentifier;
599 }
600
605 protected function buildRandomIdentifier(): int
606 {
607 $usedTriesCounter = 0;
608
609 do {
610 if ($usedTriesCounter >= self::RANDOM_IDENTIFIER_BUILD_MAX_TRIES) {
611 $this->throwCouldNotBuildRandomIdentifierException(self::RANDOM_IDENTIFIER_BUILD_MAX_TRIES);
612 }
613
614 $usedTriesCounter++;
615
618 $randomIdentifier = mt_rand($lowerBound, $upperBound);
619
620 $testElement = new ilAssOrderingElement();
621 $testElement->setRandomIdentifier($randomIdentifier);
622 } while ($this->isIdentifierRegistered($testElement, self::IDENTIFIER_TYPE_RANDOM));
623
624 return $randomIdentifier;
625 }
626
627 public static function isValidSolutionIdentifier($identifier): bool
628 {
629 return is_numeric($identifier)
630 && $identifier == (int) $identifier
631 && (int) $identifier >= 0;
632 }
633
634 public static function isValidRandomIdentifier($identifier): bool
635 {
636 return is_numeric($identifier)
637 && $identifier == (int) $identifier
638 && (int) $identifier >= self::RANDOM_IDENTIFIER_RANGE_LOWER_BOUND
639 && (int) $identifier <= self::RANDOM_IDENTIFIER_RANGE_UPPER_BOUND;
640 }
641
642 public static function isValidPosition($position): bool
643 {
644 return self::isValidSolutionIdentifier($position); // this was the position earlier
645 }
646
647 public static function isValidIndentation($indentation): bool
648 {
649 return self::isValidPosition($indentation); // horizontal position ^^
650 }
651
655 public function distributeNewRandomIdentifiers(): void
656 {
657 foreach ($this as $element) {
658 $element->setRandomIdentifier($this->buildRandomIdentifier());
659 }
660 }
661
665 public function hasSameElementSetByRandomIdentifiers(self $otherList): bool
666 {
667 $numIntersectingElements = count(array_intersect(
668 $otherList->getRandomIdentifierIndex(),
669 $this->getRandomIdentifierIndex()
670 ));
671
672 return $numIntersectingElements == $this->countElements()
673 && $numIntersectingElements == $otherList->countElements();
674 }
675
676 public function getParityTrueElementList(self $otherList): ilAssOrderingElementList
677 {
678 if (!$this->hasSameElementSetByRandomIdentifiers($otherList)) {
679 throw new ilTestQuestionPoolException('cannot compare lists having different element sets');
680 }
681
682 $parityTrueElementList = new self();
683 $parityTrueElementList->setQuestionId($this->getQuestionId());
684
685 foreach ($this as $thisElement) {
686 $otherElement = $otherList->getElementByRandomIdentifier(
687 $thisElement->getRandomIdentifier()
688 );
689
690 if ($otherElement->getPosition() != $thisElement->getPosition()) {
691 continue;
692 }
693
694 if ($otherElement->getIndentation() != $thisElement->getIndentation()) {
695 continue;
696 }
697
698 $parityTrueElementList->addElement($thisElement);
699 }
700
701 return $parityTrueElementList;
702 }
703
708 public function reorderByRandomIdentifiers($randomIdentifiers): void
709 {
710 $positionsMap = array_flip(array_values($randomIdentifiers));
711
712 $orderedElements = array();
713
714 foreach ($this as $element) {
715 if (!isset($positionsMap[$element->getRandomIdentifier()])) {
716 $this->throwMissingReorderPositionException($element->getRandomIdentifier());
717 }
718
719 $position = $positionsMap[$element->getRandomIdentifier()];
720 unset($positionsMap[$element->getRandomIdentifier()]);
721
722 $element->setPosition($position);
723 $orderedElements[$position] = $element;
724 }
725
726 if (count($positionsMap)) {
727 $this->throwUnknownRandomIdentifiersException(array_keys($positionsMap));
728 }
729
730 ksort($orderedElements);
731
732 $this->setElements(array_values($orderedElements));
733 }
734
738 public function resetElementsIndentations(): void
739 {
740 foreach ($this as $element) {
741 $element->setIndentation(0);
742 }
743 }
744
749 public function getDifferenceElementList(self $otherElementList): ilAssOrderingElementList
750 {
751 $differenceRandomIdentifierIndex = $this->getDifferenceRandomIdentifierIndex($otherElementList);
752
753 $differenceElementList = new self();
754 $differenceElementList->setQuestionId($this->getQuestionId());
755
756 foreach ($differenceRandomIdentifierIndex as $randomIdentifier) {
757 $element = $this->getElementByRandomIdentifier($randomIdentifier);
758 $differenceElementList->addElement($element);
759 }
760
761 return $differenceElementList;
762 }
763
768 protected function getDifferenceRandomIdentifierIndex(self $otherElementList): array
769 {
770 $differenceRandomIdentifierIndex = array_diff(
772 $otherElementList->getRandomIdentifierIndex()
773 );
774
775 return $differenceRandomIdentifierIndex;
776 }
777
781 public function completeContentsFromElementList(self $otherList): void
782 {
783 foreach ($this as $thisElement) {
784 if (!$otherList->elementExistByRandomIdentifier($thisElement->getRandomIdentifier())) {
785 continue;
786 }
787
788 $otherElement = $otherList->getElementByRandomIdentifier(
789 $thisElement->getRandomIdentifier()
790 );
791
792 $thisElement->setContent($otherElement->getContent());
793 }
794 }
795
799 public function current(): ilAssOrderingElement
800 {
801 return current($this->elements);
802 }
803
807 public function next()
808 {
809 return next($this->elements);
810 }
811
815 public function key()
816 {
817 return key($this->elements);
818 }
819
823 public function valid(): bool
824 {
825 return ($this->key() !== null);
826 }
827
831 public function rewind()
832 {
833 return reset($this->elements);
834 }
835
840 {
841 $element = new ilAssOrderingElement();
842 $element->setRandomIdentifier(self::FALLBACK_DEFAULT_ELEMENT_RANDOM_IDENTIFIER);
843
844 return $element;
845 }
846
847 //TODO: remove this (it's __construct, actually...)
848 public static function buildInstance(int $question_id, array $elements = []): ilAssOrderingElementList
849 {
850 $elementList = new self();
851
852 $elementList->setQuestionId($question_id);
853 $elementList->setElements($elements);
854
855 return $elementList;
856 }
857
858 public function getHash(): string
859 {
860 $items = array();
861
862 foreach ($this as $element) {
863 $items[] = implode(':', array(
864 $element->getSolutionIdentifier(),
865 $element->getRandomIdentifier(),
866 $element->getIndentation()
867 ));
868 }
869
870 return md5(serialize($items));
871 }
872
876 public function withElements(array $elements): self
877 {
878 $clone = clone $this;
879 $clone->elements = $elements;
880 return $clone;
881 }
882
883 public function withQuestionId(int $question_id): self
884 {
885 $clone = clone $this;
886 $clone->question_id = $question_id;
887 return $clone;
888 }
889}
isIdentifierRegistered(ilAssOrderingElement $element, $identifierType)
fetchIdentifier(ilAssOrderingElement $element, string $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)
@noinspection PhpInconsistentReturnPointsInspection
hasValidIdentifiers(ilAssOrderingElement $element)
ensureValidIdentifiers(ilAssOrderingElement $element)
__construct(int $question_id=null, array $elements=[])
ilAssOrderingElementList constructor.
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
setRandomIdentifier(int $random_identifier)
setSolutionIdentifier(int $solution_identifier)
string $key
Consumer key/client ID value.
Definition: System.php:193