1<?php
2
3/*
4 * This file is part of SwiftMailer.
5 * (c) 2004-2009 Chris Corbyn
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11/**
12 * A CharacterStream implementation which stores characters in an internal array.
13 *
14 * @author Xavier De Cock <xdecock@gmail.com>
15 */
16class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream
17{
18    /**
19     * The char reader (lazy-loaded) for the current charset.
20     *
21     * @var Swift_CharacterReader
22     */
23    private $_charReader;
24
25    /**
26     * A factory for creating CharacterReader instances.
27     *
28     * @var Swift_CharacterReaderFactory
29     */
30    private $_charReaderFactory;
31
32    /**
33     * The character set this stream is using.
34     *
35     * @var string
36     */
37    private $_charset;
38
39    /**
40     * The data's stored as-is.
41     *
42     * @var string
43     */
44    private $_datas = '';
45
46    /**
47     * Number of bytes in the stream.
48     *
49     * @var int
50     */
51    private $_datasSize = 0;
52
53    /**
54     * Map.
55     *
56     * @var mixed
57     */
58    private $_map;
59
60    /**
61     * Map Type.
62     *
63     * @var int
64     */
65    private $_mapType = 0;
66
67    /**
68     * Number of characters in the stream.
69     *
70     * @var int
71     */
72    private $_charCount = 0;
73
74    /**
75     * Position in the stream.
76     *
77     * @var int
78     */
79    private $_currentPos = 0;
80
81    /**
82     * Constructor.
83     *
84     * @param Swift_CharacterReaderFactory $factory
85     * @param string                       $charset
86     */
87    public function __construct(Swift_CharacterReaderFactory $factory, $charset)
88    {
89        $this->setCharacterReaderFactory($factory);
90        $this->setCharacterSet($charset);
91    }
92
93    /* -- Changing parameters of the stream -- */
94
95    /**
96     * Set the character set used in this CharacterStream.
97     *
98     * @param string $charset
99     */
100    public function setCharacterSet($charset)
101    {
102        $this->_charset = $charset;
103        $this->_charReader = null;
104        $this->_mapType = 0;
105    }
106
107    /**
108     * Set the CharacterReaderFactory for multi charset support.
109     *
110     * @param Swift_CharacterReaderFactory $factory
111     */
112    public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory)
113    {
114        $this->_charReaderFactory = $factory;
115    }
116
117    /**
118     * @see Swift_CharacterStream::flushContents()
119     */
120    public function flushContents()
121    {
122        $this->_datas = null;
123        $this->_map = null;
124        $this->_charCount = 0;
125        $this->_currentPos = 0;
126        $this->_datasSize = 0;
127    }
128
129    /**
130     * @see Swift_CharacterStream::importByteStream()
131     *
132     * @param Swift_OutputByteStream $os
133     */
134    public function importByteStream(Swift_OutputByteStream $os)
135    {
136        $this->flushContents();
137        $blocks = 512;
138        $os->setReadPointer(0);
139        while (false !== ($read = $os->read($blocks))) {
140            $this->write($read);
141        }
142    }
143
144    /**
145     * @see Swift_CharacterStream::importString()
146     *
147     * @param string $string
148     */
149    public function importString($string)
150    {
151        $this->flushContents();
152        $this->write($string);
153    }
154
155    /**
156     * @see Swift_CharacterStream::read()
157     *
158     * @param int $length
159     *
160     * @return string
161     */
162    public function read($length)
163    {
164        if ($this->_currentPos >= $this->_charCount) {
165            return false;
166        }
167        $ret = false;
168        $length = $this->_currentPos + $length > $this->_charCount ? $this->_charCount - $this->_currentPos : $length;
169        switch ($this->_mapType) {
170            case Swift_CharacterReader::MAP_TYPE_FIXED_LEN:
171                $len = $length * $this->_map;
172                $ret = substr($this->_datas,
173                        $this->_currentPos * $this->_map,
174                        $len);
175                $this->_currentPos += $length;
176                break;
177
178            case Swift_CharacterReader::MAP_TYPE_INVALID:
179                $ret = '';
180                for (; $this->_currentPos < $length; ++$this->_currentPos) {
181                    if (isset($this->_map[$this->_currentPos])) {
182                        $ret .= '?';
183                    } else {
184                        $ret .= $this->_datas[$this->_currentPos];
185                    }
186                }
187                break;
188
189            case Swift_CharacterReader::MAP_TYPE_POSITIONS:
190                $end = $this->_currentPos + $length;
191                $end = $end > $this->_charCount ? $this->_charCount : $end;
192                $ret = '';
193                $start = 0;
194                if ($this->_currentPos > 0) {
195                    $start = $this->_map['p'][$this->_currentPos - 1];
196                }
197                $to = $start;
198                for (; $this->_currentPos < $end; ++$this->_currentPos) {
199                    if (isset($this->_map['i'][$this->_currentPos])) {
200                        $ret .= substr($this->_datas, $start, $to - $start).'?';
201                        $start = $this->_map['p'][$this->_currentPos];
202                    } else {
203                        $to = $this->_map['p'][$this->_currentPos];
204                    }
205                }
206                $ret .= substr($this->_datas, $start, $to - $start);
207                break;
208        }
209
210        return $ret;
211    }
212
213    /**
214     * @see Swift_CharacterStream::readBytes()
215     *
216     * @param int $length
217     *
218     * @return int[]
219     */
220    public function readBytes($length)
221    {
222        $read = $this->read($length);
223        if ($read !== false) {
224            $ret = array_map('ord', str_split($read, 1));
225
226            return $ret;
227        }
228
229        return false;
230    }
231
232    /**
233     * @see Swift_CharacterStream::setPointer()
234     *
235     * @param int $charOffset
236     */
237    public function setPointer($charOffset)
238    {
239        if ($this->_charCount < $charOffset) {
240            $charOffset = $this->_charCount;
241        }
242        $this->_currentPos = $charOffset;
243    }
244
245    /**
246     * @see Swift_CharacterStream::write()
247     *
248     * @param string $chars
249     */
250    public function write($chars)
251    {
252        if (!isset($this->_charReader)) {
253            $this->_charReader = $this->_charReaderFactory->getReaderFor(
254                $this->_charset);
255            $this->_map = array();
256            $this->_mapType = $this->_charReader->getMapType();
257        }
258        $ignored = '';
259        $this->_datas .= $chars;
260        $this->_charCount += $this->_charReader->getCharPositions(substr($this->_datas, $this->_datasSize), $this->_datasSize, $this->_map, $ignored);
261        if ($ignored !== false) {
262            $this->_datasSize = strlen($this->_datas) - strlen($ignored);
263        } else {
264            $this->_datasSize = strlen($this->_datas);
265        }
266    }
267}
268