Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/i18npool/source/localedata/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 99 kB image not shown  

Quelle  LocaleNode.cxx   Sprache: C

 
/* -*- 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.
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.18 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.