Completed
Push — remove-trailing-whitespace ( 4f8133...59c1b8 )
by Sam
11:28
created

SS_Map_Iterator::rewind()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 22
rs 8.6738
cc 6
eloc 14
nc 5
nop 0
1
<?php
2
3
/**
4
 * Creates a map from an SS_List by defining a key column and a value column.
5
 *
6
 * @package framework
7
 * @subpackage model
8
 */
9
class SS_Map implements ArrayAccess, Countable, IteratorAggregate {
10
11
	protected $list, $keyField, $valueField;
12
13
	/**
14
	 * @see SS_Map::unshift()
15
	 *
16
	 * @var array $firstItems
17
	 */
18
	protected $firstItems = array();
19
20
	/**
21
	 * @see SS_Map::push()
22
	 *
23
	 * @var array $lastItems
24
	 */
25
	protected $lastItems = array();
26
27
	/**
28
	 * Construct a new map around an SS_list.
29
	 *
30
	 * @param $list The list to build a map from
31
	 * @param $keyField The field to use as the key of each map entry
32
	 * @param $valueField The field to use as the value of each map entry
33
	 */
34
	public function __construct(SS_List $list, $keyField = "ID", $valueField = "Title") {
35
		$this->list = $list;
36
		$this->keyField = $keyField;
37
		$this->valueField = $valueField;
38
	}
39
40
	/**
41
	 * Set the key field for this map.
42
	 *
43
	 * @var string $keyField
44
	 */
45
	public function setKeyField($keyField) {
46
		$this->keyField = $keyField;
47
	}
48
49
	/**
50
	 * Set the value field for this map.
51
	 *
52
	 * @var string $valueField
53
	 */
54
	public function setValueField($valueField) {
55
		$this->valueField = $valueField;
56
	}
57
58
	/**
59
	 * Return an array equivalent to this map.
60
	 *
61
	 * @return array
62
	 */
63
	public function toArray() {
64
		$array = array();
65
66
		foreach($this as $k => $v) {
67
			$array[$k] = $v;
68
		}
69
70
		return $array;
71
	}
72
73
	/**
74
	 * Return all the keys of this map.
75
	 *
76
	 * @return array
77
	 */
78
	public function keys() {
79
		return array_keys($this->toArray());
80
	}
81
82
	/**
83
	 * Return all the values of this map.
84
	 *
85
	 * @return array
86
	 */
87
	public function values() {
88
		return array_values($this->toArray());
89
	}
90
91
	/**
92
	 * Unshift an item onto the start of the map.
93
	 *
94
	 * Stores the value in addition to the {@link DataQuery} for the map.
95
	 *
96
	 * @var string $key
97
	 * @var mixed $value
98
	 */
99
	public function unshift($key, $value) {
100
		$oldItems = $this->firstItems;
101
		$this->firstItems = array(
102
			$key => $value
103
		);
104
105
		if($oldItems) {
106
			$this->firstItems = $this->firstItems + $oldItems;
107
		}
108
109
		return $this;
110
	}
111
112
	/**
113
	 * Pushes an item onto the end of the map.
114
	 *
115
	 * @var string $key
116
	 * @var mixed $value
117
	 */
118
	public function push($key, $value) {
119
		$oldItems = $this->lastItems;
120
121
		$this->lastItems = array(
122
			$key => $value
123
		);
124
125
		if($oldItems) {
126
			$this->lastItems = $this->lastItems + $oldItems;
127
		}
128
129
		return $this;
130
	}
131
132
	// ArrayAccess
133
134
	/**
135
	 * @var string $key
136
	 *
137
	 * @return boolean
138
	 */
139
	public function offsetExists($key) {
140
		if(isset($this->firstItems[$key])) {
141
			return true;
142
		}
143
144
		if(isset($this->lastItems[$key])) {
145
			return true;
146
		}
147
148
		$record = $this->list->find($this->keyField, $key);
149
150
		return $record != null;
151
	}
152
153
	/**
154
	 * @var string $key
155
	 *
156
	 * @return mixed
157
	 */
158
	public function offsetGet($key) {
159
		if(isset($this->firstItems[$key])) {
160
			return $this->firstItems[$key];
161
		}
162
163
		if(isset($this->lastItems[$key])) {
164
			return $this->lastItems[$key];
165
		}
166
167
		$record = $this->list->find($this->keyField, $key);
168
169
		if($record) {
170
			$col = $this->valueField;
171
172
			return $record->$col;
173
		}
174
175
		return null;
176
	}
177
178
	/**
179
	 * Sets a value in the map by a given key that has been set via
180
	 * {@link SS_Map::push()} or {@link SS_Map::unshift()}
181
	 *
182
	 * Keys in the map cannot be set since these values are derived from a
183
	 * {@link DataQuery} instance. In this case, use {@link SS_Map::toArray()}
184
	 * and manipulate the resulting array.
185
	 *
186
	 * @var string $key
187
	 * @var mixed $value
188
	 */
189
	public function offsetSet($key, $value) {
190
		if(isset($this->firstItems[$key])) {
191
			return $this->firstItems[$key] = $value;
192
		}
193
194
		if(isset($this->lastItems[$key])) {
195
			return $this->lastItems[$key] = $value;
196
		}
197
198
		user_error(
199
			"SS_Map is read-only. Please use $map->push($key, $value) to append values",
200
			E_USER_ERROR
201
		);
202
	}
203
204
	/**
205
	 * Removes a value in the map by a given key which has been added to the map
206
	 * via {@link SS_Map::push()} or {@link SS_Map::unshift()}
207
	 *
208
	 * Keys in the map cannot be unset since these values are derived from a
209
	 * {@link DataQuery} instance. In this case, use {@link SS_Map::toArray()}
210
	 * and manipulate the resulting array.
211
	 *
212
	 * @var string $key
213
	 * @var mixed $value
214
	 */
215
	public function offsetUnset($key) {
216
		if(isset($this->firstItems[$key])) {
217
			unset($this->firstItems[$key]);
218
219
			return;
220
		}
221
222
		if(isset($this->lastItems[$key])) {
223
			unset($this->lastItems[$key]);
224
225
			return;
226
		}
227
228
		user_error(
229
			"SS_Map is read-only. Unset cannot be called on keys derived from the DataQuery",
230
			E_USER_ERROR
231
		);
232
	}
233
234
	/**
235
	 * Returns an SS_Map_Iterator instance for iterating over the complete set
236
	 * of items in the map.
237
	 *
238
	 * Satisfies the IteratorAggreagte interface.
239
	 *
240
	 * @return SS_Map_Iterator
241
	 */
242
	public function getIterator() {
243
		return new SS_Map_Iterator(
244
			$this->list->getIterator(),
245
			$this->keyField,
246
			$this->valueField,
247
			$this->firstItems,
248
			$this->lastItems
249
		);
250
	}
251
252
	/**
253
	 * Returns the count of items in the list including the additional items set
254
	 * through {@link SS_Map::push()} and {@link SS_Map::unshift}.
255
	 *
256
	 * @return int
257
	 */
258
	public function count() {
259
		return $this->list->count()
260
			count($this->firstItems)
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_STRING, expecting ';'
Loading history...
261
			count($this->lastItems);
262
	}
263
}
264
265
/**
266
 * Builds a map iterator around an Iterator.  Called by SS_Map
267
 *
268
 * @package framework
269
 * @subpackage model
270
 */
