/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include "LocaleNode.hxx" #include #include #include // 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;igetError(); 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; igenerateCode (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(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(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(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( pCollationOptions->getNumberOfChildren() ); for( sal_Int16 i=0; igetChildAt( 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; jgetNumberOfChildren(); for( i=0; igetChildAt( i )->getValue(), sal::static_int_cast(i) ); } of.writeAsciiString("static const sal_Int16 nbOfSearchOptions = "); of.writeInt( sal::static_int_cast( nSearchOptions ) ); of.writeAsciiString(";\n\n"); of.writeAsciiString("static constexpr OUString searchOptions[] = {"); for( i=0; i(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; igetChildAt(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( getNumberOfChildren() ); OUString str; std::unique_ptr nbOfDays( new sal_Int16[nbOfCalendars] ); std::unique_ptr nbOfMonths( new sal_Int16[nbOfCalendars] ); std::unique_ptr nbOfGenitiveMonths( new sal_Int16[nbOfCalendars] ); std::unique_ptr nbOfPartitiveMonths( new sal_Int16[nbOfCalendars] ); std::unique_ptr 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( 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 sun"); 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( 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 jan"); 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( 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 jan"); 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( 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 jan"); 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( 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 bc"); if ( j == 1 && bGregorian && eraID != "ad" ) incError( "Second era of a Gregorian calendar must be ad"); 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: must be one of the , 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( 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(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(i) ); } } // record number of styles and attributes. of.writeAsciiString("static const sal_Int16 continuousNbOfStyles = "); of.writeInt( sal::static_int_cast( 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(i) ); of.writeAsciiString("[] = {\n"); for( sal_Int32 j=0; j(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(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 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; jgetChildAt( j )->getAttr(); for( sal_Int32 k=0; k(i), sal::static_int_cast(j) ); } } } // verify that each style has the same number of levels. for( size_t i=0; i(i) ); of.writeAsciiString("Level"); of.writeInt( sal::static_int_cast(j) ); of.writeAsciiString("[] = { "); for( sal_Int32 k=0; k(i) ); of.writeInt( sal::static_int_cast(j) ); of.writeAsciiString(", "); } of.writeAsciiString(" };\n"); } } of.writeAsciiString("\n"); for( sal_Int32 i=0; i(i) ); of.writeAsciiString("[] = { "); for( sal_Int32 j=0; j(i) ); of.writeAsciiString("Level"); of.writeInt( sal::static_int_cast(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(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(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: */