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"
33 #include <i18npool/reservedconstants.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
LocaleNode(const OUString & name,const Reference<XAttributeList> & attr)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
getError() const53 int LocaleNode::getError() const
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
addChild(LocaleNode * node)61 void LocaleNode::addChild ( LocaleNode * node) {
62 children.emplace_back(node);
63 node->parent = this;
64 }
65
getRoot() const66 const LocaleNode* LocaleNode::getRoot() const
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
findNode(const char * name) const75 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
~LocaleNode()86 LocaleNode::~LocaleNode()
87 {
88 }
89
createNode(const OUString & name,const Reference<XAttributeList> & attr)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
generateCode(const OFileWriter & of) const128 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
writeParameterCheckLen(const OFileWriter & of,const char * pParameterName,const LocaleNode * pNode,sal_Int32 nMinLen,sal_Int32 nMaxLen) const142 OUString LocaleNode::writeParameterCheckLen( const OFileWriter &of,
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
writeParameterCheckLen(const OFileWriter & of,const char * pNodeName,const char * pParameterName,sal_Int32 nMinLen,sal_Int32 nMaxLen) const181 OUString LocaleNode::writeParameterCheckLen( const OFileWriter &of,
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
incError(const char * pStr) const199 void LocaleNode::incError( const char* pStr ) const
200 {
201 ++nError;
202 fprintf( stderr, "Error: %s\n", pStr);
203 }
204
incError(std::u16string_view rStr) const205 void LocaleNode::incError( std::u16string_view rStr ) const
206 {
207 incError( OSTR( rStr));
208 }
209
incErrorInt(const char * pStr,int nVal) const210 void LocaleNode::incErrorInt( const char* pStr, int nVal ) const
211 {
212 ++nError;
213 fprintf( stderr, pStr, nVal);
214 }
215
incErrorStr(const char * pStr,std::u16string_view rVal) const216 void LocaleNode::incErrorStr( const char* pStr, std::u16string_view rVal ) const
217 {
218 ++nError;
219 fprintf( stderr, pStr, OSTR( rVal));
220 }
221
incErrorStrStr(const char * pStr,std::u16string_view rVal1,std::u16string_view rVal2) const222 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
generateCode(const OFileWriter & of) const228 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
generateCode(const OFileWriter & of) const283 void LCCTYPENode::generateCode (const OFileWriter &of) const
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
generateCode(const OFileWriter & of) const516 void LCFormatNode::generateCode (const OFileWriter &of) const
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 {
555 incErrorStrStr(
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 :
658 sTheDateEditFormat = n->getValue();
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 :
872 i18npool::nStopPredefinedFormatIndex),
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
generateCode(const OFileWriter & of) const1256 void LCCollationNode::generateCode (const OFileWriter &of) const
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
generateCode(const OFileWriter & of) const1329 void LCSearchNode::generateCode (const OFileWriter &of) const
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",
1343 getNumberOfChildren());
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
generateCode(const OFileWriter & of) const1368 void LCIndexNode::generateCode (const OFileWriter &of) const
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
lcl_writeAbbrFullNarrNames(const OFileWriter & of,const LocaleNode * currNode,const char * elementTag,sal_Int16 i,sal_Int16 j)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
lcl_writeTabTagString(const OFileWriter & of,const char * pTag,const char * pStr)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
lcl_writeTabTagStringNums(const OFileWriter & of,const char * pTag,const char * pStr,sal_Int16 i,sal_Int16 j)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
lcl_writeAbbrFullNarrArrays(const OFileWriter & of,sal_Int16 nCount,const char * elementTag,sal_Int16 i,bool bNarrow)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
expectedCalendarElement(std::u16string_view rName,const LocaleNode * pNode,sal_Int16 nChild,std::u16string_view rCalendarID) const1527 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
generateCode(const OFileWriter & of) const1545 void LCCalendarNode::generateCode (const OFileWriter &of) const
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
isIso4217(const OUString & rStr)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
generateCode(const OFileWriter & of) const1902 void LCCurrencyNode::generateCode (const OFileWriter &of) const
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
generateCode(const OFileWriter & of) const2003 void LCTransliterationNode::generateCode (const OFileWriter &of) const
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
generateCode(const OFileWriter & of) const2057 void LCMiscNode::generateCode (const OFileWriter &of) const
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
generateCode(const OFileWriter & of) const2154 void LCNumberingLevelNode::generateCode (const OFileWriter &of) const
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
generateCode(const OFileWriter & of) const2225 void LCOutlineNumberingLevelNode::generateCode (const OFileWriter &of) const
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
Attr(const Reference<XAttributeList> & attr)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
getValueByName(const char * str) const2372 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
getValueByIndex(sal_Int32 idx) const2383 const OUString& Attr::getValueByIndex (sal_Int32 idx) const
2384 {
2385 return value[idx];
2386 }
2387
2388 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2389