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