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\Generator;
13
14use Symfony\Component\Intl\Data\Bundle\Compiler\GenrbCompiler;
15use Symfony\Component\Intl\Data\Bundle\Reader\BundleReaderInterface;
16use Symfony\Component\Intl\Data\Util\ArrayAccessibleResourceBundle;
17use Symfony\Component\Intl\Data\Util\LocaleScanner;
18
19/**
20 * The rule for compiling the currency bundle.
21 *
22 * @author Bernhard Schussek <bschussek@gmail.com>
23 *
24 * @internal
25 */
26class CurrencyDataGenerator extends AbstractDataGenerator
27{
28    const UNKNOWN_CURRENCY_ID = 'XXX';
29    const EUROPEAN_COMPOSITE_UNIT_ID = 'XBA';
30    const EUROPEAN_MONETARY_UNIT_ID = 'XBB';
31    const EUROPEAN_UNIT_OF_ACCOUNT_XBC_ID = 'XBC';
32    const EUROPEAN_UNIT_OF_ACCOUNT_XBD_ID = 'XBD';
33    const TESTING_CURRENCY_CODE_ID = 'XTS';
34    const ADB_UNIT_OF_ACCOUNT_ID = 'XUA';
35    const GOLD_ID = 'XAU';
36    const SILVER_ID = 'XAG';
37    const PLATINUM_ID = 'XPT';
38    const PALLADIUM_ID = 'XPD';
39    const SUCRE_ID = 'XSU';
40    const SPECIAL_DRAWING_RIGHTS_ID = 'XDR';
41
42    /**
43     * Monetary units excluded from generation.
44     */
45    private static $blacklist = array(
46        self::UNKNOWN_CURRENCY_ID => true,
47        self::EUROPEAN_COMPOSITE_UNIT_ID => true,
48        self::EUROPEAN_MONETARY_UNIT_ID => true,
49        self::EUROPEAN_UNIT_OF_ACCOUNT_XBC_ID => true,
50        self::EUROPEAN_UNIT_OF_ACCOUNT_XBD_ID => true,
51        self::TESTING_CURRENCY_CODE_ID => true,
52        self::ADB_UNIT_OF_ACCOUNT_ID => true,
53        self::GOLD_ID => true,
54        self::SILVER_ID => true,
55        self::PLATINUM_ID => true,
56        self::PALLADIUM_ID => true,
57        self::SUCRE_ID => true,
58        self::SPECIAL_DRAWING_RIGHTS_ID => true,
59    );
60
61    /**
62     * Collects all available currency codes.
63     *
64     * @var string[]
65     */
66    private $currencyCodes = array();
67
68    /**
69     * {@inheritdoc}
70     */
71    protected function scanLocales(LocaleScanner $scanner, $sourceDir)
72    {
73        return $scanner->scanLocales($sourceDir.'/curr');
74    }
75
76    /**
77     * {@inheritdoc}
78     */
79    protected function compileTemporaryBundles(GenrbCompiler $compiler, $sourceDir, $tempDir)
80    {
81        $compiler->compile($sourceDir.'/curr', $tempDir);
82        $compiler->compile($sourceDir.'/misc/currencyNumericCodes.txt', $tempDir);
83    }
84
85    /**
86     * {@inheritdoc}
87     */
88    protected function preGenerate()
89    {
90        $this->currencyCodes = array();
91    }
92
93    /**
94     * {@inheritdoc}
95     */
96    protected function generateDataForLocale(BundleReaderInterface $reader, $tempDir, $displayLocale)
97    {
98        $localeBundle = $reader->read($tempDir, $displayLocale);
99
100        if (isset($localeBundle['Currencies']) && null !== $localeBundle['Currencies']) {
101            $data = array(
102                'Version' => $localeBundle['Version'],
103                'Names' => $this->generateSymbolNamePairs($localeBundle),
104            );
105
106            $this->currencyCodes = array_merge($this->currencyCodes, array_keys($data['Names']));
107
108            return $data;
109        }
110    }
111
112    /**
113     * {@inheritdoc}
114     */
115    protected function generateDataForRoot(BundleReaderInterface $reader, $tempDir)
116    {
117        $rootBundle = $reader->read($tempDir, 'root');
118
119        return array(
120            'Version' => $rootBundle['Version'],
121            'Names' => $this->generateSymbolNamePairs($rootBundle),
122        );
123    }
124
125    /**
126     * {@inheritdoc}
127     */
128    protected function generateDataForMeta(BundleReaderInterface $reader, $tempDir)
129    {
130        $rootBundle = $reader->read($tempDir, 'root');
131        $supplementalDataBundle = $reader->read($tempDir, 'supplementalData');
132        $numericCodesBundle = $reader->read($tempDir, 'currencyNumericCodes');
133
134        $this->currencyCodes = array_unique($this->currencyCodes);
135
136        sort($this->currencyCodes);
137
138        $data = array(
139            'Version' => $rootBundle['Version'],
140            'Currencies' => $this->currencyCodes,
141            'Meta' => $this->generateCurrencyMeta($supplementalDataBundle),
142            'Alpha3ToNumeric' => $this->generateAlpha3ToNumericMapping($numericCodesBundle, $this->currencyCodes),
143        );
144
145        $data['NumericToAlpha3'] = $this->generateNumericToAlpha3Mapping($data['Alpha3ToNumeric']);
146
147        return $data;
148    }
149
150    /**
151     * @return array
152     */
153    private function generateSymbolNamePairs(ArrayAccessibleResourceBundle $rootBundle)
154    {
155        $symbolNamePairs = iterator_to_array($rootBundle['Currencies']);
156
157        // Remove unwanted currencies
158        $symbolNamePairs = array_diff_key($symbolNamePairs, self::$blacklist);
159
160        return $symbolNamePairs;
161    }
162
163    private function generateCurrencyMeta(ArrayAccessibleResourceBundle $supplementalDataBundle)
164    {
165        // The metadata is already de-duplicated. It contains one key "DEFAULT"
166        // which is used for currencies that don't have dedicated entries.
167        return iterator_to_array($supplementalDataBundle['CurrencyMeta']);
168    }
169
170    private function generateAlpha3ToNumericMapping(ArrayAccessibleResourceBundle $numericCodesBundle, array $currencyCodes)
171    {
172        $alpha3ToNumericMapping = iterator_to_array($numericCodesBundle['codeMap']);
173
174        asort($alpha3ToNumericMapping);
175
176        // Filter unknown currencies (e.g. "AYM")
177        $alpha3ToNumericMapping = array_intersect_key($alpha3ToNumericMapping, array_flip($currencyCodes));
178
179        return $alpha3ToNumericMapping;
180    }
181
182    private function generateNumericToAlpha3Mapping(array $alpha3ToNumericMapping)
183    {
184        $numericToAlpha3Mapping = array();
185
186        foreach ($alpha3ToNumericMapping as $alpha3 => $numeric) {
187            // Make sure that the mapping is stored as table and not as array
188            $numeric = (string) $numeric;
189
190            if (!isset($numericToAlpha3Mapping[$numeric])) {
191                $numericToAlpha3Mapping[$numeric] = array();
192            }
193
194            $numericToAlpha3Mapping[$numeric][] = $alpha3;
195        }
196
197        return $numericToAlpha3Mapping;
198    }
199}
200