271
class SS_Map_Iterator implements Iterator {
272
273
	protected $items;
274
	protected $keyField, $titleField;
275
276
	protected $firstItemIdx = 0;
277
278
	protected $endItemIdx;
279
280
	protected $firstItems = array();
281
	protected $lastItems = array();
282
283
	protected $excludedItems = array();
284
285
	/**
286
	 * @param Iterator $items The iterator to build this map from
287
	 * @param string $keyField The field to use for the keys
288
	 * @param string $titleField The field to use for the values
289
	 * @param array $fristItems An optional map of items to show first
290
	 * @param array $lastItems An optional map of items to show last
291
	 */
292
	public function __construct(Iterator $items, $keyField, $titleField, $firstItems = null, $lastItems = null) {
293
		$this->items = $items;
294
		$this->keyField = $keyField;
295
		$this->titleField = $titleField;
296
		$this->endItemIdx = null;
297
298
		if($firstItems) {
299
			foreach($firstItems as $k => $v) {
300
				$this->firstItems[] = array($k,$v);
301
				$this->excludedItems[] = $k;
302
			}
303
		}
304
305
		if($lastItems) {
306
			foreach($lastItems as $k => $v) {
307
				$this->lastItems[] = array($k, $v);
308
				$this->excludedItems[] = $k;
309
			}
310
		}
311
312
	}
313
314
	/**
315
	 * Rewind the Iterator to the first element.
316
	 *
317
	 * @return mixed
318
	 */
319
	public function rewind() {
320
		$this->firstItemIdx = 0;
321
		$this->endItemIdx = null;
322
323
		$rewoundItem = $this->items->rewind();
324
325
		if(isset($this->firstItems[$this->firstItemIdx])) {
326
			return $this->firstItems[$this->firstItemIdx][1];
327
		} else {
328
			if($rewoundItem) {
329
				if($rewoundItem->hasMethod($this->titleField)) {
330
					return $rewoundItem->{$this->titleField}();
331
				}
332
333
				return $rewoundItem->{$this->titleField};
334
			} else if(!$this->items->valid() && $this->lastItems) {
335
				$this->endItemIdx = 0;
336
337
				return $this->lastItems[0][1];
338
			}
339
		}
340
	}
341
342
	/**
343
	 * Return the current element.
344
	 *
345
	 * @return mixed
346
	 */
347
	public function current() {
348
		if(($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) {
349
			return $this->lastItems[$this->endItemIdx][1];
350
		} else if(isset($this->firstItems[$this->firstItemIdx])) {
351
			return $this->firstItems[$this->firstItemIdx][1];
352
		} else {
353
			if($this->items->current()->hasMethod($this->titleField)) {
354
				return $this->items->current()->{$this->titleField}();
355
			}
356
357
			return $this->items->current()->{$this->titleField};
358
		}
359
	}
360
361
	/**
362
	 * Return the key of the current element.
363
	 *
364
	 * @return string
365
	 */
366
	public function key() {
367
		if(($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) {
368
			return $this->lastItems[$this->endItemIdx][0];
369
		} else if(isset($this->firstItems[$this->firstItemIdx])) {
370
			return $this->firstItems[$this->firstItemIdx][0];
371
		} else {
372
			return $this->items->current()->{$this->keyField};
373
		}
374
	}
375
376
	/**
377
	 * Move forward to next element.
378
	 *
379
	 * @return mixed
380
	 */
381
	public function next() {
382
		$this->firstItemIdx++;
383
384
		if(isset($this->firstItems[$this->firstItemIdx])) {
385
			return $this->firstItems[$this->firstItemIdx][1];
386
		} else {
387
			if(!isset($this->firstItems[$this->firstItemIdx-1])) {
388
				$this->items->next();
389
			}
390
391
			if($this->excludedItems) {
392
				while(($c = $this->items->current()) && in_array($c->{$this->keyField}, $this->excludedItems, true)) {
393
					$this->items->next();
394
				}
395
			}
396
		}
397
398
		if(!$this->items->valid()) {
399
			// iterator has passed the preface items, off the end of the items
400
			// list. Track through the end items to go through to the next
401
			if($this->endItemIdx === null) {
402
				$this->endItemIdx = -1;
403
			}
404
405
			$this->endItemIdx++;
406
407
			if(isset($this->lastItems[$this->endItemIdx])) {
408
				return $this->lastItems[$this->endItemIdx];
409
			}
410
411
			return false;
412
		}
413
	}
414
415
	/**
416
	 * Checks if current position is valid.
417
	 *
418
	 * @return boolean
419
	 */
420
	public function valid() {
421
		return (
422
			(isset($this->firstItems[$this->firstItemIdx])) ||
423
			(($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) ||
424
			$this->items->valid()
425
		);
426
	}
427
}
428