Yaffs site version 1.1
[yaffs-website] / vendor / ezyang / htmlpurifier / library / HTMLPurifier / Zipper.php
1 <?php
2
3 /**
4  * A zipper is a purely-functional data structure which contains
5  * a focus that can be efficiently manipulated.  It is known as
6  * a "one-hole context".  This mutable variant implements a zipper
7  * for a list as a pair of two arrays, laid out as follows:
8  *
9  *      Base list: 1 2 3 4 [ ] 6 7 8 9
10  *      Front list: 1 2 3 4
11  *      Back list: 9 8 7 6
12  *
13  * User is expected to keep track of the "current element" and properly
14  * fill it back in as necessary.  (ToDo: Maybe it's more user friendly
15  * to implicitly track the current element?)
16  *
17  * Nota bene: the current class gets confused if you try to store NULLs
18  * in the list.
19  */
20
21 class HTMLPurifier_Zipper
22 {
23     public $front, $back;
24
25     public function __construct($front, $back) {
26         $this->front = $front;
27         $this->back = $back;
28     }
29
30     /**
31      * Creates a zipper from an array, with a hole in the
32      * 0-index position.
33      * @param Array to zipper-ify.
34      * @return Tuple of zipper and element of first position.
35      */
36     static public function fromArray($array) {
37         $z = new self(array(), array_reverse($array));
38         $t = $z->delete(); // delete the "dummy hole"
39         return array($z, $t);
40     }
41
42     /**
43      * Convert zipper back into a normal array, optionally filling in
44      * the hole with a value. (Usually you should supply a $t, unless you
45      * are at the end of the array.)
46      */
47     public function toArray($t = NULL) {
48         $a = $this->front;
49         if ($t !== NULL) $a[] = $t;
50         for ($i = count($this->back)-1; $i >= 0; $i--) {
51             $a[] = $this->back[$i];
52         }
53         return $a;
54     }
55
56     /**
57      * Move hole to the next element.
58      * @param $t Element to fill hole with
59      * @return Original contents of new hole.
60      */
61     public function next($t) {
62         if ($t !== NULL) array_push($this->front, $t);
63         return empty($this->back) ? NULL : array_pop($this->back);
64     }
65
66     /**
67      * Iterated hole advancement.
68      * @param $t Element to fill hole with
69      * @param $i How many forward to advance hole
70      * @return Original contents of new hole, i away
71      */
72     public function advance($t, $n) {
73         for ($i = 0; $i < $n; $i++) {
74             $t = $this->next($t);
75         }
76         return $t;
77     }
78
79     /**
80      * Move hole to the previous element
81      * @param $t Element to fill hole with
82      * @return Original contents of new hole.
83      */
84     public function prev($t) {
85         if ($t !== NULL) array_push($this->back, $t);
86         return empty($this->front) ? NULL : array_pop($this->front);
87     }
88
89     /**
90      * Delete contents of current hole, shifting hole to
91      * next element.
92      * @return Original contents of new hole.
93      */
94     public function delete() {
95         return empty($this->back) ? NULL : array_pop($this->back);
96     }
97
98     /**
99      * Returns true if we are at the end of the list.
100      * @return bool
101      */
102     public function done() {
103         return empty($this->back);
104     }
105
106     /**
107      * Insert element before hole.
108      * @param Element to insert
109      */
110     public function insertBefore($t) {
111         if ($t !== NULL) array_push($this->front, $t);
112     }
113
114     /**
115      * Insert element after hole.
116      * @param Element to insert
117      */
118     public function insertAfter($t) {
119         if ($t !== NULL) array_push($this->back, $t);
120     }
121
122     /**
123      * Splice in multiple elements at hole.  Functional specification
124      * in terms of array_splice:
125      *
126      *      $arr1 = $arr;
127      *      $old1 = array_splice($arr1, $i, $delete, $replacement);
128      *
129      *      list($z, $t) = HTMLPurifier_Zipper::fromArray($arr);
130      *      $t = $z->advance($t, $i);
131      *      list($old2, $t) = $z->splice($t, $delete, $replacement);
132      *      $arr2 = $z->toArray($t);
133      *
134      *      assert($old1 === $old2);
135      *      assert($arr1 === $arr2);
136      *
137      * NB: the absolute index location after this operation is
138      * *unchanged!*
139      *
140      * @param Current contents of hole.
141      */
142     public function splice($t, $delete, $replacement) {
143         // delete
144         $old = array();
145         $r = $t;
146         for ($i = $delete; $i > 0; $i--) {
147             $old[] = $r;
148             $r = $this->delete();
149         }
150         // insert
151         for ($i = count($replacement)-1; $i >= 0; $i--) {
152             $this->insertAfter($r);
153             $r = $replacement[$i];
154         }
155         return array($old, $r);
156     }
157 }