2 Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
35 #include "compose.hpp"
36 #include "dcp_assert.h"
37 #include "exceptions.h"
38 #include "language_tag.h"
39 #include <boost/algorithm/string.hpp>
40 #include <boost/foreach.hpp>
49 using boost::optional;
53 #include "language_tag_lists.cc"
57 optional<LanguageTag::SubtagData>
58 find_in_list (LanguageTag::SubtagData* list, int length, string subtag)
60 for (int i = 0; i < length; ++i) {
61 if (list[i].subtag == subtag) {
66 return optional<LanguageTag::SubtagData>();
70 LanguageTag::Subtag::Subtag (string subtag, SubtagType type)
73 if (!get_subtag_data(type, subtag)) {
74 throw LanguageTagError(String::compose("Unknown %1 string %2", subtag_type_name(type), subtag));
79 LanguageTag::LanguageTag (string tag)
82 boost::split (parts, tag, boost::is_any_of("-"));
84 throw LanguageTagError (String::compose("Could not parse language tag %1", tag));
87 vector<string>::size_type p = 0;
88 _language = LanguageSubtag (parts[p]);
91 if (p == parts.size()) {
96 _script = ScriptSubtag (parts[p]);
100 if (p == parts.size()) {
105 _region = RegionSubtag (parts[p]);
109 if (p == parts.size()) {
115 _variants.push_back (VariantSubtag(parts[p]));
117 if (p == parts.size()) {
125 _extlangs.push_back (ExtlangSubtag(parts[p]));
127 if (p == parts.size()) {
133 if (p < parts.size()) {
134 throw LanguageTagError (String::compose("Unrecognised subtag %1", parts[p]));
140 LanguageTag::to_string () const
143 throw LanguageTagError("No language set up");
146 string s = _language->subtag();
149 s += "-" + _script->subtag();
153 s += "-" + _region->subtag();
156 BOOST_FOREACH (VariantSubtag i, _variants) {
157 s += "-" + i.subtag();
160 BOOST_FOREACH (ExtlangSubtag i, _extlangs) {
161 s += "-" + i.subtag();
169 LanguageTag::set_language (LanguageSubtag language)
171 _language = language;
176 LanguageTag::set_script (ScriptSubtag script)
183 LanguageTag::set_region (RegionSubtag region)
190 LanguageTag::add_variant (VariantSubtag variant)
192 if (find(_variants.begin(), _variants.end(), variant) != _variants.end()) {
193 throw LanguageTagError (String::compose("Duplicate Variant subtag %1", variant.subtag()));
196 _variants.push_back (variant);
202 check_for_duplicates (vector<T> const& subtags, dcp::LanguageTag::SubtagType type)
204 vector<T> sorted = subtags;
205 sort (sorted.begin(), sorted.end());
207 BOOST_FOREACH (T const& i, sorted) {
208 if (last && i == *last) {
209 throw LanguageTagError (String::compose("Duplicate %1 subtag %2", dcp::LanguageTag::subtag_type_name(type), i.subtag()));
217 LanguageTag::set_variants (vector<VariantSubtag> variants)
219 check_for_duplicates (variants, VARIANT);
220 _variants = variants;
225 LanguageTag::add_extlang (ExtlangSubtag extlang)
227 if (find(_extlangs.begin(), _extlangs.end(), extlang) != _extlangs.end()) {
228 throw LanguageTagError (String::compose("Duplicate Extlang subtag %1", extlang.subtag()));
231 _extlangs.push_back (extlang);
236 LanguageTag::set_extlangs (vector<ExtlangSubtag> extlangs)
238 check_for_duplicates (extlangs, EXTLANG);
239 _extlangs = extlangs;
244 LanguageTag::description () const
247 throw LanguageTagError("No language set up");
252 BOOST_FOREACH (VariantSubtag const& i, _variants) {
253 optional<SubtagData> variant = get_subtag_data (VARIANT, i.subtag());
254 DCP_ASSERT (variant);
255 d += variant->description + " dialect of ";
258 optional<SubtagData> language = get_subtag_data (LANGUAGE, _language->subtag());
259 DCP_ASSERT (language);
260 d += language->description;
263 optional<SubtagData> script = get_subtag_data (SCRIPT, _script->subtag());
265 d += " written using the " + script->description + " script";
269 optional<SubtagData> region = get_subtag_data (REGION, _region->subtag());
271 d += " for " + region->description;
274 BOOST_FOREACH (ExtlangSubtag const& i, _extlangs) {
275 optional<SubtagData> extlang = get_subtag_data (EXTLANG, i.subtag());
276 DCP_ASSERT (extlang);
277 d += ", " + extlang->description;
284 vector<LanguageTag::SubtagData>
285 LanguageTag::get_all (SubtagType type)
287 vector<LanguageTag::SubtagData> all;
291 for (size_t i = 0; i < sizeof(language_list) / sizeof(LanguageTag::SubtagData); ++i) {
292 all.push_back (language_list[i]);
296 for (size_t i = 0; i < sizeof(script_list) / sizeof(LanguageTag::SubtagData); ++i) {
297 all.push_back (script_list[i]);
301 for (size_t i = 0; i < sizeof(region_list) / sizeof(LanguageTag::SubtagData); ++i) {
302 all.push_back (region_list[i]);
306 for (size_t i = 0; i < sizeof(variant_list) / sizeof(LanguageTag::SubtagData); ++i) {
307 all.push_back (variant_list[i]);
311 for (size_t i = 0; i < sizeof(extlang_list) / sizeof(LanguageTag::SubtagData); ++i) {
312 all.push_back (extlang_list[i]);
322 LanguageTag::subtag_type_name (SubtagType type)
341 dcp::LanguageTag::VariantSubtag::operator== (VariantSubtag const & other) const
343 return subtag() == other.subtag();
348 dcp::LanguageTag::VariantSubtag::operator< (VariantSubtag const & other) const
350 return subtag() < other.subtag();
355 dcp::LanguageTag::ExtlangSubtag::operator== (ExtlangSubtag const & other) const
357 return subtag() == other.subtag();
362 dcp::LanguageTag::ExtlangSubtag::operator< (ExtlangSubtag const & other) const
364 return subtag() < other.subtag();
369 dcp::operator== (dcp::LanguageTag const& a, dcp::LanguageTag const& b)
371 return a.to_string() == b.to_string();
376 dcp::operator<< (ostream& os, dcp::LanguageTag const& tag)
378 os << tag.to_string();
383 vector<pair<LanguageTag::SubtagType, LanguageTag::SubtagData> >
384 LanguageTag::subtags () const
386 vector<pair<SubtagType, SubtagData> > s;
389 s.push_back (make_pair(LANGUAGE, *get_subtag_data(LANGUAGE, _language->subtag())));
393 s.push_back (make_pair(SCRIPT, *get_subtag_data(SCRIPT, _script->subtag())));
397 s.push_back (make_pair(REGION, *get_subtag_data(REGION, _region->subtag())));
400 BOOST_FOREACH (VariantSubtag const& i, _variants) {
401 s.push_back (make_pair(VARIANT, *get_subtag_data(VARIANT, i.subtag())));
404 BOOST_FOREACH (ExtlangSubtag const& i, _extlangs) {
405 s.push_back (make_pair(EXTLANG, *get_subtag_data(EXTLANG, i.subtag())));
412 optional<LanguageTag::SubtagData>
413 LanguageTag::get_subtag_data (LanguageTag::SubtagType type, string subtag)
416 case dcp::LanguageTag::LANGUAGE:
417 return find_in_list(language_list, sizeof(language_list) / sizeof(LanguageTag::SubtagData), subtag);
418 case dcp::LanguageTag::SCRIPT:
419 return find_in_list(script_list, sizeof(script_list) / sizeof(LanguageTag::SubtagData), subtag);
420 case dcp::LanguageTag::REGION:
421 return find_in_list(region_list, sizeof(region_list) / sizeof(LanguageTag::SubtagData), subtag);
422 case dcp::LanguageTag::VARIANT:
423 return find_in_list(variant_list, sizeof(variant_list) / sizeof(LanguageTag::SubtagData), subtag);
424 case dcp::LanguageTag::EXTLANG:
425 return find_in_list(extlang_list, sizeof(extlang_list) / sizeof(LanguageTag::SubtagData), subtag);
428 return optional<LanguageTag::SubtagData>();
433 LanguageTag::get_subtag_description (LanguageTag::SubtagType type, string subtag)
435 optional<SubtagData> data = get_subtag_data (type, subtag);
437 return optional<string>();
440 return data->description;