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 <vector>
25 #include <map>
26 #include <o3tl/sorted_vector.hxx>
27 #include <o3tl/temporary.hxx>
28 #include <rtl/ustrbuf.hxx>
29 #include <sal/macros.h>
30 #include <sal/types.h>
31 
32 #include "LocaleNode.hxx"
34 #include <com/sun/star/i18n/NumberFormatIndex.hpp>
35 #include <com/sun/star/xml/sax/XAttributeList.hpp>
36 
37 // NOTE: MUST match the Locale versionDTD attribute defined in data/locale.dtd
38 #define LOCALE_VERSION_DTD "2.0.3"
39 
40 typedef ::o3tl::sorted_vector< OUString > NameSet;
41 typedef ::o3tl::sorted_vector< sal_Int16 > ValueSet;
42 
43 namespace cssi = ::com::sun::star::i18n;
44 using namespace std;
45 
46 LocaleNode::LocaleNode (const OUString& name, const Reference< XAttributeList > & attr)
47  : aName(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 
76 const 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 
92 LocaleNode* 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 
130 void LocaleNode::generateCode (const OFileWriter &of) const
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.writeParameter( 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 
182 
184  const char* pNodeName, const char* pParameterName,
185  sal_Int32 nMinLen, sal_Int32 nMaxLen ) const
186 {
187  OUString aVal;
188  const LocaleNode * pNode = findNode( pNodeName);
189  if (pNode || nMinLen < 0)
190  aVal = writeParameterCheckLen( of, pParameterName, pNode, nMinLen, nMaxLen);
191  else
192  {
193  ++nError;
194  fprintf( stderr, "Error: node %s not found.\n", pNodeName);
195  // write empty data if error
196  of.writeParameter( pParameterName, aVal);
197  }
198  return aVal;
199 }
200 
201 void LocaleNode::incError( const char* pStr ) const
202 {
203  ++nError;
204  fprintf( stderr, "Error: %s\n", pStr);
205 }
206 
207 void LocaleNode::incError( std::u16string_view rStr ) const
208 {
209  incError( OSTR( rStr));
210 }
211 
212 void LocaleNode::incErrorInt( const char* pStr, int nVal ) const
213 {
214  ++nError;
215  fprintf( stderr, pStr, nVal);
216 }
217 
218 void LocaleNode::incErrorStr( const char* pStr, std::u16string_view rVal ) const
219 {
220  ++nError;
221  fprintf( stderr, pStr, OSTR( rVal));
222 }
223 
224 void LocaleNode::incErrorStrStr( const char* pStr, std::u16string_view rVal1, std::u16string_view rVal2 ) const
225 {
226  ++nError;
227  fprintf(stderr, pStr, OSTR(rVal1), OSTR(rVal2));
228 }
229 
230 void LCInfoNode::generateCode (const OFileWriter &of) const
231 {
232 
233  const LocaleNode * languageNode = findNode("Language");
234  const LocaleNode * countryNode = findNode("Country");
235  const LocaleNode * variantNode = findNode("Variant");
236 
237  OUString aLanguage;
238 
239  if (languageNode)
240  {
241  aLanguage = languageNode->getChildAt(0)->getValue();
242  if (aLanguage.getLength() != 2 && aLanguage.getLength() != 3)
243  incErrorStr( "Error: langID '%s' not 2-3 characters\n", aLanguage);
244  of.writeParameter("langID", aLanguage);
245  of.writeParameter("langDefaultName", languageNode->getChildAt(1)->getValue());
246  }
247  else
248  incError( "No Language node.");
249  if (countryNode)
250  {
251  OUString aCountry( countryNode->getChildAt(0)->getValue());
252  if (!(aCountry.isEmpty() || aCountry.getLength() == 2))
253  incErrorStr( "Error: countryID '%s' not empty or more than 2 characters\n", aCountry);
254  of.writeParameter("countryID", aCountry);
255  of.writeParameter("countryDefaultName", countryNode->getChildAt(1)->getValue());
256  }
257  else
258  incError( "No Country node.");
259  if (variantNode)
260  {
261  // If given Variant must be at least ll-Ssss and language must be 'qlt'
262  const OUString& aVariant( variantNode->getValue());
263  if (!(aVariant.isEmpty() || (aVariant.getLength() >= 7 && aVariant.indexOf('-') >= 2)))
264  incErrorStr( "Error: invalid Variant '%s'\n", aVariant);
265  if (!(aVariant.isEmpty() || aLanguage == "qlt"))
266  incErrorStrStr( "Error: Variant '%s' given but Language '%s' is not 'qlt'\n", aVariant, aLanguage);
267  of.writeParameter("Variant", aVariant);
268  }
269  else
270  of.writeParameter("Variant", OUString());
271  of.writeAsciiString("\nstatic const sal_Unicode* LCInfoArray[] = {\n");
272  of.writeAsciiString("\tlangID,\n");
273  of.writeAsciiString("\tlangDefaultName,\n");
274  of.writeAsciiString("\tcountryID,\n");
275  of.writeAsciiString("\tcountryDefaultName,\n");
276  of.writeAsciiString("\tVariant\n");
277  of.writeAsciiString("};\n\n");
278  of.writeFunction("getLCInfo_", "SAL_N_ELEMENTS(LCInfoArray)", "LCInfoArray");
279 }
280 
281 
282 static OUString aDateSep;
283 static OUString aDecSep;
284 
286 {
287  const LocaleNode * sepNode = nullptr;
288  OUString useLocale = getAttr().getValueByName("ref");
289  if (!useLocale.isEmpty()) {
290  useLocale = useLocale.replace( '-', '_');
291  of.writeRefFunction("getLocaleItem_", useLocale);
292  return;
293  }
294  OUString str = getAttr().getValueByName("unoid");
295  of.writeAsciiString("\n\n");
296  of.writeParameter("LC_CTYPE_Unoid", str);
297 
298  aDateSep =
299  writeParameterCheckLen( of, "DateSeparator", "dateSeparator", 1, 1);
300  OUString aThoSep =
301  writeParameterCheckLen( of, "ThousandSeparator", "thousandSeparator", 1, 1);
302  aDecSep =
303  writeParameterCheckLen( of, "DecimalSeparator", "decimalSeparator", 1, 1);
304  OUString aDecSepAlt =
305  writeParameterCheckLen( of, "DecimalSeparatorAlternative", "decimalSeparatorAlternative", -1, 1);
306  OUString aTimeSep =
307  writeParameterCheckLen( of, "TimeSeparator", "timeSeparator", 1, 1);
308  OUString aTime100Sep =
309  writeParameterCheckLen( of, "Time100SecSeparator", "time100SecSeparator", 1, 1);
310  OUString aListSep =
311  writeParameterCheckLen( of, "ListSeparator", "listSeparator", 1, 1);
312 
313  OUString aLDS;
314 
315  sepNode = findNode("LongDateDayOfWeekSeparator");
316  aLDS = sepNode->getValue();
317  of.writeParameter("LongDateDayOfWeekSeparator", aLDS);
318  if (aLDS == ",")
319  fprintf( stderr, "Warning: %s\n",
320  "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\".");
321 
322  sepNode = findNode("LongDateDaySeparator");
323  aLDS = sepNode->getValue();
324  of.writeParameter("LongDateDaySeparator", aLDS);
325  if (aLDS == "," || aLDS == ".")
326  fprintf( stderr, "Warning: %s\n",
327  "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\".");
328 
329  sepNode = findNode("LongDateMonthSeparator");
330  aLDS = sepNode->getValue();
331  of.writeParameter("LongDateMonthSeparator", aLDS);
332  if (aLDS.isEmpty())
333  fprintf( stderr, "Warning: %s\n",
334  "LongDateMonthSeparator is empty. Usually this is not the case and may lead to concatenated display names like \"Wednesday, May9, 2007\".");
335 
336  sepNode = findNode("LongDateYearSeparator");
337  aLDS = sepNode->getValue();
338  of.writeParameter("LongDateYearSeparator", aLDS);
339  if (aLDS.isEmpty())
340  fprintf( stderr, "Warning: %s\n",
341  "LongDateYearSeparator is empty. Usually this is not the case and may lead to concatenated display names like \"Wednesday, 2007May 9\".");
342 
343  int nSavErr = nError;
344  int nWarn = 0;
345  if (aDateSep == aTimeSep)
346  incError( "DateSeparator equals TimeSeparator.");
347  if (aDecSep == aThoSep)
348  incError( "DecimalSeparator equals ThousandSeparator.");
349  if (aDecSepAlt == aThoSep)
350  incError( "DecimalSeparatorAlternative equals ThousandSeparator.");
351  if (aDecSepAlt == aDecSep)
352  incError( "DecimalSeparatorAlternative equals DecimalSeparator, it must not be specified then.");
353  if ( aThoSep == " " )
354  incError( "ThousandSeparator is an ' ' ordinary space, this should be a non-breaking space U+00A0 instead.");
355  if (aListSep == aDecSep)
356  fprintf( stderr, "Warning: %s\n",
357  "ListSeparator equals DecimalSeparator.");
358  if (aListSep == aThoSep)
359  fprintf( stderr, "Warning: %s\n",
360  "ListSeparator equals ThousandSeparator.");
361  if (aListSep.getLength() != 1 || aListSep[0] != ';')
362  {
363  incError( "ListSeparator not ';' semicolon. Strongly recommended. Currently required.");
364  ++nSavErr; // format codes not affected
365  }
366  if (aTimeSep == aTime100Sep)
367  {
368  ++nWarn;
369  fprintf( stderr, "Warning: %s\n",
370  "Time100SecSeparator equals TimeSeparator, this is probably an error.");
371  }
372  if (aDecSep != aTime100Sep)
373  {
374  ++nWarn;
375  fprintf( stderr, "Warning: %s\n",
376  "Time100SecSeparator is different from DecimalSeparator, this may be correct or not. Intended?");
377  }
378  if (nSavErr != nError || nWarn)
379  fprintf( stderr, "Warning: %s\n",
380  "Don't forget to adapt corresponding FormatCode elements when changing separators.");
381 
382  OUString aQuoteStart =
383  writeParameterCheckLen( of, "QuotationStart", "quotationStart", 1, 1);
384  OUString aQuoteEnd =
385  writeParameterCheckLen( of, "QuotationEnd", "quotationEnd", 1, 1);
386  OUString aDoubleQuoteStart =
387  writeParameterCheckLen( of, "DoubleQuotationStart", "doubleQuotationStart", 1, 1);
388  OUString aDoubleQuoteEnd =
389  writeParameterCheckLen( of, "DoubleQuotationEnd", "doubleQuotationEnd", 1, 1);
390 
391  if (aQuoteStart.toChar() <= 127 && aQuoteEnd.toChar() > 127)
392  fprintf( stderr, "Warning: %s\n",
393  "QuotationStart is an ASCII character but QuotationEnd is not.");
394  if (aQuoteEnd.toChar() <= 127 && aQuoteStart.toChar() > 127)
395  fprintf( stderr, "Warning: %s\n",
396  "QuotationEnd is an ASCII character but QuotationStart is not.");
397  if (aDoubleQuoteStart.toChar() <= 127 && aDoubleQuoteEnd.toChar() > 127)
398  fprintf( stderr, "Warning: %s\n",
399  "DoubleQuotationStart is an ASCII character but DoubleQuotationEnd is not.");
400  if (aDoubleQuoteEnd.toChar() <= 127 && aDoubleQuoteStart.toChar() > 127)
401  fprintf( stderr, "Warning: %s\n",
402  "DoubleQuotationEnd is an ASCII character but DoubleQuotationStart is not.");
403  if (aQuoteStart.toChar() <= 127 && aQuoteEnd.toChar() <= 127)
404  fprintf( stderr, "Warning: %s\n",
405  "QuotationStart and QuotationEnd are both ASCII characters. Not necessarily an issue, but unusual.");
406  if (aDoubleQuoteStart.toChar() <= 127 && aDoubleQuoteEnd.toChar() <= 127)
407  fprintf( stderr, "Warning: %s\n",
408  "DoubleQuotationStart and DoubleQuotationEnd are both ASCII characters. Not necessarily an issue, but unusual.");
409  if (aQuoteStart == aQuoteEnd)
410  fprintf( stderr, "Warning: %s\n",
411  "QuotationStart equals QuotationEnd. Not necessarily an issue, but unusual.");
412  if (aDoubleQuoteStart == aDoubleQuoteEnd)
413  fprintf( stderr, "Warning: %s\n",
414  "DoubleQuotationStart equals DoubleQuotationEnd. Not necessarily an issue, but unusual.");
415  /* TODO: should equalness of single and double quotes be an error? Would
416  * need to adapt quite some locales' data. */
417  if (aQuoteStart == aDoubleQuoteStart)
418  fprintf( stderr, "Warning: %s\n",
419  "QuotationStart equals DoubleQuotationStart. Not necessarily an issue, but unusual.");
420  if (aQuoteEnd == aDoubleQuoteEnd)
421  fprintf( stderr, "Warning: %s\n",
422  "QuotationEnd equals DoubleQuotationEnd. Not necessarily an issue, but unusual.");
423  // Known good values, exclude ASCII single (U+0027, ') and double (U+0022, ") quotes.
424  switch (int ic = aQuoteStart.toChar())
425  {
426  case 0x2018: // LEFT SINGLE QUOTATION MARK
427  case 0x201a: // SINGLE LOW-9 QUOTATION MARK
428  case 0x201b: // SINGLE HIGH-REVERSED-9 QUOTATION MARK
429  case 0x2039: // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
430  case 0x203a: // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
431  case 0x300c: // LEFT CORNER BRACKET (Chinese)
432  ;
433  break;
434  default:
435  fprintf( stderr, "Warning: %s U+%04X %s\n",
436  "QuotationStart may be wrong:", ic, OSTR( aQuoteStart));
437  }
438  switch (int ic = aQuoteEnd.toChar())
439  {
440  case 0x2019: // RIGHT SINGLE QUOTATION MARK
441  case 0x201a: // SINGLE LOW-9 QUOTATION MARK
442  case 0x201b: // SINGLE HIGH-REVERSED-9 QUOTATION MARK
443  case 0x2039: // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
444  case 0x203a: // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
445  case 0x300d: // RIGHT CORNER BRACKET (Chinese)
446  ;
447  break;
448  default:
449  fprintf( stderr, "Warning: %s U+%04X %s\n",
450  "QuotationEnd may be wrong:", ic, OSTR( aQuoteEnd));
451  }
452  switch (int ic = aDoubleQuoteStart.toChar())
453  {
454  case 0x00ab: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
455  case 0x00bb: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
456  case 0x201c: // LEFT DOUBLE QUOTATION MARK
457  case 0x201e: // DOUBLE LOW-9 QUOTATION MARK
458  case 0x201f: // DOUBLE HIGH-REVERSED-9 QUOTATION MARK
459  case 0x300e: // LEFT WHITE CORNER BRACKET (Chinese)
460  ;
461  break;
462  default:
463  fprintf( stderr, "Warning: %s U+%04X %s\n",
464  "DoubleQuotationStart may be wrong:", ic, OSTR( aDoubleQuoteStart));
465  }
466  switch (int ic = aDoubleQuoteEnd.toChar())
467  {
468  case 0x00ab: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
469  case 0x00bb: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
470  case 0x201d: // RIGHT DOUBLE QUOTATION MARK
471  case 0x201e: // DOUBLE LOW-9 QUOTATION MARK
472  case 0x201f: // DOUBLE HIGH-REVERSED-9 QUOTATION MARK
473  case 0x300f: // RIGHT WHITE CORNER BRACKET (Chinese)
474  ;
475  break;
476  default:
477  fprintf( stderr, "Warning: %s U+%04X %s\n",
478  "DoubleQuotationEnd may be wrong:", ic, OSTR( aDoubleQuoteEnd));
479  }
480 
481  writeParameterCheckLen( of, "TimeAM", "timeAM", 1, -1);
482  writeParameterCheckLen( of, "TimePM", "timePM", 1, -1);
483  sepNode = findNode("MeasurementSystem");
484  of.writeParameter("measurementSystem", sepNode->getValue());
485 
486  of.writeAsciiString("\nstatic const sal_Unicode* LCType[] = {\n");
487  of.writeAsciiString("\tLC_CTYPE_Unoid,\n");
488  of.writeAsciiString("\tdateSeparator,\n");
489  of.writeAsciiString("\tthousandSeparator,\n");
490  of.writeAsciiString("\tdecimalSeparator,\n");
491  of.writeAsciiString("\ttimeSeparator,\n");
492  of.writeAsciiString("\ttime100SecSeparator,\n");
493  of.writeAsciiString("\tlistSeparator,\n");
494  of.writeAsciiString("\tquotationStart,\n");
495  of.writeAsciiString("\tquotationEnd,\n");
496  of.writeAsciiString("\tdoubleQuotationStart,\n");
497  of.writeAsciiString("\tdoubleQuotationEnd,\n");
498  of.writeAsciiString("\ttimeAM,\n");
499  of.writeAsciiString("\ttimePM,\n");
500  of.writeAsciiString("\tmeasurementSystem,\n");
501  of.writeAsciiString("\tLongDateDayOfWeekSeparator,\n");
502  of.writeAsciiString("\tLongDateDaySeparator,\n");
503  of.writeAsciiString("\tLongDateMonthSeparator,\n");
504  of.writeAsciiString("\tLongDateYearSeparator,\n");
505  of.writeAsciiString("\tdecimalSeparatorAlternative\n");
506  of.writeAsciiString("};\n\n");
507  of.writeFunction("getLocaleItem_", "SAL_N_ELEMENTS(LCType)", "LCType");
508 }
509 
510 
511 static OUString sTheCurrencyReplaceTo;
512 static OUString sTheCompatibleCurrency;
513 static OUString sTheDateEditFormat;
514 
515 sal_Int16 LCFormatNode::mnSection = 0;
516 sal_Int16 LCFormatNode::mnFormats = 0;
517 
519 {
520  if (mnSection >= 2)
521  incError("more than 2 LC_FORMAT sections");
522 
523  ::std::vector< OUString > theDateAcceptancePatterns;
524 
525  OUString useLocale(getAttr().getValueByName("ref"));
526 
527  OUString str;
528  OUString strFrom( getAttr().getValueByName("replaceFrom"));
529  if (useLocale.isEmpty())
530  {
531  of.writeParameter("replaceFrom", strFrom, mnSection);
532  }
533  str = getAttr().getValueByName("replaceTo");
534  if (!strFrom.isEmpty() && str.isEmpty())
535  incErrorStr("replaceFrom=\"%s\" replaceTo=\"\" is empty replacement.\n", strFrom);
536  // Locale data generator inserts FFFF for LangID, we need to adapt that.
537  if (str.endsWithIgnoreAsciiCase( "-FFFF]"))
538  incErrorStr("replaceTo=\"%s\" needs FFFF to be adapted to the real LangID value.\n", str);
539  of.writeParameter("replaceTo", str, mnSection);
540  // Remember the replaceTo value for "[CURRENCY]" to check format codes.
541  if ( strFrom == "[CURRENCY]" )
542  sTheCurrencyReplaceTo = str;
543  // Remember the currency symbol if present.
544  if (str.startsWith( "[$" ))
545  {
546  sal_Int32 nHyphen = str.indexOf( '-');
547  if (nHyphen >= 3)
548  {
549  sTheCompatibleCurrency = str.copy( 2, nHyphen - 2);
550  }
551  }
552 
553  if (!useLocale.isEmpty())
554  {
555  if (!strFrom.isEmpty() && strFrom != "[CURRENCY]") //???
556  {
558  "Error: non-empty replaceFrom=\"%s\" with non-empty ref=\"%s\".",
559  strFrom, useLocale);
560  }
561  useLocale = useLocale.replace( '-', '_');
562  switch (mnSection)
563  {
564  case 0:
565  of.writeRefFunction("getAllFormats0_", useLocale, "replaceTo0");
566  break;
567  case 1:
568  of.writeRefFunction("getAllFormats1_", useLocale, "replaceTo1");
569  break;
570  }
571  of.writeRefFunction("getDateAcceptancePatterns_", useLocale);
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.writeParameter("FormatKey", str, formatCount);
613 
614  str = currNodeAttr.getValueByName("default");
615  bool bDefault = str == "true";
616  of.writeDefaultParameter("FormatElement", str, formatCount);
617 
618  aType = currNodeAttr.getValueByName("type");
619  of.writeParameter("FormatType", aType, formatCount);
620 
621  aUsage = currNodeAttr.getValueByName("usage");
622  of.writeParameter("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).
628  if (i18npool::nStopPredefinedFormatIndex <= formatindex && formatindex < i18npool::nFirstFreeFormatIndex)
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.writeIntParameter("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.writeParameter("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 :
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( OUString(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.writeParameter("FormatDefaultName", n->getValue(), formatCount);
824  else
825  of.writeParameter("FormatDefaultName", OUString(), 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;
871  nNext < i18npool::nStopPredefinedFormatIndex; ++nNext)
872  {
873  sal_Int16 nHere = ::std::min( (aIter != aFormatIndexSet.end() ? *aIter :
875  i18npool::nStopPredefinedFormatIndex);
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");
916  of.writeInt(mnSection);
917  of.writeAsciiString(" = ");
918  of.writeInt( formatCount - mnFormats);
919  of.writeAsciiString(";\n");
920  of.writeAsciiString("static const sal_Unicode* ");
921  of.writeAsciiString("FormatElementsArray");
922  of.writeInt(mnSection);
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.writeFunction("getAllFormats0_", "FormatElementsCount0", "FormatElementsArray0", "replaceFrom0", "replaceTo0");
967  break;
968  case 1:
969  of.writeFunction("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 (vector<OUString>::const_iterator aIt = theDateAcceptancePatterns.begin();
1216  aIt != theDateAcceptancePatterns.end(); ++aIt)
1217  {
1218  for (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.writeParameter("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 const sal_Unicode* 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.writeFunction("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.writeRefFunction("getCollatorImplementation_", useLocale);
1264  of.writeRefFunction("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.writeParameter("CollatorID", str, j);
1277  str = currNode->getValue();
1278  of.writeParameter("CollatorRule", str, j);
1279  str = currNode -> getAttr().getValueByName("default");
1280  of.writeDefaultParameter("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.writeParameter("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 const sal_Unicode* 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 const sal_Unicode* collationOptions[] = {");
1320  for( sal_Int16 j=0; j<nbOfCollationOptions; j++ )
1321  {
1322  of.writeAsciiString( "collationOption" );
1323  of.writeInt( j );
1324  of.writeAsciiString( ", " );
1325  }
1326  of.writeAsciiString("NULL };\n");
1327  of.writeFunction("getCollatorImplementation_", "nbOfCollations", "LCCollatorArray");
1328  of.writeFunction("getCollationOptions_", "nbOfCollationOptions", "collationOptions");
1329 }
1330 
1332 {
1333  OUString useLocale = getAttr().getValueByName("ref");
1334  if (!useLocale.isEmpty()) {
1335  useLocale = useLocale.replace( '-', '_');
1336  of.writeRefFunction("getSearchOptions_", useLocale);
1337  return;
1338  }
1339 
1340  if( getNumberOfChildren() != 1 )
1341  {
1342  ++nError;
1343  fprintf(
1344  stderr, "Error: LC_SEARCH: more than 1 child: %" SAL_PRIdINT32 "\n",
1346  }
1347  sal_Int32 i;
1348  LocaleNode* pSearchOptions = getChildAt( 0 );
1349  sal_Int32 nSearchOptions = pSearchOptions->getNumberOfChildren();
1350  for( i=0; i<nSearchOptions; i++ )
1351  {
1352  of.writeParameter("searchOption", pSearchOptions->getChildAt( i )->getValue(), sal::static_int_cast<sal_Int16>(i) );
1353  }
1354 
1355  of.writeAsciiString("static const sal_Int16 nbOfSearchOptions = ");
1356  of.writeInt( sal::static_int_cast<sal_Int16>( nSearchOptions ) );
1357  of.writeAsciiString(";\n\n");
1358 
1359  of.writeAsciiString("static const sal_Unicode* searchOptions[] = {");
1360  for( i=0; i<nSearchOptions; i++ )
1361  {
1362  of.writeAsciiString( "searchOption" );
1363  of.writeInt( sal::static_int_cast<sal_Int16>(i) );
1364  of.writeAsciiString( ", " );
1365  }
1366  of.writeAsciiString("NULL };\n");
1367  of.writeFunction("getSearchOptions_", "nbOfSearchOptions", "searchOptions");
1368 }
1369 
1371 {
1372  OUString useLocale = getAttr().getValueByName("ref");
1373  if (!useLocale.isEmpty()) {
1374  useLocale = useLocale.replace( '-', '_');
1375  of.writeRefFunction("getIndexAlgorithm_", useLocale);
1376  of.writeRefFunction("getUnicodeScripts_", useLocale);
1377  of.writeRefFunction("getFollowPageWords_", useLocale);
1378  return;
1379  }
1380  sal_Int16 nbOfIndexs = 0;
1381  sal_Int16 nbOfUnicodeScripts = 0;
1382  sal_Int16 nbOfPageWords = 0;
1383  for (sal_Int32 i = 0; i< getNumberOfChildren();i++) {
1384  LocaleNode * currNode = getChildAt (i);
1385  if( currNode->getName() == "IndexKey" )
1386  {
1387  OUString str;
1388  str = currNode->getAttr().getValueByName("unoid");
1389  of.writeParameter("IndexID", str, nbOfIndexs);
1390  str = currNode->getAttr().getValueByName("module");
1391  of.writeParameter("IndexModule", str, nbOfIndexs);
1392  str = currNode->getValue();
1393  of.writeParameter("IndexKey", str, nbOfIndexs);
1394  str = currNode -> getAttr().getValueByName("default");
1395  of.writeDefaultParameter("Index", str, nbOfIndexs);
1396  str = currNode -> getAttr().getValueByName("phonetic");
1397  of.writeDefaultParameter("Phonetic", str, nbOfIndexs);
1398  of.writeAsciiString("\n");
1399 
1400  nbOfIndexs++;
1401  }
1402  if( currNode->getName() == "UnicodeScript" )
1403  {
1404  of.writeParameter("unicodeScript", currNode->getValue(), nbOfUnicodeScripts );
1405  nbOfUnicodeScripts++;
1406 
1407  }
1408  if( currNode->getName() == "FollowPageWord" )
1409  {
1410  of.writeParameter("followPageWord", currNode->getValue(), nbOfPageWords);
1411  nbOfPageWords++;
1412  }
1413  }
1414  of.writeAsciiString("static const sal_Int16 nbOfIndexs = ");
1415  of.writeInt(nbOfIndexs);
1416  of.writeAsciiString(";\n\n");
1417 
1418  of.writeAsciiString("\nstatic const sal_Unicode* IndexArray[] = {\n");
1419  for(sal_Int16 i = 0; i < nbOfIndexs; i++) {
1420  of.writeAsciiString("\tIndexID");
1421  of.writeInt(i);
1422  of.writeAsciiString(",\n");
1423 
1424  of.writeAsciiString("\tIndexModule");
1425  of.writeInt(i);
1426  of.writeAsciiString(",\n");
1427 
1428  of.writeAsciiString("\tIndexKey");
1429  of.writeInt(i);
1430  of.writeAsciiString(",\n");
1431 
1432  of.writeAsciiString("\tdefaultIndex");
1433  of.writeInt(i);
1434  of.writeAsciiString(",\n");
1435 
1436  of.writeAsciiString("\tdefaultPhonetic");
1437  of.writeInt(i);
1438  of.writeAsciiString(",\n");
1439  }
1440  of.writeAsciiString("};\n\n");
1441 
1442  of.writeAsciiString("static const sal_Int16 nbOfUnicodeScripts = ");
1443  of.writeInt( nbOfUnicodeScripts );
1444  of.writeAsciiString(";\n\n");
1445 
1446  of.writeAsciiString("static const sal_Unicode* UnicodeScriptArray[] = {");
1447  for( sal_Int16 i=0; i<nbOfUnicodeScripts; i++ )
1448  {
1449  of.writeAsciiString( "unicodeScript" );
1450  of.writeInt( i );
1451  of.writeAsciiString( ", " );
1452  }
1453  of.writeAsciiString("NULL };\n\n");
1454 
1455  of.writeAsciiString("static const sal_Int16 nbOfPageWords = ");
1456  of.writeInt(nbOfPageWords);
1457  of.writeAsciiString(";\n\n");
1458 
1459  of.writeAsciiString("static const sal_Unicode* FollowPageWordArray[] = {\n");
1460  for(sal_Int16 i = 0; i < nbOfPageWords; i++) {
1461  of.writeAsciiString("\tfollowPageWord");
1462  of.writeInt(i);
1463  of.writeAsciiString(",\n");
1464  }
1465  of.writeAsciiString("\tNULL\n};\n\n");
1466 
1467  of.writeFunction("getIndexAlgorithm_", "nbOfIndexs", "IndexArray");
1468  of.writeFunction("getUnicodeScripts_", "nbOfUnicodeScripts", "UnicodeScriptArray");
1469  of.writeFunction("getFollowPageWords_", "nbOfPageWords", "FollowPageWordArray");
1470 }
1471 
1472 
1473 static void lcl_writeAbbrFullNarrNames( const OFileWriter & of, const LocaleNode* currNode,
1474  const char* elementTag, sal_Int16 i, sal_Int16 j )
1475 {
1476  OUString aAbbrName = currNode->getChildAt(1)->getValue();
1477  OUString aFullName = currNode->getChildAt(2)->getValue();
1478  OUString aNarrName;
1479  LocaleNode* p = (currNode->getNumberOfChildren() > 3 ? currNode->getChildAt(3) : nullptr);
1480  if ( p && p->getName() == "DefaultNarrowName" )
1481  aNarrName = p->getValue();
1482  else
1483  {
1484  sal_uInt32 nChar = aFullName.iterateCodePoints( &o3tl::temporary(sal_Int32(0)));
1485  aNarrName = OUString( &nChar, 1);
1486  }
1487  of.writeParameter( elementTag, "DefaultAbbrvName", aAbbrName, i, j);
1488  of.writeParameter( elementTag, "DefaultFullName", aFullName, i, j);
1489  of.writeParameter( elementTag, "DefaultNarrowName", aNarrName, i, j);
1490 }
1491 
1492 static void lcl_writeTabTagString( const OFileWriter & of, const char* pTag, const char* pStr )
1493 {
1494  of.writeAsciiString("\t");
1495  of.writeAsciiString( pTag);
1496  of.writeAsciiString( pStr);
1497 }
1498 
1499 static void lcl_writeTabTagStringNums( const OFileWriter & of,
1500  const char* pTag, const char* pStr, sal_Int16 i, sal_Int16 j )
1501 {
1502  lcl_writeTabTagString( of, pTag, pStr);
1503  of.writeInt(i); of.writeInt(j); of.writeAsciiString(",\n");
1504 }
1505 
1506 static void lcl_writeAbbrFullNarrArrays( const OFileWriter & of, sal_Int16 nCount,
1507  const char* elementTag, sal_Int16 i, bool bNarrow )
1508 {
1509  if (nCount == 0)
1510  {
1511  lcl_writeTabTagString( of, elementTag, "Ref");
1512  of.writeInt(i); of.writeAsciiString(",\n");
1513  lcl_writeTabTagString( of, elementTag, "RefName");
1514  of.writeInt(i); of.writeAsciiString(",\n");
1515  }
1516  else
1517  {
1518  for (sal_Int16 j = 0; j < nCount; j++)
1519  {
1520  lcl_writeTabTagStringNums( of, elementTag, "ID", i, j);
1521  lcl_writeTabTagStringNums( of, elementTag, "DefaultAbbrvName", i, j);
1522  lcl_writeTabTagStringNums( of, elementTag, "DefaultFullName", i, j);
1523  if (bNarrow)
1524  lcl_writeTabTagStringNums( of, elementTag, "DefaultNarrowName", i, j);
1525  }
1526  }
1527 }
1528 
1529 bool LCCalendarNode::expectedCalendarElement( std::u16string_view rName,
1530  const LocaleNode* pNode, sal_Int16 nChild, std::u16string_view rCalendarID ) const
1531 {
1532  bool bFound = true;
1533  if (nChild >= 0)
1534  {
1535  if (nChild >= pNode->getNumberOfChildren())
1536  bFound = false;
1537  else
1538  pNode = pNode->getChildAt(nChild);
1539  }
1540  if (bFound && (!pNode || pNode->getName() != rName))
1541  bFound = false;
1542  if (!bFound)
1543  incErrorStrStr( "Error: <%s> element expected in calendar '%s'\n", rName, rCalendarID);
1544  return bFound;
1545 }
1546 
1548 {
1549  OUString useLocale = getAttr().getValueByName("ref");
1550  if (!useLocale.isEmpty()) {
1551  useLocale = useLocale.replace( '-', '_');
1552  of.writeRefFunction("getAllCalendars_", useLocale);
1553  return;
1554  }
1555  sal_Int16 nbOfCalendars = sal::static_int_cast<sal_Int16>( getNumberOfChildren() );
1556  OUString str;
1557  std::unique_ptr<sal_Int16[]> nbOfDays( new sal_Int16[nbOfCalendars] );
1558  std::unique_ptr<sal_Int16[]> nbOfMonths( new sal_Int16[nbOfCalendars] );
1559  std::unique_ptr<sal_Int16[]> nbOfGenitiveMonths( new sal_Int16[nbOfCalendars] );
1560  std::unique_ptr<sal_Int16[]> nbOfPartitiveMonths( new sal_Int16[nbOfCalendars] );
1561  std::unique_ptr<sal_Int16[]> nbOfEras( new sal_Int16[nbOfCalendars] );
1562 
1563  // Known allowed calendar identifiers (unoid) and whether used or not.
1564  // Of course there must be an implementation for new to be added
1565  // identifiers.. see data/locale.dtd
1566  std::map< OUString, bool > aCalendars;
1567  aCalendars["buddhist"] = false;
1568  aCalendars["gengou"] = false;
1569  aCalendars["gregorian"] = false;
1570  aCalendars["hanja"] = false;
1571  aCalendars["hanja_yoil"] = false;
1572  aCalendars["hijri"] = false;
1573  aCalendars["jewish"] = false;
1574  aCalendars["ROC"] = false;
1575  // Not in ODF:
1576  aCalendars["dangi"] = false;
1577  aCalendars["persian"] = false;
1578 
1579  sal_Int16 j;
1580  sal_Int16 i;
1581  bool bHasGregorian = false;
1582 
1583 
1584  for ( i = 0; i < nbOfCalendars; i++) {
1585  LocaleNode * calNode = getChildAt (i);
1586  OUString calendarID = calNode -> getAttr().getValueByName("unoid");
1587  of.writeParameter( "calendarID", calendarID, i);
1588  bool bGregorian = calendarID == "gregorian";
1589  if (!bHasGregorian)
1590  bHasGregorian = bGregorian;
1591  auto calIt = aCalendars.find(calendarID);
1592  if (calIt == aCalendars.end())
1593  incErrorStr( "Error: unknown Calendar unoid: %s\n", calendarID);
1594  else if (calIt->second)
1595  incErrorStr( "Error: duplicate Calendar unoid: %s\n", calendarID);
1596  else
1597  calIt->second = true;
1598  str = calNode -> getAttr().getValueByName("default");
1599  of.writeDefaultParameter("Calendar", str, i);
1600 
1601  sal_Int16 nChild = 0;
1602 
1603  // Generate Days of Week
1604  const char *elementTag;
1605  LocaleNode * daysNode = nullptr;
1606  OUString ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
1607  ref_name = ref_name.replace( '-', '_');
1608  if (!ref_name.isEmpty() && i > 0) {
1609  for (j = 0; j < i; j++) {
1610  str = getChildAt(j)->getAttr().getValueByName("unoid");
1611  if (str == ref_name)
1612  daysNode = getChildAt(j)->getChildAt(0);
1613  }
1614  }
1615  if (!ref_name.isEmpty() && daysNode == nullptr) {
1616  of.writeParameter("dayRef", "ref", i);
1617  of.writeParameter("dayRefName", ref_name, i);
1618  nbOfDays[i] = 0;
1619  } else {
1620  if (daysNode == nullptr)
1621  daysNode = calNode -> getChildAt(nChild);
1622  nbOfDays[i] = sal::static_int_cast<sal_Int16>( daysNode->getNumberOfChildren() );
1623  if (bGregorian && nbOfDays[i] != 7)
1624  incErrorInt( "Error: A Gregorian calendar must have 7 days per week, this one has %d\n", nbOfDays[i]);
1625  elementTag = "day";
1626  for (j = 0; j < nbOfDays[i]; j++) {
1627  LocaleNode *currNode = daysNode -> getChildAt(j);
1628  OUString dayID( currNode->getChildAt(0)->getValue());
1629  of.writeParameter("dayID", dayID, i, j);
1630  if ( j == 0 && bGregorian && dayID != "sun" )
1631  incError( "First day of a week of a Gregorian calendar must be <DayID>sun</DayID>");
1632  lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
1633  }
1634  }
1635  ++nChild;
1636 
1637  // Generate Months of Year
1638  LocaleNode * monthsNode = nullptr;
1639  ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
1640  ref_name = ref_name.replace( '-', '_');
1641  if (!ref_name.isEmpty() && i > 0) {
1642  for (j = 0; j < i; j++) {
1643  str = getChildAt(j)->getAttr().getValueByName("unoid");
1644  if (str == ref_name)
1645  monthsNode = getChildAt(j)->getChildAt(1);
1646  }
1647  }
1648  if (!ref_name.isEmpty() && monthsNode == nullptr) {
1649  of.writeParameter("monthRef", "ref", i);
1650  of.writeParameter("monthRefName", ref_name, i);
1651  nbOfMonths[i] = 0;
1652  } else {
1653  if (monthsNode == nullptr)
1654  monthsNode = calNode -> getChildAt(nChild);
1655  nbOfMonths[i] = sal::static_int_cast<sal_Int16>( monthsNode->getNumberOfChildren() );
1656  if (bGregorian && nbOfMonths[i] != 12)
1657  incErrorInt( "Error: A Gregorian calendar must have 12 months, this one has %d\n", nbOfMonths[i]);
1658  elementTag = "month";
1659  for (j = 0; j < nbOfMonths[i]; j++) {
1660  LocaleNode *currNode = monthsNode -> getChildAt(j);
1661  OUString monthID( currNode->getChildAt(0)->getValue());
1662  of.writeParameter("monthID", monthID, i, j);
1663  if ( j == 0 && bGregorian && monthID != "jan" )
1664  incError( "First month of a year of a Gregorian calendar must be <MonthID>jan</MonthID>");
1665  lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
1666  }
1667  }
1668  ++nChild;
1669 
1670  // Generate genitive Months of Year
1671  // Optional, if not present fall back to month nouns.
1672  if ( calNode->getChildAt(nChild)->getName() != "GenitiveMonths" )
1673  --nChild;
1674  LocaleNode * genitiveMonthsNode = nullptr;
1675  ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
1676  ref_name = ref_name.replace( '-', '_');
1677  if (!ref_name.isEmpty() && i > 0) {
1678  for (j = 0; j < i; j++) {
1679  str = getChildAt(j)->getAttr().getValueByName("unoid");
1680  if (str == ref_name)
1681  genitiveMonthsNode = getChildAt(j)->getChildAt(1);
1682  }
1683  }
1684  if (!ref_name.isEmpty() && genitiveMonthsNode == nullptr) {
1685  of.writeParameter("genitiveMonthRef", "ref", i);
1686  of.writeParameter("genitiveMonthRefName", ref_name, i);
1687  nbOfGenitiveMonths[i] = 0;
1688  } else {
1689  if (genitiveMonthsNode == nullptr)
1690  genitiveMonthsNode = calNode -> getChildAt(nChild);
1691  nbOfGenitiveMonths[i] = sal::static_int_cast<sal_Int16>( genitiveMonthsNode->getNumberOfChildren() );
1692  if (bGregorian && nbOfGenitiveMonths[i] != 12)
1693  incErrorInt( "Error: A Gregorian calendar must have 12 genitive months, this one has %d\n", nbOfGenitiveMonths[i]);
1694  elementTag = "genitiveMonth";
1695  for (j = 0; j < nbOfGenitiveMonths[i]; j++) {
1696  LocaleNode *currNode = genitiveMonthsNode -> getChildAt(j);
1697  OUString genitiveMonthID( currNode->getChildAt(0)->getValue());
1698  of.writeParameter("genitiveMonthID", genitiveMonthID, i, j);
1699  if ( j == 0 && bGregorian && genitiveMonthID != "jan" )
1700  incError( "First genitive month of a year of a Gregorian calendar must be <MonthID>jan</MonthID>");
1701  lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
1702  }
1703  }
1704  ++nChild;
1705 
1706  // Generate partitive Months of Year
1707  // Optional, if not present fall back to genitive months, or nominative
1708  // months (nouns) if that isn't present either.
1709  if ( calNode->getChildAt(nChild)->getName() != "PartitiveMonths" )
1710  --nChild;
1711  LocaleNode * partitiveMonthsNode = nullptr;
1712  ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
1713  ref_name = ref_name.replace( '-', '_');
1714  if (!ref_name.isEmpty() && i > 0) {
1715  for (j = 0; j < i; j++) {
1716  str = getChildAt(j)->getAttr().getValueByName("unoid");
1717  if (str == ref_name)
1718  partitiveMonthsNode = getChildAt(j)->getChildAt(1);
1719  }
1720  }
1721  if (!ref_name.isEmpty() && partitiveMonthsNode == nullptr) {
1722  of.writeParameter("partitiveMonthRef", "ref", i);
1723  of.writeParameter("partitiveMonthRefName", ref_name, i);
1724  nbOfPartitiveMonths[i] = 0;
1725  } else {
1726  if (partitiveMonthsNode == nullptr)
1727  partitiveMonthsNode = calNode -> getChildAt(nChild);
1728  nbOfPartitiveMonths[i] = sal::static_int_cast<sal_Int16>( partitiveMonthsNode->getNumberOfChildren() );
1729  if (bGregorian && nbOfPartitiveMonths[i] != 12)
1730  incErrorInt( "Error: A Gregorian calendar must have 12 partitive months, this one has %d\n", nbOfPartitiveMonths[i]);
1731  elementTag = "partitiveMonth";
1732  for (j = 0; j < nbOfPartitiveMonths[i]; j++) {
1733  LocaleNode *currNode = partitiveMonthsNode -> getChildAt(j);
1734  OUString partitiveMonthID( currNode->getChildAt(0)->getValue());
1735  of.writeParameter("partitiveMonthID", partitiveMonthID, i, j);
1736  if ( j == 0 && bGregorian && partitiveMonthID != "jan" )
1737  incError( "First partitive month of a year of a Gregorian calendar must be <MonthID>jan</MonthID>");
1738  lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
1739  }
1740  }
1741  ++nChild;
1742 
1743  // Generate Era name
1744  LocaleNode * erasNode = nullptr;
1745  ref_name = calNode -> getChildAt(nChild) ->getAttr().getValueByName("ref");
1746  ref_name = ref_name.replace( '-', '_');
1747  if (!ref_name.isEmpty() && i > 0) {
1748  for (j = 0; j < i; j++) {
1749  str = getChildAt(j)->getAttr().getValueByName("unoid");
1750  if (str == ref_name)
1751  erasNode = getChildAt(j)->getChildAt(2);
1752  }
1753  }
1754  if (!ref_name.isEmpty() && erasNode == nullptr) {
1755  of.writeParameter("eraRef", "ref", i);
1756  of.writeParameter("eraRefName", ref_name, i);
1757  nbOfEras[i] = 0;
1758  } else {
1759  if (erasNode == nullptr)
1760  erasNode = calNode -> getChildAt(nChild);
1761  if (!expectedCalendarElement(u"Eras", erasNode, -1, calendarID))
1762  {
1763  --nChild;
1764  }
1765  else
1766  {
1767  nbOfEras[i] = sal::static_int_cast<sal_Int16>( erasNode->getNumberOfChildren() );
1768  if (bGregorian && nbOfEras[i] != 2)
1769  incErrorInt( "Error: A Gregorian calendar must have 2 eras, this one has %d\n", nbOfEras[i]);
1770  elementTag = "era";
1771  for (j = 0; j < nbOfEras[i]; j++) {
1772  LocaleNode *currNode = erasNode -> getChildAt(j);
1773  if (!expectedCalendarElement(u"Era", currNode, -1, calendarID))
1774  {
1775  continue; // for
1776  }
1777  OUString eraID( currNode->getChildAt(0)->getValue());
1778  of.writeParameter("eraID", eraID, i, j);
1779  if ( j == 0 && bGregorian && eraID != "bc" )
1780  incError( "First era of a Gregorian calendar must be <EraID>bc</EraID>");
1781  if ( j == 1 && bGregorian && eraID != "ad" )
1782  incError( "Second era of a Gregorian calendar must be <EraID>ad</EraID>");
1783  of.writeAsciiString("\n");
1784  of.writeParameter(elementTag, "DefaultAbbrvName",currNode->getChildAt(1)->getValue() ,i, j);
1785  of.writeParameter(elementTag, "DefaultFullName",currNode->getChildAt(2)->getValue() , i, j);
1786  }
1787  }
1788  }
1789  ++nChild;
1790 
1791  if (expectedCalendarElement(u"StartDayOfWeek", calNode, nChild, calendarID))
1792  {
1793  str = calNode->getChildAt(nChild)->getChildAt(0)->getValue();
1794  if (nbOfDays[i])
1795  {
1796  for (j = 0; j < nbOfDays[i]; j++)
1797  {
1798  LocaleNode *currNode = daysNode->getChildAt(j);
1799  OUString dayID( currNode->getChildAt(0)->getValue());
1800  if (str == dayID)
1801  break; // for
1802  }
1803  if (j >= nbOfDays[i])
1804  incErrorStr( "Error: <StartDayOfWeek> <DayID> must be one of the <DaysOfWeek>, but is: %s\n", str);
1805  }
1806  of.writeParameter("startDayOfWeek", str, i);
1807  ++nChild;
1808  }
1809 
1810  if (expectedCalendarElement(u"MinimalDaysInFirstWeek", calNode, nChild, calendarID))
1811  {
1812  str = calNode ->getChildAt(nChild)-> getValue();
1813  sal_Int16 nDays = sal::static_int_cast<sal_Int16>( str.toInt32() );
1814  if (nDays < 1 || (0 < nbOfDays[i] && nbOfDays[i] < nDays))
1815  incErrorInt( "Error: Bad value of MinimalDaysInFirstWeek: %d, must be 1 <= value <= days_in_week\n",
1816  nDays);
1817  of.writeIntParameter("minimalDaysInFirstWeek", i, nDays);
1818  }
1819  }
1820  if (!bHasGregorian)
1821  fprintf( stderr, "Warning: %s\n", "No Gregorian calendar defined, are you sure?");
1822 
1823  of.writeAsciiString("static const sal_Int16 calendarsCount = ");
1824  of.writeInt(nbOfCalendars);
1825  of.writeAsciiString(";\n\n");
1826 
1827  of.writeAsciiString("static const sal_Unicode nbOfDays[] = {");
1828  for(i = 0; i < nbOfCalendars - 1; i++) {
1829  of.writeInt(nbOfDays[i]);
1830  of.writeAsciiString(", ");
1831  }
1832  of.writeInt(nbOfDays[i]);
1833  of.writeAsciiString("};\n");
1834 
1835  of.writeAsciiString("static const sal_Unicode nbOfMonths[] = {");
1836  for(i = 0; i < nbOfCalendars - 1; i++) {
1837  of.writeInt(nbOfMonths[i]);
1838  of.writeAsciiString(", ");
1839  }
1840  of.writeInt(nbOfMonths[i]);
1841  of.writeAsciiString("};\n");
1842 
1843  of.writeAsciiString("static const sal_Unicode nbOfGenitiveMonths[] = {");
1844  for(i = 0; i < nbOfCalendars - 1; i++) {
1845  of.writeInt(nbOfGenitiveMonths[i]);
1846  of.writeAsciiString(", ");
1847  }
1848  of.writeInt(nbOfGenitiveMonths[i]);
1849  of.writeAsciiString("};\n");
1850 
1851  of.writeAsciiString("static const sal_Unicode nbOfPartitiveMonths[] = {");
1852  for(i = 0; i < nbOfCalendars - 1; i++) {
1853  of.writeInt(nbOfPartitiveMonths[i]);
1854  of.writeAsciiString(", ");
1855  }
1856  of.writeInt(nbOfPartitiveMonths[i]);
1857  of.writeAsciiString("};\n");
1858 
1859  of.writeAsciiString("static const sal_Unicode nbOfEras[] = {");
1860  for(i = 0; i < nbOfCalendars - 1; i++) {
1861  of.writeInt(nbOfEras[i]);
1862  of.writeAsciiString(", ");
1863  }
1864  of.writeInt(nbOfEras[i]);
1865  of.writeAsciiString("};\n");
1866 
1867 
1868  of.writeAsciiString("static const sal_Unicode* calendars[] = {\n");
1869  of.writeAsciiString("\tnbOfDays,\n");
1870  of.writeAsciiString("\tnbOfMonths,\n");
1871  of.writeAsciiString("\tnbOfGenitiveMonths,\n");
1872  of.writeAsciiString("\tnbOfPartitiveMonths,\n");
1873  of.writeAsciiString("\tnbOfEras,\n");
1874  for(i = 0; i < nbOfCalendars; i++) {
1875  of.writeAsciiString("\tcalendarID");
1876  of.writeInt(i);
1877  of.writeAsciiString(",\n");
1878  of.writeAsciiString("\tdefaultCalendar");
1879  of.writeInt(i);
1880  of.writeAsciiString(",\n");
1881  lcl_writeAbbrFullNarrArrays( of, nbOfDays[i], "day", i, true);
1882  lcl_writeAbbrFullNarrArrays( of, nbOfMonths[i], "month", i, true);
1883  lcl_writeAbbrFullNarrArrays( of, nbOfGenitiveMonths[i], "genitiveMonth", i, true);
1884  lcl_writeAbbrFullNarrArrays( of, nbOfPartitiveMonths[i], "partitiveMonth", i, true);
1885  lcl_writeAbbrFullNarrArrays( of, nbOfEras[i], "era", i, false /*noNarrow*/);
1886  of.writeAsciiString("\tstartDayOfWeek");of.writeInt(i); of.writeAsciiString(",\n");
1887  of.writeAsciiString("\tminimalDaysInFirstWeek");of.writeInt(i); of.writeAsciiString(",\n");
1888  }
1889 
1890  of.writeAsciiString("};\n\n");
1891  of.writeFunction("getAllCalendars_", "calendarsCount", "calendars");
1892 }
1893 
1894 static bool isIso4217( const OUString& rStr )
1895 {
1896  const sal_Unicode* p = rStr.getStr();
1897  return rStr.getLength() == 3
1898  && 'A' <= p[0] && p[0] <= 'Z'
1899  && 'A' <= p[1] && p[1] <= 'Z'
1900  && 'A' <= p[2] && p[2] <= 'Z'
1901  ;
1902 }
1903 
1905 {
1906  OUString useLocale = getAttr().getValueByName("ref");
1907  if (!useLocale.isEmpty()) {
1908  useLocale = useLocale.replace( '-', '_');
1909  of.writeRefFunction("getAllCurrencies_", useLocale);
1910  return;
1911  }
1912  sal_Int16 nbOfCurrencies = 0;
1913  OUString str;
1914 
1915  bool bTheDefault= false;
1916  bool bTheCompatible = false;
1917  for ( sal_Int32 i = 0; i < getNumberOfChildren(); i++,nbOfCurrencies++) {
1918  LocaleNode * currencyNode = getChildAt (i);
1919  str = currencyNode->getAttr().getValueByName("default");
1920  bool bDefault = of.writeDefaultParameter("Currency", str, nbOfCurrencies);
1921  str = currencyNode->getAttr().getValueByName("usedInCompatibleFormatCodes");
1922  bool bCompatible = of.writeDefaultParameter("CurrencyUsedInCompatibleFormatCodes", str, nbOfCurrencies);
1923  str = currencyNode->getAttr().getValueByName("legacyOnly");
1924  bool bLegacy = of.writeDefaultParameter("CurrencyLegacyOnly", str, nbOfCurrencies);
1925  if (bLegacy && (bDefault || bCompatible))
1926  incError( "Currency: if legacyOnly==true, both 'default' and 'usedInCompatibleFormatCodes' must be false.");
1927  if (bDefault)
1928  {
1929  if (bTheDefault)
1930  incError( "Currency: more than one default currency.");
1931  bTheDefault = true;
1932  }
1933  if (bCompatible)
1934  {
1935  if (bTheCompatible)
1936  incError( "Currency: more than one currency flagged as usedInCompatibleFormatCodes.");
1937  bTheCompatible = true;
1938  }
1939  str = currencyNode -> findNode ("CurrencyID") -> getValue();
1940  of.writeParameter("currencyID", str, nbOfCurrencies);
1941  // CurrencyID MUST be ISO 4217.
1942  if (!bLegacy && !isIso4217(str))
1943  incError( "CurrencyID is not ISO 4217");
1944  str = currencyNode -> findNode ("CurrencySymbol") -> getValue();
1945  of.writeParameter("currencySymbol", str, nbOfCurrencies);
1946  // Check if this currency really is the one used in number format
1947  // codes. In case of ref=... mechanisms it may be that TheCurrency
1948  // couldn't had been determined from the current locale (i.e. is
1949  // empty), silently assume the referred locale has things right.
1950  if (bCompatible && !sTheCompatibleCurrency.isEmpty() && sTheCompatibleCurrency != str)
1951  incErrorStrStr( "Error: CurrencySymbol \"%s\" flagged as usedInCompatibleFormatCodes doesn't match \"%s\" determined from format codes.\n", str, sTheCompatibleCurrency);
1952  str = currencyNode -> findNode ("BankSymbol") -> getValue();
1953  of.writeParameter("bankSymbol", str, nbOfCurrencies);
1954  // BankSymbol currently must be ISO 4217. May change later if
1955  // application always uses CurrencyID instead of BankSymbol.
1956  if (!bLegacy && !isIso4217(str))
1957  incError( "BankSymbol is not ISO 4217");
1958  str = currencyNode -> findNode ("CurrencyName") -> getValue();
1959  of.writeParameter("currencyName", str, nbOfCurrencies);
1960  str = currencyNode -> findNode ("DecimalPlaces") -> getValue();
1961  sal_Int16 nDecimalPlaces = static_cast<sal_Int16>(str.toInt32());
1962  of.writeIntParameter("currencyDecimalPlaces", nbOfCurrencies, nDecimalPlaces);
1963  of.writeAsciiString("\n");
1964  };
1965 
1966  if (!bTheDefault)
1967  incError( "Currency: no default currency.");
1968  if (!bTheCompatible)
1969  incError( "Currency: no currency flagged as usedInCompatibleFormatCodes.");
1970 
1971  of.writeAsciiString("static const sal_Int16 currencyCount = ");
1972  of.writeInt(nbOfCurrencies);
1973  of.writeAsciiString(";\n\n");
1974  of.writeAsciiString("static const sal_Unicode* currencies[] = {\n");
1975  for(sal_Int16 i = 0; i < nbOfCurrencies; i++) {
1976  of.writeAsciiString("\tcurrencyID");
1977  of.writeInt(i);
1978  of.writeAsciiString(",\n");
1979  of.writeAsciiString("\tcurrencySymbol");
1980  of.writeInt(i);
1981  of.writeAsciiString(",\n");
1982  of.writeAsciiString("\tbankSymbol");
1983  of.writeInt(i);
1984  of.writeAsciiString(",\n");
1985  of.writeAsciiString("\tcurrencyName");
1986  of.writeInt(i);
1987  of.writeAsciiString(",\n");
1988  of.writeAsciiString("\tdefaultCurrency");
1989  of.writeInt(i);
1990  of.writeAsciiString(",\n");
1991  of.writeAsciiString("\tdefaultCurrencyUsedInCompatibleFormatCodes");
1992  of.writeInt(i);
1993  of.writeAsciiString(",\n");
1994  of.writeAsciiString("\tcurrencyDecimalPlaces");
1995  of.writeInt(i);
1996  of.writeAsciiString(",\n");
1997  of.writeAsciiString("\tdefaultCurrencyLegacyOnly");
1998  of.writeInt(i);
1999  of.writeAsciiString(",\n");
2000  }
2001  of.writeAsciiString("};\n\n");
2002  of.writeFunction("getAllCurrencies_", "currencyCount", "currencies");
2003 }
2004 
2006 {
2007  OUString useLocale = getAttr().getValueByName("ref");
2008  if (!useLocale.isEmpty()) {
2009  useLocale = useLocale.replace( '-', '_');
2010  of.writeRefFunction("getTransliterations_", useLocale);
2011  return;
2012  }
2013  sal_Int16 nbOfModules = 0;
2014  OUString str;
2015 
2016  for ( sal_Int32 i = 0; i < getNumberOfChildren(); i++,nbOfModules++) {
2017  LocaleNode * transNode = getChildAt (i);
2018  str = transNode->getAttr().getValueByIndex(0);
2019  of.writeParameter("Transliteration", str, nbOfModules);
2020  }
2021  of.writeAsciiString("static const sal_Int16 nbOfTransliterations = ");
2022  of.writeInt(nbOfModules);
2023  of.writeAsciiString(";\n\n");
2024 
2025  of.writeAsciiString("\nstatic const sal_Unicode* LCTransliterationsArray[] = {\n");
2026  for( sal_Int16 i = 0; i < nbOfModules; i++) {
2027  of.writeAsciiString("\tTransliteration");
2028  of.writeInt(i);
2029  of.writeAsciiString(",\n");
2030  }
2031  of.writeAsciiString("};\n\n");
2032  of.writeFunction("getTransliterations_", "nbOfTransliterations", "LCTransliterationsArray");
2033 }
2034 
2035 namespace {
2036 
2037 struct NameValuePair {
2038  const char *name;
2039  const char *value;
2040 };
2041 
2042 }
2043 
2044 const NameValuePair ReserveWord[] = {
2045  { "trueWord", "true" },
2046  { "falseWord", "false" },
2047  { "quarter1Word", "1st quarter" },
2048  { "quarter2Word", "2nd quarter" },
2049  { "quarter3Word", "3rd quarter" },
2050  { "quarter4Word", "4th quarter" },
2051  { "aboveWord", "above" },
2052  { "belowWord", "below" },
2053  { "quarter1Abbreviation", "Q1" },
2054  { "quarter2Abbreviation", "Q2" },
2055  { "quarter3Abbreviation", "Q3" },
2056  { "quarter4Abbreviation", "Q4" }
2057 };
2058 
2060 {
2061  OUString useLocale = getAttr().getValueByName("ref");
2062  if (!useLocale.isEmpty()) {
2063  useLocale = useLocale.replace( '-', '_');
2064  of.writeRefFunction("getForbiddenCharacters_", useLocale);
2065  of.writeRefFunction("getBreakIteratorRules_", useLocale);
2066  of.writeRefFunction("getReservedWords_", useLocale);
2067  return;
2068  }
2069  const LocaleNode * reserveNode = findNode("ReservedWords");
2070  if (!reserveNode)
2071  incError( "No ReservedWords element."); // should not happen if validated...
2072  const LocaleNode * forbidNode = findNode("ForbiddenCharacters");
2073  const LocaleNode * breakNode = findNode("BreakIteratorRules");
2074 
2075  bool bEnglishLocale = (strncmp( of.getLocale(), "en_", 3) == 0);
2076 
2077  sal_Int16 nbOfWords = 0;
2078  OUString str;
2079  sal_Int16 i;
2080 
2081  for ( i = 0; i < sal_Int16(SAL_N_ELEMENTS(ReserveWord)); i++,nbOfWords++) {
2082  const LocaleNode * curNode = (reserveNode ? reserveNode->findNode(
2083  ReserveWord[i].name) : nullptr);
2084  if (!curNode)
2085  fprintf( stderr,
2086  "Warning: No %s in ReservedWords, using en_US default: \"%s\".\n",
2087  ReserveWord[i].name, ReserveWord[i].value);
2088  str = curNode ? curNode -> getValue() : OUString::createFromAscii(ReserveWord[i].value);
2089  if (str.isEmpty())
2090  {
2091  ++nError;
2092  fprintf( stderr, "Error: No content for ReservedWords %s.\n", ReserveWord[i].name);
2093  }
2094  of.writeParameter("ReservedWord", str, nbOfWords);
2095  // "true", ..., "below" trigger untranslated warning.
2096  if (!bEnglishLocale && curNode && i <= 7 &&
2097  str.equalsIgnoreAsciiCaseAscii( ReserveWord[i].value))
2098  {
2099  fprintf( stderr,
2100  "Warning: ReservedWord %s seems to be untranslated \"%s\".\n",
2101  ReserveWord[i].name, ReserveWord[i].value);
2102  }
2103  }
2104  of.writeAsciiString("static const sal_Int16 nbOfReservedWords = ");
2105  of.writeInt(nbOfWords);
2106  of.writeAsciiString(";\n\n");
2107  of.writeAsciiString("\nstatic const sal_Unicode* LCReservedWordsArray[] = {\n");
2108  for( i = 0; i < nbOfWords; i++) {
2109  of.writeAsciiString("\tReservedWord");
2110  of.writeInt(i);
2111  of.writeAsciiString(",\n");
2112  }
2113  of.writeAsciiString("};\n\n");
2114  of.writeFunction("getReservedWords_", "nbOfReservedWords", "LCReservedWordsArray");
2115 
2116  if (forbidNode) {
2117  of.writeParameter( "forbiddenBegin", forbidNode -> getChildAt(0)->getValue());
2118  of.writeParameter( "forbiddenEnd", forbidNode -> getChildAt(1)->getValue());
2119  of.writeParameter( "hangingChars", forbidNode -> getChildAt(2)->getValue());
2120  } else {
2121  of.writeParameter( "forbiddenBegin", OUString());
2122  of.writeParameter( "forbiddenEnd", OUString());
2123  of.writeParameter( "hangingChars", OUString());
2124  }
2125  of.writeAsciiString("\nstatic const sal_Unicode* LCForbiddenCharactersArray[] = {\n");
2126  of.writeAsciiString("\tforbiddenBegin,\n");
2127  of.writeAsciiString("\tforbiddenEnd,\n");
2128  of.writeAsciiString("\thangingChars\n");
2129  of.writeAsciiString("};\n\n");
2130  of.writeFunction("getForbiddenCharacters_", "3", "LCForbiddenCharactersArray");
2131 
2132  if (breakNode) {
2133  of.writeParameter( "EditMode", breakNode -> getChildAt(0)->getValue());
2134  of.writeParameter( "DictionaryMode", breakNode -> getChildAt(1)->getValue());
2135  of.writeParameter( "WordCountMode", breakNode -> getChildAt(2)->getValue());
2136  of.writeParameter( "CharacterMode", breakNode -> getChildAt(3)->getValue());
2137  of.writeParameter( "LineMode", breakNode -> getChildAt(4)->getValue());
2138  } else {
2139  of.writeParameter( "EditMode", OUString());
2140  of.writeParameter( "DictionaryMode", OUString());
2141  of.writeParameter( "WordCountMode", OUString());
2142  of.writeParameter( "CharacterMode", OUString());
2143  of.writeParameter( "LineMode", OUString());
2144  }
2145  of.writeAsciiString("\nstatic const sal_Unicode* LCBreakIteratorRulesArray[] = {\n");
2146  of.writeAsciiString("\tEditMode,\n");
2147  of.writeAsciiString("\tDictionaryMode,\n");
2148  of.writeAsciiString("\tWordCountMode,\n");
2149  of.writeAsciiString("\tCharacterMode,\n");
2150  of.writeAsciiString("\tLineMode\n");
2151  of.writeAsciiString("};\n\n");
2152  of.writeFunction("getBreakIteratorRules_", "5", "LCBreakIteratorRulesArray");
2153 
2154 }
2155 
2157 {
2158  of.writeAsciiString("// ---> ContinuousNumbering\n");
2159  OUString useLocale = getAttr().getValueByName("ref");
2160  if (!useLocale.isEmpty()) {
2161  useLocale = useLocale.replace( '-', '_');
2162  of.writeRefFunction2("getContinuousNumberingLevels_", useLocale);
2163  return;
2164  }
2165 
2166  // hard code number of attributes per style.
2167  const int nAttributes = 5;
2168  const char* attr[ nAttributes ] = { "Prefix", "NumType", "Suffix", "Transliteration", "NatNum" };
2169 
2170  // record each attribute of each style in a static C++ variable.
2171  // determine number of styles on the fly.
2172  sal_Int32 nStyles = getNumberOfChildren();
2173  sal_Int32 i;
2174 
2175  for( i = 0; i < nStyles; ++i )
2176  {
2177  const Attr &q = getChildAt( i )->getAttr();
2178  for( sal_Int32 j=0; j<nAttributes; ++j )
2179  {
2180  const char* name = attr[j];
2181  OUString value = q.getValueByName( name );
2182  of.writeParameter("continuous", name, value, sal::static_int_cast<sal_Int16>(i) );
2183  }
2184  }
2185 
2186  // record number of styles and attributes.
2187  of.writeAsciiString("static const sal_Int16 continuousNbOfStyles = ");
2188  of.writeInt( sal::static_int_cast<sal_Int16>( nStyles ) );
2189  of.writeAsciiString(";\n\n");
2190  of.writeAsciiString("static const sal_Int16 continuousNbOfAttributesPerStyle = ");
2191  of.writeInt( nAttributes );
2192  of.writeAsciiString(";\n\n");
2193 
2194  // generate code. (intermediate arrays)
2195  for( i=0; i<nStyles; i++ )
2196  {
2197  of.writeAsciiString("\nstatic const sal_Unicode* continuousStyle" );
2198  of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2199  of.writeAsciiString("[] = {\n");
2200  for( sal_Int32 j=0; j<nAttributes; j++)
2201  {
2202  of.writeAsciiString("\t");
2203  of.writeAsciiString( "continuous" );
2204  of.writeAsciiString( attr[j] );
2205  of.writeInt(sal::static_int_cast<sal_Int16>(i));
2206  of.writeAsciiString(",\n");
2207  }
2208  of.writeAsciiString("\t0\n};\n\n");
2209  }
2210 
2211  // generate code. (top-level array)
2212  of.writeAsciiString("\n");
2213  of.writeAsciiString("static const sal_Unicode** LCContinuousNumberingLevelsArray[] = {\n" );
2214  for( i=0; i<nStyles; i++ )
2215  {
2216  of.writeAsciiString( "\t" );
2217  of.writeAsciiString( "continuousStyle" );
2218  of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2219  of.writeAsciiString( ",\n");
2220  }
2221  of.writeAsciiString("\t0\n};\n\n");
2222  of.writeFunction2("getContinuousNumberingLevels_", "continuousNbOfStyles",
2223  "continuousNbOfAttributesPerStyle", "LCContinuousNumberingLevelsArray");
2224 }
2225 
2226 
2228 {
2229  of.writeAsciiString("// ---> OutlineNumbering\n");
2230  OUString useLocale = getAttr().getValueByName("ref");
2231  if (!useLocale.isEmpty()) {
2232  useLocale = useLocale.replace( '-', '_');
2233  of.writeRefFunction3("getOutlineNumberingLevels_", useLocale);
2234  return;
2235  }
2236 
2237  // hardcode number of attributes per level
2238  const int nAttributes = 11;
2239  const char* attr[ nAttributes ] =
2240  {
2241  "Prefix",
2242  "NumType",
2243  "Suffix",
2244  "BulletChar",
2245  "BulletFontName",
2246  "ParentNumbering",
2247  "LeftMargin",
2248  "SymbolTextDistance",
2249  "FirstLineOffset",
2250  "Transliteration",
2251  "NatNum",
2252  };
2253 
2254  // record each attribute of each level of each style in a static C++ variable.
2255  // determine number of styles and number of levels per style on the fly.
2256  sal_Int32 nStyles = getNumberOfChildren();
2257  vector<sal_Int32> nLevels; // may be different for each style?
2258  for( sal_Int32 i = 0; i < nStyles; i++ )
2259  {
2260  LocaleNode* p = getChildAt( i );
2261  nLevels.push_back( p->getNumberOfChildren() );
2262  for( sal_Int32 j=0; j<nLevels.back(); j++ )
2263  {
2264  const Attr& q = p->getChildAt( j )->getAttr();
2265  for( sal_Int32 k=0; k<nAttributes; ++k )
2266  {
2267  const char* name = attr[k];
2268  OUString value = q.getValueByName( name );
2269  of.writeParameter("outline", name, value,
2270  sal::static_int_cast<sal_Int16>(i),
2271  sal::static_int_cast<sal_Int16>(j) );
2272  }
2273  }
2274  }
2275 
2276  // verify that each style has the same number of levels.
2277  for( size_t i=0; i<nLevels.size(); i++ )
2278  {
2279  if( nLevels[0] != nLevels[i] )
2280  {
2281  incError( "Numbering levels don't match.");
2282  }
2283  }
2284 
2285  // record number of attributes, levels, and styles.
2286  of.writeAsciiString("static const sal_Int16 outlineNbOfStyles = ");
2287  of.writeInt( sal::static_int_cast<sal_Int16>( nStyles ) );
2288  of.writeAsciiString(";\n\n");
2289  of.writeAsciiString("static const sal_Int16 outlineNbOfLevelsPerStyle = ");
2290  of.writeInt( sal::static_int_cast<sal_Int16>( nLevels.back() ) );
2291  of.writeAsciiString(";\n\n");
2292  of.writeAsciiString("static const sal_Int16 outlineNbOfAttributesPerLevel = ");
2293  of.writeInt( nAttributes );
2294  of.writeAsciiString(";\n\n");
2295 
2296  // too complicated for now...
2297  // of.writeAsciiString("static const sal_Int16 nbOfOutlineNumberingLevels[] = { ");
2298  // for( sal_Int32 j=0; j<nStyles; j++ )
2299  // {
2300  // of.writeInt( nLevels[j] );
2301  // of.writeAsciiString(", ");
2302  // }
2303  // of.writeAsciiString("};\n\n");
2304 
2305 
2306  for( sal_Int32 i=0; i<nStyles; i++ )
2307  {
2308  for( sal_Int32 j=0; j<nLevels.back(); j++ )
2309  {
2310  of.writeAsciiString("static const sal_Unicode* outline");
2311  of.writeAsciiString("Style");
2312  of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2313  of.writeAsciiString("Level");
2314  of.writeInt( sal::static_int_cast<sal_Int16>(j) );
2315  of.writeAsciiString("[] = { ");
2316 
2317  for( sal_Int32 k=0; k<nAttributes; k++ )
2318  {
2319  of.writeAsciiString( "outline" );
2320  of.writeAsciiString( attr[k] );
2321  of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2322  of.writeInt( sal::static_int_cast<sal_Int16>(j) );
2323  of.writeAsciiString(", ");
2324  }
2325  of.writeAsciiString("NULL };\n");
2326  }
2327  }
2328 
2329  of.writeAsciiString("\n");
2330 
2331 
2332  for( sal_Int32 i=0; i<nStyles; i++ )
2333  {
2334  of.writeAsciiString("static const sal_Unicode** outline");
2335  of.writeAsciiString( "Style" );
2336  of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2337  of.writeAsciiString("[] = { ");
2338 
2339  for( sal_Int32 j=0; j<nLevels.back(); j++ )
2340  {
2341  of.writeAsciiString("outlineStyle");
2342  of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2343  of.writeAsciiString("Level");
2344  of.writeInt( sal::static_int_cast<sal_Int16>(j) );
2345  of.writeAsciiString(", ");
2346  }
2347  of.writeAsciiString("NULL };\n");
2348  }
2349  of.writeAsciiString("\n");
2350 
2351  of.writeAsciiString("static const sal_Unicode*** LCOutlineNumberingLevelsArray[] = {\n" );
2352  for( sal_Int32 i=0; i<nStyles; i++ )
2353  {
2354  of.writeAsciiString( "\t" );
2355  of.writeAsciiString( "outlineStyle" );
2356  of.writeInt( sal::static_int_cast<sal_Int16>(i) );
2357  of.writeAsciiString(",\n");
2358  }
2359  of.writeAsciiString("\tNULL\n};\n\n");
2360  of.writeFunction3("getOutlineNumberingLevels_", "outlineNbOfStyles", "outlineNbOfLevelsPerStyle",
2361  "outlineNbOfAttributesPerLevel", "LCOutlineNumberingLevelsArray");
2362 }
2363 
2364 Attr::Attr (const Reference< XAttributeList > & attr) {
2365  sal_Int16 len = attr->getLength();
2366  name.realloc (len);
2367  value.realloc (len);
2368  for (sal_Int16 i =0; i< len;i++) {
2369  name[i] = attr->getNameByIndex(i);
2370  value[i] = attr -> getValueByIndex(i);
2371  }
2372 }
2373 
2374 OUString Attr::getValueByName (const char *str) const {
2375  auto pName = std::find_if(std::cbegin(name), std::cend(name),
2376  [&str](const OUString& rName) { return rName.equalsAscii(str); });
2377  if (pName != std::cend(name))
2378  {
2379  auto i = static_cast<sal_Int32>(std::distance(std::cbegin(name), pName));
2380  return value[i];
2381  }
2382  return OUString();
2383 }
2384 
2385 const OUString& Attr::getValueByIndex (sal_Int32 idx) const
2386 {
2387  return value[idx];
2388 }
2389 
2390 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static LocaleNode * createNode(const OUString &name, const Reference< XAttributeList > &attr)
Definition: LocaleNode.cxx:92
Sequence< OUString > name
Definition: LocaleNode.hxx:67
OUString aName
Definition: LocaleNode.hxx:78
sal_Int32 nIndex
virtual void generateCode(const OFileWriter &of) const override
virtual void generateCode(const OFileWriter &of) const override
Definition: LocaleNode.cxx:230
const OUString & getName() const
Definition: LocaleNode.hxx:90
sal_Int16 value
const OUString & getValueByIndex(sal_Int32 idx) const
virtual void generateCode(const OFileWriter &of) const override
virtual void generateCode(const OFileWriter &of) const override
Definition: LocaleNode.cxx:518
sal_Int64 n
static sal_Int16 mnFormats
Definition: LocaleNode.hxx:141
void addChild(LocaleNode *node)
Definition: LocaleNode.cxx:62
constexpr sal_Int16 nFirstFreeFormatIndex
The number of reserved (with defined meaning) built-in format code indices, additional locale data fo...
void writeIntParameter(const char *pAsciiStr, const sal_Int16 count, sal_Int16 val) const
Definition: filewriter.cxx:124
void incErrorStr(const char *pStr, std::u16string_view rVal) const
Definition: LocaleNode.cxx:218
const OUString & getValue() const
Definition: LocaleNode.hxx:91
sal_uInt16 sal_Unicode
const NameValuePair ReserveWord[]
virtual void generateCode(const OFileWriter &of) const override
void writeRefFunction3(const char *func, std::u16string_view useLocale) const
Definition: filewriter.cxx:115
LocaleNode * parent
Definition: LocaleNode.hxx:81
static bool isIso4217(const OUString &rStr)
const Attr & getAttr() const
Definition: LocaleNode.hxx:92
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:283
#define SAL_N_ELEMENTS(arr)
err
sal_uInt16 char * pName
static void lcl_writeTabTagString(const OFileWriter &of, const char *pTag, const char *pStr)
int i
virtual void generateCode(const OFileWriter &of) const
Definition: LocaleNode.cxx:130
OUString writeParameterCheckLen(const OFileWriter &of, const char *pParameterName, const LocaleNode *pNode, sal_Int32 nMinLen, sal_Int32 nMaxLen) const
Definition: LocaleNode.cxx:144
void writeParameter(const char *pAsciiStr, const OUString &aChars) const
Definition: filewriter.cxx:136
void writeAsciiString(const char *str) const
Definition: filewriter.cxx:42
static void lcl_writeAbbrFullNarrNames(const OFileWriter &of, const LocaleNode *currNode, const char *elementTag, sal_Int16 i, sal_Int16 j)
virtual void generateCode(const OFileWriter &of) const override
void incError(const char *pStr) const
Definition: LocaleNode.cxx:201
sal_Int16 nAttributes
float u
void writeRefFunction2(const char *func, std::u16string_view useLocale) const
Definition: filewriter.cxx:97
Attr(const Reference< XAttributeList > &attr)
void writeFunction(const char *func, const char *count, const char *array) const
Definition: filewriter.cxx:53
void incErrorInt(const char *pStr, int nVal) const
Definition: LocaleNode.cxx:212
virtual void generateCode(const OFileWriter &of) const override
int getError() const
Definition: LocaleNode.cxx:54
virtual void generateCode(const OFileWriter &of) const override
constexpr T & temporary(T &&x)
const sal_uInt16 idx[]
void writeInt(sal_Int16 nb) const
Definition: filewriter.cxx:37
virtual void generateCode(const OFileWriter &of) const override
static void lcl_writeAbbrFullNarrArrays(const OFileWriter &of, sal_Int16 nCount, const char *elementTag, sal_Int16 i, bool bNarrow)
::o3tl::sorted_vector< OUString > NameSet
Definition: LocaleNode.cxx:40
sal_Int32 getNumberOfChildren() const
Definition: LocaleNode.hxx:93
virtual void generateCode(const OFileWriter &of) const override
static OUString aDateSep
Definition: LocaleNode.cxx:282
void writeRefFunction(const char *func, std::u16string_view useLocale) const
Definition: filewriter.cxx:60
const LocaleNode * findNode(const char *name) const
Definition: LocaleNode.cxx:76
Sequence< OUString > value
Definition: LocaleNode.hxx:68
static OUString sTheCompatibleCurrency
Definition: LocaleNode.cxx:512
#define OSTR(s)
Definition: LocaleNode.cxx:128
unsigned char sal_uInt8
#define LOCALE_VERSION_DTD
Definition: LocaleNode.cxx:38
OUString getValueByName(const char *str) const
LocaleNode * getChildAt(sal_Int32 idx) const
Definition: LocaleNode.hxx:94
OUString aName
bool expectedCalendarElement(std::u16string_view rName, const LocaleNode *pNode, sal_Int16 nChild, std::u16string_view rCalendarID) const
void incErrorStrStr(const char *pStr, std::u16string_view rVal1, std::u16string_view rVal2) const
Definition: LocaleNode.cxx:224
void * p
const char * name
bool writeDefaultParameter(const char *pAsciiStr, std::u16string_view str, sal_Int16 count) const
Definition: filewriter.cxx:129
static sal_Int16 mnSection
Definition: LocaleNode.hxx:140
void writeFunction3(const char *func, const char *style, const char *levels, const char *attr, const char *array) const
Definition: filewriter.cxx:106
virtual ~LocaleNode()
Definition: LocaleNode.cxx:88
std::vector< std::unique_ptr< LocaleNode > > children
Definition: LocaleNode.hxx:82
const char * getLocale() const
Return the locale string, something like en_US or de_DE.
Definition: LocaleNode.hxx:60
constexpr sal_Int16 nStopPredefinedFormatIndex
The number of predefined format code indices that must be defined by locale data, except BOOLEAN and ...
LocaleNode(const OUString &name, const Reference< XAttributeList > &attr)
Definition: LocaleNode.cxx:46
void writeFunction2(const char *func, const char *style, const char *attr, const char *array) const
Definition: filewriter.cxx:89
std::pair< const_iterator, bool > insert(Value &&x)
static OUString sTheCurrencyReplaceTo
Definition: LocaleNode.cxx:511
virtual void generateCode(const OFileWriter &of) const override
aStr
const LocaleNode * getRoot() const
Definition: LocaleNode.cxx:67
virtual void generateCode(const OFileWriter &of) const override
Definition: LocaleNode.cxx:285
std::vector< sal_Int16 >::const_iterator const_iterator
::o3tl::sorted_vector< sal_Int16 > ValueSet
Definition: LocaleNode.cxx:41
static OUString sTheDateEditFormat
Definition: LocaleNode.cxx:513