LibreOffice Module i18npool (master) 1
LocaleNode.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <stdio.h>
21#include <string.h>
22#include <algorithm>
23#include <memory>
24#include <utility>
25#include <vector>
26#include <map>
28#include <o3tl/temporary.hxx>
29#include <rtl/ustrbuf.hxx>
30#include <sal/macros.h>
31#include <sal/types.h>
32
33#include "LocaleNode.hxx"
35#include <com/sun/star/i18n/NumberFormatIndex.hpp>
36#include <com/sun/star/xml/sax/XAttributeList.hpp>
37
38// NOTE: MUST match the Locale versionDTD attribute defined in data/locale.dtd
39#define LOCALE_VERSION_DTD "2.0.3"
40
41typedef ::o3tl::sorted_vector< OUString > NameSet;
42typedef ::o3tl::sorted_vector< sal_Int16 > ValueSet;
43
44namespace cssi = ::com::sun::star::i18n;
45
46LocaleNode::LocaleNode (OUString name, const Reference< XAttributeList > & attr)
47 : aName(std::move(name))
48 , aAttribs(attr)
49 , parent(nullptr)
50 , nError(0)
51{
52}
53
55{
56 int err = nError;
57 for (size_t i=0;i<children.size();i++)
58 err += children[i]->getError();
59 return err;
60}
61
63 children.emplace_back(node);
64 node->parent = this;
65}
66
68{
69 const LocaleNode* pRoot = nullptr;
70 const LocaleNode* pParent = this;
71 while ( (pParent = pParent->parent) != nullptr )
72 pRoot = pParent;
73 return pRoot;
74}
75
76const LocaleNode * LocaleNode::findNode ( const char *name) const {
77 if (aName.equalsAscii(name))
78 return this;
79 for (size_t i = 0; i< children.size(); i++)
80 {
81 const LocaleNode *n=children[i]->findNode(name);
82 if (n)
83 return n;
84 }
85 return nullptr;
86}
87
89{
90}
91
92LocaleNode* LocaleNode::createNode (const OUString& name, const Reference< XAttributeList > & attr)
93{
94 if ( name == "LC_INFO" )
95 return new LCInfoNode (name,attr);
96 if ( name == "LC_CTYPE" )
97 return new LCCTYPENode (name,attr);
98 if ( name == "LC_FORMAT" )
99 return new LCFormatNode (name,attr);
100 if ( name == "LC_FORMAT_1" )
101 return new LCFormatNode (name,attr);
102 if ( name == "LC_CALENDAR" )
103 return new LCCalendarNode (name,attr);
104 if ( name == "LC_CURRENCY" )
105 return new LCCurrencyNode (name,attr);
106 if ( name == "LC_TRANSLITERATION" )
107 return new LCTransliterationNode (name,attr);
108 if ( name == "LC_COLLATION" )
109 return new LCCollationNode (name,attr);
110 if ( name == "LC_INDEX" )
111 return new LCIndexNode (name,attr);
112 if ( name == "LC_SEARCH" )
113 return new LCSearchNode (name,attr);
114 if ( name == "LC_MISC" )
115 return new LCMiscNode (name,attr);
116 if ( name == "LC_NumberingLevel" )
117 return new LCNumberingLevelNode (name, attr);
118 if ( name == "LC_OutLineNumberingLevel" )
119 return new LCOutlineNumberingLevelNode (name, attr);
120
121 return new LocaleNode(name,attr);
122}
123
124
125// printf(" name: '%s'\n", p->getName().pData->buffer );
126// printf("value: '%s'\n", p->getValue().pData->buffer );
127
128#define OSTR(s) (OUStringToOString( (s), RTL_TEXTENCODING_UTF8).getStr())
129
131{
132 OUString aDTD = getAttr().getValueByName("versionDTD");
133 if ( aDTD != LOCALE_VERSION_DTD )
134 {
135 ++nError;
136 fprintf( stderr, "Error: Locale versionDTD is not %s, see comment in locale.dtd\n", LOCALE_VERSION_DTD);
137 }
138 for (size_t i=0; i<children.size(); i++)
139 children[i]->generateCode (of);
140// print_node( this );
141}
142
143
145 const char* pParameterName, const LocaleNode* pNode,
146 sal_Int32 nMinLen, sal_Int32 nMaxLen ) const
147{
148 OUString aVal;
149 if (pNode)
150 aVal = pNode->getValue();
151 else if (nMinLen >= 0) // -1: optional => empty, 0: must be present, empty
152 {
153 ++nError;
154 fprintf( stderr, "Error: node NULL pointer for parameter %s.\n",
155 pParameterName);
156 }
157 // write empty data if error
158 of.writeOUStringLiteralParameter( pParameterName, aVal);
159 sal_Int32 nLen = aVal.getLength();
160 if (nLen < nMinLen)
161 {
162 ++nError;
163 fprintf( stderr, "Error: less than %" SAL_PRIdINT32 " character%s (%" SAL_PRIdINT32 ") in %s '%s'.\n",
164 nMinLen, (nMinLen > 1 ? "s" : ""),
165 nLen,
166 (pNode ? OSTR( pNode->getName()) : ""),
167 OSTR( aVal));
168 }
169 else if (nLen > nMaxLen && nMaxLen >= 0)
170 {
171 ++nError;
172 fprintf( stderr,
173 "Error: more than %" SAL_PRIdINT32 " character%s (%" SAL_PRIdINT32 ") in %s '%s' not supported by application.\n",
174 nMaxLen, (nMaxLen > 1 ? "s" : ""),
175 nLen,
176 (pNode ? OSTR( pNode->getName()) : ""),
177 OSTR( aVal));
178 }
179 return aVal;
180}
181
183 const char* pNodeName, const char* pParameterName,
184 sal_Int32 nMinLen, sal_Int32 nMaxLen ) const
185{
186 OUString aVal;
187 const LocaleNode * pNode = findNode( pNodeName);
188 if (pNode || nMinLen < 0)
189 aVal = writeOUStringLiteralParameterCheckLen( of, pParameterName, pNode, nMinLen, nMaxLen);
190 else
191 {
192 ++nError;
193 fprintf( stderr, "Error: node %s not found.\n", pNodeName);
194 // write empty data if error
195 of.writeOUStringLiteralParameter( pParameterName, aVal);
196 }
197 return aVal;
198}
199
200void LocaleNode::incError( const char* pStr ) const
201{
202 ++nError;
203 fprintf( stderr, "Error: %s\n", pStr);
204}
205
206void LocaleNode::incError( std::u16string_view rStr ) const
207{
208 incError( OSTR( rStr));
209}
210
211void LocaleNode::incErrorInt( const char* pStr, int nVal ) const
212{
213 ++nError;
214 fprintf( stderr, pStr, nVal);
215}
216
217void LocaleNode::incErrorStr( const char* pStr, std::u16string_view rVal ) const
218{
219 ++nError;
220 fprintf( stderr, pStr, OSTR( rVal));
221}
222
223void LocaleNode::incErrorStrStr( const char* pStr, std::u16string_view rVal1, std::u16string_view rVal2 ) const
224{
225 ++nError;
226 fprintf(stderr, pStr, OSTR(rVal1), OSTR(rVal2));
227}
228
230{
231
232 const LocaleNode * languageNode = findNode("Language");
233 const LocaleNode * countryNode = findNode("Country");
234 const LocaleNode * variantNode = findNode("Variant");
235
236 OUString aLanguage;
237
238 if (languageNode)
239 {
240 aLanguage = languageNode->getChildAt(0)->getValue();
241 if (aLanguage.getLength() != 2 && aLanguage.getLength() != 3)
242 incErrorStr( "Error: langID '%s' not 2-3 characters\n", aLanguage);
243 of.writeOUStringLiteralParameter("langID", aLanguage);
244 of.writeOUStringLiteralParameter("langDefaultName", languageNode->getChildAt(1)->getValue());
245 }
246 else
247 incError( "No Language node.");
248 if (countryNode)
249 {
250 OUString aCountry( countryNode->getChildAt(0)->getValue());
251 if (!(aCountry.isEmpty() || aCountry.getLength() == 2))
252 incErrorStr( "Error: countryID '%s' not empty or more than 2 characters\n", aCountry);
253 of.writeOUStringLiteralParameter("countryID", aCountry);
254 of.writeOUStringLiteralParameter("countryDefaultName", countryNode->getChildAt(1)->getValue());
255 }
256 else
257 incError( "No Country node.");
258 if (variantNode)
259 {
260 // If given Variant must be at least ll-Ssss and language must be 'qlt'
261 const OUString& aVariant( variantNode->getValue());
262 if (!(aVariant.isEmpty() || (aVariant.getLength() >= 7 && aVariant.indexOf('-') >= 2)))
263 incErrorStr( "Error: invalid Variant '%s'\n", aVariant);
264 if (!(aVariant.isEmpty() || aLanguage == "qlt"))
265 incErrorStrStr( "Error: Variant '%s' given but Language '%s' is not 'qlt'\n", aVariant, aLanguage);
266 of.writeOUStringLiteralParameter("Variant", aVariant);
267 }
268 else
269 of.writeOUStringLiteralParameter("Variant", std::u16string_view());
270 of.writeAsciiString("\nstatic constexpr rtl::OUStringConstExpr LCInfoArray[] = {\n");
271 of.writeAsciiString("\tlangID,\n");
272 of.writeAsciiString("\tlangDefaultName,\n");
273 of.writeAsciiString("\tcountryID,\n");
274 of.writeAsciiString("\tcountryDefaultName,\n");
275 of.writeAsciiString("\tVariant\n");
276 of.writeAsciiString("};\n\n");
277 of.writeOUStringFunction("getLCInfo_", "SAL_N_ELEMENTS(LCInfoArray)", "LCInfoArray");
278}
279
280
281static OUString aDateSep;
282static OUString aDecSep;
283
285{
286 const LocaleNode * sepNode = nullptr;
287 OUString useLocale = getAttr().getValueByName("ref");
288 if (!useLocale.isEmpty()) {
289 useLocale = useLocale.replace( '-', '_');
290 of.writeOUStringRefFunction("getLocaleItem_", useLocale);
291 return;
292 }
293 OUString str = getAttr().getValueByName("unoid");
294 of.writeAsciiString("\n\n");
295 of.writeOUStringLiteralParameter("LC_CTYPE_Unoid", str);
296
297 aDateSep =
298 writeOUStringLiteralParameterCheckLen( of, "DateSeparator", "dateSeparator", 1, 1);
299 OUString aThoSep =
300 writeOUStringLiteralParameterCheckLen( of, "ThousandSeparator", "thousandSeparator", 1, 1);
301 aDecSep =
302 writeOUStringLiteralParameterCheckLen( of, "DecimalSeparator", "decimalSeparator", 1, 1);
303 OUString aDecSepAlt =
304 writeOUStringLiteralParameterCheckLen( of, "DecimalSeparatorAlternative", "decimalSeparatorAlternative", -1, 1);
305 OUString aTimeSep =
306 writeOUStringLiteralParameterCheckLen( of, "TimeSeparator", "timeSeparator", 1, 1);
307 OUString aTime100Sep =
308 writeOUStringLiteralParameterCheckLen( of, "Time100SecSeparator", "time100SecSeparator", 1, 1);
309 OUString aListSep =
310 writeOUStringLiteralParameterCheckLen( of, "ListSeparator", "listSeparator", 1, 1);
311
312 OUString aLDS;
313
314 sepNode = findNode("LongDateDayOfWeekSeparator");
315 aLDS = sepNode->getValue();
316 of.writeOUStringLiteralParameter("LongDateDayOfWeekSeparator", aLDS);
317 if (aLDS == ",")
318 fprintf( stderr, "Warning: %s\n",
319 "LongDateDayOfWeekSeparator is only a comma not followed by a space. Usually this is not the case and may lead to concatenated display names like \"Wednesday,May 9, 2007\".");
320
321 sepNode = findNode("LongDateDaySeparator");
322 aLDS = sepNode->getValue();
323 of.writeOUStringLiteralParameter("LongDateDaySeparator", aLDS);
324 if (aLDS == "," || aLDS == ".")
325 fprintf( stderr, "Warning: %s\n",
326 "LongDateDaySeparator is only a comma or dot not followed by a space. Usually this is not the case and may lead to concatenated display names like \"Wednesday, May 9,2007\".");
327
328 sepNode = findNode("LongDateMonthSeparator");
329 aLDS = sepNode->getValue();
330 of.writeOUStringLiteralParameter("LongDateMonthSeparator", aLDS);
331 if (aLDS.isEmpty())
332 fprintf( stderr, "Warning: %s\n",
333 "LongDateMonthSeparator is empty. Usually this is not the case and may lead to concatenated display names like \"Wednesday, May9, 2007\".");
334
335 sepNode = findNode("LongDateYearSeparator");
336 aLDS = sepNode->getValue();
337 of.writeOUStringLiteralParameter("LongDateYearSeparator", aLDS);
338 if (aLDS.isEmpty())
339 fprintf( stderr, "Warning: %s\n",
340 "LongDateYearSeparator is empty. Usually this is not the case and may lead to concatenated display names like \"Wednesday, 2007May 9\".");
341
342 int nSavErr = nError;
343 int nWarn = 0;
344 if (aDateSep == aTimeSep)
345 incError( "DateSeparator equals TimeSeparator.");
346 if (aDecSep == aThoSep)
347 incError( "DecimalSeparator equals ThousandSeparator.");
348 if (aDecSepAlt == aThoSep)
349 incError( "DecimalSeparatorAlternative equals ThousandSeparator.");
350 if (aDecSepAlt == aDecSep)
351 incError( "DecimalSeparatorAlternative equals DecimalSeparator, it must not be specified then.");
352 if ( aThoSep == " " )
353 incError( "ThousandSeparator is an ' ' ordinary space, this should be a non-breaking space U+00A0 instead.");
354 if (aListSep == aDecSep)
355 fprintf( stderr, "Warning: %s\n",
356 "ListSeparator equals DecimalSeparator.");
357 if (aListSep == aThoSep)
358 fprintf( stderr, "Warning: %s\n",
359 "ListSeparator equals ThousandSeparator.");
360 if (aListSep.getLength() != 1 || aListSep[0] != ';')
361 {
362 incError( "ListSeparator not ';' semicolon. Strongly recommended. Currently required.");
363 ++nSavErr; // format codes not affected
364 }
365 if (aTimeSep == aTime100Sep)
366 {
367 ++nWarn;
368 fprintf( stderr, "Warning: %s\n",
369 "Time100SecSeparator equals TimeSeparator, this is probably an error.");
370 }
371 if (aDecSep != aTime100Sep)
372 {
373 ++nWarn;
374 fprintf( stderr, "Warning: %s\n",
375 "Time100SecSeparator is different from DecimalSeparator, this may be correct or not. Intended?");
376 }
377 if (nSavErr != nError || nWarn)
378 fprintf( stderr, "Warning: %s\n",
379 "Don't forget to adapt corresponding FormatCode elements when changing separators.");
380
381 OUString aQuoteStart =
382 writeOUStringLiteralParameterCheckLen( of, "QuotationStart", "quotationStart", 1, 1);
383 OUString aQuoteEnd =
384 writeOUStringLiteralParameterCheckLen( of, "QuotationEnd", "quotationEnd", 1, 1);
385 OUString aDoubleQuoteStart =
386 writeOUStringLiteralParameterCheckLen( of, "DoubleQuotationStart", "doubleQuotationStart", 1, 1);
387 OUString aDoubleQuoteEnd =
388 writeOUStringLiteralParameterCheckLen( of, "DoubleQuotationEnd", "doubleQuotationEnd", 1, 1);
389
390 if (aQuoteStart.toChar() <= 127 && aQuoteEnd.toChar() > 127)
391 fprintf( stderr, "Warning: %s\n",
392 "QuotationStart is an ASCII character but QuotationEnd is not.");
393 if (aQuoteEnd.toChar() <= 127 && aQuoteStart.toChar() > 127)
394 fprintf( stderr, "Warning: %s\n",
395 "QuotationEnd is an ASCII character but QuotationStart is not.");
396 if (aDoubleQuoteStart.toChar() <= 127 && aDoubleQuoteEnd.toChar() > 127)
397 fprintf( stderr, "Warning: %s\n",
398 "DoubleQuotationStart is an ASCII character but DoubleQuotationEnd is not.");
399 if (aDoubleQuoteEnd.toChar() <= 127 && aDoubleQuoteStart.toChar() > 127)
400 fprintf( stderr, "Warning: %s\n",
401 "DoubleQuotationEnd is an ASCII character but DoubleQuotationStart is not.");
402 if (aQuoteStart.toChar() <= 127 && aQuoteEnd.toChar() <= 127)
403 fprintf( stderr, "Warning: %s\n",
404 "QuotationStart and QuotationEnd are both ASCII characters. Not necessarily an issue, but unusual.");
405 if (aDoubleQuoteStart.toChar() <= 127 && aDoubleQuoteEnd.toChar() <= 127)
406 fprintf( stderr, "Warning: %s\n",
407 "DoubleQuotationStart and DoubleQuotationEnd are both ASCII characters. Not necessarily an issue, but unusual.");
408 if (aQuoteStart == aQuoteEnd)
409 fprintf( stderr, "Warning: %s\n",
410 "QuotationStart equals QuotationEnd. Not necessarily an issue, but unusual.");
411 if (aDoubleQuoteStart == aDoubleQuoteEnd)
412 fprintf( stderr, "Warning: %s\n",
413 "DoubleQuotationStart equals DoubleQuotationEnd. Not necessarily an issue, but unusual.");
414 /* TODO: should equalness of single and double quotes be an error? Would
415 * need to adapt quite some locales' data. */
416 if (aQuoteStart == aDoubleQuoteStart)
417 fprintf( stderr, "Warning: %s\n",
418 "QuotationStart equals DoubleQuotationStart. Not necessarily an issue, but unusual.");
419 if (aQuoteEnd == aDoubleQuoteEnd)
420 fprintf( stderr, "Warning: %s\n",
421 "QuotationEnd equals DoubleQuotationEnd. Not necessarily an issue, but unusual.");
422 // Known good values, exclude ASCII single (U+0027, ') and double (U+0022, ") quotes.
423 switch (int ic = aQuoteStart.toChar())
424 {
425 case 0x2018: // LEFT SINGLE QUOTATION MARK
426 case 0x201a: // SINGLE LOW-9 QUOTATION MARK
427 case 0x201b: // SINGLE HIGH-REVERSED-9 QUOTATION MARK
428 case 0x2039: // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
429 case 0x203a: // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
430 case 0x300c: // LEFT CORNER BRACKET (Chinese)
431 ;
432 break;
433 default:
434 fprintf( stderr, "Warning: %s U+%04X %s\n",
435 "QuotationStart may be wrong:", ic, OSTR( aQuoteStart));
436 }
437 switch (int ic = aQuoteEnd.toChar())
438 {
439 case 0x2019: // RIGHT SINGLE QUOTATION MARK
440 case 0x201a: // SINGLE LOW-9 QUOTATION MARK
441 case 0x201b: // SINGLE HIGH-REVERSED-9 QUOTATION MARK
442 case 0x2039: // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
443 case 0x203a: // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
444 case 0x300d: // RIGHT CORNER BRACKET (Chinese)
445 ;
446 break;
447 default:
448 fprintf( stderr, "Warning: %s U+%04X %s\n",
449 "QuotationEnd may be wrong:", ic, OSTR( aQuoteEnd));
450 }
451 switch (int ic = aDoubleQuoteStart.toChar())
452 {
453 case 0x00ab: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
454 case 0x00bb: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
455 case 0x201c: // LEFT DOUBLE QUOTATION MARK
456 case 0x201e: // DOUBLE LOW-9 QUOTATION MARK
457 case 0x201f: // DOUBLE HIGH-REVERSED-9 QUOTATION MARK
458 case 0x300e: // LEFT WHITE CORNER BRACKET (Chinese)
459 ;
460 break;
461 default:
462 fprintf( stderr, "Warning: %s U+%04X %s\n",
463 "DoubleQuotationStart may be wrong:", ic, OSTR( aDoubleQuoteStart));
464 }
465 switch (int ic = aDoubleQuoteEnd.toChar())
466 {
467 case 0x00ab: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
468 case 0x00bb: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
469 case 0x201d: // RIGHT DOUBLE QUOTATION MARK
470 case 0x201e: // DOUBLE LOW-9 QUOTATION MARK
471 case 0x201f: // DOUBLE HIGH-REVERSED-9 QUOTATION MARK
472 case 0x300f: // RIGHT WHITE CORNER BRACKET (Chinese)
473 ;
474 break;
475 default:
476 fprintf( stderr, "Warning: %s U+%04X %s\n",
477 "DoubleQuotationEnd may be wrong:", ic, OSTR( aDoubleQuoteEnd));
478 }
479
480 writeOUStringLiteralParameterCheckLen( of, "TimeAM", "timeAM", 1, -1);
481 writeOUStringLiteralParameterCheckLen( of, "TimePM", "timePM", 1, -1);
482 sepNode = findNode("MeasurementSystem");
483 of.writeOUStringLiteralParameter("measurementSystem", sepNode->getValue());
484
485 of.writeAsciiString("\nstatic constexpr rtl::OUStringConstExpr LCType[] = {\n");
486 of.writeAsciiString("\tLC_CTYPE_Unoid,\n");
487 of.writeAsciiString("\tdateSeparator,\n");
488 of.writeAsciiString("\tthousandSeparator,\n");
489 of.writeAsciiString("\tdecimalSeparator,\n");
490 of.writeAsciiString("\ttimeSeparator,\n");
491 of.writeAsciiString("\ttime100SecSeparator,\n");
492 of.writeAsciiString("\tlistSeparator,\n");
493 of.writeAsciiString("\tquotationStart,\n");
494 of.writeAsciiString("\tquotationEnd,\n");
495 of.writeAsciiString("\tdoubleQuotationStart,\n");
496 of.writeAsciiString("\tdoubleQuotationEnd,\n");
497 of.writeAsciiString("\ttimeAM,\n");
498 of.writeAsciiString("\ttimePM,\n");
499 of.writeAsciiString("\tmeasurementSystem,\n");
500 of.writeAsciiString("\tLongDateDayOfWeekSeparator,\n");
501 of.writeAsciiString("\tLongDateDaySeparator,\n");
502 of.writeAsciiString("\tLongDateMonthSeparator,\n");
503 of.writeAsciiString("\tLongDateYearSeparator,\n");
504 of.writeAsciiString("\tdecimalSeparatorAlternative\n");
505 of.writeAsciiString("};\n\n");
506 of.writeOUStringFunction("getLocaleItem_", "SAL_N_ELEMENTS(LCType)", "LCType");
507}
508
509
510static OUString sTheCurrencyReplaceTo;
512static OUString sTheDateEditFormat;
513
514sal_Int16 LCFormatNode::mnSection = 0;
515sal_Int16 LCFormatNode::mnFormats = 0;
516
518{
519 if (mnSection >= 2)
520 incError("more than 2 LC_FORMAT sections");
521
522 ::std::vector< OUString > theDateAcceptancePatterns;
523
524 OUString useLocale(getAttr().getValueByName("ref"));
525
526 OUString str;
527 OUString strFrom( getAttr().getValueByName("replaceFrom"));
528 if (useLocale.isEmpty())
529 {
530 of.writeParameter("replaceFrom", strFrom, mnSection);
531 }
532 str = getAttr().getValueByName("replaceTo");
533 if (!strFrom.isEmpty() && str.isEmpty())
534 incErrorStr("replaceFrom=\"%s\" replaceTo=\"\" is empty replacement.\n", strFrom);
535 // Locale data generator inserts FFFF for LangID, we need to adapt that.
536 if (str.endsWithIgnoreAsciiCase( "-FFFF]"))
537 incErrorStr("replaceTo=\"%s\" needs FFFF to be adapted to the real LangID value.\n", str);
538 of.writeParameter("replaceTo", str, mnSection);
539 // Remember the replaceTo value for "[CURRENCY]" to check format codes.
540 if ( strFrom == "[CURRENCY]" )
542 // Remember the currency symbol if present.
543 if (str.startsWith( "[$" ))
544 {
545 sal_Int32 nHyphen = str.indexOf( '-');
546 if (nHyphen >= 3)
547 {
548 sTheCompatibleCurrency = str.copy( 2, nHyphen - 2);
549 }
550 }
551
552 if (!useLocale.isEmpty())
553 {
554 if (!strFrom.isEmpty() && strFrom != "[CURRENCY]") //???
555 {
557 "Error: non-empty replaceFrom=\"%s\" with non-empty ref=\"%s\".",
558 strFrom, useLocale);
559 }
560 useLocale = useLocale.replace( '-', '_');
561 switch (mnSection)
562 {
563 case 0:
564 of.writeOUStringRefFunction("getAllFormats0_", useLocale, "replaceTo0");
565 of.writeOUStringRefFunction("getDateAcceptancePatterns_", useLocale);
566 break;
567 case 1:
568 of.writeOUStringRefFunction("getAllFormats1_", useLocale, "replaceTo1");
569 break;
570 }
571 ++mnSection;
572 return;
573 }
574
575 sal_Int16 formatCount = mnFormats;
576 NameSet aMsgIdSet;
577 ValueSet aFormatIndexSet;
578 NameSet aDefaultsSet;
579 bool bCtypeIsRef = false;
580 bool bHaveEngineering = false;
581 bool bShowNextFreeFormatIndex = false;
582
583 for (sal_Int32 i = 0; i< getNumberOfChildren() ; i++, formatCount++)
584 {
585 LocaleNode * currNode = getChildAt (i);
586 if ( currNode->getName() == "DateAcceptancePattern" )
587 {
588 if (mnSection > 0)
589 incError( "DateAcceptancePattern only handled in LC_FORMAT, not LC_FORMAT_1");
590 else
591 theDateAcceptancePatterns.push_back( currNode->getValue());
592 --formatCount;
593 continue; // for
594 }
595 if ( currNode->getName() != "FormatElement" )
596 {
597 incErrorStr( "Error: Undefined element '%s' in LC_FORMAT\n", currNode->getName());
598 --formatCount;
599 continue; // for
600 }
601
602 OUString aUsage;
603 OUString aType;
604 OUString aFormatIndex;
605 // currNode -> print();
606 const Attr &currNodeAttr = currNode->getAttr();
607 //printf ("getLen() = %d\n", currNode->getAttr().getLength());
608
609 str = currNodeAttr.getValueByName("msgid");
610 if (!aMsgIdSet.insert( str).second)
611 incErrorStr( "Error: Duplicated msgid=\"%s\" in FormatElement.\n", str);
612 of.writeOUStringLiteralParameter("FormatKey", str, formatCount);
613
614 str = currNodeAttr.getValueByName("default");
615 bool bDefault = str == "true";
616 of.writeOUStringLiteralDefaultParameter("FormatElement", str, formatCount);
617
618 aType = currNodeAttr.getValueByName("type");
619 of.writeOUStringLiteralParameter("FormatType", aType, formatCount);
620
621 aUsage = currNodeAttr.getValueByName("usage");
622 of.writeOUStringLiteralParameter("FormatUsage", aUsage, formatCount);
623
624 aFormatIndex = currNodeAttr.getValueByName("formatindex");
625 sal_Int16 formatindex = static_cast<sal_Int16>(aFormatIndex.toInt32());
626 // Ensure the new reserved range is not used anymore, free usage start
627 // was up'ed from 50 to 60 (and more later).
629 {
630 incErrorInt( "Error: Reserved formatindex=\"%d\" in FormatElement.\n", formatindex);
631 bShowNextFreeFormatIndex = true;
632 }
633 if (!aFormatIndexSet.insert( formatindex).second)
634 {
635 incErrorInt( "Error: Duplicated formatindex=\"%d\" in FormatElement.\n", formatindex);
636 bShowNextFreeFormatIndex = true;
637 }
638 of.writeOUStringLiteralIntParameter("Formatindex", formatCount, formatindex);
639
640 // Ensure only one default per usage and type.
641 if (bDefault)
642 {
643 OUString aKey( aUsage + "," + aType);
644 if (!aDefaultsSet.insert( aKey).second)
645 {
646 OUString aStr = "Duplicated default for usage=\"" + aUsage + "\" type=\"" + aType + "\": formatindex=\"" + aFormatIndex + "\".";
647 incError( aStr);
648 }
649 }
650
651 const LocaleNode * n = currNode -> findNode("FormatCode");
652 if (n)
653 {
654 of.writeOUStringLiteralParameter("FormatCode", n->getValue(), formatCount);
655 // Check separator usage for some FormatCode elements.
656 const LocaleNode* pCtype = nullptr;
657 switch (formatindex)
658 {
659 case cssi::NumberFormatIndex::DATE_SYS_DDMMYYYY :
660 sTheDateEditFormat = n->getValue();
661 break;
662 case cssi::NumberFormatIndex::NUMBER_1000DEC2 : // #,##0.00
663 case cssi::NumberFormatIndex::TIME_MMSS00 : // MM:SS.00
664 case cssi::NumberFormatIndex::TIME_HH_MMSS00 : // [HH]:MM:SS.00
665 {
666 const LocaleNode* pRoot = getRoot();
667 if (!pRoot)
668 incError( "No root for FormatCode.");
669 else
670 {
671 pCtype = pRoot->findNode( "LC_CTYPE");
672 if (!pCtype)
673 incError( "No LC_CTYPE found for FormatCode.");
674 else
675 {
676 OUString aRef( pCtype->getAttr().getValueByName("ref"));
677 if (!aRef.isEmpty())
678 {
679 aRef = aRef.replace( '-', '_');
680 if (!bCtypeIsRef)
681 fprintf( stderr,
682 "Warning: Can't check separators used in FormatCode due to LC_CTYPE ref=\"%s\".\n"
683 "If these two locales use identical format codes, you should consider to use the ref= mechanism also for the LC_FORMAT element, together with replaceFrom= and replaceTo= for the currency.\n",
684 OSTR( aRef));
685 bCtypeIsRef = true;
686 pCtype = nullptr;
687 }
688 }
689 }
690 }
691 break;
692 case cssi::NumberFormatIndex::CURRENCY_1000DEC2 :
693 // Remember the currency symbol if present.
694 {
695 if (sTheCompatibleCurrency.isEmpty())
696 {
697 sal_Int32 nStart = n->getValue().indexOf("[$");
698 if (nStart >= 0)
699 {
700 const OUString& aCode( n->getValue());
701 sal_Int32 nHyphen = aCode.indexOf( '-', nStart);
702 if (nHyphen >= nStart + 3)
703 sTheCompatibleCurrency = aCode.copy( nStart + 2, nHyphen - nStart - 2);
704 }
705 }
706 }
707 [[fallthrough]];
708 case cssi::NumberFormatIndex::CURRENCY_1000INT :
709 case cssi::NumberFormatIndex::CURRENCY_1000INT_RED :
710 case cssi::NumberFormatIndex::CURRENCY_1000DEC2_RED :
711 case cssi::NumberFormatIndex::CURRENCY_1000DEC2_CCC :
712 case cssi::NumberFormatIndex::CURRENCY_1000DEC2_DASHED :
713 // Currency formats should be something like [C]###0;-[C]###0
714 // and not parenthesized [C]###0;([C]###0) if not en_US.
715 if (strcmp( of.getLocale(), "en_US") != 0)
716 {
717 const OUString& aCode( n->getValue());
718 if (aCode.indexOf( "0)" ) > 0 || aCode.indexOf( "-)" ) > 0 ||
719 aCode.indexOf( " )" ) > 0 || aCode.indexOf( "])" ) > 0)
720 fprintf( stderr, "Warning: FormatCode formatindex=\"%d\" for currency uses parentheses for negative amounts, which probably is not correct for locales not based on en_US.\n", formatindex);
721 }
722 // Check if we have replaceTo for "[CURRENCY]" placeholder.
723 if (sTheCurrencyReplaceTo.isEmpty())
724 {
725 const OUString& aCode( n->getValue());
726 if (aCode.indexOf( "[CURRENCY]" ) >= 0)
727 incErrorInt( "Error: [CURRENCY] replaceTo not found for formatindex=\"%d\".\n", formatindex);
728 }
729 break;
730 default:
731 if (aUsage == "SCIENTIFIC_NUMBER")
732 {
733 // Check for presence of ##0.00E+00
734 const OUString& aCode( n->getValue());
735 // Simple check without decimal separator (assumed to
736 // be one UTF-16 character). May be prefixed with
737 // [NatNum1] or other tags.
738 sal_Int32 nInt = aCode.indexOf("##0");
739 sal_Int32 nDec = (nInt < 0 ? -1 : aCode.indexOf("00E+00", nInt));
740 if (nInt >= 0 && nDec == nInt+4)
741 bHaveEngineering = true;
742 }
743 break;
744 }
745 if (pCtype)
746 {
747 int nSavErr = nError;
748 const OUString& aCode( n->getValue());
749 if (formatindex == cssi::NumberFormatIndex::NUMBER_1000DEC2)
750 {
751 sal_Int32 nDec = -1;
752 sal_Int32 nGrp = -1;
753 const LocaleNode* pSep = pCtype->findNode( "DecimalSeparator");
754 if (!pSep)
755 incError( "No DecimalSeparator found for FormatCode.");
756 else
757 {
758 nDec = aCode.indexOf( pSep->getValue());
759 if (nDec < 0)
760 incErrorInt( "Error: DecimalSeparator not present in FormatCode formatindex=\"%d\".\n",
761 formatindex);
762 }
763 pSep = pCtype->findNode( "ThousandSeparator");
764 if (!pSep)
765 incError( "No ThousandSeparator found for FormatCode.");
766 else
767 {
768 nGrp = aCode.indexOf( pSep->getValue());
769 if (nGrp < 0)
770 incErrorInt( "Error: ThousandSeparator not present in FormatCode formatindex=\"%d\".\n",
771 formatindex);
772 }
773 if (nDec >= 0 && nGrp >= 0 && nDec <= nGrp)
774 incErrorInt( "Error: Ordering of ThousandSeparator and DecimalSeparator not correct in formatindex=\"%d\".\n",
775 formatindex);
776 }
777 if (formatindex == cssi::NumberFormatIndex::TIME_MMSS00 ||
778 formatindex == cssi::NumberFormatIndex::TIME_HH_MMSS00)
779 {
780 sal_Int32 nTime = -1;
781 sal_Int32 n100s = -1;
782 const LocaleNode* pSep = pCtype->findNode( "TimeSeparator");
783 if (!pSep)
784 incError( "No TimeSeparator found for FormatCode.");
785 else
786 {
787 nTime = aCode.indexOf( pSep->getValue());
788 if (nTime < 0)
789 incErrorInt( "Error: TimeSeparator not present in FormatCode formatindex=\"%d\".\n",
790 formatindex);
791 }
792 pSep = pCtype->findNode( "Time100SecSeparator");
793 if (!pSep)
794 incError( "No Time100SecSeparator found for FormatCode.");
795 else
796 {
797 n100s = aCode.indexOf( pSep->getValue());
798 if (n100s < 0)
799 incErrorInt( "Error: Time100SecSeparator not present in FormatCode formatindex=\"%d\".\n",
800 formatindex);
801 n100s = aCode.indexOf( Concat2View(pSep->getValue() + "00"));
802 if (n100s < 0)
803 incErrorInt( "Error: Time100SecSeparator+00 not present in FormatCode formatindex=\"%d\".\n",
804 formatindex);
805 }
806 if (n100s >= 0 && nTime >= 0 && n100s <= nTime)
807 incErrorInt( "Error: Ordering of Time100SecSeparator and TimeSeparator not correct in formatindex=\"%d\".\n",
808 formatindex);
809 }
810 if (nSavErr != nError)
811 fprintf( stderr,
812 "Warning: formatindex=\"%d\",\"%d\",\"%d\" are the only FormatCode elements checked for separator usage, there may be others that have errors.\n",
813 int(cssi::NumberFormatIndex::NUMBER_1000DEC2),
814 int(cssi::NumberFormatIndex::TIME_MMSS00),
815 int(cssi::NumberFormatIndex::TIME_HH_MMSS00));
816
817 }
818 }
819 else
820 incError( "No FormatCode in FormatElement.");
821 n = currNode -> findNode("DefaultName");
822 if (n)
823 of.writeOUStringLiteralParameter("FormatDefaultName", n->getValue(), formatCount);
824 else
825 of.writeOUStringLiteralParameter("FormatDefaultName", std::u16string_view(), formatCount);
826
827 }
828
829 if (bShowNextFreeFormatIndex)
830 {
831 sal_Int16 nNext = i18npool::nFirstFreeFormatIndex;
832 auto it = aFormatIndexSet.find( nNext);
833 if (it != aFormatIndexSet.end())
834 {
835 // nFirstFreeFormatIndex already used, find next free including gaps.
836 do
837 {
838 ++nNext;
839 }
840 while (++it != aFormatIndexSet.end() && *it == nNext);
841 }
842 fprintf( stderr, "Hint: Next free formatindex is %d.\n", static_cast<int>(nNext));
843 }
844
845 // Check presence of all required format codes only in first section
846 // LC_FORMAT, not in optional LC_FORMAT_1
847 if (mnSection == 0)
848 {
849 // At least one abbreviated date acceptance pattern must be present.
850 if (theDateAcceptancePatterns.empty())
851 incError( "No DateAcceptancePattern present.\n");
852 else
853 {
854 bool bHaveAbbr = false;
855 for (auto const& elem : theDateAcceptancePatterns)
856 {
857 if (elem.indexOf('D') > -1 && elem.indexOf('M') > -1 && elem.indexOf('Y') <= -1)
858 {
859 bHaveAbbr = true;
860 break;
861 }
862 }
863 if (!bHaveAbbr)
864 incError( "No abbreviated DateAcceptancePattern present. For example M/D or D.M.\n");
865 }
866
867 // 0..9 MUST be present, 10,11 MUST NOT be present, 12..47 MUST be
868 // present, 48,49 MUST NOT be present, 50 MUST be present.
869 ValueSet::const_iterator aIter( aFormatIndexSet.begin());
870 for (sal_Int16 nNext = cssi::NumberFormatIndex::NUMBER_START;
872 {
873 sal_Int16 nHere = ::std::min( (aIter != aFormatIndexSet.end() ? *aIter :
876 if (aIter != aFormatIndexSet.end()) ++aIter;
877 for ( ; nNext < nHere; ++nNext)
878 {
879 switch (nNext)
880 {
881 case cssi::NumberFormatIndex::FRACTION_1 :
882 case cssi::NumberFormatIndex::FRACTION_2 :
883 case cssi::NumberFormatIndex::BOOLEAN :
884 case cssi::NumberFormatIndex::TEXT :
885 // generated internally
886 break;
887 default:
888 incErrorInt( "Error: FormatElement formatindex=\"%d\" not present.\n", nNext);
889 }
890 }
891 switch (nHere)
892 {
893 case cssi::NumberFormatIndex::FRACTION_1 :
894 incErrorInt( "Error: FormatElement formatindex=\"%d\" reserved for internal ``# ?/?''.\n", nNext);
895 break;
896 case cssi::NumberFormatIndex::FRACTION_2 :
897 incErrorInt( "Error: FormatElement formatindex=\"%d\" reserved for internal ``# ?\?/?\?''.\n", nNext);
898 break;
899 case cssi::NumberFormatIndex::BOOLEAN :
900 incErrorInt( "Error: FormatElement formatindex=\"%d\" reserved for internal ``BOOLEAN''.\n", nNext);
901 break;
902 case cssi::NumberFormatIndex::TEXT :
903 incErrorInt( "Error: FormatElement formatindex=\"%d\" reserved for internal ``@'' (TEXT).\n", nNext);
904 break;
905 default:
906 ; // nothing
907 }
908 }
909
910 if (!bHaveEngineering)
911 incError("Engineering notation format not present, e.g. ##0.00E+00 or ##0,00E+00 for usage=\"SCIENTIFIC_NUMBER\"\n");
912 }
913
914 of.writeAsciiString("\nstatic const sal_Int16 ");
915 of.writeAsciiString("FormatElementsCount");
917 of.writeAsciiString(" = ");
918 of.writeInt( formatCount - mnFormats);
919 of.writeAsciiString(";\n");
920 of.writeAsciiString("static constexpr rtl::OUStringConstExpr ");
921 of.writeAsciiString("FormatElementsArray");
923 of.writeAsciiString("[] = {\n");
924 for(sal_Int16 i = mnFormats; i < formatCount; i++) {
925
926 of.writeAsciiString("\t");
927 of.writeAsciiString("FormatCode");
928 of.writeInt(i);
929 of.writeAsciiString(",\n");
930
931 of.writeAsciiString("\t");
932 of.writeAsciiString("FormatDefaultName");
933 of.writeInt(i);
934 of.writeAsciiString(",\n");
935
936 of.writeAsciiString("\t");
937 of.writeAsciiString("FormatKey");
938 of.writeInt(i);
939 of.writeAsciiString(",\n");
940
941 of.writeAsciiString("\t");
942 of.writeAsciiString("FormatType");
943 of.writeInt(i);
944 of.writeAsciiString(",\n");
945
946 of.writeAsciiString("\t");
947 of.writeAsciiString("FormatUsage");
948 of.writeInt(i);
949 of.writeAsciiString(",\n");
950
951 of.writeAsciiString("\t");
952 of.writeAsciiString("Formatindex");
953 of.writeInt(i);
954 of.writeAsciiString(",\n");
955
956
957 of.writeAsciiString("\tdefaultFormatElement");
958 of.writeInt(i);
959 of.writeAsciiString(",\n");
960 }
961 of.writeAsciiString("};\n\n");
962
963 switch (mnSection)
964 {
965 case 0:
966 of.writeOUStringFunction("getAllFormats0_", "FormatElementsCount0", "FormatElementsArray0", "replaceFrom0", "replaceTo0");
967 break;
968 case 1:
969 of.writeOUStringFunction("getAllFormats1_", "FormatElementsCount1", "FormatElementsArray1", "replaceFrom1", "replaceTo1");
970 break;
971 }
972
973 mnFormats = mnFormats + formatCount;
974
975 if (mnSection == 0)
976 {
977 // Extract and add date acceptance pattern for full date, so we provide
978 // at least one valid pattern, even if the number parser doesn't need
979 // that one.
980 /* XXX NOTE: only simple [...] modifier and "..." quotes detected and
981 * ignored, not nested, no fancy stuff. */
982 // aDateSep can be empty if LC_CTYPE was a ref=..., determine from
983 // FormatCode then.
984 sal_uInt32 cDateSep = (aDateSep.isEmpty()
985 ? 0 : aDateSep.iterateCodePoints( &o3tl::temporary(sal_Int32(0))));
986 sal_uInt32 cDateSep2 = cDateSep;
987 sal_Int32 nIndex = 0;
988 OUStringBuffer aPatternBuf(5);
989 OUStringBuffer aPatternBuf2(5);
990 sal_uInt8 nDetected = 0; // bits Y,M,D
991 bool bInModifier = false;
992 bool bQuoted = false;
993 while (nIndex < sTheDateEditFormat.getLength() && nDetected < 7)
994 {
995 sal_uInt32 cChar = sTheDateEditFormat.iterateCodePoints( &nIndex);
996 if (bInModifier)
997 {
998 if (cChar == ']')
999 bInModifier = false;
1000 continue; // while
1001 }
1002 if (bQuoted)
1003 {
1004 if (cChar == '"')
1005 bQuoted = false;
1006 continue; // while
1007 }
1008 switch (cChar)
1009 {
1010 case 'Y':
1011 case 'y':
1012 if (!(nDetected & 4))
1013 {
1014 aPatternBuf.append( 'Y');
1015 if (!aPatternBuf2.isEmpty())
1016 aPatternBuf2.append( 'Y');
1017 nDetected |= 4;
1018 }
1019 break;
1020 case 'M':
1021 case 'm':
1022 if (!(nDetected & 2))
1023 {
1024 aPatternBuf.append( 'M');
1025 if (!aPatternBuf2.isEmpty())
1026 aPatternBuf2.append( 'M');
1027 nDetected |= 2;
1028 }
1029 break;
1030 case 'D':
1031 case 'd':
1032 if (!(nDetected & 1))
1033 {
1034 aPatternBuf.append( 'D');
1035 if (!aPatternBuf2.isEmpty())
1036 aPatternBuf2.append( 'D');
1037 nDetected |= 1;
1038 }
1039 break;
1040 case '[':
1041 bInModifier = true;
1042 break;
1043 case '"':
1044 bQuoted = true;
1045 break;
1046 case '\\':
1047 cChar = sTheDateEditFormat.iterateCodePoints( &nIndex);
1048 goto handleDefault;
1049 case '-':
1050 case '.':
1051 case '/':
1052 // There are locales that use an ISO 8601 edit format
1053 // regardless of what the date separator or other formats
1054 // say, for example hu-HU. Generalize this for all cases
1055 // where the used separator differs and is one of the known
1056 // separators and generate a second pattern with the
1057 // format's separator at the current position.
1058 cDateSep2 = cChar;
1059 [[fallthrough]];
1060 default:
1061 handleDefault:
1062 if (!cDateSep)
1063 cDateSep = cChar;
1064 if (!cDateSep2)
1065 cDateSep2 = cChar;
1066 if (cDateSep != cDateSep2 && aPatternBuf2.isEmpty())
1067 aPatternBuf2 = aPatternBuf;
1068 if (cChar == cDateSep || cChar == cDateSep2)
1069 aPatternBuf.append( OUString( &cDateSep, 1)); // always the defined separator
1070 if (cChar == cDateSep2 && !aPatternBuf2.isEmpty())
1071 aPatternBuf2.append( OUString( &cDateSep2, 1)); // always the format's separator
1072 break;
1073 // The localized legacy:
1074 case 'A':
1075 if (((nDetected & 7) == 3) || ((nDetected & 7) == 0))
1076 {
1077 // es DD/MM/AAAA
1078 // fr JJ.MM.AAAA
1079 // it GG/MM/AAAA
1080 // fr_CA AAAA-MM-JJ
1081 aPatternBuf.append( 'Y');
1082 if (!aPatternBuf2.isEmpty())
1083 aPatternBuf2.append( 'Y');
1084 nDetected |= 4;
1085 }
1086 break;
1087 case 'J':
1088 if (((nDetected & 7) == 0) || ((nDetected & 7) == 6))
1089 {
1090 // fr JJ.MM.AAAA
1091 // fr_CA AAAA-MM-JJ
1092 aPatternBuf.append( 'D');
1093 if (!aPatternBuf2.isEmpty())
1094 aPatternBuf2.append( 'D');
1095 nDetected |= 1;
1096 }
1097 else if ((nDetected & 7) == 3)
1098 {
1099 // nl DD-MM-JJJJ
1100 // de TT.MM.JJJJ
1101 aPatternBuf.append( 'Y');
1102 if (!aPatternBuf2.isEmpty())
1103 aPatternBuf2.append( 'Y');
1104 nDetected |= 4;
1105 }
1106 break;
1107 case 'T':
1108 if ((nDetected & 7) == 0)
1109 {
1110 // de TT.MM.JJJJ
1111 aPatternBuf.append( 'D');
1112 if (!aPatternBuf2.isEmpty())
1113 aPatternBuf2.append( 'D');
1114 nDetected |= 1;
1115 }
1116 break;
1117 case 'G':
1118 if ((nDetected & 7) == 0)
1119 {
1120 // it GG/MM/AAAA
1121 aPatternBuf.append( 'D');
1122 if (!aPatternBuf2.isEmpty())
1123 aPatternBuf2.append( 'D');
1124 nDetected |= 1;
1125 }
1126 break;
1127 case 'P':
1128 if ((nDetected & 7) == 0)
1129 {
1130 // fi PP.KK.VVVV
1131 aPatternBuf.append( 'D');
1132 if (!aPatternBuf2.isEmpty())
1133 aPatternBuf2.append( 'D');
1134 nDetected |= 1;
1135 }
1136 break;
1137 case 'K':
1138 if ((nDetected & 7) == 1)
1139 {
1140 // fi PP.KK.VVVV
1141 aPatternBuf.append( 'M');
1142 if (!aPatternBuf2.isEmpty())
1143 aPatternBuf2.append( 'M');
1144 nDetected |= 2;
1145 }
1146 break;
1147 case 'V':
1148 if ((nDetected & 7) == 3)
1149 {
1150 // fi PP.KK.VVVV
1151 aPatternBuf.append( 'Y');
1152 if (!aPatternBuf2.isEmpty())
1153 aPatternBuf2.append( 'Y');
1154 nDetected |= 4;
1155 }
1156 break;
1157 }
1158 }
1159 OUString aPattern( aPatternBuf.makeStringAndClear());
1160 if (((nDetected & 7) != 7) || aPattern.getLength() < 5)
1161 {
1162 incErrorStr( "Error: failed to extract full date acceptance pattern: %s\n", aPattern);
1163 fprintf( stderr, " with DateSeparator '%s' from FormatCode '%s' (formatindex=\"%d\")\n",
1164 OSTR( OUString(&cDateSep, 1)), OSTR( sTheDateEditFormat),
1165 int(cssi::NumberFormatIndex::DATE_SYS_DDMMYYYY));
1166 }
1167 else
1168 {
1169 fprintf( stderr, "Generated date acceptance pattern: '%s' from '%s' (formatindex=\"%d\" and defined DateSeparator '%s')\n",
1170 OSTR( aPattern), OSTR( sTheDateEditFormat),
1171 int(cssi::NumberFormatIndex::DATE_SYS_DDMMYYYY),
1172 OSTR( OUString(&cDateSep, 1)));
1173 // Insert at front so full date pattern is first in checks.
1174 theDateAcceptancePatterns.insert( theDateAcceptancePatterns.begin(), aPattern);
1175 }
1176 if (!aPatternBuf2.isEmpty())
1177 {
1178 OUString aPattern2( aPatternBuf2.makeStringAndClear());
1179 if (aPattern2.getLength() < 5)
1180 {
1181 incErrorStr( "Error: failed to extract 2nd date acceptance pattern: %s\n", aPattern2);
1182 fprintf( stderr, " with DateSeparator '%s' from FormatCode '%s' (formatindex=\"%d\")\n",
1183 OSTR( OUString(&cDateSep2, 1)), OSTR( sTheDateEditFormat),
1184 int(cssi::NumberFormatIndex::DATE_SYS_DDMMYYYY));
1185 }
1186 else
1187 {
1188 fprintf( stderr, "Generated 2nd acceptance pattern: '%s' from '%s' (formatindex=\"%d\")\n",
1189 OSTR( aPattern2), OSTR( sTheDateEditFormat),
1190 int(cssi::NumberFormatIndex::DATE_SYS_DDMMYYYY));
1191 theDateAcceptancePatterns.insert( theDateAcceptancePatterns.begin(), aPattern2);
1192 }
1193 }
1194
1195 // Rudimentary check if a pattern interferes with decimal number.
1196 // But only if not inherited in which case we don't have aDecSep here.
1197 if (!aDecSep.isEmpty())
1198 {
1199 sal_uInt32 cDecSep = aDecSep.iterateCodePoints( &o3tl::temporary(sal_Int32(0)));
1200 for (auto const& elem : theDateAcceptancePatterns)
1201 {
1202 if (elem.getLength() == (cDecSep <= 0xffff ? 3 : 4))
1203 {
1204 if (elem.iterateCodePoints( &o3tl::temporary(sal_Int32(1))) == cDecSep)
1205 {
1206 ++nError;
1207 fprintf( stderr, "Error: Date acceptance pattern '%s' matches decimal number '#%s#'\n",
1208 OSTR(elem), OSTR( aDecSep));
1209 }
1210 }
1211 }
1212 }
1213
1214 // Check for duplicates.
1215 for (std::vector<OUString>::const_iterator aIt = theDateAcceptancePatterns.begin();
1216 aIt != theDateAcceptancePatterns.end(); ++aIt)
1217 {
1218 for (std::vector<OUString>::iterator aComp = theDateAcceptancePatterns.begin();
1219 aComp != theDateAcceptancePatterns.end(); /*nop*/)
1220 {
1221 if (aIt != aComp && *aIt == *aComp)
1222 {
1223 incErrorStr( "Error: Duplicated DateAcceptancePattern: %s\n", *aComp);
1224 aComp = theDateAcceptancePatterns.erase( aComp);
1225 }
1226 else
1227 ++aComp;
1228 }
1229 }
1230
1231 sal_Int16 nbOfDateAcceptancePatterns = static_cast<sal_Int16>(theDateAcceptancePatterns.size());
1232
1233 for (sal_Int16 i = 0; i < nbOfDateAcceptancePatterns; ++i)
1234 {
1235 of.writeOUStringLiteralParameter("DateAcceptancePattern", theDateAcceptancePatterns[i], i);
1236 }
1237
1238 of.writeAsciiString("static const sal_Int16 DateAcceptancePatternsCount = ");
1239 of.writeInt( nbOfDateAcceptancePatterns);
1240 of.writeAsciiString(";\n");
1241
1242 of.writeAsciiString("static constexpr rtl::OUStringConstExpr DateAcceptancePatternsArray[] = {\n");
1243 for (sal_Int16 i = 0; i < nbOfDateAcceptancePatterns; ++i)
1244 {
1245 of.writeAsciiString("\t");
1246 of.writeAsciiString("DateAcceptancePattern");
1247 of.writeInt(i);
1248 of.writeAsciiString(",\n");
1249 }
1250 of.writeAsciiString("};\n\n");
1251
1252 of.writeOUStringFunction("getDateAcceptancePatterns_", "DateAcceptancePatternsCount", "DateAcceptancePatternsArray");
1253 }
1254
1255 ++mnSection;
1256}
1257
1259{
1260 OUString useLocale = getAttr().getValueByName("ref");
1261 if (!useLocale.isEmpty()) {
1262 useLocale = useLocale.replace( '-', '_');
1263 of.writeOUStringRefFunction("getCollatorImplementation_", useLocale);
1264 of.writeOUStringRefFunction("getCollationOptions_", useLocale);
1265 return;
1266 }
1267 sal_Int16 nbOfCollations = 0;
1268 sal_Int16 nbOfCollationOptions = 0;
1269
1270 for ( sal_Int32 j = 0; j < getNumberOfChildren(); j++ ) {
1271 LocaleNode * currNode = getChildAt (j);
1272 if( currNode->getName() == "Collator" )
1273 {
1274 OUString str;
1275 str = currNode->getAttr().getValueByName("unoid");
1276 of.writeOUStringLiteralParameter("CollatorID", str, j);
1277 str = currNode->getValue();
1278 of.writeOUStringLiteralParameter("CollatorRule", str, j);
1279 str = currNode -> getAttr().getValueByName("default");
1280 of.writeOUStringLiteralDefaultParameter("Collator", str, j);
1281 of.writeAsciiString("\n");
1282
1283 nbOfCollations++;
1284 }
1285 if( currNode->getName() == "CollationOptions" )
1286 {
1287 LocaleNode* pCollationOptions = currNode;
1288 nbOfCollationOptions = sal::static_int_cast<sal_Int16>( pCollationOptions->getNumberOfChildren() );
1289 for( sal_Int16 i=0; i<nbOfCollationOptions; i++ )
1290 {
1291 of.writeOUStringLiteralParameter("collationOption", pCollationOptions->getChildAt( i )->getValue(), i );
1292 }
1293
1294 of.writeAsciiString("static const sal_Int16 nbOfCollationOptions = ");
1295 of.writeInt( nbOfCollationOptions );
1296 of.writeAsciiString(";\n\n");
1297 }
1298 }
1299 of.writeAsciiString("static const sal_Int16 nbOfCollations = ");
1300 of.writeInt(nbOfCollations);
1301 of.writeAsciiString(";\n\n");
1302
1303 of.writeAsciiString("\nstatic constexpr rtl::OUStringConstExpr LCCollatorArray[] = {\n");
1304 for(sal_Int16 j = 0; j < nbOfCollations; j++) {
1305 of.writeAsciiString("\tCollatorID");
1306 of.writeInt(j);
1307 of.writeAsciiString(",\n");
1308
1309 of.writeAsciiString("\tdefaultCollator");
1310 of.writeInt(j);
1311 of.writeAsciiString(",\n");
1312
1313 of.writeAsciiString("\tCollatorRule");
1314 of.writeInt(j);
1315 of.writeAsciiString(",\n");
1316 }
1317 of.writeAsciiString("};\n\n");
1318
1319 of.writeAsciiString("static constexpr rtl::OUStringConstExpr collationOptions[] = {");
1320 for( sal_Int16 j=0; j<nbOfCollationOptions; j++ )
1321 {
1322 if (j)
1323 of.writeAsciiString( ", " );
1324 of.writeAsciiString( "collationOption" );
1325 of.writeInt( j );
1326 }
1327 of.writeAsciiString("};\n");
1328 of.writeOUStringFunction("getCollatorImplementation_", "nbOfCollations", "LCCollatorArray");
1329 of.writeOUStringFunction("getCollationOptions_", "nbOfCollationOptions", "collationOptions");
1330}
1331
1333{
1334 OUString useLocale = getAttr().getValueByName("ref");
1335 if (!useLocale.isEmpty()) {
1336 useLocale = useLocale.replace( '-', '_');
1337 of.writeOUStringRefFunction("getSearchOptions_", useLocale);
1338 return;
1339 }
1340
1341 if( getNumberOfChildren() != 1 )
1342 {
1343 ++nError;
1344 fprintf(
1345 stderr, "Error: LC_SEARCH: more than 1 child: %" SAL_PRIdINT32 "\n",
1347 }
1348 sal_Int32 i;
1349 LocaleNode* pSearchOptions = getChildAt( 0 );
1350 sal_Int32 nSearchOptions = pSearchOptions->getNumberOfChildren();
1351 for( i=0; i<nSearchOptions; i++ )
1352 {
1353 of.writeOUStringLiteralParameter("searchOption", pSearchOptions->getChildAt( i )->getValue(), sal::static_int_cast<sal_Int16>(i) );
1354 }
1355
1356 of.writeAsciiString("static const sal_Int16 nbOfSearchOptions = ");
1357 of.writeInt( sal::static_int_cast<sal_Int16>( nSearchOptions ) );
1358 of.writeAsciiString(";\n\n");
1359
1360 of.writeAsciiString("static constexpr rtl::OUStringConstExpr searchOptions[] = {");
1361 for( i=0; i<nSearchOptions; i++ )
1362 {
1363 if (i)
1364 of.writeAsciiString( ", " );
1365 of.writeAsciiString( "searchOption" );
1366 of.writeInt( sal::static_int_cast<sal_Int16>(i) );
1367 }
1368 of.writeAsciiString(" };\n");
1369 of.writeOUStringFunction("getSearchOptions_", "nbOfSearchOptions", "searchOptions");
1370}
1371
1373{
1374 OUString useLocale = getAttr().getValueByName("ref");
1375 if (!useLocale.isEmpty()) {
1376 useLocale = useLocale.replace( '-', '_');
1377 of.writeOUStringRefFunction("getIndexAlgorithm_", useLocale);
1378 of.writeOUStringRefFunction("getUnicodeScripts_", useLocale);
1379 of.writeOUStringRefFunction("getFollowPageWords_", useLocale);
1380 return;
1381 }
1382 sal_Int16 nbOfIndexs = 0;
1383 sal_Int16 nbOfUnicodeScripts = 0;
1384 sal_Int16 nbOfPageWords = 0;
1385 for (sal_Int32 i = 0; i< getNumberOfChildren();i++) {
1386 LocaleNode * currNode = getChildAt (i);
1387 if( currNode->getName() == "IndexKey" )
1388 {
1389 OUString str;
1390 str = currNode->getAttr().getValueByName("unoid");
1391 of.writeOUStringLiteralParameter("IndexID", str, nbOfIndexs);
1392 str = currNode->getAttr().getValueByName("module");
1393 of.writeOUStringLiteralParameter("IndexModule", str, nbOfIndexs);
1394 str = currNode->getValue();
1395 of.writeOUStringLiteralParameter("IndexKey", str, nbOfIndexs);
1396 str = currNode -> getAttr().getValueByName("default");
1397 of.writeOUStringLiteralDefaultParameter("Index", str, nbOfIndexs);
1398 str = currNode -> getAttr().getValueByName("phonetic");
1399 of.writeOUStringLiteralDefaultParameter("Phonetic", str, nbOfIndexs);
1400 of.writeAsciiString("\n");
1401
1402 nbOfIndexs++;
1403 }
1404 if( currNode->getName() == "UnicodeScript" )
1405 {
1406 of.writeOUStringLiteralParameter("unicodeScript", currNode->getValue(), nbOfUnicodeScripts );
1407 nbOfUnicodeScripts++;
1408
1409 }
1410 if( currNode->getName() == "FollowPageWord" )
1411 {
1412 of.writeOUStringLiteralParameter("followPageWord", currNode->getValue(), nbOfPageWords);
1413 nbOfPageWords++;
1414 }
1415 }
1416 of.writeAsciiString("static const sal_Int16 nbOfIndexs = ");
1417 of.writeInt(nbOfIndexs);
1418 of.writeAsciiString(";\n\n");
1419
1420 of.writeAsciiString("\nstatic constexpr rtl::OUStringConstExpr IndexArray[] = {\n");
1421 for(sal_Int16 i = 0; i < nbOfIndexs; i++) {
1422 of.writeAsciiString("\tIndexID");
1423 of.writeInt(i);
1424 of.writeAsciiString(",\n");
1425
1426 of.writeAsciiString("\tIndexModule");
1427 of.writeInt(i);
1428 of.writeAsciiString(",\n");
1429
1430 of.writeAsciiString("\tIndexKey");
1431 of.writeInt(i);
1432 of.writeAsciiString(",\n");
1433
1434 of.writeAsciiString("\tdefaultIndex");
1435 of.writeInt(i);
1436 of.writeAsciiString(",\n");
1437
1438 of.writeAsciiString("\tdefaultPhonetic");
1439 of.writeInt(i);
1440 of.writeAsciiString(",\n");
1441 }
1442 of.writeAsciiString("};\n\n");
1443
1444 of.writeAsciiString("static const sal_Int16 nbOfUnicodeScripts = ");
1445 of.writeInt( nbOfUnicodeScripts );
1446 of.writeAsciiString(";\n\n");
1447
1448 of.writeAsciiString("static constexpr rtl::OUStringConstExpr UnicodeScriptArray[] = {");
1449 for( sal_Int16 i=0; i<nbOfUnicodeScripts; i++ )
1450 {
1451 if (i)
1452 of.writeAsciiString( ", " );
1453 of.writeAsciiString( "unicodeScript" );
1454 of.writeInt( i );
1455 }
1456 of.writeAsciiString(" };\n\n");
1457
1458 of.writeAsciiString("static const sal_Int16 nbOfPageWords = ");
1459 of.writeInt(nbOfPageWords);
1460 of.writeAsciiString(";\n\n");
1461
1462 // MSVC doesn't like zero sized arrays
1463 if (nbOfPageWords == 0)
1464 {
1465 // generate dummy array, reuse unicodeScript0 for dummy entry
1466 of.writeAsciiString("//dummy array, we have zero entries\n");
1467 of.writeAsciiString("static constexpr rtl::OUStringConstExpr FollowPageWordArray[] = { unicodeScript0 };\n\n");
1468 }
1469 else
1470 {
1471 of.writeAsciiString("static constexpr rtl::OUStringConstExpr FollowPageWordArray[] = {\n");
1472 for(sal_Int16 i = 0; i < nbOfPageWords; i++) {
1473 if (i)
1474 of.writeAsciiString(",\n");
1475 of.writeAsciiString("\tfollowPageWord");
1476 of.writeInt(i);
1477 }
1478 of.writeAsciiString("\t\n};\n\n");
1479 }
1480 of.writeOUStringFunction("getIndexAlgorithm_", "nbOfIndexs", "IndexArray");
1481 of.writeOUStringFunction("getUnicodeScripts_", "nbOfUnicodeScripts", "UnicodeScriptArray");
1482 of.writeOUStringFunction("getFollowPageWords_", "nbOfPageWords", "FollowPageWordArray");
1483}
1484
1485
1486static void lcl_writeAbbrFullNarrNames( const OFileWriter & of, const LocaleNode* currNode,
1487 const char* elementTag, sal_Int16 i, sal_Int16 j )
1488{
1489 OUString aAbbrName = currNode->getChildAt(1)->getValue();
1490 OUString aFullName = currNode->getChildAt(2)->getValue();
1491 OUString aNarrName;
1492 LocaleNode* p = (currNode->getNumberOfChildren() > 3 ? currNode->getChildAt(3) : nullptr);
1493 if ( p && p->getName() == "DefaultNarrowName" )
1494 aNarrName = p->getValue();
1495 else
1496 {
1497 sal_uInt32 nChar = aFullName.iterateCodePoints( &o3tl::temporary(sal_Int32(0)));
1498 aNarrName = OUString( &nChar, 1);
1499 }
1500 of.writeOUStringLiteralParameter( elementTag, "DefaultAbbrvName", aAbbrName, i, j);
1501 of.writeOUStringLiteralParameter( elementTag, "DefaultFullName", aFullName, i, j);
1502 of.writeOUStringLiteralParameter( elementTag, "DefaultNarrowName", aNarrName, i, j);
1503}
1504
1505static void lcl_writeTabTagString( const OFileWriter & of, const char* pTag, const char* pStr )
1506{
1507 of.writeAsciiString("\t");
1508 of.writeAsciiString( pTag);
1509 of.writeAsciiString( pStr);
1510}
1511
1513 const char* pTag, const char* pStr, sal_Int16 i, sal_Int16 j )
1514{
1515 lcl_writeTabTagString( of, pTag, pStr);
1516 of.writeInt(i); of.writeInt(j); of.writeAsciiString(",\n");
1517}
1518
1519static void lcl_writeAbbrFullNarrArrays( const OFileWriter & of, sal_Int16 nCount,
1520 const char* elementTag, sal_Int16 i, bool bNarrow )
1521{
1522 if (nCount == 0)
1523 {
1524 lcl_writeTabTagString( of, elementTag, "Ref");
1525 of.writeInt(i); of.writeAsciiString(",\n");
1526 lcl_writeTabTagString( of, elementTag, "RefName");
1527 of.writeInt(i); of.writeAsciiString(",\n");
1528 }
1529 else
1530 {
1531 for (sal_Int16 j = 0; j < nCount; j++)
1532 {
1533 lcl_writeTabTagStringNums( of, elementTag, "ID", i, j);
1534 lcl_writeTabTagStringNums( of, elementTag, "DefaultAbbrvName", i, j);
1535 lcl_writeTabTagStringNums( of, elementTag, "DefaultFullName", i, j);
1536 if (bNarrow)
1537 lcl_writeTabTagStringNums( of, elementTag, "DefaultNarrowName", i, j);
1538 }
1539 }
1540}
1541
1542bool LCCalendarNode::expectedCalendarElement( std::u16string_view rName,
1543 const LocaleNode* pNode, sal_Int16 nChild, std::u16string_view rCalendarID ) const
1544{
1545 bool bFound = true;
1546 if (nChild >= 0)
1547 {
1548 if (nChild >= pNode->getNumberOfChildren())
1549 bFound = false;
1550 else
1551 pNode = pNode->getChildAt(nChild);
1552 }
1553 if (bFound && (!pNode || pNode->getName() != rName))
1554 bFound = false;
1555 if (!bFound)
1556 incErrorStrStr( "Error: <%s> element expected in calendar '%s'\n", rName, rCalendarID);
1557 return bFound;
1558}
1559
1561{
1562 OUString useLocale = getAttr().getValueByName("ref");
1563 if (!useLocale.isEmpty()) {
1564 useLocale = useLocale.replace( '-', '_');
1565 of.writeOUStringRefFunction("getAllCalendars_", useLocale);
1566 return;
1567 }
1568 sal_Int16 nbOfCalendars = sal::static_int_cast<sal_Int16>( getNumberOfChildren() );
1569 OUString str;
1570 std::unique_ptr<sal_Int16[]> nbOfDays( new sal_Int16[nbOfCalendars] );
1571 std::unique_ptr<sal_Int16[]> nbOfMonths( new sal_Int16[nbOfCalendars] );
1572 std::unique_ptr<sal_Int16[]> nbOfGenitiveMonths( new sal_Int16[nbOfCalendars] );
1573 std::unique_ptr<sal_Int16[]> nbOfPartitiveMonths( new sal_Int16[nbOfCalendars] );
1574 std::unique_ptr<sal_Int16[]> nbOfEras( new sal_Int16[nbOfCalendars] );
1575
1576 // Known allowed calendar identifiers (unoid) and whether used or not.
1577 // Of course there must be an implementation for new to be added
1578 // identifiers.. see data/locale.dtd
1579 std::map< OUString, bool > aCalendars;
1580 aCalendars["buddhist"] = false;
1581 aCalendars["gengou"] = false;
1582 aCalendars["gregorian"] = false;
1583 aCalendars["hanja"] = false;
1584 aCalendars["hanja_yoil"] = false;
1585 aCalendars["hijri"] = false;
1586 aCalendars["jewish"] = false;
1587 aCalendars["ROC"] = false;
1588 // Not in ODF:
1589 aCalendars["dangi"] = false;
1590 aCalendars["persian"] = false;
1591
1592 sal_Int16 j;
1593 sal_Int16 i;
1594 bool bHasGregorian = false;
1595
1596
1597 for ( i = 0; i < nbOfCalendars; i++) {
1598 LocaleNode * calNode = getChildAt (i);
1599 OUString calendarID = calNode -> getAttr().getValueByName("unoid");
1600 of.writeOUStringLiteralParameter( "calendarID", calendarID, i);
1601 bool bGregorian = calendarID == "gregorian";
1602 if (!bHasGregorian)
1603 bHasGregorian = bGregorian;
1604 auto calIt = aCalendars.find(calendarID);
1605 if (calIt == aCalendars.end())
1606 incErrorStr( "Error: unknown Calendar unoid: %s\n", calendarID);
1607 else if (calIt->second)
1608 incErrorStr( "Error: duplicate Calendar unoid: %s\n", calendarID);
1609 else
1610 calIt->second = true;
1611 str = calNode -> getAttr().getValueByName("default");
1612 of.writeOUStringLiteralDefaultParameter("Calendar", str, i);
1613
1614 sal_Int16 nChild = 0;
1615
1616 // Generate Days of Week
1617 const char *elementTag;
1618 LocaleNode * daysNode = nullptr;
1619 OUString ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
1620 ref_name = ref_name.replace( '-', '_');
1621 if (!ref_name.isEmpty() && i > 0) {
1622 for (j = 0; j < i; j++) {
1623 str = getChildAt(j)->getAttr().getValueByName("unoid");
1624 if (str == ref_name)
1625 daysNode = getChildAt(j)->getChildAt(0);
1626 }
1627 }
1628 if (!ref_name.isEmpty() && daysNode == nullptr) {
1629 of.writeOUStringLiteralParameter("dayRef", u"ref", i);
1630 of.writeOUStringLiteralParameter("dayRefName", ref_name, i);
1631 nbOfDays[i] = 0;
1632 } else {
1633 if (daysNode == nullptr)
1634 daysNode = calNode -> getChildAt(nChild);
1635 nbOfDays[i] = sal::static_int_cast<sal_Int16>( daysNode->getNumberOfChildren() );
1636 if (bGregorian && nbOfDays[i] != 7)
1637 incErrorInt( "Error: A Gregorian calendar must have 7 days per week, this one has %d\n", nbOfDays[i]);
1638 elementTag = "day";
1639 for (j = 0; j < nbOfDays[i]; j++) {
1640 LocaleNode *currNode = daysNode -> getChildAt(j);
1641 OUString dayID( currNode->getChildAt(0)->getValue());
1642 of.writeOUStringLiteralParameter("dayID", dayID, i, j);
1643 if ( j == 0 && bGregorian && dayID != "sun" )
1644 incError( "First day of a week of a Gregorian calendar must be <DayID>sun</DayID>");
1645 lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
1646 }
1647 }
1648 ++nChild;
1649
1650 // Generate Months of Year
1651 LocaleNode * monthsNode = nullptr;
1652 ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
1653 ref_name = ref_name.replace( '-', '_');
1654 if (!ref_name.isEmpty() && i > 0) {
1655 for (j = 0; j < i; j++) {
1656 str = getChildAt(j)->getAttr().getValueByName("unoid");
1657 if (str == ref_name)
1658 monthsNode = getChildAt(j)->getChildAt(1);
1659 }
1660 }
1661 if (!ref_name.isEmpty() && monthsNode == nullptr) {
1662 of.writeOUStringLiteralParameter("monthRef", u"ref", i);
1663 of.writeOUStringLiteralParameter("monthRefName", ref_name, i);
1664 nbOfMonths[i] = 0;
1665 } else {
1666 if (monthsNode == nullptr)
1667 monthsNode = calNode -> getChildAt(nChild);
1668 nbOfMonths[i] = sal::static_int_cast<sal_Int16>( monthsNode->getNumberOfChildren() );
1669 if (bGregorian && nbOfMonths[i] != 12)
1670 incErrorInt( "Error: A Gregorian calendar must have 12 months, this one has %d\n", nbOfMonths[i]);
1671 elementTag = "month";
1672 for (j = 0; j < nbOfMonths[i]; j++) {
1673 LocaleNode *currNode = monthsNode -> getChildAt(j);
1674 OUString monthID( currNode->getChildAt(0)->getValue());
1675 of.writeOUStringLiteralParameter("monthID", monthID, i, j);
1676 if ( j == 0 && bGregorian && monthID != "jan" )
1677 incError( "First month of a year of a Gregorian calendar must be <MonthID>jan</MonthID>");
1678 lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
1679 }
1680 }
1681 ++nChild;
1682
1683 // Generate genitive Months of Year
1684 // Optional, if not present fall back to month nouns.
1685 if ( calNode->getChildAt(nChild)->getName() != "GenitiveMonths" )
1686 --nChild;
1687 LocaleNode * genitiveMonthsNode = nullptr;
1688 ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
1689 ref_name = ref_name.replace( '-', '_');
1690 if (!ref_name.isEmpty() && i > 0) {
1691 for (j = 0; j < i; j++) {
1692 str = getChildAt(j)->getAttr().getValueByName("unoid");
1693 if (str == ref_name)
1694 genitiveMonthsNode = getChildAt(j)->getChildAt(1);
1695 }
1696 }
1697 if (!ref_name.isEmpty() && genitiveMonthsNode == nullptr) {
1698 of.writeOUStringLiteralParameter("genitiveMonthRef", u"ref", i);
1699 of.writeOUStringLiteralParameter("genitiveMonthRefName", ref_name, i);
1700 nbOfGenitiveMonths[i] = 0;
1701 } else {
1702 if (genitiveMonthsNode == nullptr)
1703 genitiveMonthsNode = calNode -> getChildAt(nChild);
1704 nbOfGenitiveMonths[i] = sal::static_int_cast<sal_Int16>( genitiveMonthsNode->getNumberOfChildren() );
1705 if (bGregorian && nbOfGenitiveMonths[i] != 12)
1706 incErrorInt( "Error: A Gregorian calendar must have 12 genitive months, this one has %d\n", nbOfGenitiveMonths[i]);
1707 elementTag = "genitiveMonth";
1708 for (j = 0; j < nbOfGenitiveMonths[i]; j++) {
1709 LocaleNode *currNode = genitiveMonthsNode -> getChildAt(j);
1710 OUString genitiveMonthID( currNode->getChildAt(0)->getValue());
1711 of.writeOUStringLiteralParameter("genitiveMonthID", genitiveMonthID, i, j);
1712 if ( j == 0 && bGregorian && genitiveMonthID != "jan" )
1713 incError( "First genitive month of a year of a Gregorian calendar must be <MonthID>jan</MonthID>");
1714 lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
1715 }
1716 }
1717 ++nChild;
1718
1719 // Generate partitive Months of Year
1720 // Optional, if not present fall back to genitive months, or nominative
1721 // months (nouns) if that isn't present either.
1722 if ( calNode->getChildAt(nChild)->getName() != "PartitiveMonths" )
1723 --nChild;
1724 LocaleNode * partitiveMonthsNode = nullptr;
1725 ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
1726 ref_name = ref_name.replace( '-', '_');
1727 if (!ref_name.isEmpty() && i > 0) {
1728 for (j = 0; j < i; j++) {
1729 str = getChildAt(j)->getAttr().getValueByName("unoid");
1730 if (str == ref_name)
1731 partitiveMonthsNode = getChildAt(j)->getChildAt(1);
1732 }
1733 }
1734 if (!ref_name.isEmpty() && partitiveMonthsNode == nullptr) {
1735 of.writeOUStringLiteralParameter("partitiveMonthRef", u"ref", i);
1736 of.writeOUStringLiteralParameter("partitiveMonthRefName", ref_name, i);
1737 nbOfPartitiveMonths[i] = 0;
1738 } else {
1739 if (partitiveMonthsNode == nullptr)
1740 partitiveMonthsNode = calNode -> getChildAt(nChild);
1741 nbOfPartitiveMonths[i] = sal::static_int_cast<sal_Int16>( partitiveMonthsNode->getNumberOfChildren() );
1742 if (bGregorian && nbOfPartitiveMonths[i] != 12)
1743 incErrorInt( "Error: A Gregorian calendar must have 12 partitive months, this one has %d\n", nbOfPartitiveMonths[i]);
1744 elementTag = "partitiveMonth";
1745 for (j = 0; j < nbOfPartitiveMonths[i]; j++) {
1746 LocaleNode *currNode = partitiveMonthsNode -> getChildAt(j);
1747 OUString partitiveMonthID( currNode->getChildAt(0)->getValue());
1748 of.writeOUStringLiteralParameter("partitiveMonthID", partitiveMonthID, i, j);
1749 if ( j == 0 && bGregorian && partitiveMonthID != "jan" )
1750 incError( "First partitive month of a year of a Gregorian calendar must be <MonthID>jan</MonthID>");
1751 lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
1752 }
1753 }
1754 ++nChild;
1755
1756 // Generate Era name
1757 LocaleNode * erasNode = nullptr;
1758 ref_name = calNode -> getChildAt(nChild) ->getAttr().getValueByName("ref");
1759 ref_name = ref_name.replace( '-', '_');
1760 if (!ref_name.isEmpty() && i > 0) {
1761 for (j = 0; j < i; j++) {
1762 str = getChildAt(j)->getAttr().getValueByName("unoid");
1763 if (str == ref_name)
1764 erasNode = getChildAt(j)->getChildAt(2);
1765 }
1766 }
1767 if (!ref_name.isEmpty() && erasNode == nullptr) {
1768 of.writeOUStringLiteralParameter("eraRef", u"ref", i);
1769 of.writeOUStringLiteralParameter("eraRefName", ref_name, i);
1770 nbOfEras[i] = 0;
1771 } else {
1772 if (erasNode == nullptr)
1773 erasNode = calNode -> getChildAt(nChild);
1774 if (!expectedCalendarElement(u"Eras", erasNode, -1, calendarID))
1775 {
1776 --nChild;
1777 }
1778 else
1779 {
1780 nbOfEras[i] = sal::static_int_cast<sal_Int16>( erasNode->getNumberOfChildren() );
1781 if (bGregorian && nbOfEras[i] != 2)
1782 incErrorInt( "Error: A Gregorian calendar must have 2 eras, this one has %d\n", nbOfEras[i]);
1783 elementTag = "era";
1784 for (j = 0; j < nbOfEras[i]; j++) {
1785 LocaleNode *currNode = erasNode -> getChildAt(j);
1786 if (!expectedCalendarElement(u"Era", currNode, -1, calendarID))
1787 {
1788 continue; // for
1789 }
1790 OUString eraID( currNode->getChildAt(0)->getValue());
1791 of.writeOUStringLiteralParameter("eraID", eraID, i, j);
1792 if ( j == 0 && bGregorian && eraID != "bc" )
1793 incError( "First era of a Gregorian calendar must be <EraID>bc</EraID>");
1794 if ( j == 1 && bGregorian && eraID != "ad" )
1795 incError( "Second era of a Gregorian calendar must be <EraID>ad</EraID>");
1796 of.writeAsciiString("\n");
1797 of.writeOUStringLiteralParameter(elementTag, "DefaultAbbrvName",currNode->getChildAt(1)->getValue() ,i, j);
1798 of.writeOUStringLiteralParameter(elementTag, "DefaultFullName",currNode->getChildAt(2)->getValue() , i, j);
1799 }
1800 }
1801 }
1802 ++nChild;
1803
1804 if (expectedCalendarElement(u"StartDayOfWeek", calNode, nChild, calendarID))
1805 {
1806 str = calNode->getChildAt(nChild)->getChildAt(0)->getValue();
1807 if (nbOfDays[i])
1808 {
1809 for (j = 0; j < nbOfDays[i]; j++)
1810 {
1811 LocaleNode *currNode = daysNode->getChildAt(j);
1812 OUString dayID( currNode->getChildAt(0)->getValue());
1813 if (str == dayID)
1814 break; // for
1815 }
1816 if (j >= nbOfDays[i])
1817 incErrorStr( "Error: <StartDayOfWeek> <DayID> must be one of the <DaysOfWeek>, but is: %s\n", str);
1818 }
1819 of.writeOUStringLiteralParameter("startDayOfWeek", str, i);
1820 ++nChild;
1821 }
1822
1823 if (expectedCalendarElement(u"MinimalDaysInFirstWeek", calNode, nChild, calendarID))
1824 {
1825 str = calNode ->getChildAt(nChild)-> getValue();
1826 sal_Int16 nDays = sal::static_int_cast<sal_Int16>( str.toInt32() );
1827 if (nDays < 1 || (0 < nbOfDays[i] && nbOfDays[i] < nDays))
1828 incErrorInt( "Error: Bad value of MinimalDaysInFirstWeek: %d, must be 1 <= value <= days_in_week\n",
1829 nDays);
1830 of.writeOUStringLiteralIntParameter("minimalDaysInFirstWeek", i, nDays);
1831 }
1832 }
1833 if (!bHasGregorian)
1834 fprintf( stderr, "Warning: %s\n", "No Gregorian calendar defined, are you sure?");
1835
1836 of.writeAsciiString("static const sal_Int16 calendarsCount = ");
1837 of.writeInt(nbOfCalendars);
1838 of.writeAsciiString(";\n\n");
1839
1840 of.writeAsciiString("static constexpr OUStringLiteral nbOfDays = u\"");
1841 for(i = 0; i < nbOfCalendars; i++) {
1842 of.writeAsciiString("\\x");
1843 of.writeHexInt(nbOfDays[i]);
1844 }
1845 of.writeAsciiString("\";\n");
1846
1847 of.writeAsciiString("static constexpr OUStringLiteral nbOfMonths = u\"");
1848 for(i = 0; i < nbOfCalendars; i++) {
1849 of.writeAsciiString("\\x");
1850 of.writeHexInt(nbOfMonths[i]);
1851 }
1852 of.writeAsciiString("\";\n");
1853
1854 of.writeAsciiString("static constexpr OUStringLiteral nbOfGenitiveMonths = u\"");
1855 for(i = 0; i < nbOfCalendars; i++) {
1856 of.writeAsciiString("\\x");
1857 of.writeHexInt(nbOfGenitiveMonths[i]);
1858 }
1859 of.writeAsciiString("\";\n");
1860
1861 of.writeAsciiString("static constexpr OUStringLiteral nbOfPartitiveMonths = u\"");
1862 for(i = 0; i < nbOfCalendars; i++) {
1863 of.writeAsciiString("\\x");
1864 of.writeHexInt(nbOfPartitiveMonths[i]);
1865 }
1866 of.writeAsciiString("\";\n");
1867
1868 of.writeAsciiString("static constexpr OUStringLiteral nbOfEras = u\"");
1869 for(i = 0; i < nbOfCalendars; i++) {
1870 of.writeAsciiString("\\x");
1871 of.writeHexInt(nbOfEras[i]);
1872 }
1873 of.writeAsciiString("\";\n");
1874
1875
1876 of.writeAsciiString("static constexpr rtl::OUStringConstExpr calendars[] = {\n");
1877 of.writeAsciiString("\tnbOfDays,\n");
1878 of.writeAsciiString("\tnbOfMonths,\n");
1879 of.writeAsciiString("\tnbOfGenitiveMonths,\n");
1880 of.writeAsciiString("\tnbOfPartitiveMonths,\n");
1881 of.writeAsciiString("\tnbOfEras,\n");
1882 for(i = 0; i < nbOfCalendars; i++) {
1883 of.writeAsciiString("\tcalendarID");
1884 of.writeInt(i);
1885 of.writeAsciiString(",\n");
1886 of.writeAsciiString("\tdefaultCalendar");
1887 of.writeInt(i);
1888 of.writeAsciiString(",\n");
1889 lcl_writeAbbrFullNarrArrays( of, nbOfDays[i], "day", i, true);
1890 lcl_writeAbbrFullNarrArrays( of, nbOfMonths[i], "month", i, true);
1891 lcl_writeAbbrFullNarrArrays( of, nbOfGenitiveMonths[i], "genitiveMonth", i, true);
1892 lcl_writeAbbrFullNarrArrays( of, nbOfPartitiveMonths[i], "partitiveMonth", i, true);
1893 lcl_writeAbbrFullNarrArrays( of, nbOfEras[i], "era", i, false /*noNarrow*/);
1894 of.writeAsciiString("\tstartDayOfWeek");of.writeInt(i); of.writeAsciiString(",\n");
1895 of.writeAsciiString("\tminimalDaysInFirstWeek");of.writeInt(i); of.writeAsciiString(",\n");
1896 }
1897
1898 of.writeAsciiString("};\n\n");
1899 of.writeOUStringFunction("getAllCalendars_", "calendarsCount", "calendars");
1900}
1901
1902static bool isIso4217( const OUString& rStr )
1903{
1904 const sal_Unicode* p = rStr.getStr();
1905 return rStr.getLength() == 3
1906 && 'A' <= p[0] && p[0] <= 'Z'
1907 && 'A' <= p[1] && p[1] <= 'Z'
1908 && 'A' <= p[2] && p[2] <= 'Z'
1909 ;
1910}
1911
1913{
1914 OUString useLocale = getAttr().getValueByName("ref");
1915 if (!useLocale.isEmpty()) {
1916 useLocale = useLocale.replace( '-', '_');
1917 of.writeOUStringRefFunction("getAllCurrencies_", useLocale);
1918 return;
1919 }
1920 sal_Int16 nbOfCurrencies = 0;
1921 OUString str;
1922
1923 bool bTheDefault= false;
1924 bool bTheCompatible = false;
1925 for ( sal_Int32 i = 0; i < getNumberOfChildren(); i++,nbOfCurrencies++) {
1926 LocaleNode * currencyNode = getChildAt (i);
1927 str = currencyNode->getAttr().getValueByName("default");
1928 bool bDefault = of.writeOUStringLiteralDefaultParameter("Currency", str, nbOfCurrencies);
1929 str = currencyNode->getAttr().getValueByName("usedInCompatibleFormatCodes");
1930 bool bCompatible = of.writeOUStringLiteralDefaultParameter("CurrencyUsedInCompatibleFormatCodes", str, nbOfCurrencies);
1931 str = currencyNode->getAttr().getValueByName("legacyOnly");
1932 bool bLegacy = of.writeOUStringLiteralDefaultParameter("CurrencyLegacyOnly", str, nbOfCurrencies);
1933 if (bLegacy && (bDefault || bCompatible))
1934 incError( "Currency: if legacyOnly==true, both 'default' and 'usedInCompatibleFormatCodes' must be false.");
1935 if (bDefault)
1936 {
1937 if (bTheDefault)
1938 incError( "Currency: more than one default currency.");
1939 bTheDefault = true;
1940 }
1941 if (bCompatible)
1942 {
1943 if (bTheCompatible)
1944 incError( "Currency: more than one currency flagged as usedInCompatibleFormatCodes.");
1945 bTheCompatible = true;
1946 }
1947 str = currencyNode -> findNode ("CurrencyID") -> getValue();
1948 of.writeOUStringLiteralParameter("currencyID", str, nbOfCurrencies);
1949 // CurrencyID MUST be ISO 4217.
1950 if (!bLegacy && !isIso4217(str))
1951 incError( "CurrencyID is not ISO 4217");
1952 str = currencyNode -> findNode ("CurrencySymbol") -> getValue();
1953 of.writeOUStringLiteralParameter("currencySymbol", str, nbOfCurrencies);
1954 // Check if this currency really is the one used in number format
1955 // codes. In case of ref=... mechanisms it may be that TheCurrency
1956 // couldn't had been determined from the current locale (i.e. is
1957 // empty), silently assume the referred locale has things right.
1958 if (bCompatible && !sTheCompatibleCurrency.isEmpty() && sTheCompatibleCurrency != str)
1959 incErrorStrStr( "Error: CurrencySymbol \"%s\" flagged as usedInCompatibleFormatCodes doesn't match \"%s\" determined from format codes.\n", str, sTheCompatibleCurrency);
1960 str = currencyNode -> findNode ("BankSymbol") -> getValue();
1961 of.writeOUStringLiteralParameter("bankSymbol", str, nbOfCurrencies);
1962 // BankSymbol currently must be ISO 4217. May change later if
1963 // application always uses CurrencyID instead of BankSymbol.
1964 if (!bLegacy && !isIso4217(str))
1965 incError( "BankSymbol is not ISO 4217");
1966 str = currencyNode -> findNode ("CurrencyName") -> getValue();
1967 of.writeOUStringLiteralParameter("currencyName", str, nbOfCurrencies);
1968 str = currencyNode -> findNode ("DecimalPlaces") -> getValue();
1969 sal_Int16 nDecimalPlaces = static_cast<sal_Int16>(str.toInt32());
1970 of.writeOUStringLiteralIntParameter("currencyDecimalPlaces", nbOfCurrencies, nDecimalPlaces);
1971 of.writeAsciiString("\n");
1972 };
1973
1974 if (!bTheDefault)
1975 incError( "Currency: no default currency.");
1976 if (!bTheCompatible)
1977 incError( "Currency: no currency flagged as usedInCompatibleFormatCodes.");
1978
1979 of.writeAsciiString("static const sal_Int16 currencyCount = ");
1980 of.writeInt(nbOfCurrencies);
1981 of.writeAsciiString(";\n\n");
1982 of.writeAsciiString("static constexpr rtl::OUStringConstExpr currencies[] = {\n");
1983 for(sal_Int16 i = 0; i < nbOfCurrencies; i++) {
1984 of.writeAsciiString("\tcurrencyID");
1985 of.writeInt(i);
1986 of.writeAsciiString(",\n");
1987 of.writeAsciiString("\tcurrencySymbol");
1988 of.writeInt(i);
1989 of.writeAsciiString(",\n");
1990 of.writeAsciiString("\tbankSymbol");
1991 of.writeInt(i);
1992 of.writeAsciiString(",\n");
1993 of.writeAsciiString("\tcurrencyName");
1994 of.writeInt(i);
1995 of.writeAsciiString(",\n");
1996 of.writeAsciiString("\tdefaultCurrency");
1997 of.writeInt(i);
1998 of.writeAsciiString(",\n");
1999 of.writeAsciiString("\tdefaultCurrencyUsedInCompatibleFormatCodes");
2000 of.writeInt(i);
2001 of.writeAsciiString(",\n");
2002 of.writeAsciiString("\tcurrencyDecimalPlaces");
2003 of.writeInt(i);
2004 of.writeAsciiString(",\n");
2005 of.writeAsciiString("\tdefaultCurrencyLegacyOnly");
2006 of.writeInt(i);
2007 of.writeAsciiString(",\n");
2008 }
2009 of.writeAsciiString("};\n\n");
2010 of.writeOUStringFunction("getAllCurrencies_", "currencyCount", "currencies");
2011}
2012
2014{
2015 OUString useLocale = getAttr().getValueByName("ref");
2016 if (!useLocale.isEmpty()) {
2017 useLocale = useLocale.replace( '-', '_');
2018 of.writeOUStringRefFunction("getTransliterations_", useLocale);
2019 return;
2020 }
2021 sal_Int16 nbOfModules = 0;
2022 OUString str;
2023
2024 for ( sal_Int32 i = 0; i < getNumberOfChildren(); i++,nbOfModules++) {
2025 LocaleNode * transNode = getChildAt (i);
2026 str = transNode->getAttr().getValueByIndex(0);
2027 of.writeOUStringLiteralParameter("Transliteration", str, nbOfModules);
2028 }
2029 of.writeAsciiString("static const sal_Int16 nbOfTransliterations = ");
2030 of.writeInt(nbOfModules);
2031 of.writeAsciiString(";\n\n");
2032
2033 of.writeAsciiString("\nstatic constexpr rtl::OUStringConstExpr LCTransliterationsArray[] = {\n");
2034 for( sal_Int16 i = 0; i < nbOfModules; i++) {
2035 of.writeAsciiString("\tTransliteration");
2036 of.writeInt(i);
2037 of.writeAsciiString(",\n");
2038 }
2039 of.writeAsciiString("};\n\n");
2040 of.writeOUStringFunction("getTransliterations_", "nbOfTransliterations", "LCTransliterationsArray");
2041}
2042
2043namespace {
2044
2045struct NameValuePair {
2046 const char *name;
2047 const char *value;
2048};
2049
2050}
2051
2052const NameValuePair ReserveWord[] = {
2053 { "trueWord", "true" },
2054 { "falseWord", "false" },
2055 { "quarter1Word", "1st quarter" },
2056 { "quarter2Word", "2nd quarter" },
2057 { "quarter3Word", "3rd quarter" },
2058 { "quarter4Word", "4th quarter" },
2059 { "aboveWord", "above" },
2060 { "belowWord", "below" },
2061 { "quarter1Abbreviation", "Q1" },
2062 { "quarter2Abbreviation", "Q2" },
2063 { "quarter3Abbreviation", "Q3" },
2064 { "quarter4Abbreviation", "Q4" }
2065};
2066
2068{
2069 OUString useLocale = getAttr().getValueByName("ref");
2070 if (!useLocale.isEmpty()) {
2071 useLocale = useLocale.replace( '-', '_');
2072 of.writeOUStringRefFunction("getForbiddenCharacters_", useLocale);
2073 of.writeOUStringRefFunction("getBreakIteratorRules_", useLocale);
2074 of.writeOUStringRefFunction("getReservedWords_", useLocale);
2075 return;
2076 }
2077 const LocaleNode * reserveNode = findNode("ReservedWords");
2078 if (!reserveNode)
2079 incError( "No ReservedWords element."); // should not happen if validated...
2080 const LocaleNode * forbidNode = findNode("ForbiddenCharacters");
2081 const LocaleNode * breakNode = findNode("BreakIteratorRules");
2082
2083 bool bEnglishLocale = (strncmp( of.getLocale(), "en_", 3) == 0);
2084
2085 sal_Int16 nbOfWords = 0;
2086 OUString str;
2087 sal_Int16 i;
2088
2089 for ( i = 0; i < sal_Int16(SAL_N_ELEMENTS(ReserveWord)); i++,nbOfWords++) {
2090 const LocaleNode * curNode = (reserveNode ? reserveNode->findNode(
2091 ReserveWord[i].name) : nullptr);
2092 if (!curNode)
2093 fprintf( stderr,
2094 "Warning: No %s in ReservedWords, using en_US default: \"%s\".\n",
2096 str = curNode ? curNode -> getValue() : OUString::createFromAscii(ReserveWord[i].value);
2097 if (str.isEmpty())
2098 {
2099 ++nError;
2100 fprintf( stderr, "Error: No content for ReservedWords %s.\n", ReserveWord[i].name);
2101 }
2102 of.writeOUStringLiteralParameter("ReservedWord", str, nbOfWords);
2103 // "true", ..., "below" trigger untranslated warning.
2104 if (!bEnglishLocale && curNode && i <= 7 &&
2105 str.equalsIgnoreAsciiCaseAscii( ReserveWord[i].value))
2106 {
2107 fprintf( stderr,
2108 "Warning: ReservedWord %s seems to be untranslated \"%s\".\n",
2110 }
2111 }
2112 of.writeAsciiString("static const sal_Int16 nbOfReservedWords = ");
2113 of.writeInt(nbOfWords);
2114 of.writeAsciiString(";\n\n");
2115 of.writeAsciiString("\nstatic constexpr rtl::OUStringConstExpr LCReservedWordsArray[] = {\n");
2116 for( i = 0; i < nbOfWords; i++) {
2117 of.writeAsciiString("\tReservedWord");
2118 of.writeInt(i);
2119 of.writeAsciiString(",\n");
2120 }
2121 of.writeAsciiString("};\n\n");
2122 of.writeOUStringFunction("getReservedWords_", "nbOfReservedWords", "LCReservedWordsArray");
2123
2124 if (forbidNode) {
2125 of.writeOUStringLiteralParameter( "forbiddenBegin", forbidNode -> getChildAt(0)->getValue());
2126 of.writeOUStringLiteralParameter( "forbiddenEnd", forbidNode -> getChildAt(1)->getValue());
2127 of.writeOUStringLiteralParameter( "hangingChars", forbidNode -> getChildAt(2)->getValue());
2128 } else {
2129 of.writeOUStringLiteralParameter( "forbiddenBegin", std::u16string_view());
2130 of.writeOUStringLiteralParameter( "forbiddenEnd", std::u16string_view());
2131 of.writeOUStringLiteralParameter( "hangingChars", std::u16string_view());
2132 }
2133 of.writeAsciiString("\nstatic constexpr rtl::OUStringConstExpr LCForbiddenCharactersArray[] = {\n");
2134 of.writeAsciiString("\tforbiddenBegin,\n");
2135 of.writeAsciiString("\tforbiddenEnd,\n");
2136 of.writeAsciiString("\thangingChars\n");
2137 of.writeAsciiString("};\n\n");
2138 of.writeOUStringFunction("getForbiddenCharacters_", "3", "LCForbiddenCharactersArray");
2139
2140 if (breakNode) {
2141 of.writeOUStringLiteralParameter( "EditMode", breakNode -> getChildAt(0)->getValue());
2142 of.writeOUStringLiteralParameter( "DictionaryMode", breakNode -> getChildAt(1)->getValue());
2143 of.writeOUStringLiteralParameter( "WordCountMode", breakNode -> getChildAt(2)->getValue());
2144 of.writeOUStringLiteralParameter( "CharacterMode", breakNode -> getChildAt(3)->getValue());
2145 of.writeOUStringLiteralParameter( "LineMode", breakNode -> getChildAt(4)->getValue());
2146 } else {
2147 of.writeOUStringLiteralParameter( "EditMode", std::u16string_view());
2148 of.writeOUStringLiteralParameter( "DictionaryMode", std::u16string_view());
2149 of.writeOUStringLiteralParameter( "WordCountMode", std::u16string_view());
2150 of.writeOUStringLiteralParameter( "CharacterMode", std::u16string_view());
2151 of.writeOUStringLiteralParameter( "LineMode", std::u16string_view());
2152 }
2153 of.writeAsciiString("\nstatic constexpr rtl::OUStringConstExpr LCBreakIteratorRulesArray[] = {\n");
2154 of.writeAsciiString("\tEditMode,\n");
2155 of.writeAsciiString("\tDictionaryMode,\n");
2156 of.writeAsciiString("\tWordCountMode,\n");
2157 of.writeAsciiString("\tCharacterMode,\n");
2158 of.writeAsciiString("\tLineMode\n");
2159 of.writeAsciiString("};\n\n");
2160 of.writeOUStringFunction("getBreakIteratorRules_", "5", "LCBreakIteratorRulesArray");
2161
2162}
2163
2165{
2166 of.writeAsciiString("// ---> ContinuousNumbering\n");
2167 OUString useLocale = getAttr().getValueByName("ref");
2168 if (!useLocale.isEmpty()) {
2169 useLocale = useLocale.replace( '-', '_');
2170 of.writeOUStringRefFunction2("getContinuousNumberingLevels_", useLocale);
2171 return;
2172 }
2173
2174 // hard code number of attributes per style.
2175 const int nAttributes = 5;
2176 const char* attr[ nAttributes ] = { "Prefix", "NumType", "Suffix", "Transliteration", "NatNum" };
2177
2178 // record each attribute of each style in a static C++ variable.
2179 // determine number of styles on the fly.
2180 sal_Int32 nStyles = getNumberOfChildren();
2181 sal_Int32 i;
2182
2183 for( i = 0; i < nStyles; ++i )
2184 {
2185 const Attr &q = getChildAt( i )->getAttr();
2186 for( sal_Int32 j=0; j<nAttributes; ++j )
2187 {
2188 const char* name = attr[j];
2189 OUString value = q.getValueByName( name );
2190 of.writeOUStringLiteralParameter("continuous", name, value, sal::static_int_cast<sal_Int16>(i) );
2191 }
2192 }
2193
2194 // record number of styles and attributes.
2195 of.writeAsciiString("static const sal_Int16 continuousNbOfStyles = ");
2196 of.writeInt( sal::static_int_cast<sal_Int16>( nStyles ) );
2197 of.writeAsciiString(";\n\n");
2198 of.writeAsciiString("static const sal_Int16 continuousNbOfAttributesPerStyle = ");
2199 of.writeInt( nAttributes );
2200 of.writeAsciiString(";\n\n");
2201
2202 // generate code. (intermediate arrays)
2203 for( i=0; i<nStyles; i++ )
2204 {
2205 of.writeAsciiString("\nstatic constexpr rtl::OUStringConstExpr continuousStyle" );
2206 of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2207 of.writeAsciiString("[] = {\n");
2208 for( sal_Int32 j=0; j<nAttributes; j++)
2209 {
2210 of.writeAsciiString("\t");
2211 of.writeAsciiString( "continuous" );
2212 of.writeAsciiString( attr[j] );
2213 of.writeInt(sal::static_int_cast<sal_Int16>(i));
2214 of.writeAsciiString(",\n");
2215 }
2216 of.writeAsciiString("\t\n};\n\n");
2217 }
2218
2219 // generate code. (top-level array)
2220 of.writeAsciiString("\n");
2221 of.writeAsciiString("static const rtl::OUStringConstExpr* LCContinuousNumberingLevelsArray[] = {\n" );
2222 for( i=0; i<nStyles; i++ )
2223 {
2224 of.writeAsciiString( "\t" );
2225 of.writeAsciiString( "continuousStyle" );
2226 of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2227 of.writeAsciiString( ",\n");
2228 }
2229 of.writeAsciiString("\t0\n};\n\n");
2230 of.writeOUStringFunction2("getContinuousNumberingLevels_", "continuousNbOfStyles",
2231 "continuousNbOfAttributesPerStyle", "LCContinuousNumberingLevelsArray");
2232}
2233
2234
2236{
2237 of.writeAsciiString("// ---> OutlineNumbering\n");
2238 OUString useLocale = getAttr().getValueByName("ref");
2239 if (!useLocale.isEmpty()) {
2240 useLocale = useLocale.replace( '-', '_');
2241 of.writeOUStringRefFunction3("getOutlineNumberingLevels_", useLocale);
2242 return;
2243 }
2244
2245 // hardcode number of attributes per level
2246 const int nAttributes = 12;
2247 const char* attr[ nAttributes ] =
2248 {
2249 "Prefix",
2250 "NumType",
2251 "Suffix",
2252 "BulletChar",
2253 "BulletFontName",
2254 "ParentNumbering",
2255 "LeftMargin",
2256 "SymbolTextDistance",
2257 "FirstLineOffset",
2258 "Adjust",
2259 "Transliteration",
2260 "NatNum",
2261 };
2262
2263 // record each attribute of each level of each style in a static C++ variable.
2264 // determine number of styles and number of levels per style on the fly.
2265 sal_Int32 nStyles = getNumberOfChildren();
2266 std::vector<sal_Int32> nLevels; // may be different for each style?
2267 for( sal_Int32 i = 0; i < nStyles; i++ )
2268 {
2269 LocaleNode* p = getChildAt( i );
2270 nLevels.push_back( p->getNumberOfChildren() );
2271 for( sal_Int32 j=0; j<nLevels.back(); j++ )
2272 {
2273 const Attr& q = p->getChildAt( j )->getAttr();
2274 for( sal_Int32 k=0; k<nAttributes; ++k )
2275 {
2276 const char* name = attr[k];
2277 OUString value = q.getValueByName( name );
2279 sal::static_int_cast<sal_Int16>(i),
2280 sal::static_int_cast<sal_Int16>(j) );
2281 }
2282 }
2283 }
2284
2285 // verify that each style has the same number of levels.
2286 for( size_t i=0; i<nLevels.size(); i++ )
2287 {
2288 if( nLevels[0] != nLevels[i] )
2289 {
2290 incError( "Numbering levels don't match.");
2291 }
2292 }
2293
2294 // record number of attributes, levels, and styles.
2295 of.writeAsciiString("static const sal_Int16 outlineNbOfStyles = ");
2296 of.writeInt( sal::static_int_cast<sal_Int16>( nStyles ) );
2297 of.writeAsciiString(";\n\n");
2298 of.writeAsciiString("static const sal_Int16 outlineNbOfLevelsPerStyle = ");
2299 of.writeInt( sal::static_int_cast<sal_Int16>( nLevels.back() ) );
2300 of.writeAsciiString(";\n\n");
2301 of.writeAsciiString("static const sal_Int16 outlineNbOfAttributesPerLevel = ");
2302 of.writeInt( nAttributes );
2303 of.writeAsciiString(";\n\n");
2304
2305 // too complicated for now...
2306 // of.writeAsciiString("static const sal_Int16 nbOfOutlineNumberingLevels[] = { ");
2307 // for( sal_Int32 j=0; j<nStyles; j++ )
2308 // {
2309 // of.writeInt( nLevels[j] );
2310 // of.writeAsciiString(", ");
2311 // }
2312 // of.writeAsciiString("};\n\n");
2313
2314
2315 for( sal_Int32 i=0; i<nStyles; i++ )
2316 {
2317 for( sal_Int32 j=0; j<nLevels.back(); j++ )
2318 {
2319 of.writeAsciiString("static constexpr rtl::OUStringConstExpr outline");
2320 of.writeAsciiString("Style");
2321 of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2322 of.writeAsciiString("Level");
2323 of.writeInt( sal::static_int_cast<sal_Int16>(j) );
2324 of.writeAsciiString("[] = { ");
2325
2326 for( sal_Int32 k=0; k<nAttributes; k++ )
2327 {
2328 of.writeAsciiString( "outline" );
2329 of.writeAsciiString( attr[k] );
2330 of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2331 of.writeInt( sal::static_int_cast<sal_Int16>(j) );
2332 of.writeAsciiString(", ");
2333 }
2334 of.writeAsciiString(" };\n");
2335 }
2336 }
2337
2338 of.writeAsciiString("\n");
2339
2340
2341 for( sal_Int32 i=0; i<nStyles; i++ )
2342 {
2343 of.writeAsciiString("static constexpr rtl::OUStringConstExpr const * outlineStyle" );
2344 of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2345 of.writeAsciiString("[] = { ");
2346
2347 for( sal_Int32 j=0; j<nLevels.back(); j++ )
2348 {
2349 of.writeAsciiString("outlineStyle");
2350 of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2351 of.writeAsciiString("Level");
2352 of.writeInt( sal::static_int_cast<sal_Int16>(j) );
2353 of.writeAsciiString(", ");
2354 }
2355 of.writeAsciiString("NULL };\n");
2356 }
2357 of.writeAsciiString("\n");
2358
2359 of.writeAsciiString("static constexpr rtl::OUStringConstExpr const * const * LCOutlineNumberingLevelsArray[] = {\n" );
2360 for( sal_Int32 i=0; i<nStyles; i++ )
2361 {
2362 of.writeAsciiString( "\t" );
2363 of.writeAsciiString( "outlineStyle" );
2364 of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2365 of.writeAsciiString(",\n");
2366 }
2367 of.writeAsciiString("\tNULL\n};\n\n");
2368 of.writeOUStringFunction3("getOutlineNumberingLevels_", "outlineNbOfStyles", "outlineNbOfLevelsPerStyle",
2369 "outlineNbOfAttributesPerLevel", "LCOutlineNumberingLevelsArray");
2370}
2371
2372Attr::Attr (const Reference< XAttributeList > & attr) {
2373 sal_Int16 len = attr->getLength();
2374 name.realloc (len);
2375 auto pName = name.getArray();
2376 value.realloc (len);
2377 auto pValue = value.getArray();
2378 for (sal_Int16 i =0; i< len;i++) {
2379 pName[i] = attr->getNameByIndex(i);
2380 pValue[i] = attr -> getValueByIndex(i);
2381 }
2382}
2383
2384OUString Attr::getValueByName (const char *str) const {
2385 auto pName = std::find_if(std::cbegin(name), std::cend(name),
2386 [&str](const OUString& rName) { return rName.equalsAscii(str); });
2387 if (pName != std::cend(name))
2388 {
2389 auto i = static_cast<sal_Int32>(std::distance(std::cbegin(name), pName));
2390 return value[i];
2391 }
2392 return OUString();
2393}
2394
2395const OUString& Attr::getValueByIndex (sal_Int32 idx) const
2396{
2397 return value[idx];
2398}
2399
2400/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static OUString aDateSep
Definition: LocaleNode.cxx:281
static bool isIso4217(const OUString &rStr)
::o3tl::sorted_vector< sal_Int16 > ValueSet
Definition: LocaleNode.cxx:42
static OUString sTheCurrencyReplaceTo
Definition: LocaleNode.cxx:510
const NameValuePair ReserveWord[]
static OUString sTheDateEditFormat
Definition: LocaleNode.cxx:512
static void lcl_writeAbbrFullNarrArrays(const OFileWriter &of, sal_Int16 nCount, const char *elementTag, sal_Int16 i, bool bNarrow)
#define OSTR(s)
Definition: LocaleNode.cxx:128
static void lcl_writeAbbrFullNarrNames(const OFileWriter &of, const LocaleNode *currNode, const char *elementTag, sal_Int16 i, sal_Int16 j)
#define LOCALE_VERSION_DTD
Definition: LocaleNode.cxx:39
::o3tl::sorted_vector< OUString > NameSet
Definition: LocaleNode.cxx:41
static void lcl_writeTabTagString(const OFileWriter &of, const char *pTag, const char *pStr)
static OUString sTheCompatibleCurrency
Definition: LocaleNode.cxx:511
static void lcl_writeTabTagStringNums(const OFileWriter &of, const char *pTag, const char *pStr, sal_Int16 i, sal_Int16 j)
static OUString aDecSep
Definition: LocaleNode.cxx:282
const char * pName
Sequence< OUString > name
Definition: LocaleNode.hxx:69
const OUString & getValueByIndex(sal_Int32 idx) const
OUString getValueByName(const char *str) const
Sequence< OUString > value
Definition: LocaleNode.hxx:70
Attr(const Reference< XAttributeList > &attr)
virtual void generateCode(const OFileWriter &of) const override
Definition: LocaleNode.cxx:284
bool expectedCalendarElement(std::u16string_view rName, const LocaleNode *pNode, sal_Int16 nChild, std::u16string_view rCalendarID) const
virtual void generateCode(const OFileWriter &of) const override
virtual void generateCode(const OFileWriter &of) const override
virtual void generateCode(const OFileWriter &of) const override
virtual void generateCode(const OFileWriter &of) const override
Definition: LocaleNode.cxx:517
static sal_Int16 mnFormats
Definition: LocaleNode.hxx:143
static sal_Int16 mnSection
Definition: LocaleNode.hxx:142
virtual void generateCode(const OFileWriter &of) const override
virtual void generateCode(const OFileWriter &of) const override
Definition: LocaleNode.cxx:229
virtual void generateCode(const OFileWriter &of) const override
virtual void generateCode(const OFileWriter &of) const override
virtual void generateCode(const OFileWriter &of) const override
virtual void generateCode(const OFileWriter &of) const override
virtual void generateCode(const OFileWriter &of) const override
void incErrorStrStr(const char *pStr, std::u16string_view rVal1, std::u16string_view rVal2) const
Definition: LocaleNode.cxx:223
void addChild(LocaleNode *node)
Definition: LocaleNode.cxx:62
const OUString & getName() const
Definition: LocaleNode.hxx:92
void incErrorInt(const char *pStr, int nVal) const
Definition: LocaleNode.cxx:211
const OUString & getValue() const
Definition: LocaleNode.hxx:93
sal_Int32 getNumberOfChildren() const
Definition: LocaleNode.hxx:95
const LocaleNode * getRoot() const
Definition: LocaleNode.cxx:67
static LocaleNode * createNode(const OUString &name, const Reference< XAttributeList > &attr)
Definition: LocaleNode.cxx:92
int getError() const
Definition: LocaleNode.cxx:54
OUString writeOUStringLiteralParameterCheckLen(const OFileWriter &of, const char *pParameterName, const LocaleNode *pNode, sal_Int32 nMinLen, sal_Int32 nMaxLen) const
Definition: LocaleNode.cxx:144
const Attr & getAttr() const
Definition: LocaleNode.hxx:94
virtual ~LocaleNode()
Definition: LocaleNode.cxx:88
const LocaleNode * findNode(const char *name) const
Definition: LocaleNode.cxx:76
OUString aName
Definition: LocaleNode.hxx:80
LocaleNode * getChildAt(sal_Int32 idx) const
Definition: LocaleNode.hxx:96
LocaleNode * parent
Definition: LocaleNode.hxx:83
void incError(const char *pStr) const
Definition: LocaleNode.cxx:200
LocaleNode(OUString name, const Reference< XAttributeList > &attr)
Definition: LocaleNode.cxx:46
virtual void generateCode(const OFileWriter &of) const
Definition: LocaleNode.cxx:130
std::vector< std::unique_ptr< LocaleNode > > children
Definition: LocaleNode.hxx:84
void incErrorStr(const char *pStr, std::u16string_view rVal) const
Definition: LocaleNode.cxx:217
void writeOUStringRefFunction(const char *func, std::u16string_view useLocale) const
Definition: filewriter.cxx:64
void writeOUStringFunction3(const char *func, const char *style, const char *levels, const char *attr, const char *array) const
Definition: filewriter.cxx:117
void writeOUStringFunction2(const char *func, const char *style, const char *attr, const char *array) const
Definition: filewriter.cxx:100
void writeOUStringLiteralIntParameter(const char *pAsciiStr, const sal_Int16 count, sal_Int16 val) const
Definition: filewriter.cxx:135
bool writeOUStringLiteralDefaultParameter(const char *pAsciiStr, std::u16string_view str, sal_Int16 count) const
Definition: filewriter.cxx:140
void writeOUStringLiteralParameter(const char *pAsciiStr, std::u16string_view aChars) const
Definition: filewriter.cxx:147
void writeOUStringRefFunction3(const char *func, std::u16string_view useLocale) const
Definition: filewriter.cxx:126
void writeOUStringFunction(const char *func, const char *count, const char *array) const
Definition: filewriter.cxx:84
const char * getLocale() const
Return the locale string, something like en_US or de_DE.
Definition: LocaleNode.hxx:62
void writeAsciiString(const char *str) const
Definition: filewriter.cxx:47
void writeInt(sal_Int16 nb) const
Definition: filewriter.cxx:37
void writeParameter(const char *pAsciiStr, std::u16string_view aChars, sal_Int16 count) const
Definition: filewriter.cxx:154
void writeHexInt(sal_Int16 nb) const
Definition: filewriter.cxx:42
void writeOUStringRefFunction2(const char *func, std::u16string_view useLocale) const
Definition: filewriter.cxx:108
std::vector< Value >::const_iterator const_iterator
std::pair< const_iterator, bool > insert(Value &&x)
Any value
int nCount
float u
const char * name
const sal_uInt16 idx[]
sal_Int32 nIndex
OUString aName
void * p
sal_Int64 n
#define SAL_N_ELEMENTS(arr)
aStr
err
int i
constexpr sal_Int16 nFirstFreeFormatIndex
The number of reserved (with defined meaning) built-in format code indices, additional locale data fo...
constexpr sal_Int16 nStopPredefinedFormatIndex
The number of predefined format code indices that must be defined by locale data, except BOOLEAN and ...
constexpr T & temporary(T &&x)
sal_Int16 nAttributes
unsigned char sal_uInt8
sal_uInt16 sal_Unicode