1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\Intl\Data\Bundle\Writer;
13
14use Symfony\Component\Intl\Exception\UnexpectedTypeException;
15
16/**
17 * Writes .txt resource bundles.
18 *
19 * The resulting files can be converted to binary .res files using a
20 * {@link \Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompilerInterface}
21 * implementation.
22 *
23 * @author Bernhard Schussek <bschussek@gmail.com>
24 *
25 * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
26 *
27 * @internal
28 */
29class TextBundleWriter implements BundleWriterInterface
30{
31    /**
32     * {@inheritdoc}
33     */
34    public function write($path, $locale, $data, $fallback = true)
35    {
36        $file = fopen($path.'/'.$locale.'.txt', 'w');
37
38        $this->writeResourceBundle($file, $locale, $data, $fallback);
39
40        fclose($file);
41    }
42
43    /**
44     * Writes a "resourceBundle" node.
45     *
46     * @param resource $file       The file handle to write to
47     * @param string   $bundleName The name of the bundle
48     * @param mixed    $value      The value of the node
49     * @param bool     $fallback   Whether the resource bundle should be merged
50     *                             with the fallback locale
51     *
52     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
53     */
54    private function writeResourceBundle($file, $bundleName, $value, $fallback)
55    {
56        fwrite($file, $bundleName);
57
58        $this->writeTable($file, $value, 0, $fallback);
59
60        fwrite($file, "\n");
61    }
62
63    /**
64     * Writes a "resource" node.
65     *
66     * @param resource $file          The file handle to write to
67     * @param mixed    $value         The value of the node
68     * @param int      $indentation   The number of levels to indent
69     * @param bool     $requireBraces Whether to require braces to be printedaround the value
70     *
71     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
72     */
73    private function writeResource($file, $value, $indentation, $requireBraces = true)
74    {
75        if (\is_int($value)) {
76            $this->writeInteger($file, $value);
77
78            return;
79        }
80
81        if ($value instanceof \Traversable) {
82            $value = iterator_to_array($value);
83        }
84
85        if (\is_array($value)) {
86            $intValues = \count($value) === \count(array_filter($value, 'is_int'));
87
88            $keys = array_keys($value);
89
90            // check that the keys are 0-indexed and ascending
91            $intKeys = $keys === range(0, \count($keys) - 1);
92
93            if ($intValues && $intKeys) {
94                $this->writeIntVector($file, $value, $indentation);
95
96                return;
97            }
98
99            if ($intKeys) {
100                $this->writeArray($file, $value, $indentation);
101
102                return;
103            }
104
105            $this->writeTable($file, $value, $indentation);
106
107            return;
108        }
109
110        if (\is_bool($value)) {
111            $value = $value ? 'true' : 'false';
112        }
113
114        $this->writeString($file, (string) $value, $requireBraces);
115    }
116
117    /**
118     * Writes an "integer" node.
119     *
120     * @param resource $file  The file handle to write to
121     * @param int      $value The value of the node
122     *
123     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
124     */
125    private function writeInteger($file, $value)
126    {
127        fprintf($file, ':int{%d}', $value);
128    }
129
130    /**
131     * Writes an "intvector" node.
132     *
133     * @param resource $file        The file handle to write to
134     * @param array    $value       The value of the node
135     * @param int      $indentation The number of levels to indent
136     *
137     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
138     */
139    private function writeIntVector($file, array $value, $indentation)
140    {
141        fwrite($file, ":intvector{\n");
142
143        foreach ($value as $int) {
144            fprintf($file, "%s%d,\n", str_repeat('    ', $indentation + 1), $int);
145        }
146
147        fprintf($file, '%s}', str_repeat('    ', $indentation));
148    }
149
150    /**
151     * Writes a "string" node.
152     *
153     * @param resource $file          The file handle to write to
154     * @param string   $value         The value of the node
155     * @param bool     $requireBraces Whether to require braces to be printed
156     *                                around the value
157     *
158     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
159     */
160    private function writeString($file, $value, $requireBraces = true)
161    {
162        if ($requireBraces) {
163            fprintf($file, '{"%s"}', $value);
164
165            return;
166        }
167
168        fprintf($file, '"%s"', $value);
169    }
170
171    /**
172     * Writes an "array" node.
173     *
174     * @param resource $file        The file handle to write to
175     * @param array    $value       The value of the node
176     * @param int      $indentation The number of levels to indent
177     *
178     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
179     */
180    private function writeArray($file, array $value, $indentation)
181    {
182        fwrite($file, "{\n");
183
184        foreach ($value as $entry) {
185            fwrite($file, str_repeat('    ', $indentation + 1));
186
187            $this->writeResource($file, $entry, $indentation + 1, false);
188
189            fwrite($file, ",\n");
190        }
191
192        fprintf($file, '%s}', str_repeat('    ', $indentation));
193    }
194
195    /**
196     * Writes a "table" node.
197     *
198     * @param resource $file        The file handle to write to
199     * @param iterable $value       The value of the node
200     * @param int      $indentation The number of levels to indent
201     * @param bool     $fallback    Whether the table should be merged
202     *                              with the fallback locale
203     *
204     * @throws UnexpectedTypeException when $value is not an array and not a
205     *                                 \Traversable instance
206     */
207    private function writeTable($file, $value, $indentation, $fallback = true)
208    {
209        if (!\is_array($value) && !$value instanceof \Traversable) {
210            throw new UnexpectedTypeException($value, 'array or \Traversable');
211        }
212
213        if (!$fallback) {
214            fwrite($file, ':table(nofallback)');
215        }
216
217        fwrite($file, "{\n");
218
219        foreach ($value as $key => $entry) {
220            fwrite($file, str_repeat('    ', $indentation + 1));
221
222            // escape colons, otherwise they are interpreted as resource types
223            if (false !== strpos($key, ':') || false !== strpos($key, ' ')) {
224                $key = '"'.$key.'"';
225            }
226
227            fwrite($file, $key);
228
229            $this->writeResource($file, $entry, $indentation + 1);
230
231            fwrite($file, "\n");
232        }
233
234        fprintf($file, '%s}', str_repeat('    ', $indentation));
235    }
236}
237