1<?php
2/**
3 * List of Css Selector Sequences.
4 *
5 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
6 * @copyright Copyright 2010-2014 PhpCss Team
7 */
8
9namespace PhpCss\Ast\Selector {
10
11  use ArrayAccess;
12  use ArrayIterator;
13  use Countable;
14  use InvalidArgumentException;
15  use IteratorAggregate;
16  use PhpCss\Ast;
17
18  /**
19   * List of Css Selector Sequences.
20   *
21   * This is the root element of a standard selector string like:
22   * "element, .class"
23   *
24   * Because it is a list the some standard interfaces are implemented for
25   * easier usage.
26   *
27   * @package PhpCss
28   * @subpackage Ast
29   */
30  class Group
31    extends Ast\Selector
32    implements ArrayAccess, Countable, IteratorAggregate {
33
34    private $_sequences = [];
35
36    /**
37     * Create the object and assign sequences if provided. They
38     * can be added later of course.
39     *
40     * @param array $sequences
41     */
42    public function __construct(array $sequences = []) {
43      foreach ($sequences as $sequence) {
44        $this->offsetSet(NULL, $sequence);
45      }
46    }
47
48    /**
49     * Check if a sequence at the given position is available in the list.
50     *
51     * @param integer $offset
52     * @return boolean
53     * @see ArrayAccess::offsetExists()
54     */
55    public function offsetExists($offset): bool {
56      return isset($this->_sequences[$offset]);
57    }
58
59    /**
60     * Return the sequence at the given position.
61     *
62     * @param integer $offset
63     * @return Sequence
64     * @see ArrayAccess::offsetGet()
65     */
66    public function offsetGet($offset): Sequence {
67      return $this->_sequences[$offset];
68    }
69
70    /**
71     * Set/Add and sequence at the given position or top the end
72     *
73     * @param integer|NULL $offset
74     * @param Sequence $value
75     * @throws InvalidArgumentException
76     * @see \ArrayAccess::offsetSet()
77     */
78    public function offsetSet($offset, $value): void {
79      if (!$value instanceof Sequence) {
80        throw new InvalidArgumentException(
81          sprintf(
82            '$sequence is not an instance of %s but %s.',
83            Sequence::CLASS,
84            is_object($value) ? get_class($value) : gettype($value)
85          )
86        );
87      }
88      if (is_null($offset)) {
89        $this->_sequences[] = $value;
90      } else {
91        $this->_sequences[(int)$offset] = $value;
92        $this->_sequences = array_values($this->_sequences);
93      }
94    }
95
96    /**
97     * Remove the sequence at the given position
98     *
99     * @param integer $offset
100     * @see ArrayAccess::offsetUnset()
101     */
102    public function offsetUnset($offset): void {
103      unset($this->_sequences[$offset]);
104      $this->_sequences = array_values($this->_sequences);
105    }
106
107    /**
108     * Return the sequence list count.
109     *
110     * @return integer
111     * @see Countable::count()
112     */
113    public function count(): int {
114      return count($this->_sequences);
115    }
116
117    /**
118     * Return an iterator for the sequences
119     *
120     * @return ArrayIterator
121     * @see IteratorAggregate::getIterator()
122     */
123    public function getIterator(): ArrayIterator {
124      return new ArrayIterator($this->_sequences);
125    }
126
127    /**
128     * Accept visitors, because this element has children, enter and leave are called.
129     *
130     * @param Ast\Visitor $visitor
131     * @return void
132     */
133    public function accept(Ast\Visitor $visitor): void {
134      if ($visitor->visitEnter($this)) {
135        /**
136         * @var Sequence $sequence
137         */
138        foreach ($this as $sequence) {
139          $sequence->accept($visitor);
140        }
141        $visitor->visitLeave($this);
142      }
143    }
144  }
145}
146