/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include <map>
#include <o3tl/sorted_vector.hxx>
#include <o3tl/temporary.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/macros.h>
#include <sal/types.h>
#include "LocaleNode.hxx"
#include <i18npool/reservedconstants.hxx>
#include <com/sun/star/i18n/NumberFormatIndex.hpp>
#include <com/sun/star/xml/sax/XAttributeList.hpp>
// NOTE: MUST match the Locale versionDTD attribute defined in data/locale.dtd
#define LOCALE_VERSION_DTD "2.0.3"
typedef ::o3tl::sorted_vector< OUString > NameSet;
typedef ::o3tl::sorted_vector< sal_Int16 > ValueSet;
namespace cssi = css::i18n;
LocaleNode::LocaleNode (OUString name, const Reference< XAttributeList > & attr)
: aName(std::move(name))
, aAttribs(attr)
, parent(nullptr)
, nError(0)
{
}
int LocaleNode::getError() const
{
int err = nError;
for (size_t i=0;i<children.size();i++)
err += children[i]->getError();
return err;
}
void LocaleNode::addChild ( LocaleNode * node) {
children.emplace_back(node);
node->parent = this;
}
const LocaleNode* LocaleNode::getRoot() const
{
const LocaleNode* pRoot = nullptr;
const LocaleNode* pParent = this;
while ( (pParent = pParent->parent) != nullptr )
pRoot = pParent;
return pRoot;
}
const LocaleNode * LocaleNode::findNode ( const char *name) const {
if (aName.equalsAscii(name))
return this;
for (size_t i = 0; i< children.size(); i++)
{
const LocaleNode *n=children[i]->findNode(name);
if (n)
return n;
}
return nullptr;
}
LocaleNode::~LocaleNode()
{
}
LocaleNode* LocaleNode::createNode (const OUString& name, const Reference< XAttributeList > & attr)
{
if ( name == "LC_INFO" )
return new LCInfoNode (name,attr);
if ( name == "LC_CTYPE" )
return new LCCTYPENode (name,attr);
if ( name == "LC_FORMAT" )
return new LCFormatNode (name,attr);
if ( name == "LC_FORMAT_1" )
return new LCFormatNode (name,attr);
if ( name == "LC_CALENDAR" )
return new LCCalendarNode (name,attr);
if ( name == "LC_CURRENCY" )
return new LCCurrencyNode (name,attr);
if ( name == "LC_TRANSLITERATION" )
return new LCTransliterationNode (name,attr);
if ( name == "LC_COLLATION" )
return new LCCollationNode (name,attr);
if ( name == "LC_INDEX" )
return new LCIndexNode (name,attr);
if ( name == "LC_SEARCH" )
return new LCSearchNode (name,attr);
if ( name == "LC_MISC" )
return new LCMiscNode (name,attr);
if ( name == "LC_NumberingLevel" )
return new LCNumberingLevelNode (name, attr);
if ( name == "LC_OutLineNumberingLevel" )
return new LCOutlineNumberingLevelNode (name, attr);
return new LocaleNode(name,attr);
}
// printf(" name: '%s'\n", p->getName().pData->buffer );
// printf("value: '%s'\n", p->getValue().pData->buffer );
#define OSTR(s) (OUStringToOString( (s), RTL_TEXTENCODING_UTF8).getStr())
void LocaleNode::generateCode (const OFileWriter &of) const
{
OUString aDTD = getAttr().getValueByName("versionDTD");
if ( aDTD != LOCALE_VERSION_DTD )
{
++nError;
fprintf( stderr, "Error: Locale versionDTD is not %s, see comment in locale.dtd\n", LOCALE_VERSION_DTD);
}
for (size_t i=0; i<children.size(); i++)
children[i]->generateCode (of);
// print_node( this );
}
OUString LocaleNode::writeOUStringLiteralParameterCheckLen( const OFileWriter &of,
const char* pParameterName, const LocaleNode* pNode,
sal_Int32 nMinLen, sal_Int32 nMaxLen ) const
{
OUString aVal;
if (pNode)
aVal = pNode->getValue();
else if (nMinLen >= 0) // -1: optional => empty, 0: must be present, empty
{
++nError;
fprintf( stderr, "Error: node NULL pointer for parameter %s.\n",
pParameterName);
}
// write empty data if error
of.writeOUStringLiteralParameter( pParameterName, aVal);
sal_Int32 nLen = aVal.getLength();
if (nLen < nMinLen)
{
++nError;
fprintf( stderr, "Error: less than %" SAL_PRIdINT32 " character%s (%" SAL_PRIdINT32 ") in %s '%s'.\n",
nMinLen, (nMinLen > 1 ? "s" : ""),
nLen,
(pNode ? OSTR( pNode->getName()) : ""),
OSTR( aVal));
}
else if (nLen > nMaxLen && nMaxLen >= 0)
{
++nError;
fprintf( stderr,
"Error: more than %" SAL_PRIdINT32 " character%s (%" SAL_PRIdINT32 ") in %s '%s' not supported by application.\n",
nMaxLen, (nMaxLen > 1 ? "s" : ""),
nLen,
(pNode ? OSTR( pNode->getName()) : ""),
OSTR( aVal));
}
return aVal;
}
OUString LocaleNode::writeOUStringLiteralParameterCheckLen( const OFileWriter &of,
const char* pNodeName, const char* pParameterName,
sal_Int32 nMinLen, sal_Int32 nMaxLen ) const
{
OUString aVal;
const LocaleNode * pNode = findNode( pNodeName);
if (pNode || nMinLen < 0)
aVal = writeOUStringLiteralParameterCheckLen( of, pParameterName, pNode, nMinLen, nMaxLen);
else
{
++nError;
fprintf( stderr, "Error: node %s not found.\n", pNodeName);
// write empty data if error
of.writeOUStringLiteralParameter( pParameterName, aVal);
}
return aVal;
}
void LocaleNode::incError( const char* pStr ) const
{
++nError;
fprintf( stderr, "Error: %s\n", pStr);
}
void LocaleNode::incError( std::u16string_view rStr ) const
{
incError( OSTR( rStr));
}
void LocaleNode::incErrorInt( const char* pStr, int nVal ) const
{
++nError;
fprintf( stderr, pStr, nVal);
}
void LocaleNode::incErrorStr( const char* pStr, std::u16string_view rVal ) const
{
++nError;
fprintf( stderr, pStr, OSTR( rVal));
}
void LocaleNode::incErrorStrStr( const char* pStr, std::u16string_view rVal1, std::u16string_view rVal2 ) const
{
++nError;
fprintf(stderr, pStr, OSTR(rVal1), OSTR(rVal2));
}
void LCInfoNode::generateCode (const OFileWriter &of) const
{
const LocaleNode * languageNode = findNode("Language");
const LocaleNode * countryNode = findNode("Country");
const LocaleNode * variantNode = findNode("Variant");
OUString aLanguage;
if (languageNode)
{
aLanguage = languageNode->getChildAt(0)->getValue();
if (aLanguage.getLength() != 2 && aLanguage.getLength() != 3)
incErrorStr( "Error: langID '%s' not 2-3 characters\n", aLanguage);
of.writeOUStringLiteralParameter("langID", aLanguage);
of.writeOUStringLiteralParameter("langDefaultName", languageNode->getChildAt(1)->getValue());
}
else
incError( "No Language node.");
if (countryNode)
{
OUString aCountry( countryNode->getChildAt(0)->getValue());
if (!(aCountry.isEmpty() || aCountry.getLength() == 2))
incErrorStr( "Error: countryID '%s' not empty or more than 2 characters\n", aCountry);
of.writeOUStringLiteralParameter("countryID", aCountry);
of.writeOUStringLiteralParameter("countryDefaultName", countryNode->getChildAt(1)->getValue());
}
else
incError( "No Country node.");
if (variantNode)
{
// If given Variant must be at least ll-Ssss and language must be 'qlt'
const OUString& aVariant( variantNode->getValue());
if (!(aVariant.isEmpty() || (aVariant.getLength() >= 7 && aVariant.indexOf('-') >= 2)))
incErrorStr( "Error: invalid Variant '%s'\n", aVariant);
if (!(aVariant.isEmpty() || aLanguage == "qlt"))
incErrorStrStr( "Error: Variant '%s' given but Language '%s' is not 'qlt'\n", aVariant, aLanguage);
of.writeOUStringLiteralParameter("Variant", aVariant);
}
else
of.writeOUStringLiteralParameter("Variant", std::u16string_view());
of.writeAsciiString("\nstatic constexpr OUString LCInfoArray[] = {\n");
of.writeAsciiString("\tlangID,\n");
of.writeAsciiString("\tlangDefaultName,\n");
of.writeAsciiString("\tcountryID,\n");
of.writeAsciiString("\tcountryDefaultName,\n");
of.writeAsciiString("\tVariant\n");
of.writeAsciiString("};\n\n");
of.writeOUStringFunction("getLCInfo_", "std::size(LCInfoArray)", "LCInfoArray");
}
static OUString aDateSep;
static OUString aDecSep;
void LCCTYPENode::generateCode (const OFileWriter &of) const
{
const LocaleNode * sepNode = nullptr;
OUString useLocale = getAttr().getValueByName("ref");
if (!useLocale.isEmpty()) {
useLocale = useLocale.replace( '-', '_');
of.writeOUStringRefFunction("getLocaleItem_", useLocale);
return;
}
OUString str = getAttr().getValueByName("unoid");
of.writeAsciiString("\n\n");
of.writeOUStringLiteralParameter("LC_CTYPE_Unoid", str);
aDateSep =
writeOUStringLiteralParameterCheckLen( of, "DateSeparator", "dateSeparator", 1, 1);
OUString aThoSep =
writeOUStringLiteralParameterCheckLen( of, "ThousandSeparator", "thousandSeparator", 1, 1);
aDecSep =
writeOUStringLiteralParameterCheckLen( of, "DecimalSeparator", "decimalSeparator", 1, 1);
OUString aDecSepAlt =
writeOUStringLiteralParameterCheckLen( of, "DecimalSeparatorAlternative", "decimalSeparatorAlternative", -1, 1);
OUString aTimeSep =
writeOUStringLiteralParameterCheckLen( of, "TimeSeparator", "timeSeparator", 1, 1);
OUString aTime100Sep =
writeOUStringLiteralParameterCheckLen( of, "Time100SecSeparator", "time100SecSeparator", 1, 1);
OUString aListSep =
writeOUStringLiteralParameterCheckLen( of, "ListSeparator", "listSeparator", 1, 1);
OUString aLDS;
sepNode = findNode("LongDateDayOfWeekSeparator");
aLDS = sepNode->getValue();
of.writeOUStringLiteralParameter("LongDateDayOfWeekSeparator", aLDS);
if (aLDS == ",")
fprintf( stderr, "Warning: %s\n",
"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\".");
sepNode = findNode("LongDateDaySeparator");
aLDS = sepNode->getValue();
of.writeOUStringLiteralParameter("LongDateDaySeparator", aLDS);
if (aLDS == "," || aLDS == ".")
fprintf( stderr, "Warning: %s\n",
"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\".");
sepNode = findNode("LongDateMonthSeparator");
aLDS = sepNode->getValue();
of.writeOUStringLiteralParameter("LongDateMonthSeparator", aLDS);
if (aLDS.isEmpty())
fprintf( stderr, "Warning: %s\n",
"LongDateMonthSeparator is empty. Usually this is not the case and may lead to concatenated display names like \"Wednesday, May9, 2007\".");
sepNode = findNode("LongDateYearSeparator");
aLDS = sepNode->getValue();
of.writeOUStringLiteralParameter("LongDateYearSeparator", aLDS);
if (aLDS.isEmpty())
fprintf( stderr, "Warning: %s\n",
"LongDateYearSeparator is empty. Usually this is not the case and may lead to concatenated display names like \"Wednesday, 2007May 9\".");
int nSavErr = nError;
int nWarn = 0;
if (aDateSep == aTimeSep)
incError( "DateSeparator equals TimeSeparator.");
if (aDecSep == aThoSep)
incError( "DecimalSeparator equals ThousandSeparator.");
if (aDecSepAlt == aThoSep)
incError( "DecimalSeparatorAlternative equals ThousandSeparator.");
if (aDecSepAlt == aDecSep)
incError( "DecimalSeparatorAlternative equals DecimalSeparator, it must not be specified then.");
if ( aThoSep == " " )
incError( "ThousandSeparator is an ' ' ordinary space, this should be a non-breaking space U+00A0 instead.");
if (aListSep == aDecSep)
fprintf( stderr, "Warning: %s\n",
"ListSeparator equals DecimalSeparator.");
if (aListSep == aThoSep)
fprintf( stderr, "Warning: %s\n",
"ListSeparator equals ThousandSeparator.");
if (aListSep.getLength() != 1 || aListSep[0] != ';')
{
incError( "ListSeparator not ';' semicolon. Strongly recommended. Currently required.");
++nSavErr; // format codes not affected
}
if (aTimeSep == aTime100Sep)
{
++nWarn;
fprintf( stderr, "Warning: %s\n",
"Time100SecSeparator equals TimeSeparator, this is probably an error.");
}
if (aDecSep != aTime100Sep)
{
++nWarn;
fprintf( stderr, "Warning: %s\n",
"Time100SecSeparator is different from DecimalSeparator, this may be correct or not. Intended?");
}
if (nSavErr != nError || nWarn)
fprintf( stderr, "Warning: %s\n",
"Don't forget to adapt corresponding FormatCode elements when changing separators.");
OUString aQuoteStart =
writeOUStringLiteralParameterCheckLen( of, "QuotationStart", "quotationStart", 1, 1);
OUString aQuoteEnd =
writeOUStringLiteralParameterCheckLen( of, "QuotationEnd", "quotationEnd", 1, 1);
OUString aDoubleQuoteStart =
writeOUStringLiteralParameterCheckLen( of, "DoubleQuotationStart", "doubleQuotationStart", 1, 1);
OUString aDoubleQuoteEnd =
writeOUStringLiteralParameterCheckLen( of, "DoubleQuotationEnd", "doubleQuotationEnd", 1, 1);
if (aQuoteStart.toChar() <= 127 && aQuoteEnd.toChar() > 127)
fprintf( stderr, "Warning: %s\n",
"QuotationStart is an ASCII character but QuotationEnd is not.");
if (aQuoteEnd.toChar() <= 127 && aQuoteStart.toChar() > 127)
fprintf( stderr, "Warning: %s\n",
"QuotationEnd is an ASCII character but QuotationStart is not.");
if (aDoubleQuoteStart.toChar() <= 127 && aDoubleQuoteEnd.toChar() > 127)
fprintf( stderr, "Warning: %s\n",
"DoubleQuotationStart is an ASCII character but DoubleQuotationEnd is not.");
if (aDoubleQuoteEnd.toChar() <= 127 && aDoubleQuoteStart.toChar() > 127)
fprintf( stderr, "Warning: %s\n",
"DoubleQuotationEnd is an ASCII character but DoubleQuotationStart is not.");
if (aQuoteStart.toChar() <= 127 && aQuoteEnd.toChar() <= 127)
fprintf( stderr, "Warning: %s\n",
"QuotationStart and QuotationEnd are both ASCII characters. Not necessarily an issue, but unusual.");
if (aDoubleQuoteStart.toChar() <= 127 && aDoubleQuoteEnd.toChar() <= 127)
fprintf( stderr, "Warning: %s\n",
"DoubleQuotationStart and DoubleQuotationEnd are both ASCII characters. Not necessarily an issue, but unusual.");
if (aQuoteStart == aQuoteEnd)
fprintf( stderr, "Warning: %s\n",
"QuotationStart equals QuotationEnd. Not necessarily an issue, but unusual.");
if (aDoubleQuoteStart == aDoubleQuoteEnd)
fprintf( stderr, "Warning: %s\n",
"DoubleQuotationStart equals DoubleQuotationEnd. Not necessarily an issue, but unusual.");
/* TODO: should equalness of single and double quotes be an error? Would
* need to adapt quite some locales' data. */
if (aQuoteStart == aDoubleQuoteStart)
fprintf( stderr, "Warning: %s\n",
"QuotationStart equals DoubleQuotationStart. Not necessarily an issue, but unusual.");
if (aQuoteEnd == aDoubleQuoteEnd)
fprintf( stderr, "Warning: %s\n",
"QuotationEnd equals DoubleQuotationEnd. Not necessarily an issue, but unusual.");
// Known good values, exclude ASCII single (U+0027, ') and double (U+0022, ") quotes.
switch (int ic = aQuoteStart.toChar())
{
case 0x2018: // LEFT SINGLE QUOTATION MARK
case 0x201a: // SINGLE LOW-9 QUOTATION MARK
case 0x201b: // SINGLE HIGH-REVERSED-9 QUOTATION MARK
case 0x2039: // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
case 0x203a: // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
case 0x300c: // LEFT CORNER BRACKET (Chinese)
;
break;
default:
fprintf( stderr, "Warning: %s U+%04X %s\n",
"QuotationStart may be wrong:", ic, OSTR( aQuoteStart));
}
switch (int ic = aQuoteEnd.toChar())
{
case 0x2019: // RIGHT SINGLE QUOTATION MARK
case 0x201a: // SINGLE LOW-9 QUOTATION MARK
case 0x201b: // SINGLE HIGH-REVERSED-9 QUOTATION MARK
case 0x2039: // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
case 0x203a: // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
case 0x300d: // RIGHT CORNER BRACKET (Chinese)
;
break;
default:
fprintf( stderr, "Warning: %s U+%04X %s\n",
"QuotationEnd may be wrong:", ic, OSTR( aQuoteEnd));
}
switch (int ic = aDoubleQuoteStart.toChar())
{
case 0x00ab: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
case 0x00bb: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
case 0x201c: // LEFT DOUBLE QUOTATION MARK
case 0x201e: // DOUBLE LOW-9 QUOTATION MARK
case 0x201f: // DOUBLE HIGH-REVERSED-9 QUOTATION MARK
case 0x300e: // LEFT WHITE CORNER BRACKET (Chinese)
;
break;
default:
fprintf( stderr, "Warning: %s U+%04X %s\n",
"DoubleQuotationStart may be wrong:", ic, OSTR( aDoubleQuoteStart));
}
switch (int ic = aDoubleQuoteEnd.toChar())
{
case 0x00ab: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
case 0x00bb: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
case 0x201d: // RIGHT DOUBLE QUOTATION MARK
case 0x201e: // DOUBLE LOW-9 QUOTATION MARK
case 0x201f: // DOUBLE HIGH-REVERSED-9 QUOTATION MARK
case 0x300f: // RIGHT WHITE CORNER BRACKET (Chinese)
;
break;
default:
fprintf( stderr, "Warning: %s U+%04X %s\n",
"DoubleQuotationEnd may be wrong:", ic, OSTR( aDoubleQuoteEnd));
}
writeOUStringLiteralParameterCheckLen( of, "TimeAM", "timeAM", 1, -1);
writeOUStringLiteralParameterCheckLen( of, "TimePM", "timePM", 1, -1);
sepNode = findNode("MeasurementSystem");
of.writeOUStringLiteralParameter("measurementSystem", sepNode->getValue());
of.writeAsciiString("\nstatic constexpr OUString LCType[] = {\n");
of.writeAsciiString("\tLC_CTYPE_Unoid,\n");
of.writeAsciiString("\tdateSeparator,\n");
of.writeAsciiString("\tthousandSeparator,\n");
of.writeAsciiString("\tdecimalSeparator,\n");
of.writeAsciiString("\ttimeSeparator,\n");
of.writeAsciiString("\ttime100SecSeparator,\n");
of.writeAsciiString("\tlistSeparator,\n");
of.writeAsciiString("\tquotationStart,\n");
of.writeAsciiString("\tquotationEnd,\n");
of.writeAsciiString("\tdoubleQuotationStart,\n");
of.writeAsciiString("\tdoubleQuotationEnd,\n");
of.writeAsciiString("\ttimeAM,\n");
of.writeAsciiString("\ttimePM,\n");
of.writeAsciiString("\tmeasurementSystem,\n");
of.writeAsciiString("\tLongDateDayOfWeekSeparator,\n");
of.writeAsciiString("\tLongDateDaySeparator,\n");
of.writeAsciiString("\tLongDateMonthSeparator,\n");
of.writeAsciiString("\tLongDateYearSeparator,\n");
of.writeAsciiString("\tdecimalSeparatorAlternative\n");
of.writeAsciiString("};\n\n");
of.writeOUStringFunction("getLocaleItem_", "std::size(LCType)", "LCType");
}
static OUString sTheCurrencyReplaceTo;
static OUString sTheCompatibleCurrency;
static OUString sTheDateEditFormat;
sal_Int16 LCFormatNode::mnSection = 0;
sal_Int16 LCFormatNode::mnFormats = 0;
void LCFormatNode::generateCode (const OFileWriter &of) const
{
if (mnSection >= 2)
incError("more than 2 LC_FORMAT sections");
::std::vector< OUString > theDateAcceptancePatterns;
OUString useLocale(getAttr().getValueByName("ref"));
OUString str;
OUString strFrom( getAttr().getValueByName("replaceFrom"));
if (useLocale.isEmpty())
{
of.writeParameter("replaceFrom", strFrom, mnSection);
}
str = getAttr().getValueByName("replaceTo");
if (!strFrom.isEmpty() && str.isEmpty())
incErrorStr("replaceFrom=\"%s\" replaceTo=\"\" is empty replacement.\n", strFrom);
// Locale data generator inserts FFFF for LangID, we need to adapt that.
if (str.endsWithIgnoreAsciiCase( "-FFFF]"))
incErrorStr("replaceTo=\"%s\" needs FFFF to be adapted to the real LangID value.\n", str);
of.writeParameter("replaceTo", str, mnSection);
// Remember the replaceTo value for "[CURRENCY]" to check format codes.
if ( strFrom == "[CURRENCY]" )
sTheCurrencyReplaceTo = str;
// Remember the currency symbol if present.
if (str.startsWith( "[$" ))
{
sal_Int32 nHyphen = str.indexOf( '-');
if (nHyphen >= 3)
{
sTheCompatibleCurrency = str.copy( 2, nHyphen - 2);
}
}
if (!useLocale.isEmpty())
{
if (!strFrom.isEmpty() && strFrom != "[CURRENCY]") //???
{
incErrorStrStr(
"Error: non-empty replaceFrom=\"%s\" with non-empty ref=\"%s\".",
strFrom, useLocale);
}
useLocale = useLocale.replace( '-', '_');
switch (mnSection)
{
case 0:
of.writeOUStringRefFunction("getAllFormats0_", useLocale, "replaceTo0");
of.writeOUStringRefFunction("getDateAcceptancePatterns_", useLocale);
break;
case 1:
of.writeOUStringRefFunction("getAllFormats1_", useLocale, "replaceTo1");
break;
}
++mnSection;
return;
}
sal_Int16 formatCount = mnFormats;
NameSet aMsgIdSet;
ValueSet aFormatIndexSet;
NameSet aDefaultsSet;
bool bCtypeIsRef = false;
bool bHaveEngineering = false;
bool bShowNextFreeFormatIndex = false;
for (sal_Int32 i = 0; i< getNumberOfChildren() ; i++, formatCount++)
{
LocaleNode * currNode = getChildAt (i);
if ( currNode->getName() == "DateAcceptancePattern" )
{
if (mnSection > 0)
incError( "DateAcceptancePattern only handled in LC_FORMAT, not LC_FORMAT_1");
else
{
auto val = currNode->getValue();
if (std::find(theDateAcceptancePatterns.begin(), theDateAcceptancePatterns.end(), val) == theDateAcceptancePatterns.end())
theDateAcceptancePatterns.push_back(val);
else
incErrorStr( "Error: Duplicated DateAcceptancePattern: %s\n", val);
}
--formatCount;
continue; // for
}
if ( currNode->getName() != "FormatElement" )
{
incErrorStr( "Error: Undefined element '%s' in LC_FORMAT\n", currNode->getName());
--formatCount;
continue; // for
}
OUString aUsage;
OUString aType;
OUString aFormatIndex;
// currNode -> print();
const Attr &currNodeAttr = currNode->getAttr();
//printf ("getLen() = %d\n", currNode->getAttr().getLength());
str = currNodeAttr.getValueByName("msgid");
if (!aMsgIdSet.insert( str).second)
incErrorStr( "Error: Duplicated msgid=\"%s\" in FormatElement.\n", str);
of.writeOUStringLiteralParameter("FormatKey", str, formatCount);
str = currNodeAttr.getValueByName("default");
bool bDefault = str == "true";
of.writeOUStringLiteralDefaultParameter("FormatElement", str, formatCount);
aType = currNodeAttr.getValueByName("type");
of.writeOUStringLiteralParameter("FormatType", aType, formatCount);
aUsage = currNodeAttr.getValueByName("usage");
of.writeOUStringLiteralParameter("FormatUsage", aUsage, formatCount);
aFormatIndex = currNodeAttr.getValueByName("formatindex");
sal_Int16 formatindex = static_cast<sal_Int16>(aFormatIndex.toInt32());
// Ensure the new reserved range is not used anymore, free usage start
// was up'ed from 50 to 60 (and more later).
if (i18npool::nStopPredefinedFormatIndex <= formatindex && formatindex < i18npool::nFirstFreeFormatIndex)
{
incErrorInt( "Error: Reserved formatindex=\"%d\" in FormatElement.\n", formatindex);
bShowNextFreeFormatIndex = true;
}
if (!aFormatIndexSet.insert( formatindex).second)
{
incErrorInt( "Error: Duplicated formatindex=\"%d\" in FormatElement.\n", formatindex);
bShowNextFreeFormatIndex = true;
}
of.writeOUStringLiteralIntParameter("Formatindex", formatCount, formatindex);
// Ensure only one default per usage and type.
if (bDefault)
{
OUString aKey( aUsage + "," + aType);
if (!aDefaultsSet.insert( aKey).second)
{
OUString aStr = "Duplicated default for usage=\"" + aUsage + "\" type=\"" + aType + "\": formatindex=\"" + aFormatIndex + "\".";
incError( aStr);
}
}
const LocaleNode * n = currNode -> findNode("FormatCode");
if (n)
{
of.writeOUStringLiteralParameter("FormatCode", n->getValue(), formatCount);
// Check separator usage for some FormatCode elements.
const LocaleNode* pCtype = nullptr;
switch (formatindex)
{
case cssi::NumberFormatIndex::DATE_SYS_DDMMYYYY :
sTheDateEditFormat = n->getValue();
break;
case cssi::NumberFormatIndex::NUMBER_1000DEC2 : // #,##0.00
case cssi::NumberFormatIndex::TIME_MMSS00 : // MM:SS.00
case cssi::NumberFormatIndex::TIME_HH_MMSS00 : // [HH]:MM:SS.00
{
const LocaleNode* pRoot = getRoot();
if (!pRoot)
incError( "No root for FormatCode.");
else
{
pCtype = pRoot->findNode( "LC_CTYPE");
if (!pCtype)
incError( "No LC_CTYPE found for FormatCode.");
else
{
OUString aRef( pCtype->getAttr().getValueByName("ref"));
if (!aRef.isEmpty())
{
aRef = aRef.replace( '-', '_');
if (!bCtypeIsRef)
fprintf( stderr,
"Warning: Can't check separators used in FormatCode due to LC_CTYPE ref=\"%s\".\n"
"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",
OSTR( aRef));
bCtypeIsRef = true;
pCtype = nullptr;
}
}
}
}
break;
case cssi::NumberFormatIndex::CURRENCY_1000DEC2 :
// Remember the currency symbol if present.
{
if (sTheCompatibleCurrency.isEmpty())
{
sal_Int32 nStart = n->getValue().indexOf("[$");
if (nStart >= 0)
{
const OUString& aCode( n->getValue());
sal_Int32 nHyphen = aCode.indexOf( '-', nStart);
if (nHyphen >= nStart + 3)
sTheCompatibleCurrency = aCode.copy( nStart + 2, nHyphen - nStart - 2);
}
}
}
[[fallthrough]];
case cssi::NumberFormatIndex::CURRENCY_1000INT :
case cssi::NumberFormatIndex::CURRENCY_1000INT_RED :
case cssi::NumberFormatIndex::CURRENCY_1000DEC2_RED :
case cssi::NumberFormatIndex::CURRENCY_1000DEC2_CCC :
case cssi::NumberFormatIndex::CURRENCY_1000DEC2_DASHED :
// Currency formats should be something like [C]###0;-[C]###0
// and not parenthesized [C]###0;([C]###0) if not en_US.
if (strcmp( of.getLocale(), "en_US") != 0)
{
const OUString& aCode( n->getValue());
if (aCode.indexOf( "0)" ) > 0 || aCode.indexOf( "-)" ) > 0 ||
aCode.indexOf( " )" ) > 0 || aCode.indexOf( "])" ) > 0)
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);
}
// Check if we have replaceTo for "[CURRENCY]" placeholder.
if (sTheCurrencyReplaceTo.isEmpty())
{
const OUString& aCode( n->getValue());
if (aCode.indexOf( "[CURRENCY]" ) >= 0)
incErrorInt( "Error: [CURRENCY] replaceTo not found for formatindex=\"%d\".\n", formatindex);
}
break;
default:
if (aUsage == "SCIENTIFIC_NUMBER")
{
// Check for presence of ##0.00E+00
const OUString& aCode( n->getValue());
// Simple check without decimal separator (assumed to
// be one UTF-16 character). May be prefixed with
// [NatNum1] or other tags.
sal_Int32 nInt = aCode.indexOf("##0");
sal_Int32 nDec = (nInt < 0 ? -1 : aCode.indexOf("00E+00", nInt));
if (nInt >= 0 && nDec == nInt+4)
bHaveEngineering = true;
}
break;
}
if (pCtype)
{
int nSavErr = nError;
const OUString& aCode( n->getValue());
if (formatindex == cssi::NumberFormatIndex::NUMBER_1000DEC2)
{
sal_Int32 nDec = -1;
sal_Int32 nGrp = -1;
const LocaleNode* pSep = pCtype->findNode( "DecimalSeparator");
if (!pSep)
incError( "No DecimalSeparator found for FormatCode.");
else
{
nDec = aCode.indexOf( pSep->getValue());
if (nDec < 0)
incErrorInt( "Error: DecimalSeparator not present in FormatCode formatindex=\"%d\".\n",
formatindex);
}
pSep = pCtype->findNode( "ThousandSeparator");
if (!pSep)
incError( "No ThousandSeparator found for FormatCode.");
else
{
nGrp = aCode.indexOf( pSep->getValue());
if (nGrp < 0)
incErrorInt( "Error: ThousandSeparator not present in FormatCode formatindex=\"%d\".\n",
formatindex);
}
if (nDec >= 0 && nGrp >= 0 && nDec <= nGrp)
incErrorInt( "Error: Ordering of ThousandSeparator and DecimalSeparator not correct in formatindex=\"%d\".\n",
formatindex);
}
if (formatindex == cssi::NumberFormatIndex::TIME_MMSS00 ||
formatindex == cssi::NumberFormatIndex::TIME_HH_MMSS00)
{
sal_Int32 nTime = -1;
sal_Int32 n100s = -1;
const LocaleNode* pSep = pCtype->findNode( "TimeSeparator");
if (!pSep)
incError( "No TimeSeparator found for FormatCode.");
else
{
nTime = aCode.indexOf( pSep->getValue());
if (nTime < 0)
incErrorInt( "Error: TimeSeparator not present in FormatCode formatindex=\"%d\".\n",
formatindex);
}
pSep = pCtype->findNode( "Time100SecSeparator");
if (!pSep)
incError( "No Time100SecSeparator found for FormatCode.");
else
{
n100s = aCode.indexOf( pSep->getValue());
if (n100s < 0)
incErrorInt( "Error: Time100SecSeparator not present in FormatCode formatindex=\"%d\".\n",
formatindex);
n100s = aCode.indexOf( Concat2View(pSep->getValue() + "00"));
if (n100s < 0)
incErrorInt( "Error: Time100SecSeparator+00 not present in FormatCode formatindex=\"%d\".\n",
formatindex);
}
if (n100s >= 0 && nTime >= 0 && n100s <= nTime)
incErrorInt( "Error: Ordering of Time100SecSeparator and TimeSeparator not correct in formatindex=\"%d\".\n",
formatindex);
}
if (nSavErr != nError)
fprintf( stderr,
"Warning: formatindex=\"%d\",\"%d\",\"%d\" are the only FormatCode elements checked for separator usage, there may be others that have errors.\n",
int(cssi::NumberFormatIndex::NUMBER_1000DEC2),
int(cssi::NumberFormatIndex::TIME_MMSS00),
int(cssi::NumberFormatIndex::TIME_HH_MMSS00));
}
}
else
incError( "No FormatCode in FormatElement.");
n = currNode -> findNode("DefaultName");
if (n)
of.writeOUStringLiteralParameter("FormatDefaultName", n->getValue(), formatCount);
else
of.writeOUStringLiteralParameter("FormatDefaultName", std::u16string_view(), formatCount);
}
if (bShowNextFreeFormatIndex)
{
sal_Int16 nNext = i18npool::nFirstFreeFormatIndex;
auto it = aFormatIndexSet.find( nNext);
if (it != aFormatIndexSet.end())
{
// nFirstFreeFormatIndex already used, find next free including gaps.
do
{
++nNext;
}
while (++it != aFormatIndexSet.end() && *it == nNext);
}
fprintf( stderr, "Hint: Next free formatindex is %d.\n", static_cast<int>(nNext));
}
// Check presence of all required format codes only in first section
// LC_FORMAT, not in optional LC_FORMAT_1
if (mnSection == 0)
{
// At least one abbreviated date acceptance pattern must be present.
if (theDateAcceptancePatterns.empty())
incError( "No DateAcceptancePattern present.\n");
else
{
bool bHaveAbbr = false;
for (auto const& elem : theDateAcceptancePatterns)
{
if (elem.indexOf('D') > -1 && elem.indexOf('M') > -1 && elem.indexOf('Y') <= -1)
{
bHaveAbbr = true;
break;
}
}
if (!bHaveAbbr)
incError( "No abbreviated DateAcceptancePattern present. For example M/D or D.M.\n");
}
// 0..9 MUST be present, 10,11 MUST NOT be present, 12..47 MUST be
// present, 48,49 MUST NOT be present, 50 MUST be present.
ValueSet::const_iterator aIter( aFormatIndexSet.begin());
for (sal_Int16 nNext = cssi::NumberFormatIndex::NUMBER_START;
nNext < i18npool::nStopPredefinedFormatIndex; ++nNext)
{
sal_Int16 nHere = ::std::min( (aIter != aFormatIndexSet.end() ? *aIter :
i18npool::nStopPredefinedFormatIndex),
i18npool::nStopPredefinedFormatIndex);
if (aIter != aFormatIndexSet.end()) ++aIter;
for ( ; nNext < nHere; ++nNext)
{
switch (nNext)
{
case cssi::NumberFormatIndex::FRACTION_1 :
case cssi::NumberFormatIndex::FRACTION_2 :
case cssi::NumberFormatIndex::BOOLEAN :
case cssi::NumberFormatIndex::TEXT :
// generated internally
break;
default:
incErrorInt( "Error: FormatElement formatindex=\"%d\" not present.\n", nNext);
}
}
switch (nHere)
{
case cssi::NumberFormatIndex::FRACTION_1 :
incErrorInt( "Error: FormatElement formatindex=\"%d\" reserved for internal ``# ?/?''.\n", nNext);
break;
case cssi::NumberFormatIndex::FRACTION_2 :
incErrorInt( "Error: FormatElement formatindex=\"%d\" reserved for internal ``# ?\?/?\?''.\n", nNext);
break;
case cssi::NumberFormatIndex::BOOLEAN :
incErrorInt( "Error: FormatElement formatindex=\"%d\" reserved for internal ``BOOLEAN''.\n", nNext);
break;
case cssi::NumberFormatIndex::TEXT :
incErrorInt( "Error: FormatElement formatindex=\"%d\" reserved for internal ``@'' (TEXT).\n", nNext);
break;
default:
; // nothing
}
}
if (!bHaveEngineering)
incError("Engineering notation format not present, e.g. ##0.00E+00 or ##0,00E+00 for usage=\"SCIENTIFIC_NUMBER\"\n");
}
of.writeAsciiString("\nstatic const sal_Int16 ");
of.writeAsciiString("FormatElementsCount");
of.writeInt(mnSection);
of.writeAsciiString(" = ");
of.writeInt( formatCount - mnFormats);
of.writeAsciiString(";\n");
of.writeAsciiString("static constexpr OUString ");
of.writeAsciiString("FormatElementsArray");
of.writeInt(mnSection);
of.writeAsciiString("[] = {\n");
for(sal_Int16 i = mnFormats; i < formatCount; i++) {
of.writeAsciiString("\t");
of.writeAsciiString("FormatCode");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\t");
of.writeAsciiString("FormatDefaultName");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\t");
of.writeAsciiString("FormatKey");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\t");
of.writeAsciiString("FormatType");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\t");
of.writeAsciiString("FormatUsage");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\t");
of.writeAsciiString("Formatindex");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tdefaultFormatElement");
of.writeInt(i);
of.writeAsciiString(",\n");
}
of.writeAsciiString("};\n\n");
switch (mnSection)
{
case 0:
of.writeOUStringFunction("getAllFormats0_", "FormatElementsCount0", "FormatElementsArray0", "replaceFrom0", "replaceTo0");
break;
case 1:
of.writeOUStringFunction("getAllFormats1_", "FormatElementsCount1", "FormatElementsArray1", "replaceFrom1", "replaceTo1");
break;
}
mnFormats = mnFormats + formatCount;
if (mnSection == 0)
{
// Extract and add date acceptance pattern for full date, so we provide
// at least one valid pattern, even if the number parser doesn't need
// that one.
/* XXX NOTE: only simple [...] modifier and "..." quotes detected and
* ignored, not nested, no fancy stuff. */
// aDateSep can be empty if LC_CTYPE was a ref=..., determine from
// FormatCode then.
sal_uInt32 cDateSep = (aDateSep.isEmpty()
? 0 : aDateSep.iterateCodePoints( &o3tl::temporary(sal_Int32(0))));
sal_uInt32 cDateSep2 = cDateSep;
sal_Int32 nIndex = 0;
OUStringBuffer aPatternBuf(5);
OUStringBuffer aPatternBuf2(5);
sal_uInt8 nDetected = 0; // bits Y,M,D
bool bInModifier = false;
bool bQuoted = false;
while (nIndex < sTheDateEditFormat.getLength() && nDetected < 7)
{
sal_uInt32 cChar = sTheDateEditFormat.iterateCodePoints( &nIndex);
if (bInModifier)
{
if (cChar == ']')
bInModifier = false;
continue; // while
}
if (bQuoted)
{
if (cChar == '"')
bQuoted = false;
continue; // while
}
switch (cChar)
{
case 'Y':
case 'y':
if (!(nDetected & 4))
{
aPatternBuf.append( 'Y');
if (!aPatternBuf2.isEmpty())
aPatternBuf2.append( 'Y');
nDetected |= 4;
}
break;
case 'M':
case 'm':
if (!(nDetected & 2))
{
aPatternBuf.append( 'M');
if (!aPatternBuf2.isEmpty())
aPatternBuf2.append( 'M');
nDetected |= 2;
}
break;
case 'D':
case 'd':
if (!(nDetected & 1))
{
aPatternBuf.append( 'D');
if (!aPatternBuf2.isEmpty())
aPatternBuf2.append( 'D');
nDetected |= 1;
}
break;
case '[':
bInModifier = true;
break;
case '"':
bQuoted = true;
break;
case '\\':
cChar = sTheDateEditFormat.iterateCodePoints( &nIndex);
goto handleDefault;
case '-':
case '.':
case '/':
// There are locales that use an ISO 8601 edit format
// regardless of what the date separator or other formats
// say, for example hu-HU. Generalize this for all cases
// where the used separator differs and is one of the known
// separators and generate a second pattern with the
// format's separator at the current position.
cDateSep2 = cChar;
[[fallthrough]];
default:
handleDefault:
if (!cDateSep)
cDateSep = cChar;
if (!cDateSep2)
cDateSep2 = cChar;
if (cDateSep != cDateSep2 && aPatternBuf2.isEmpty())
aPatternBuf2 = aPatternBuf;
if (cChar == cDateSep || cChar == cDateSep2)
aPatternBuf.append( OUString( &cDateSep, 1)); // always the defined separator
if (cChar == cDateSep2 && !aPatternBuf2.isEmpty())
aPatternBuf2.append( OUString( &cDateSep2, 1)); // always the format's separator
break;
// The localized legacy:
case 'A':
if (((nDetected & 7) == 3) || ((nDetected & 7) == 0))
{
// es DD/MM/AAAA
// fr JJ.MM.AAAA
// it GG/MM/AAAA
// fr_CA AAAA-MM-JJ
aPatternBuf.append( 'Y');
if (!aPatternBuf2.isEmpty())
aPatternBuf2.append( 'Y');
nDetected |= 4;
}
break;
case 'J':
if (((nDetected & 7) == 0) || ((nDetected & 7) == 6))
{
// fr JJ.MM.AAAA
// fr_CA AAAA-MM-JJ
aPatternBuf.append( 'D');
if (!aPatternBuf2.isEmpty())
aPatternBuf2.append( 'D');
nDetected |= 1;
}
else if ((nDetected & 7) == 3)
{
// nl DD-MM-JJJJ
// de TT.MM.JJJJ
aPatternBuf.append( 'Y');
if (!aPatternBuf2.isEmpty())
aPatternBuf2.append( 'Y');
nDetected |= 4;
}
break;
case 'T':
if ((nDetected & 7) == 0)
{
// de TT.MM.JJJJ
aPatternBuf.append( 'D');
if (!aPatternBuf2.isEmpty())
aPatternBuf2.append( 'D');
nDetected |= 1;
}
break;
case 'G':
if ((nDetected & 7) == 0)
{
// it GG/MM/AAAA
aPatternBuf.append( 'D');
if (!aPatternBuf2.isEmpty())
aPatternBuf2.append( 'D');
nDetected |= 1;
}
break;
case 'P':
if ((nDetected & 7) == 0)
{
// fi PP.KK.VVVV
aPatternBuf.append( 'D');
if (!aPatternBuf2.isEmpty())
aPatternBuf2.append( 'D');
nDetected |= 1;
}
break;
case 'K':
if ((nDetected & 7) == 1)
{
// fi PP.KK.VVVV
aPatternBuf.append( 'M');
if (!aPatternBuf2.isEmpty())
aPatternBuf2.append( 'M');
nDetected |= 2;
}
break;
case 'V':
if ((nDetected & 7) == 3)
{
// fi PP.KK.VVVV
aPatternBuf.append( 'Y');
if (!aPatternBuf2.isEmpty())
aPatternBuf2.append( 'Y');
nDetected |= 4;
}
break;
}
}
OUString aPattern( aPatternBuf.makeStringAndClear());
if (((nDetected & 7) != 7) || aPattern.getLength() < 5)
{
incErrorStr( "Error: failed to extract full date acceptance pattern: %s\n", aPattern);
fprintf( stderr, " with DateSeparator '%s' from FormatCode '%s' (formatindex=\"%d\")\n",
OSTR( OUString(&cDateSep, 1)), OSTR( sTheDateEditFormat),
int(cssi::NumberFormatIndex::DATE_SYS_DDMMYYYY));
}
else
{
fprintf( stderr, "Generated date acceptance pattern: '%s' from '%s' (formatindex=\"%d\" and defined DateSeparator '%s')\n",
OSTR( aPattern), OSTR( sTheDateEditFormat),
int(cssi::NumberFormatIndex::DATE_SYS_DDMMYYYY),
OSTR( OUString(&cDateSep, 1)));
if (std::find(theDateAcceptancePatterns.begin(), theDateAcceptancePatterns.end(), aPattern) == theDateAcceptancePatterns.end())
theDateAcceptancePatterns.push_back(aPattern);
else
incErrorStr( "Error: Duplicated DateAcceptancePattern: %s\n", aPattern);
}
if (!aPatternBuf2.isEmpty())
{
OUString aPattern2( aPatternBuf2.makeStringAndClear());
if (aPattern2.getLength() < 5)
{
incErrorStr( "Error: failed to extract 2nd date acceptance pattern: %s\n", aPattern2);
fprintf( stderr, " with DateSeparator '%s' from FormatCode '%s' (formatindex=\"%d\")\n",
OSTR( OUString(&cDateSep2, 1)), OSTR( sTheDateEditFormat),
int(cssi::NumberFormatIndex::DATE_SYS_DDMMYYYY));
}
else
{
fprintf( stderr, "Generated 2nd acceptance pattern: '%s' from '%s' (formatindex=\"%d\")\n",
OSTR( aPattern2), OSTR( sTheDateEditFormat),
int(cssi::NumberFormatIndex::DATE_SYS_DDMMYYYY));
if (std::find(theDateAcceptancePatterns.begin(), theDateAcceptancePatterns.end(), aPattern2) == theDateAcceptancePatterns.end())
theDateAcceptancePatterns.push_back(aPattern2);
else
incErrorStr( "Error: Duplicated DateAcceptancePattern: %s\n", aPattern2);
}
}
// Rudimentary check if a pattern interferes with decimal number.
// But only if not inherited in which case we don't have aDecSep here.
if (!aDecSep.isEmpty())
{
sal_uInt32 cDecSep = aDecSep.iterateCodePoints( &o3tl::temporary(sal_Int32(0)));
for (auto const& elem : theDateAcceptancePatterns)
{
if (elem.getLength() == (cDecSep <= 0xffff ? 3 : 4))
{
if (elem.iterateCodePoints( &o3tl::temporary(sal_Int32(1))) == cDecSep)
{
++nError;
fprintf( stderr, "Error: Date acceptance pattern '%s' matches decimal number '#%s#'\n",
OSTR(elem), OSTR( aDecSep));
}
}
}
}
sal_Int16 nbOfDateAcceptancePatterns = static_cast<sal_Int16>(theDateAcceptancePatterns.size());
for (sal_Int16 i = 0; i < nbOfDateAcceptancePatterns; ++i)
{
of.writeOUStringLiteralParameter("DateAcceptancePattern", theDateAcceptancePatterns[i], i);
}
of.writeAsciiString("static const sal_Int16 DateAcceptancePatternsCount = ");
of.writeInt( nbOfDateAcceptancePatterns);
of.writeAsciiString(";\n");
of.writeAsciiString("static constexpr OUString DateAcceptancePatternsArray[] = {\n");
for (sal_Int16 i = 0; i < nbOfDateAcceptancePatterns; ++i)
{
of.writeAsciiString("\t");
of.writeAsciiString("DateAcceptancePattern");
of.writeInt(i);
of.writeAsciiString(",\n");
}
of.writeAsciiString("};\n\n");
of.writeOUStringFunction("getDateAcceptancePatterns_", "DateAcceptancePatternsCount", "DateAcceptancePatternsArray");
}
++mnSection;
}
void LCCollationNode::generateCode (const OFileWriter &of) const
{
OUString useLocale = getAttr().getValueByName("ref");
if (!useLocale.isEmpty()) {
useLocale = useLocale.replace( '-', '_');
of.writeOUStringRefFunction("getCollatorImplementation_", useLocale);
of.writeOUStringRefFunction("getCollationOptions_", useLocale);
return;
}
sal_Int16 nbOfCollations = 0;
sal_Int16 nbOfCollationOptions = 0;
for ( sal_Int32 j = 0; j < getNumberOfChildren(); j++ ) {
LocaleNode * currNode = getChildAt (j);
if( currNode->getName() == "Collator" )
{
OUString str;
str = currNode->getAttr().getValueByName("unoid");
of.writeOUStringLiteralParameter("CollatorID", str, j);
str = currNode->getValue();
of.writeOUStringLiteralParameter("CollatorRule", str, j);
str = currNode -> getAttr().getValueByName("default");
of.writeOUStringLiteralDefaultParameter("Collator", str, j);
of.writeAsciiString("\n");
nbOfCollations++;
}
if( currNode->getName() == "CollationOptions" )
{
LocaleNode* pCollationOptions = currNode;
nbOfCollationOptions = sal::static_int_cast<sal_Int16>( pCollationOptions->getNumberOfChildren() );
for( sal_Int16 i=0; i<nbOfCollationOptions; i++ )
{
of.writeOUStringLiteralParameter("collationOption", pCollationOptions->getChildAt( i )->getValue(), i );
}
of.writeAsciiString("static const sal_Int16 nbOfCollationOptions = ");
of.writeInt( nbOfCollationOptions );
of.writeAsciiString(";\n\n");
}
}
of.writeAsciiString("static const sal_Int16 nbOfCollations = ");
of.writeInt(nbOfCollations);
of.writeAsciiString(";\n\n");
of.writeAsciiString("\nstatic constexpr OUString LCCollatorArray[] = {\n");
for(sal_Int16 j = 0; j < nbOfCollations; j++) {
of.writeAsciiString("\tCollatorID");
of.writeInt(j);
of.writeAsciiString(",\n");
of.writeAsciiString("\tdefaultCollator");
of.writeInt(j);
of.writeAsciiString(",\n");
of.writeAsciiString("\tCollatorRule");
of.writeInt(j);
of.writeAsciiString(",\n");
}
of.writeAsciiString("};\n\n");
of.writeAsciiString("static constexpr OUString collationOptions[] = {");
for( sal_Int16 j=0; j<nbOfCollationOptions; j++ )
{
if (j)
of.writeAsciiString( ", " );
of.writeAsciiString( "collationOption" );
of.writeInt( j );
}
of.writeAsciiString("};\n");
of.writeOUStringFunction("getCollatorImplementation_", "nbOfCollations", "LCCollatorArray");
of.writeOUStringFunction("getCollationOptions_", "nbOfCollationOptions", "collationOptions");
}
void LCSearchNode::generateCode (const OFileWriter &of) const
{
OUString useLocale = getAttr().getValueByName("ref");
if (!useLocale.isEmpty()) {
useLocale = useLocale.replace( '-', '_');
of.writeOUStringRefFunction("getSearchOptions_", useLocale);
return;
}
if( getNumberOfChildren() != 1 )
{
++nError;
fprintf(
stderr, "Error: LC_SEARCH: more than 1 child: %" SAL_PRIdINT32 "\n",
getNumberOfChildren());
}
sal_Int32 i;
LocaleNode* pSearchOptions = getChildAt( 0 );
sal_Int32 nSearchOptions = pSearchOptions->getNumberOfChildren();
for( i=0; i<nSearchOptions; i++ )
{
of.writeOUStringLiteralParameter("searchOption", pSearchOptions->getChildAt( i )->getValue(), sal::static_int_cast<sal_Int16>(i) );
}
of.writeAsciiString("static const sal_Int16 nbOfSearchOptions = ");
of.writeInt( sal::static_int_cast<sal_Int16>( nSearchOptions ) );
of.writeAsciiString(";\n\n");
of.writeAsciiString("static constexpr OUString searchOptions[] = {");
for( i=0; i<nSearchOptions; i++ )
{
if (i)
of.writeAsciiString( ", " );
of.writeAsciiString( "searchOption" );
of.writeInt( sal::static_int_cast<sal_Int16>(i) );
}
of.writeAsciiString(" };\n");
of.writeOUStringFunction("getSearchOptions_", "nbOfSearchOptions", "searchOptions");
}
void LCIndexNode::generateCode (const OFileWriter &of) const
{
OUString useLocale = getAttr().getValueByName("ref");
if (!useLocale.isEmpty()) {
useLocale = useLocale.replace( '-', '_');
of.writeOUStringRefFunction("getIndexAlgorithm_", useLocale);
of.writeOUStringRefFunction("getUnicodeScripts_", useLocale);
of.writeOUStringRefFunction("getFollowPageWords_", useLocale);
return;
}
sal_Int16 nbOfIndexs = 0;
sal_Int16 nbOfUnicodeScripts = 0;
sal_Int16 nbOfPageWords = 0;
for (sal_Int32 i = 0; i< getNumberOfChildren();i++) {
LocaleNode * currNode = getChildAt (i);
if( currNode->getName() == "IndexKey" )
{
OUString str;
str = currNode->getAttr().getValueByName("unoid");
of.writeOUStringLiteralParameter("IndexID", str, nbOfIndexs);
str = currNode->getAttr().getValueByName("module");
of.writeOUStringLiteralParameter("IndexModule", str, nbOfIndexs);
str = currNode->getValue();
of.writeOUStringLiteralParameter("IndexKey", str, nbOfIndexs);
str = currNode -> getAttr().getValueByName("default");
of.writeOUStringLiteralDefaultParameter("Index", str, nbOfIndexs);
str = currNode -> getAttr().getValueByName("phonetic");
of.writeOUStringLiteralDefaultParameter("Phonetic", str, nbOfIndexs);
of.writeAsciiString("\n");
nbOfIndexs++;
}
if( currNode->getName() == "UnicodeScript" )
{
of.writeOUStringLiteralParameter("unicodeScript", currNode->getValue(), nbOfUnicodeScripts );
nbOfUnicodeScripts++;
}
if( currNode->getName() == "FollowPageWord" )
{
of.writeOUStringLiteralParameter("followPageWord", currNode->getValue(), nbOfPageWords);
nbOfPageWords++;
}
}
of.writeAsciiString("static const sal_Int16 nbOfIndexs = ");
of.writeInt(nbOfIndexs);
of.writeAsciiString(";\n\n");
of.writeAsciiString("\nstatic constexpr OUString IndexArray[] = {\n");
for(sal_Int16 i = 0; i < nbOfIndexs; i++) {
of.writeAsciiString("\tIndexID");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tIndexModule");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tIndexKey");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tdefaultIndex");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tdefaultPhonetic");
of.writeInt(i);
of.writeAsciiString(",\n");
}
of.writeAsciiString("};\n\n");
of.writeAsciiString("static const sal_Int16 nbOfUnicodeScripts = ");
of.writeInt( nbOfUnicodeScripts );
of.writeAsciiString(";\n\n");
of.writeAsciiString("static constexpr OUString UnicodeScriptArray[] = {");
for( sal_Int16 i=0; i<nbOfUnicodeScripts; i++ )
{
if (i)
of.writeAsciiString( ", " );
of.writeAsciiString( "unicodeScript" );
of.writeInt( i );
}
of.writeAsciiString(" };\n\n");
of.writeAsciiString("static const sal_Int16 nbOfPageWords = ");
of.writeInt(nbOfPageWords);
of.writeAsciiString(";\n\n");
// MSVC doesn't like zero sized arrays
if (nbOfPageWords == 0)
{
// generate dummy array, reuse unicodeScript0 for dummy entry
of.writeAsciiString("//dummy array, we have zero entries\n");
of.writeAsciiString("static constexpr OUString FollowPageWordArray[] = { unicodeScript0 };\n\n");
}
else
{
of.writeAsciiString("static constexpr OUString FollowPageWordArray[] = {\n");
for(sal_Int16 i = 0; i < nbOfPageWords; i++) {
if (i)
of.writeAsciiString(",\n");
of.writeAsciiString("\tfollowPageWord");
of.writeInt(i);
}
of.writeAsciiString("\t\n};\n\n");
}
of.writeOUStringFunction("getIndexAlgorithm_", "nbOfIndexs", "IndexArray");
of.writeOUStringFunction("getUnicodeScripts_", "nbOfUnicodeScripts", "UnicodeScriptArray");
of.writeOUStringFunction("getFollowPageWords_", "nbOfPageWords", "FollowPageWordArray");
}
static void lcl_writeAbbrFullNarrNames( const OFileWriter & of, const LocaleNode* currNode,
const char* elementTag, sal_Int16 i, sal_Int16 j )
{
OUString aAbbrName = currNode->getChildAt(1)->getValue();
OUString aFullName = currNode->getChildAt(2)->getValue();
OUString aNarrName;
LocaleNode* p = (currNode->getNumberOfChildren() > 3 ? currNode->getChildAt(3) : nullptr);
if ( p && p->getName() == "DefaultNarrowName" )
aNarrName = p->getValue();
else
{
sal_uInt32 nChar = aFullName.iterateCodePoints( &o3tl::temporary(sal_Int32(0)));
aNarrName = OUString( &nChar, 1);
}
of.writeOUStringLiteralParameter( elementTag, "DefaultAbbrvName", aAbbrName, i, j);
of.writeOUStringLiteralParameter( elementTag, "DefaultFullName", aFullName, i, j);
of.writeOUStringLiteralParameter( elementTag, "DefaultNarrowName", aNarrName, i, j);
}
static void lcl_writeTabTagString( const OFileWriter & of, const char* pTag, const char* pStr )
{
of.writeAsciiString("\t");
of.writeAsciiString( pTag);
of.writeAsciiString( pStr);
}
static void lcl_writeTabTagStringNums( const OFileWriter & of,
const char* pTag, const char* pStr, sal_Int16 i, sal_Int16 j )
{
lcl_writeTabTagString( of, pTag, pStr);
of.writeInt(i); of.writeInt(j); of.writeAsciiString(",\n");
}
static void lcl_writeAbbrFullNarrArrays( const OFileWriter & of, sal_Int16 nCount,
const char* elementTag, sal_Int16 i, bool bNarrow )
{
if (nCount == 0)
{
lcl_writeTabTagString( of, elementTag, "Ref");
of.writeInt(i); of.writeAsciiString(",\n");
lcl_writeTabTagString( of, elementTag, "RefName");
of.writeInt(i); of.writeAsciiString(",\n");
}
else
{
for (sal_Int16 j = 0; j < nCount; j++)
{
lcl_writeTabTagStringNums( of, elementTag, "ID", i, j);
lcl_writeTabTagStringNums( of, elementTag, "DefaultAbbrvName", i, j);
lcl_writeTabTagStringNums( of, elementTag, "DefaultFullName", i, j);
if (bNarrow)
lcl_writeTabTagStringNums( of, elementTag, "DefaultNarrowName", i, j);
}
}
}
bool LCCalendarNode::expectedCalendarElement( std::u16string_view rName,
const LocaleNode* pNode, sal_Int16 nChild, std::u16string_view rCalendarID ) const
{
bool bFound = true;
if (nChild >= 0)
{
if (nChild >= pNode->getNumberOfChildren())
bFound = false;
else
pNode = pNode->getChildAt(nChild);
}
if (bFound && (!pNode || pNode->getName() != rName))
bFound = false;
if (!bFound)
incErrorStrStr( "Error: <%s> element expected in calendar '%s'\n", rName, rCalendarID);
return bFound;
}
void LCCalendarNode::generateCode (const OFileWriter &of) const
{
OUString useLocale = getAttr().getValueByName("ref");
if (!useLocale.isEmpty()) {
useLocale = useLocale.replace( '-', '_');
of.writeOUStringRefFunction("getAllCalendars_", useLocale);
return;
}
sal_Int16 nbOfCalendars = sal::static_int_cast<sal_Int16>( getNumberOfChildren() );
OUString str;
std::unique_ptr<sal_Int16[]> nbOfDays( new sal_Int16[nbOfCalendars] );
std::unique_ptr<sal_Int16[]> nbOfMonths( new sal_Int16[nbOfCalendars] );
std::unique_ptr<sal_Int16[]> nbOfGenitiveMonths( new sal_Int16[nbOfCalendars] );
std::unique_ptr<sal_Int16[]> nbOfPartitiveMonths( new sal_Int16[nbOfCalendars] );
std::unique_ptr<sal_Int16[]> nbOfEras( new sal_Int16[nbOfCalendars] );
// Known allowed calendar identifiers (unoid) and whether used or not.
// Of course there must be an implementation for new to be added
// identifiers.. see data/locale.dtd
std::map< OUString, bool > aCalendars;
aCalendars[u"buddhist"_ustr] = false;
aCalendars[u"gengou"_ustr] = false;
aCalendars[u"gregorian"_ustr] = false;
aCalendars[u"hanja"_ustr] = false;
aCalendars[u"hanja_yoil"_ustr] = false;
aCalendars[u"hijri"_ustr] = false;
aCalendars[u"jewish"_ustr] = false;
aCalendars[u"ROC"_ustr] = false;
// Not in ODF:
aCalendars[u"dangi"_ustr] = false;
aCalendars[u"persian"_ustr] = false;
sal_Int16 j;
sal_Int16 i;
bool bHasGregorian = false;
for ( i = 0; i < nbOfCalendars; i++) {
LocaleNode * calNode = getChildAt (i);
OUString calendarID = calNode -> getAttr().getValueByName("unoid");
of.writeOUStringLiteralParameter( "calendarID", calendarID, i);
bool bGregorian = calendarID == "gregorian";
if (!bHasGregorian)
bHasGregorian = bGregorian;
auto calIt = aCalendars.find(calendarID);
if (calIt == aCalendars.end())
incErrorStr( "Error: unknown Calendar unoid: %s\n", calendarID);
else if (calIt->second)
incErrorStr( "Error: duplicate Calendar unoid: %s\n", calendarID);
else
calIt->second = true;
str = calNode -> getAttr().getValueByName("default");
of.writeOUStringLiteralDefaultParameter("Calendar", str, i);
sal_Int16 nChild = 0;
// Generate Days of Week
const char *elementTag;
LocaleNode * daysNode = nullptr;
OUString ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
ref_name = ref_name.replace( '-', '_');
if (!ref_name.isEmpty() && i > 0) {
for (j = 0; j < i; j++) {
str = getChildAt(j)->getAttr().getValueByName("unoid");
if (str == ref_name)
daysNode = getChildAt(j)->getChildAt(0);
}
}
if (!ref_name.isEmpty() && daysNode == nullptr) {
of.writeOUStringLiteralParameter("dayRef", u"ref", i);
of.writeOUStringLiteralParameter("dayRefName", ref_name, i);
nbOfDays[i] = 0;
} else {
if (daysNode == nullptr)
daysNode = calNode -> getChildAt(nChild);
nbOfDays[i] = sal::static_int_cast<sal_Int16>( daysNode->getNumberOfChildren() );
if (bGregorian && nbOfDays[i] != 7)
incErrorInt( "Error: A Gregorian calendar must have 7 days per week, this one has %d\n", nbOfDays[i]);
elementTag = "day";
for (j = 0; j < nbOfDays[i]; j++) {
LocaleNode *currNode = daysNode -> getChildAt(j);
OUString dayID( currNode->getChildAt(0)->getValue());
of.writeOUStringLiteralParameter("dayID", dayID, i, j);
if ( j == 0 && bGregorian && dayID != "sun" )
incError( "First day of a week of a Gregorian calendar must be <DayID>sun</DayID>");
lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
}
}
++nChild;
// Generate Months of Year
LocaleNode * monthsNode = nullptr;
ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
ref_name = ref_name.replace( '-', '_');
if (!ref_name.isEmpty() && i > 0) {
for (j = 0; j < i; j++) {
str = getChildAt(j)->getAttr().getValueByName("unoid");
if (str == ref_name)
monthsNode = getChildAt(j)->getChildAt(1);
}
}
if (!ref_name.isEmpty() && monthsNode == nullptr) {
of.writeOUStringLiteralParameter("monthRef", u"ref", i);
of.writeOUStringLiteralParameter("monthRefName", ref_name, i);
nbOfMonths[i] = 0;
} else {
if (monthsNode == nullptr)
monthsNode = calNode -> getChildAt(nChild);
nbOfMonths[i] = sal::static_int_cast<sal_Int16>( monthsNode->getNumberOfChildren() );
if (bGregorian && nbOfMonths[i] != 12)
incErrorInt( "Error: A Gregorian calendar must have 12 months, this one has %d\n", nbOfMonths[i]);
elementTag = "month";
for (j = 0; j < nbOfMonths[i]; j++) {
LocaleNode *currNode = monthsNode -> getChildAt(j);
OUString monthID( currNode->getChildAt(0)->getValue());
of.writeOUStringLiteralParameter("monthID", monthID, i, j);
if ( j == 0 && bGregorian && monthID != "jan" )
incError( "First month of a year of a Gregorian calendar must be <MonthID>jan</MonthID>");
lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
}
}
++nChild;
// Generate genitive Months of Year
// Optional, if not present fall back to month nouns.
if ( calNode->getChildAt(nChild)->getName() != "GenitiveMonths" )
--nChild;
LocaleNode * genitiveMonthsNode = nullptr;
ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
ref_name = ref_name.replace( '-', '_');
if (!ref_name.isEmpty() && i > 0) {
for (j = 0; j < i; j++) {
str = getChildAt(j)->getAttr().getValueByName("unoid");
if (str == ref_name)
genitiveMonthsNode = getChildAt(j)->getChildAt(1);
}
}
if (!ref_name.isEmpty() && genitiveMonthsNode == nullptr) {
of.writeOUStringLiteralParameter("genitiveMonthRef", u"ref", i);
of.writeOUStringLiteralParameter("genitiveMonthRefName", ref_name, i);
nbOfGenitiveMonths[i] = 0;
} else {
if (genitiveMonthsNode == nullptr)
genitiveMonthsNode = calNode -> getChildAt(nChild);
nbOfGenitiveMonths[i] = sal::static_int_cast<sal_Int16>( genitiveMonthsNode->getNumberOfChildren() );
if (bGregorian && nbOfGenitiveMonths[i] != 12)
incErrorInt( "Error: A Gregorian calendar must have 12 genitive months, this one has %d\n", nbOfGenitiveMonths[i]);
elementTag = "genitiveMonth";
for (j = 0; j < nbOfGenitiveMonths[i]; j++) {
LocaleNode *currNode = genitiveMonthsNode -> getChildAt(j);
OUString genitiveMonthID( currNode->getChildAt(0)->getValue());
of.writeOUStringLiteralParameter("genitiveMonthID", genitiveMonthID, i, j);
if ( j == 0 && bGregorian && genitiveMonthID != "jan" )
incError( "First genitive month of a year of a Gregorian calendar must be <MonthID>jan</MonthID>");
lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
}
}
++nChild;
// Generate partitive Months of Year
// Optional, if not present fall back to genitive months, or nominative
// months (nouns) if that isn't present either.
if ( calNode->getChildAt(nChild)->getName() != "PartitiveMonths" )
--nChild;
LocaleNode * partitiveMonthsNode = nullptr;
ref_name = calNode->getChildAt(nChild)->getAttr().getValueByName("ref");
ref_name = ref_name.replace( '-', '_');
if (!ref_name.isEmpty() && i > 0) {
for (j = 0; j < i; j++) {
str = getChildAt(j)->getAttr().getValueByName("unoid");
if (str == ref_name)
partitiveMonthsNode = getChildAt(j)->getChildAt(1);
}
}
if (!ref_name.isEmpty() && partitiveMonthsNode == nullptr) {
of.writeOUStringLiteralParameter("partitiveMonthRef", u"ref", i);
of.writeOUStringLiteralParameter("partitiveMonthRefName", ref_name, i);
nbOfPartitiveMonths[i] = 0;
} else {
if (partitiveMonthsNode == nullptr)
partitiveMonthsNode = calNode -> getChildAt(nChild);
nbOfPartitiveMonths[i] = sal::static_int_cast<sal_Int16>( partitiveMonthsNode->getNumberOfChildren() );
if (bGregorian && nbOfPartitiveMonths[i] != 12)
incErrorInt( "Error: A Gregorian calendar must have 12 partitive months, this one has %d\n", nbOfPartitiveMonths[i]);
elementTag = "partitiveMonth";
for (j = 0; j < nbOfPartitiveMonths[i]; j++) {
LocaleNode *currNode = partitiveMonthsNode -> getChildAt(j);
OUString partitiveMonthID( currNode->getChildAt(0)->getValue());
of.writeOUStringLiteralParameter("partitiveMonthID", partitiveMonthID, i, j);
if ( j == 0 && bGregorian && partitiveMonthID != "jan" )
incError( "First partitive month of a year of a Gregorian calendar must be <MonthID>jan</MonthID>");
lcl_writeAbbrFullNarrNames( of, currNode, elementTag, i, j);
}
}
++nChild;
// Generate Era name
LocaleNode * erasNode = nullptr;
ref_name = calNode -> getChildAt(nChild) ->getAttr().getValueByName("ref");
ref_name = ref_name.replace( '-', '_');
if (!ref_name.isEmpty() && i > 0) {
for (j = 0; j < i; j++) {
str = getChildAt(j)->getAttr().getValueByName("unoid");
if (str == ref_name)
erasNode = getChildAt(j)->getChildAt(2);
}
}
if (!ref_name.isEmpty() && erasNode == nullptr) {
of.writeOUStringLiteralParameter("eraRef", u"ref", i);
of.writeOUStringLiteralParameter("eraRefName", ref_name, i);
nbOfEras[i] = 0;
} else {
if (erasNode == nullptr)
erasNode = calNode -> getChildAt(nChild);
if (!expectedCalendarElement(u"Eras", erasNode, -1, calendarID))
{
--nChild;
}
else
{
nbOfEras[i] = sal::static_int_cast<sal_Int16>( erasNode->getNumberOfChildren() );
if (bGregorian && nbOfEras[i] != 2)
incErrorInt( "Error: A Gregorian calendar must have 2 eras, this one has %d\n", nbOfEras[i]);
elementTag = "era";
for (j = 0; j < nbOfEras[i]; j++) {
LocaleNode *currNode = erasNode -> getChildAt(j);
if (!expectedCalendarElement(u"Era", currNode, -1, calendarID))
{
continue; // for
}
OUString eraID( currNode->getChildAt(0)->getValue());
of.writeOUStringLiteralParameter("eraID", eraID, i, j);
if ( j == 0 && bGregorian && eraID != "bc" )
incError( "First era of a Gregorian calendar must be <EraID>bc</EraID>");
if ( j == 1 && bGregorian && eraID != "ad" )
incError( "Second era of a Gregorian calendar must be <EraID>ad</EraID>");
of.writeAsciiString("\n");
of.writeOUStringLiteralParameter(elementTag, "DefaultAbbrvName",currNode->getChildAt(1)->getValue() ,i, j);
of.writeOUStringLiteralParameter(elementTag, "DefaultFullName",currNode->getChildAt(2)->getValue() , i, j);
}
}
}
++nChild;
if (expectedCalendarElement(u"StartDayOfWeek", calNode, nChild, calendarID))
{
str = calNode->getChildAt(nChild)->getChildAt(0)->getValue();
if (nbOfDays[i])
{
for (j = 0; j < nbOfDays[i]; j++)
{
LocaleNode *currNode = daysNode->getChildAt(j);
OUString dayID( currNode->getChildAt(0)->getValue());
if (str == dayID)
break; // for
}
if (j >= nbOfDays[i])
incErrorStr( "Error: <StartDayOfWeek> <DayID> must be one of the <DaysOfWeek>, but is: %s\n", str);
}
of.writeOUStringLiteralParameter("startDayOfWeek", str, i);
++nChild;
}
if (expectedCalendarElement(u"MinimalDaysInFirstWeek", calNode, nChild, calendarID))
{
str = calNode ->getChildAt(nChild)-> getValue();
sal_Int16 nDays = sal::static_int_cast<sal_Int16>( str.toInt32() );
if (nDays < 1 || (0 < nbOfDays[i] && nbOfDays[i] < nDays))
incErrorInt( "Error: Bad value of MinimalDaysInFirstWeek: %d, must be 1 <= value <= days_in_week\n",
nDays);
of.writeOUStringLiteralIntParameter("minimalDaysInFirstWeek", i, nDays);
}
}
if (!bHasGregorian)
fprintf( stderr, "Warning: %s\n", "No Gregorian calendar defined, are you sure?");
of.writeAsciiString("static const sal_Int16 calendarsCount = ");
of.writeInt(nbOfCalendars);
of.writeAsciiString(";\n\n");
of.writeAsciiString("static constexpr OUStringLiteral nbOfDays = u\"");
for(i = 0; i < nbOfCalendars; i++) {
of.writeAsciiString("\\x");
of.writeHexInt(nbOfDays[i]);
}
of.writeAsciiString("\";\n");
of.writeAsciiString("static constexpr OUStringLiteral nbOfMonths = u\"");
for(i = 0; i < nbOfCalendars; i++) {
of.writeAsciiString("\\x");
of.writeHexInt(nbOfMonths[i]);
}
of.writeAsciiString("\";\n");
of.writeAsciiString("static constexpr OUStringLiteral nbOfGenitiveMonths = u\"");
for(i = 0; i < nbOfCalendars; i++) {
of.writeAsciiString("\\x");
of.writeHexInt(nbOfGenitiveMonths[i]);
}
of.writeAsciiString("\";\n");
of.writeAsciiString("static constexpr OUStringLiteral nbOfPartitiveMonths = u\"");
for(i = 0; i < nbOfCalendars; i++) {
of.writeAsciiString("\\x");
of.writeHexInt(nbOfPartitiveMonths[i]);
}
of.writeAsciiString("\";\n");
of.writeAsciiString("static constexpr OUStringLiteral nbOfEras = u\"");
for(i = 0; i < nbOfCalendars; i++) {
of.writeAsciiString("\\x");
of.writeHexInt(nbOfEras[i]);
}
of.writeAsciiString("\";\n");
of.writeAsciiString("static constexpr OUString calendars[] = {\n");
of.writeAsciiString("\tnbOfDays,\n");
of.writeAsciiString("\tnbOfMonths,\n");
of.writeAsciiString("\tnbOfGenitiveMonths,\n");
of.writeAsciiString("\tnbOfPartitiveMonths,\n");
of.writeAsciiString("\tnbOfEras,\n");
for(i = 0; i < nbOfCalendars; i++) {
of.writeAsciiString("\tcalendarID");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tdefaultCalendar");
of.writeInt(i);
of.writeAsciiString(",\n");
lcl_writeAbbrFullNarrArrays( of, nbOfDays[i], "day", i, true);
lcl_writeAbbrFullNarrArrays( of, nbOfMonths[i], "month", i, true);
lcl_writeAbbrFullNarrArrays( of, nbOfGenitiveMonths[i], "genitiveMonth", i, true);
lcl_writeAbbrFullNarrArrays( of, nbOfPartitiveMonths[i], "partitiveMonth", i, true);
lcl_writeAbbrFullNarrArrays( of, nbOfEras[i], "era", i, false /*noNarrow*/);
of.writeAsciiString("\tstartDayOfWeek");of.writeInt(i); of.writeAsciiString(",\n");
of.writeAsciiString("\tminimalDaysInFirstWeek");of.writeInt(i); of.writeAsciiString(",\n");
}
of.writeAsciiString("};\n\n");
of.writeOUStringFunction("getAllCalendars_", "calendarsCount", "calendars");
}
static bool isIso4217( const OUString& rStr )
{
const sal_Unicode* p = rStr.getStr();
return rStr.getLength() == 3
&& 'A' <= p[0] && p[0] <= 'Z'
&& 'A' <= p[1] && p[1] <= 'Z'
&& 'A' <= p[2] && p[2] <= 'Z'
;
}
void LCCurrencyNode::generateCode (const OFileWriter &of) const
{
OUString useLocale = getAttr().getValueByName("ref");
if (!useLocale.isEmpty()) {
useLocale = useLocale.replace( '-', '_');
of.writeOUStringRefFunction("getAllCurrencies_", useLocale);
return;
}
sal_Int16 nbOfCurrencies = 0;
OUString str;
bool bTheDefault= false;
bool bTheCompatible = false;
for ( sal_Int32 i = 0; i < getNumberOfChildren(); i++,nbOfCurrencies++) {
LocaleNode * currencyNode = getChildAt (i);
str = currencyNode->getAttr().getValueByName("default");
bool bDefault = of.writeOUStringLiteralDefaultParameter("Currency", str, nbOfCurrencies);
str = currencyNode->getAttr().getValueByName("usedInCompatibleFormatCodes");
bool bCompatible = of.writeOUStringLiteralDefaultParameter("CurrencyUsedInCompatibleFormatCodes", str, nbOfCurrencies);
str = currencyNode->getAttr().getValueByName("legacyOnly");
bool bLegacy = of.writeOUStringLiteralDefaultParameter("CurrencyLegacyOnly", str, nbOfCurrencies);
if (bLegacy && (bDefault || bCompatible))
incError( "Currency: if legacyOnly==true, both 'default' and 'usedInCompatibleFormatCodes' must be false.");
if (bDefault)
{
if (bTheDefault)
incError( "Currency: more than one default currency.");
bTheDefault = true;
}
if (bCompatible)
{
if (bTheCompatible)
incError( "Currency: more than one currency flagged as usedInCompatibleFormatCodes.");
bTheCompatible = true;
}
str = currencyNode -> findNode ("CurrencyID") -> getValue();
of.writeOUStringLiteralParameter("currencyID", str, nbOfCurrencies);
// CurrencyID MUST be ISO 4217.
if (!bLegacy && !isIso4217(str))
incError( "CurrencyID is not ISO 4217");
str = currencyNode -> findNode ("CurrencySymbol") -> getValue();
of.writeOUStringLiteralParameter("currencySymbol", str, nbOfCurrencies);
// Check if this currency really is the one used in number format
// codes. In case of ref=... mechanisms it may be that TheCurrency
// couldn't had been determined from the current locale (i.e. is
// empty), silently assume the referred locale has things right.
if (bCompatible && !sTheCompatibleCurrency.isEmpty() && sTheCompatibleCurrency != str)
incErrorStrStr( "Error: CurrencySymbol \"%s\" flagged as usedInCompatibleFormatCodes doesn't match \"%s\" determined from format codes.\n", str, sTheCompatibleCurrency);
str = currencyNode -> findNode ("BankSymbol") -> getValue();
of.writeOUStringLiteralParameter("bankSymbol", str, nbOfCurrencies);
// BankSymbol currently must be ISO 4217. May change later if
// application always uses CurrencyID instead of BankSymbol.
if (!bLegacy && !isIso4217(str))
incError( "BankSymbol is not ISO 4217");
str = currencyNode -> findNode ("CurrencyName") -> getValue();
of.writeOUStringLiteralParameter("currencyName", str, nbOfCurrencies);
str = currencyNode -> findNode ("DecimalPlaces") -> getValue();
sal_Int16 nDecimalPlaces = static_cast<sal_Int16>(str.toInt32());
of.writeOUStringLiteralIntParameter("currencyDecimalPlaces", nbOfCurrencies, nDecimalPlaces);
of.writeAsciiString("\n");
};
if (!bTheDefault)
incError( "Currency: no default currency.");
if (!bTheCompatible)
incError( "Currency: no currency flagged as usedInCompatibleFormatCodes.");
of.writeAsciiString("static const sal_Int16 currencyCount = ");
of.writeInt(nbOfCurrencies);
of.writeAsciiString(";\n\n");
of.writeAsciiString("static constexpr OUString currencies[] = {\n");
for(sal_Int16 i = 0; i < nbOfCurrencies; i++) {
of.writeAsciiString("\tcurrencyID");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tcurrencySymbol");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tbankSymbol");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tcurrencyName");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tdefaultCurrency");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tdefaultCurrencyUsedInCompatibleFormatCodes");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tcurrencyDecimalPlaces");
of.writeInt(i);
of.writeAsciiString(",\n");
of.writeAsciiString("\tdefaultCurrencyLegacyOnly");
of.writeInt(i);
of.writeAsciiString(",\n");
}
of.writeAsciiString("};\n\n");
of.writeOUStringFunction("getAllCurrencies_", "currencyCount", "currencies");
}
void LCTransliterationNode::generateCode (const OFileWriter &of) const
{
OUString useLocale = getAttr().getValueByName("ref");
if (!useLocale.isEmpty()) {
useLocale = useLocale.replace( '-', '_');
of.writeOUStringRefFunction("getTransliterations_", useLocale);
return;
}
sal_Int16 nbOfModules = 0;
OUString str;
for ( sal_Int32 i = 0; i < getNumberOfChildren(); i++,nbOfModules++) {
LocaleNode * transNode = getChildAt (i);
str = transNode->getAttr().getValueByIndex(0);
of.writeOUStringLiteralParameter("Transliteration", str, nbOfModules);
}
of.writeAsciiString("static const sal_Int16 nbOfTransliterations = ");
of.writeInt(nbOfModules);
of.writeAsciiString(";\n\n");
of.writeAsciiString("\nstatic constexpr OUString LCTransliterationsArray[] = {\n");
for( sal_Int16 i = 0; i < nbOfModules; i++) {
of.writeAsciiString("\tTransliteration");
of.writeInt(i);
of.writeAsciiString(",\n");
}
of.writeAsciiString("};\n\n");
of.writeOUStringFunction("getTransliterations_", "nbOfTransliterations", "LCTransliterationsArray");
}
namespace {
struct NameValuePair {
const char *name;
const char *value;
};
}
const NameValuePair ReserveWord[] = {
{ "trueWord", "true" },
{ "falseWord", "false" },
{ "quarter1Word", "1st quarter" },
{ "quarter2Word", "2nd quarter" },
{ "quarter3Word", "3rd quarter" },
{ "quarter4Word", "4th quarter" },
{ "aboveWord", "above" },
{ "belowWord", "below" },
{ "quarter1Abbreviation", "Q1" },
{ "quarter2Abbreviation", "Q2" },
{ "quarter3Abbreviation", "Q3" },
{ "quarter4Abbreviation", "Q4" }
};
void LCMiscNode::generateCode (const OFileWriter &of) const
{
OUString useLocale = getAttr().getValueByName("ref");
if (!useLocale.isEmpty()) {
useLocale = useLocale.replace( '-', '_');
of.writeOUStringRefFunction("getForbiddenCharacters_", useLocale);
of.writeOUStringRefFunction("getBreakIteratorRules_", useLocale);
of.writeOUStringRefFunction("getReservedWords_", useLocale);
return;
}
const LocaleNode * reserveNode = findNode("ReservedWords");
if (!reserveNode)
incError( "No ReservedWords element."); // should not happen if validated...
const LocaleNode * forbidNode = findNode("ForbiddenCharacters");
const LocaleNode * breakNode = findNode("BreakIteratorRules");
bool bEnglishLocale = (strncmp( of.getLocale(), "en_", 3) == 0);
sal_Int16 nbOfWords = 0;
OUString str;
sal_Int16 i;
for ( i = 0; i < sal_Int16(SAL_N_ELEMENTS(ReserveWord)); i++,nbOfWords++) {
const LocaleNode * curNode = (reserveNode ? reserveNode->findNode(
ReserveWord[i].name) : nullptr);
if (!curNode)
fprintf( stderr,
"Warning: No %s in ReservedWords, using en_US default: \"%s\".\n",
ReserveWord[i].name, ReserveWord[i].value);
str = curNode ? curNode -> getValue() : OUString::createFromAscii(ReserveWord[i].value);
if (str.isEmpty())
{
++nError;
fprintf( stderr, "Error: No content for ReservedWords %s.\n", ReserveWord[i].name);
}
of.writeOUStringLiteralParameter("ReservedWord", str, nbOfWords);
// "true", ..., "below" trigger untranslated warning.
if (!bEnglishLocale && curNode && i <= 7 &&
str.equalsIgnoreAsciiCaseAscii( ReserveWord[i].value))
{
fprintf( stderr,
"Warning: ReservedWord %s seems to be untranslated \"%s\".\n",
ReserveWord[i].name, ReserveWord[i].value);
}
}
of.writeAsciiString("static const sal_Int16 nbOfReservedWords = ");
of.writeInt(nbOfWords);
of.writeAsciiString(";\n\n");
of.writeAsciiString("\nstatic constexpr OUString LCReservedWordsArray[] = {\n");
for( i = 0; i < nbOfWords; i++) {
of.writeAsciiString("\tReservedWord");
of.writeInt(i);
of.writeAsciiString(",\n");
}
of.writeAsciiString("};\n\n");
of.writeOUStringFunction("getReservedWords_", "nbOfReservedWords", "LCReservedWordsArray");
if (forbidNode) {
of.writeOUStringLiteralParameter( "forbiddenBegin", forbidNode -> getChildAt(0)->getValue());
of.writeOUStringLiteralParameter( "forbiddenEnd", forbidNode -> getChildAt(1)->getValue());
of.writeOUStringLiteralParameter( "hangingChars", forbidNode -> getChildAt(2)->getValue());
} else {
of.writeOUStringLiteralParameter( "forbiddenBegin", std::u16string_view());
of.writeOUStringLiteralParameter( "forbiddenEnd", std::u16string_view());
of.writeOUStringLiteralParameter( "hangingChars", std::u16string_view());
}
of.writeAsciiString("\nstatic constexpr OUString LCForbiddenCharactersArray[] = {\n");
of.writeAsciiString("\tforbiddenBegin,\n");
of.writeAsciiString("\tforbiddenEnd,\n");
of.writeAsciiString("\thangingChars\n");
of.writeAsciiString("};\n\n");
of.writeOUStringFunction("getForbiddenCharacters_", "3", "LCForbiddenCharactersArray");
if (breakNode) {
of.writeOUStringLiteralParameter( "EditMode", breakNode -> getChildAt(0)->getValue());
of.writeOUStringLiteralParameter( "DictionaryMode", breakNode -> getChildAt(1)->getValue());
of.writeOUStringLiteralParameter( "WordCountMode", breakNode -> getChildAt(2)->getValue());
of.writeOUStringLiteralParameter( "CharacterMode", breakNode -> getChildAt(3)->getValue());
of.writeOUStringLiteralParameter( "LineMode", breakNode -> getChildAt(4)->getValue());
} else {
of.writeOUStringLiteralParameter( "EditMode", std::u16string_view());
of.writeOUStringLiteralParameter( "DictionaryMode", std::u16string_view());
of.writeOUStringLiteralParameter( "WordCountMode", std::u16string_view());
of.writeOUStringLiteralParameter( "CharacterMode", std::u16string_view());
of.writeOUStringLiteralParameter( "LineMode", std::u16string_view());
}
of.writeAsciiString("\nstatic constexpr OUString LCBreakIteratorRulesArray[] = {\n");
of.writeAsciiString("\tEditMode,\n");
of.writeAsciiString("\tDictionaryMode,\n");
of.writeAsciiString("\tWordCountMode,\n");
of.writeAsciiString("\tCharacterMode,\n");
of.writeAsciiString("\tLineMode\n");
of.writeAsciiString("};\n\n");
of.writeOUStringFunction("getBreakIteratorRules_", "5", "LCBreakIteratorRulesArray");
}
void LCNumberingLevelNode::generateCode (const OFileWriter &of) const
{
of.writeAsciiString("// ---> ContinuousNumbering\n");
OUString useLocale = getAttr().getValueByName("ref");
if (!useLocale.isEmpty()) {
useLocale = useLocale.replace( '-', '_');
of.writeOUStringRefFunction2("getContinuousNumberingLevels_", useLocale);
return;
}
// hard code number of attributes per style.
const int nAttributes = 5;
const char* attr[ nAttributes ] = { "Prefix", "NumType", "Suffix", "Transliteration", "NatNum" };
// record each attribute of each style in a static C++ variable.
// determine number of styles on the fly.
sal_Int32 nStyles = getNumberOfChildren();
sal_Int32 i;
for( i = 0; i < nStyles; ++i )
{
const Attr &q = getChildAt( i )->getAttr();
for( sal_Int32 j=0; j<nAttributes; ++j )
{
const char* name = attr[j];
OUString value = q.getValueByName( name );
of.writeOUStringLiteralParameter("continuous", name, value, sal::static_int_cast<sal_Int16>(i) );
}
}
// record number of styles and attributes.
of.writeAsciiString("static const sal_Int16 continuousNbOfStyles = ");
of.writeInt( sal::static_int_cast<sal_Int16>( nStyles ) );
of.writeAsciiString(";\n\n");
of.writeAsciiString("static const sal_Int16 continuousNbOfAttributesPerStyle = ");
of.writeInt( nAttributes );
of.writeAsciiString(";\n\n");
// generate code. (intermediate arrays)
for( i=0; i<nStyles; i++ )
{
of.writeAsciiString("\nstatic constexpr OUString continuousStyle" );
of.writeInt( sal::static_int_cast<sal_Int16>(i) );
of.writeAsciiString("[] = {\n");
for( sal_Int32 j=0; j<nAttributes; j++)
{
of.writeAsciiString("\t");
of.writeAsciiString( "continuous" );
of.writeAsciiString( attr[j] );
of.writeInt(sal::static_int_cast<sal_Int16>(i));
of.writeAsciiString(",\n");
}
of.writeAsciiString("\t\n};\n\n");
}
// generate code. (top-level array)
of.writeAsciiString("\n");
of.writeAsciiString("static const OUString* LCContinuousNumberingLevelsArray[] = {\n" );
for( i=0; i<nStyles; i++ )
{
of.writeAsciiString( "\t" );
of.writeAsciiString( "continuousStyle" );
of.writeInt( sal::static_int_cast<sal_Int16>(i) );
of.writeAsciiString( ",\n");
}
of.writeAsciiString("\t0\n};\n\n");
of.writeOUStringFunction2("getContinuousNumberingLevels_", "continuousNbOfStyles",
"continuousNbOfAttributesPerStyle", "LCContinuousNumberingLevelsArray");
}
void LCOutlineNumberingLevelNode::generateCode (const OFileWriter &of) const
{
of.writeAsciiString("// ---> OutlineNumbering\n");
OUString useLocale = getAttr().getValueByName("ref");
if (!useLocale.isEmpty()) {
useLocale = useLocale.replace( '-', '_');
of.writeOUStringRefFunction3("getOutlineNumberingLevels_", useLocale);
return;
}
// hardcode number of attributes per level
const int nAttributes = 12;
const char* attr[ nAttributes ] =
{
"Prefix",
"NumType",
"Suffix",
"BulletChar",
"BulletFontName",
"ParentNumbering",
"LeftMargin",
"SymbolTextDistance",
"FirstLineOffset",
"Adjust",
"Transliteration",
"NatNum",
};
// record each attribute of each level of each style in a static C++ variable.
// determine number of styles and number of levels per style on the fly.
sal_Int32 nStyles = getNumberOfChildren();
std::vector<sal_Int32> nLevels; // may be different for each style?
for( sal_Int32 i = 0; i < nStyles; i++ )
{
LocaleNode* p = getChildAt( i );
nLevels.push_back( p->getNumberOfChildren() );
for( sal_Int32 j=0; j<nLevels.back(); j++ )
{
const Attr& q = p->getChildAt( j )->getAttr();
for( sal_Int32 k=0; k<nAttributes; ++k )
{
const char* name = attr[k];
OUString value = q.getValueByName( name );
of.writeOUStringLiteralParameter("outline", name, value,
sal::static_int_cast<sal_Int16>(i),
sal::static_int_cast<sal_Int16>(j) );
}
}
}
// verify that each style has the same number of levels.
for( size_t i=0; i<nLevels.size(); i++ )
{
if( nLevels[0] != nLevels[i] )
{
incError( "Numbering levels don't match.");
}
}
// record number of attributes, levels, and styles.
of.writeAsciiString("static const sal_Int16 outlineNbOfStyles = ");
of.writeInt( sal::static_int_cast<sal_Int16>( nStyles ) );
of.writeAsciiString(";\n\n");
of.writeAsciiString("static const sal_Int16 outlineNbOfLevelsPerStyle = ");
of.writeInt( sal::static_int_cast<sal_Int16>( nLevels.back() ) );
of.writeAsciiString(";\n\n");
of.writeAsciiString("static const sal_Int16 outlineNbOfAttributesPerLevel = ");
of.writeInt( nAttributes );
of.writeAsciiString(";\n\n");
// too complicated for now...
// of.writeAsciiString("static const sal_Int16 nbOfOutlineNumberingLevels[] = { ");
// for( sal_Int32 j=0; j<nStyles; j++ )
// {
// of.writeInt( nLevels[j] );
// of.writeAsciiString(", ");
// }
// of.writeAsciiString("};\n\n");
for( sal_Int32 i=0; i<nStyles; i++ )
{
for( sal_Int32 j=0; j<nLevels.back(); j++ )
{
of.writeAsciiString("static constexpr OUString outline");
of.writeAsciiString("Style");
of.writeInt( sal::static_int_cast<sal_Int16>(i) );
of.writeAsciiString("Level");
of.writeInt( sal::static_int_cast<sal_Int16>(j) );
of.writeAsciiString("[] = { ");
for( sal_Int32 k=0; k<nAttributes; k++ )
{
of.writeAsciiString( "outline" );
of.writeAsciiString( attr[k] );
of.writeInt( sal::static_int_cast<sal_Int16>(i) );
of.writeInt( sal::static_int_cast<sal_Int16>(j) );
of.writeAsciiString(", ");
}
of.writeAsciiString(" };\n");
}
}
of.writeAsciiString("\n");
for( sal_Int32 i=0; i<nStyles; i++ )
{
of.writeAsciiString("static constexpr OUString const * outlineStyle" );
of.writeInt( sal::static_int_cast<sal_Int16>(i) );
of.writeAsciiString("[] = { ");
for( sal_Int32 j=0; j<nLevels.back(); j++ )
{
of.writeAsciiString("outlineStyle");
of.writeInt( sal::static_int_cast<sal_Int16>(i) );
of.writeAsciiString("Level");
of.writeInt( sal::static_int_cast<sal_Int16>(j) );
of.writeAsciiString(", ");
}
of.writeAsciiString("NULL };\n");
}
of.writeAsciiString("\n");
of.writeAsciiString("static constexpr OUString const * const * LCOutlineNumberingLevelsArray[] = {\n" );
for( sal_Int32 i=0; i<nStyles; i++ )
{
of.writeAsciiString( "\t" );
of.writeAsciiString( "outlineStyle" );
of.writeInt( sal::static_int_cast<sal_Int16>(i) );
of.writeAsciiString(",\n");
}
of.writeAsciiString("\tNULL\n};\n\n");
of.writeOUStringFunction3("getOutlineNumberingLevels_", "outlineNbOfStyles", "outlineNbOfLevelsPerStyle",
"outlineNbOfAttributesPerLevel", "LCOutlineNumberingLevelsArray");
}
Attr::Attr (const Reference< XAttributeList > & attr) {
sal_Int16 len = attr->getLength();
name.realloc (len);
auto pName = name.getArray();
value.realloc (len);
auto pValue = value.getArray();
for (sal_Int16 i =0; i< len;i++) {
pName[i] = attr->getNameByIndex(i);
pValue[i] = attr -> getValueByIndex(i);
}
}
OUString Attr::getValueByName (const char *str) const {
auto pName = std::find_if(std::cbegin(name), std::cend(name),
[&str](const OUString& rName) { return rName.equalsAscii(str); });
if (pName != std::cend(name))
{
auto i = static_cast<sal_Int32>(std::distance(std::cbegin(name), pName));
return value[i];
}
return OUString();
}
const OUString& Attr::getValueByIndex (sal_Int32 idx) const
{
return value[idx];
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'writeOUStringLiteralParameterCheckLen' is required to be utilized.
↑ V530 The return value of function 'writeOUStringLiteralParameterCheckLen' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V1037 Two or more case-branches perform the same actions. Check lines: 1114, 1124, 1134