LCOV - code coverage report
Current view: top level - i18npool/source/languagetag - languagetag.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 214 302 70.9 %
Date: 2012-08-25 Functions: 40 46 87.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 157 318 49.4 %

           Branch data     Line data    Source code
       1                 :            : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 :            : /*
       3                 :            :  * This file is part of the LibreOffice project.
       4                 :            :  *
       5                 :            :  * This Source Code Form is subject to the terms of the Mozilla Public
       6                 :            :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7                 :            :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8                 :            :  */
       9                 :            : 
      10                 :            : #include "i18npool/languagetag.hxx"
      11                 :            : #include "i18npool/mslangid.hxx"
      12                 :            : #include <rtl/ustrbuf.hxx>
      13                 :            : #include <rtl/bootstrap.hxx>
      14                 :            : #include <osl/file.hxx>
      15                 :            : 
      16                 :            : //#define erDEBUG
      17                 :            : 
      18                 :            : #if defined(ENABLE_LIBLANGTAG)
      19                 :            : #include <liblangtag/langtag.h>
      20                 :            : #else
      21                 :            : /* Replacement code for LGPL phobic and Android systems.
      22                 :            :  * For iOS we could probably use NSLocale instead, that should have more or
      23                 :            :  * less required functionality. If it is good enough, it could be used for Mac
      24                 :            :  * OS X, too.
      25                 :            :  */
      26                 :            : #include "simple-langtag.cxx"
      27                 :            : #endif
      28                 :            : 
      29                 :            : using rtl::OUString;
      30                 :            : using rtl::OString;
      31                 :            : using rtl::OUStringBuffer;
      32                 :            : using namespace com::sun::star;
      33                 :            : 
      34                 :            : // The actual pointer type of mpImplLangtag that is declared void* to not
      35                 :            : // pollute the entire code base with liblangtag.
      36                 :            : #define LANGTAGCAST(p) (reinterpret_cast<lt_tag_t*>(p))
      37                 :            : #define MPLANGTAG LANGTAGCAST(mpImplLangtag)
      38                 :            : 
      39                 :            : /** Convention to signal presence of BCP 47 language tag in a Locale's Variant
      40                 :            :     field. The Locale's Language field then will contain this ISO 639-2
      41                 :            :     reserved for local use code. */
      42                 :            : #define ISO639_LANGUAGE_TAG "qlt"
      43                 :            : 
      44                 :            : 
      45                 :            : /** A reference holder for liblangtag data de/initialization, one static
      46                 :            :     instance. Currently implemented such that the first "ref" inits and dtor
      47                 :            :     (our library deinitialized) tears down.
      48                 :            : */
      49                 :            : class LiblantagDataRef
      50                 :            : {
      51                 :            : public:
      52                 :            :     LiblantagDataRef();
      53                 :            :     ~LiblantagDataRef();
      54                 :         40 :     inline void incRef()
      55                 :            :     {
      56 [ +  + ][ +  - ]:         40 :         if (mnRef != SAL_MAX_UINT32 && !mnRef++)
                 [ +  + ]
      57                 :          5 :             setup();
      58                 :         40 :     }
      59                 :       1875 :     inline void decRef()
      60                 :            :     {
      61 [ +  + ][ +  + ]:       1875 :         if (mnRef != SAL_MAX_UINT32 && mnRef && !--mnRef)
         [ +  - ][ +  + ]
      62                 :          5 :             teardown();
      63                 :       1875 :     }
      64                 :            :     void presetDataPath( const rtl::OUString& rPath );
      65                 :            : private:
      66                 :            :     rtl::OString maDataPath;   // path to liblangtag data, "|" if system
      67                 :            :     sal_uInt32   mnRef;
      68                 :            : 
      69                 :            :     void setupDataPath();
      70                 :            :     void setup();
      71                 :            :     void teardown();
      72                 :            : };
      73                 :            : 
      74                 :       1835 : static LiblantagDataRef theDataRef;
      75                 :            : 
      76                 :       1835 : LiblantagDataRef::LiblantagDataRef()
      77                 :            :     :
      78                 :       1835 :         mnRef(0)
      79                 :            : {
      80                 :       1835 : }
      81                 :            : 
      82                 :       1835 : LiblantagDataRef::~LiblantagDataRef()
      83                 :            : {
      84                 :            :     // When destructed we're tearing down unconditionally.
      85         [ +  + ]:       1835 :     if (mnRef)
      86                 :          5 :         mnRef = 1;
      87         [ +  - ]:       1835 :     decRef();
      88                 :       1835 : }
      89                 :            : 
      90                 :          5 : void LiblantagDataRef::setup()
      91                 :            : {
      92         [ -  + ]:          5 :     if (maDataPath.isEmpty())
      93                 :          0 :         setupDataPath();
      94                 :          5 :     lt_db_initialize();
      95                 :            :     // Hold ref eternally.
      96                 :          5 :     mnRef = SAL_MAX_UINT32;
      97                 :          5 : }
      98                 :            : 
      99                 :          5 : void LiblantagDataRef::teardown()
     100                 :            : {
     101                 :          5 :     lt_db_finalize();
     102                 :          5 : }
     103                 :            : 
     104                 :          5 : void LiblantagDataRef::presetDataPath( const rtl::OUString& rPath )
     105                 :            : {
     106         [ +  - ]:          5 :     if (maDataPath.isEmpty())
     107                 :            :     {
     108                 :          5 :         maDataPath = OUStringToOString( rPath, RTL_TEXTENCODING_UTF8);
     109                 :          5 :         lt_db_set_datadir( maDataPath.getStr());
     110                 :            :     }
     111                 :          5 : }
     112                 :            : 
     113                 :          0 : void LiblantagDataRef::setupDataPath()
     114                 :            : {
     115                 :            :     // maDataPath is assumed to be empty here.
     116                 :          0 :     OUString aURL;
     117         [ #  # ]:          0 :     if (!rtl::Bootstrap::get( "BRAND_BASE_DIR", aURL))
     118                 :            :         OSL_FAIL( "LiblantagDataRef: can't get BRAND_BASE_DIR");
     119                 :            :     else
     120                 :            :     {
     121                 :            :         // Check if data is in our own installation, else assume system
     122                 :            :         // installation.
     123                 :          0 :         aURL += "/share/liblangtag";
     124                 :          0 :         OUString aData( aURL);
     125                 :          0 :         aData += "/language-subtag-registry.xml";
     126                 :          0 :         osl::DirectoryItem aDirItem;
     127 [ #  # ][ #  # ]:          0 :         if (osl::DirectoryItem::get( aData, aDirItem) == osl::DirectoryItem::E_None)
     128                 :            :         {
     129                 :          0 :             OUString aPath;
     130 [ #  # ][ #  # ]:          0 :             if (osl::FileBase::getSystemPathFromFileURL( aURL, aPath) == osl::FileBase::E_None)
     131         [ #  # ]:          0 :                 maDataPath = OUStringToOString( aPath, RTL_TEXTENCODING_UTF8);
     132         [ #  # ]:          0 :         }
     133                 :            :     }
     134         [ #  # ]:          0 :     if (maDataPath.isEmpty())
     135                 :          0 :         maDataPath = "|";   // assume system
     136                 :            :     else
     137         [ #  # ]:          0 :         lt_db_set_datadir( maDataPath.getStr());
     138                 :          0 : }
     139                 :            : 
     140                 :            : 
     141                 :            : // static
     142                 :          5 : void LanguageTag::overrideDataPath( const rtl::OUString& rPath )
     143                 :            : {
     144                 :          5 :     theDataRef.presetDataPath( rPath);
     145                 :          5 : }
     146                 :            : 
     147                 :            : 
     148                 :         30 : LanguageTag::LanguageTag( const rtl::OUString & rBcp47LanguageTag, bool bCanonicalize )
     149                 :            :     :
     150                 :            :         maBcp47( rBcp47LanguageTag),
     151                 :            :         mpImplLangtag( NULL),
     152                 :            :         mnLangID( LANGUAGE_DONTKNOW),
     153                 :            :         meIsValid( DECISION_DONTKNOW),
     154                 :            :         meIsIsoLocale( DECISION_DONTKNOW),
     155                 :            :         meIsIsoODF( DECISION_DONTKNOW),
     156                 :            :         mbInitializedBcp47( true),
     157                 :            :         mbInitializedLocale( false),
     158                 :            :         mbInitializedLangID( false),
     159                 :            :         mbCachedLanguage( false),
     160                 :            :         mbCachedScript( false),
     161                 :         30 :         mbCachedCountry( false)
     162                 :            : {
     163         [ +  - ]:         30 :     theDataRef.incRef();
     164                 :            : 
     165         [ +  - ]:         30 :     if (bCanonicalize)
     166         [ +  - ]:         30 :         canonicalize();
     167                 :         30 : }
     168                 :            : 
     169                 :            : 
     170                 :          5 : LanguageTag::LanguageTag( const com::sun::star::lang::Locale & rLocale )
     171                 :            :     :
     172                 :            :         maLocale( rLocale),
     173                 :            :         mpImplLangtag( NULL),
     174                 :            :         mnLangID( LANGUAGE_DONTKNOW),
     175                 :            :         meIsValid( DECISION_DONTKNOW),
     176                 :            :         meIsIsoLocale( DECISION_DONTKNOW),
     177                 :            :         meIsIsoODF( DECISION_DONTKNOW),
     178                 :            :         mbInitializedBcp47( false),
     179                 :            :         mbInitializedLocale( true),
     180                 :            :         mbInitializedLangID( false),
     181                 :            :         mbCachedLanguage( false),
     182                 :            :         mbCachedScript( false),
     183                 :          5 :         mbCachedCountry( false)
     184                 :            : {
     185         [ +  - ]:          5 :     theDataRef.incRef();
     186                 :          5 : }
     187                 :            : 
     188                 :            : 
     189                 :          5 : LanguageTag::LanguageTag( LanguageType nLanguage )
     190                 :            :     :
     191                 :            :         mpImplLangtag( NULL),
     192                 :            :         mnLangID( nLanguage),
     193                 :            :         meIsValid( DECISION_DONTKNOW),
     194                 :            :         meIsIsoLocale( DECISION_DONTKNOW),
     195                 :            :         meIsIsoODF( DECISION_DONTKNOW),
     196                 :            :         mbInitializedBcp47( false),
     197                 :            :         mbInitializedLocale( false),
     198                 :            :         mbInitializedLangID( true),
     199                 :            :         mbCachedLanguage( false),
     200                 :            :         mbCachedScript( false),
     201                 :          5 :         mbCachedCountry( false)
     202                 :            : {
     203         [ +  - ]:          5 :     theDataRef.incRef();
     204                 :          5 : }
     205                 :            : 
     206                 :            : 
     207                 :          0 : LanguageTag::LanguageTag( const rtl::OUString& rLanguage, const rtl::OUString& rCountry )
     208                 :            :     :
     209                 :            :         maLocale( rLanguage, rCountry, ""),
     210                 :            :         mpImplLangtag( NULL),
     211                 :            :         mnLangID( LANGUAGE_DONTKNOW),
     212                 :            :         meIsValid( DECISION_DONTKNOW),
     213                 :            :         meIsIsoLocale( DECISION_DONTKNOW),
     214                 :            :         meIsIsoODF( DECISION_DONTKNOW),
     215                 :            :         mbInitializedBcp47( false),
     216                 :            :         mbInitializedLocale( true),
     217                 :            :         mbInitializedLangID( false),
     218                 :            :         mbCachedLanguage( false),
     219                 :            :         mbCachedScript( false),
     220                 :          0 :         mbCachedCountry( false)
     221                 :            : {
     222         [ #  # ]:          0 :     theDataRef.incRef();
     223                 :          0 : }
     224                 :            : 
     225                 :            : 
     226                 :          0 : LanguageTag::LanguageTag( const LanguageTag & rLanguageTag )
     227                 :            :     :
     228                 :            :         maLocale( rLanguageTag.maLocale),
     229                 :            :         maBcp47( rLanguageTag.maBcp47),
     230                 :            :         maCachedLanguage( rLanguageTag.maCachedLanguage),
     231                 :            :         maCachedScript( rLanguageTag.maCachedScript),
     232                 :            :         maCachedCountry( rLanguageTag.maCachedCountry),
     233                 :            :         mpImplLangtag( rLanguageTag.mpImplLangtag ?
     234                 :          0 :                 lt_tag_copy( LANGTAGCAST( rLanguageTag.mpImplLangtag)) : NULL),
     235                 :            :         mnLangID( rLanguageTag.mnLangID),
     236                 :            :         meIsValid( rLanguageTag.meIsValid),
     237                 :            :         meIsIsoLocale( rLanguageTag.meIsIsoLocale),
     238                 :            :         meIsIsoODF( rLanguageTag.meIsIsoODF),
     239                 :            :         mbInitializedBcp47( rLanguageTag.mbInitializedBcp47),
     240                 :            :         mbInitializedLocale( rLanguageTag.mbInitializedLocale),
     241                 :            :         mbInitializedLangID( rLanguageTag.mbInitializedLangID),
     242                 :            :         mbCachedLanguage( rLanguageTag.mbCachedLanguage),
     243                 :            :         mbCachedScript( rLanguageTag.mbCachedScript),
     244 [ #  # ][ #  # ]:          0 :         mbCachedCountry( rLanguageTag.mbCachedCountry)
     245                 :            : {
     246         [ #  # ]:          0 :     theDataRef.incRef();
     247                 :          0 : }
     248                 :            : 
     249                 :            : 
     250                 :          0 : LanguageTag& LanguageTag::operator=( const LanguageTag & rLanguageTag )
     251                 :            : {
     252                 :          0 :     theDataRef.incRef();
     253                 :            : 
     254                 :          0 :     maLocale            = rLanguageTag.maLocale;
     255                 :          0 :     maBcp47             = rLanguageTag.maBcp47;
     256                 :          0 :     maCachedLanguage    = rLanguageTag.maCachedLanguage;
     257                 :          0 :     maCachedScript      = rLanguageTag.maCachedScript;
     258                 :          0 :     maCachedCountry     = rLanguageTag.maCachedCountry;
     259                 :          0 :     mpImplLangtag       = rLanguageTag.mpImplLangtag;
     260                 :            :     mpImplLangtag       = rLanguageTag.mpImplLangtag ?
     261         [ #  # ]:          0 :                             lt_tag_copy( LANGTAGCAST( rLanguageTag.mpImplLangtag)) : NULL;
     262                 :          0 :     mnLangID            = rLanguageTag.mnLangID;
     263                 :          0 :     meIsValid           = rLanguageTag.meIsValid;
     264                 :          0 :     meIsIsoLocale       = rLanguageTag.meIsIsoLocale;
     265                 :          0 :     meIsIsoODF          = rLanguageTag.meIsIsoODF;
     266                 :          0 :     mbInitializedBcp47  = rLanguageTag.mbInitializedBcp47;
     267                 :          0 :     mbInitializedLocale = rLanguageTag.mbInitializedLocale;
     268                 :          0 :     mbInitializedLangID = rLanguageTag.mbInitializedLangID;
     269                 :          0 :     mbCachedLanguage    = rLanguageTag.mbCachedLanguage;
     270                 :          0 :     mbCachedScript      = rLanguageTag.mbCachedScript;
     271                 :          0 :     mbCachedCountry     = rLanguageTag.mbCachedCountry;
     272                 :          0 :     return *this;
     273                 :            : }
     274                 :            : 
     275                 :            : 
     276                 :         40 : LanguageTag::~LanguageTag()
     277                 :            : {
     278         [ +  - ]:         40 :     lt_tag_unref( MPLANGTAG);
     279                 :            : 
     280         [ +  - ]:         40 :     theDataRef.decRef();
     281                 :         40 : }
     282                 :            : 
     283                 :            : 
     284                 :         30 : bool LanguageTag::canonicalize() const
     285                 :            : {
     286                 :            : #ifdef erDEBUG
     287                 :            :     // dump once
     288                 :            :     struct dumper
     289                 :            :     {
     290                 :            :         void** mpp;
     291                 :            :         dumper( void** pp ) : mpp( *pp ? NULL : pp) {}
     292                 :            :         ~dumper() { if (mpp && *mpp) lt_tag_dump( LANGTAGCAST( *mpp)); }
     293                 :            :     };
     294                 :            :     dumper aDumper( &mpImplLangtag);
     295                 :            : #endif
     296                 :            : 
     297                 :            :     // g_error_free() mocks about NULL, so ...
     298                 :            :     struct myerror
     299                 :            :     {
     300                 :            :         GError* p;
     301                 :         30 :         myerror() : p(NULL) {}
     302         [ +  + ]:         30 :         ~myerror() { if (p) g_error_free( p); }
     303                 :         30 :     } aError;
     304                 :            : 
     305         [ +  - ]:         30 :     getBcp47();     // side effect: have maBcp47 in any case
     306                 :            :     // Checking empty for system locale before having allocated mpImplLangtag
     307                 :            :     // may result in multiple calls of this method because that serves as flag
     308                 :            :     // whether this was canonicalized, but that's better than allocating
     309                 :            :     // lt_tag_t for all those system locales.
     310         [ -  + ]:         30 :     if (maBcp47.isEmpty())
     311                 :            :     {
     312                 :          0 :         meIsValid = DECISION_YES;
     313                 :          0 :         return true;
     314                 :            :     }
     315         [ +  - ]:         30 :     if (!mpImplLangtag)
     316         [ +  - ]:         30 :         mpImplLangtag = lt_tag_new();
     317 [ +  - ][ +  - ]:         30 :     if (lt_tag_parse( MPLANGTAG, OUStringToOString( maBcp47, RTL_TEXTENCODING_UTF8).getStr(), &aError.p))
                 [ +  + ]
     318                 :            :     {
     319         [ +  - ]:         25 :         gchar* pTag = lt_tag_canonicalize( MPLANGTAG, &aError.p);
     320                 :            :         SAL_WARN_IF( !pTag || aError.p, "i18npool.langtag", "LanguageTag::canonicalize: could not canonicalize, " <<
     321                 :            :                 (aError.p ? aError.p->message : ""));
     322         [ +  - ]:         25 :         if (pTag)
     323                 :            :         {
     324                 :         25 :             OUString aOld( maBcp47);
     325                 :         25 :             maBcp47 = OUString::createFromAscii( pTag);
     326                 :            :             // Make the lt_tag_t follow the new string if different, which
     327                 :            :             // removes default script and such.
     328         [ +  + ]:         25 :             if (maBcp47 != aOld)
     329                 :            :             {
     330 [ +  - ][ -  + ]:         10 :                 if (!lt_tag_parse( MPLANGTAG, pTag, &aError.p))
     331                 :            :                 {
     332                 :            :                     SAL_WARN( "i18npool.langtag", "LanguageTag::canonicalize: could not reparse, " <<
     333                 :            :                             (aError.p ? aError.p->message : ""));
     334         [ #  # ]:          0 :                     g_free( pTag);
     335                 :          0 :                     meIsValid = DECISION_NO;
     336                 :          0 :                     return false;
     337                 :            :                 }
     338                 :            :             }
     339         [ +  - ]:         25 :             g_free( pTag);
     340                 :         25 :             meIsValid = DECISION_YES;
     341                 :         25 :             return true;
     342                 :            :         }
     343                 :            :     }
     344                 :            :     else
     345                 :            :     {
     346                 :            :         SAL_WARN( "i18npool.langtag", "LanguageTag::canonicalize: could not parse, " <<
     347                 :            :                 (aError.p ? aError.p->message : ""));
     348                 :            :     }
     349                 :          5 :     meIsValid = DECISION_NO;
     350         [ +  - ]:         30 :     return false;
     351                 :            : }
     352                 :            : 
     353                 :            : 
     354                 :         10 : void LanguageTag::convertLocaleToBcp47()
     355                 :            : {
     356         [ -  + ]:         10 :     if (maLocale.Language.isEmpty())
     357                 :            :     {
     358                 :            :         // Special case system locale.
     359                 :          0 :         maBcp47 = OUString();
     360                 :          0 :         meIsIsoLocale = DECISION_YES;
     361                 :            :     }
     362         [ -  + ]:         10 :     else if (maLocale.Language == ISO639_LANGUAGE_TAG)
     363                 :            :     {
     364                 :          0 :         maBcp47 = maLocale.Variant;
     365                 :          0 :         meIsIsoLocale = DECISION_NO;
     366                 :            :     }
     367                 :            :     else
     368                 :            :     {
     369                 :            :         /* XXX NOTE: most legacy code never evaluated the Variant field, so for
     370                 :            :          * now just concatenate language and country. In case we stumbled over
     371                 :            :          * variant aware code we'd have to take care of that. */
     372         [ -  + ]:         10 :         if (maLocale.Country.isEmpty())
     373                 :          0 :             maBcp47 = maLocale.Language;
     374                 :            :         else
     375                 :            :         {
     376                 :         10 :             OUStringBuffer aBuf( maLocale.Language.getLength() + 1 + maLocale.Country.getLength());
     377 [ +  - ][ +  - ]:         10 :             aBuf.append( maLocale.Language).append( '-').append( maLocale.Country);
                 [ +  - ]
     378         [ +  - ]:         10 :             maBcp47 = aBuf.makeStringAndClear();
     379                 :            :         }
     380                 :            :     }
     381                 :         10 :     mbInitializedBcp47 = true;
     382                 :         10 : }
     383                 :            : 
     384                 :            : 
     385                 :         35 : void LanguageTag::convertLocaleToLang()
     386                 :            : {
     387                 :            :     /* FIXME: this is temporary until code base is converted to not use
     388                 :            :      * MsLangId::convert...() anymore. After that, proper new method has to be
     389                 :            :      * implemented to allow ISO639_LANGUAGE_TAG and sript tag and such. */
     390                 :         35 :     mnLangID = MsLangId::convertLocaleToLanguage( maLocale);
     391                 :         35 :     mbInitializedLangID = true;
     392                 :         35 : }
     393                 :            : 
     394                 :            : 
     395                 :         30 : void LanguageTag::convertBcp47ToLocale()
     396                 :            : {
     397         [ -  + ]:         30 :     if (maBcp47.isEmpty())
     398                 :            :     {
     399                 :            :         // Special case system locale.
     400                 :          0 :         maLocale = lang::Locale();
     401                 :          0 :         meIsIsoLocale = DECISION_YES;
     402                 :            :     }
     403                 :            :     else
     404                 :            :     {
     405                 :         30 :         bool bIso = isIsoLocale();
     406         [ +  + ]:         30 :         if (bIso)
     407                 :            :         {
     408                 :         20 :             maLocale.Language = getLanguageFromLangtag();
     409                 :         20 :             maLocale.Country = getRegionFromLangtag();
     410                 :         20 :             maLocale.Variant = OUString();
     411                 :            :         }
     412                 :            :         else
     413                 :            :         {
     414                 :         10 :             maLocale.Language = ISO639_LANGUAGE_TAG;
     415                 :         10 :             maLocale.Country = getCountry();
     416                 :         10 :             maLocale.Variant = maBcp47;
     417                 :            :         }
     418                 :            :     }
     419                 :         30 :     mbInitializedLocale = true;
     420                 :         30 : }
     421                 :            : 
     422                 :            : 
     423                 :         35 : void LanguageTag::convertBcp47ToLang()
     424                 :            : {
     425                 :            :     /* FIXME: this is temporary. If we support locales that consist not only of
     426                 :            :      * language and country, e.g. added script, this probably needs to be
     427                 :            :      * adapted. */
     428         [ -  + ]:         35 :     if (!mbInitializedLocale)
     429                 :          0 :         convertBcp47ToLocale();
     430                 :         35 :     convertLocaleToLang();
     431                 :         35 :     mbInitializedLangID = true;
     432                 :         35 : }
     433                 :            : 
     434                 :            : 
     435                 :          5 : void LanguageTag::convertLangToLocale()
     436                 :            : {
     437                 :            :     /* FIXME: this is temporary until code base is converted to not use
     438                 :            :      * MsLangId::convert...() anymore. After that, proper new method has to be
     439                 :            :      * implemented to allow ISO639_LANGUAGE_TAG and script tag and such. */
     440                 :            :     // Do not resolve system here!
     441                 :          5 :     maLocale = MsLangId::convertLanguageToLocale( mnLangID, false);
     442                 :          5 :     mbInitializedLocale = true;
     443                 :          5 : }
     444                 :            : 
     445                 :            : 
     446                 :          0 : void LanguageTag::convertLangToBcp47()
     447                 :            : {
     448                 :            :     /* FIXME: this is temporary. If we support locales that consist not only of
     449                 :            :      * language and country, e.g. added script, this probably needs to be
     450                 :            :      * adapted. */
     451         [ #  # ]:          0 :     if (!mbInitializedLocale)
     452                 :          0 :         convertLangToLocale();
     453                 :          0 :     convertLocaleToBcp47();
     454                 :          0 :     mbInitializedBcp47 = true;
     455                 :          0 : }
     456                 :            : 
     457                 :            : 
     458                 :         70 : rtl::OUString LanguageTag::getBcp47() const
     459                 :            : {
     460         [ +  + ]:         70 :     if (!mbInitializedBcp47)
     461                 :            :     {
     462         [ +  - ]:         10 :         if (mbInitializedLocale)
     463                 :         10 :             const_cast<LanguageTag*>(this)->convertLocaleToBcp47();
     464                 :            :         else
     465                 :          0 :             const_cast<LanguageTag*>(this)->convertLangToBcp47();
     466                 :            :     }
     467                 :         70 :     return maBcp47;
     468                 :            : }
     469                 :            : 
     470                 :            : 
     471                 :         45 : rtl::OUString LanguageTag::getLanguageFromLangtag() const
     472                 :            : {
     473                 :         45 :     rtl::OUString aLanguage;
     474         [ -  + ]:         45 :     if (!mpImplLangtag)
     475         [ #  # ]:          0 :         canonicalize();
     476         [ -  + ]:         45 :     if (maBcp47.isEmpty())
     477                 :          0 :         return aLanguage;
     478         [ +  - ]:         45 :     const lt_lang_t* pLangT = lt_tag_get_language( MPLANGTAG);
     479                 :            :     SAL_WARN_IF( !pLangT, "i18npool.langtag", "LanguageTag::getLanguageFromLangtag: pLangT==NULL");
     480         [ -  + ]:         45 :     if (!pLangT)
     481                 :          0 :         return aLanguage;
     482         [ +  - ]:         45 :     const gchar* pLang = lt_lang_get_tag( pLangT);
     483                 :            :     SAL_WARN_IF( !pLang, "i18npool.langtag", "LanguageTag::getLanguageFromLangtag: pLang==NULL");
     484         [ +  - ]:         45 :     if (pLang)
     485                 :         45 :         aLanguage = OUString::createFromAscii( pLang);
     486                 :         45 :     return aLanguage;
     487                 :            : }
     488                 :            : 
     489                 :            : 
     490                 :         25 : rtl::OUString LanguageTag::getScriptFromLangtag() const
     491                 :            : {
     492                 :         25 :     rtl::OUString aScript;
     493         [ -  + ]:         25 :     if (!mpImplLangtag)
     494         [ #  # ]:          0 :         canonicalize();
     495         [ -  + ]:         25 :     if (maBcp47.isEmpty())
     496                 :          0 :         return aScript;
     497         [ +  - ]:         25 :     const lt_script_t* pScriptT = lt_tag_get_script( MPLANGTAG);
     498                 :            :     // pScriptT==NULL is valid for default scripts
     499         [ +  + ]:         25 :     if (!pScriptT)
     500                 :         20 :         return aScript;
     501         [ +  - ]:          5 :     const gchar* pScript = lt_script_get_tag( pScriptT);
     502                 :            :     SAL_WARN_IF( !pScript, "i18npool.langtag", "LanguageTag::getScriptFromLangtag: pScript==NULL");
     503         [ +  - ]:          5 :     if (pScript)
     504                 :          5 :         aScript = OUString::createFromAscii( pScript);
     505                 :         25 :     return aScript;
     506                 :            : }
     507                 :            : 
     508                 :            : 
     509                 :         55 : rtl::OUString LanguageTag::getRegionFromLangtag() const
     510                 :            : {
     511                 :         55 :     rtl::OUString aRegion;
     512         [ -  + ]:         55 :     if (!mpImplLangtag)
     513         [ #  # ]:          0 :         canonicalize();
     514         [ -  + ]:         55 :     if (maBcp47.isEmpty())
     515                 :          0 :         return aRegion;
     516         [ +  - ]:         55 :     const lt_region_t* pRegionT = lt_tag_get_region( MPLANGTAG);
     517                 :            :     SAL_WARN_IF( !pRegionT, "i18npool.langtag", "LanguageTag::getRegionFromLangtag: pRegionT==NULL");
     518         [ +  + ]:         55 :     if (!pRegionT)
     519                 :         15 :         return aRegion;
     520         [ +  - ]:         40 :     const gchar* pRegion = lt_region_get_tag( pRegionT);
     521                 :            :     SAL_WARN_IF( !pRegion, "i18npool.langtag", "LanguageTag::getRegionFromLangtag: pRegion==NULL");
     522         [ +  - ]:         40 :     if (pRegion)
     523                 :         40 :         aRegion = OUString::createFromAscii( pRegion);
     524                 :         55 :     return aRegion;
     525                 :            : }
     526                 :            : 
     527                 :            : 
     528                 :         40 : com::sun::star::lang::Locale LanguageTag::getLocale() const
     529                 :            : {
     530         [ +  + ]:         40 :     if (!mbInitializedLocale)
     531                 :            :     {
     532         [ +  + ]:         35 :         if (mbInitializedBcp47)
     533                 :         30 :             const_cast<LanguageTag*>(this)->convertBcp47ToLocale();
     534                 :            :         else
     535                 :          5 :             const_cast<LanguageTag*>(this)->convertLangToLocale();
     536                 :            :     }
     537                 :         40 :     return maLocale;
     538                 :            : }
     539                 :            : 
     540                 :            : 
     541                 :         40 : LanguageType LanguageTag::getLanguageType() const
     542                 :            : {
     543         [ +  + ]:         40 :     if (!mbInitializedLangID)
     544                 :            :     {
     545         [ +  - ]:         35 :         if (mbInitializedBcp47)
     546                 :         35 :             const_cast<LanguageTag*>(this)->convertBcp47ToLang();
     547                 :            :         else
     548                 :          0 :             const_cast<LanguageTag*>(this)->convertLocaleToLang();
     549                 :            :     }
     550                 :         40 :     return mnLangID;
     551                 :            : }
     552                 :            : 
     553                 :            : 
     554                 :            : namespace
     555                 :            : {
     556                 :            : 
     557                 :         85 : bool isLowerAscii( sal_Unicode c )
     558                 :            : {
     559 [ +  - ][ +  - ]:         85 :     return 'a' <= c && c <= 'z';
     560                 :            : }
     561                 :            : 
     562                 :         60 : bool isUpperAscii( sal_Unicode c )
     563                 :            : {
     564 [ +  - ][ +  - ]:         60 :     return 'A' <= c && c <= 'Z';
     565                 :            : }
     566                 :            : 
     567                 :            : }
     568                 :            : 
     569                 :            : 
     570                 :            : // static
     571                 :         25 : bool LanguageTag::isIsoLanguage( const rtl::OUString& rLanguage )
     572                 :            : {
     573                 :            :     /* TODO: ignore case? For now let's see where rubbish is used. */
     574                 :            :     bool b2chars;
     575 [ +  + ][ +  -  :         80 :     if (((b2chars = (rLanguage.getLength() == 2)) || rLanguage.getLength() == 3) &&
             +  -  +  - ]
           [ +  +  +  - ]
                 [ +  - ]
     576                 :         50 :             isLowerAscii( rLanguage[0]) && isLowerAscii( rLanguage[1]) &&
     577                 :          5 :             (b2chars || isLowerAscii( rLanguage[2])))
     578                 :         25 :         return true;
     579                 :            :     SAL_WARN_IF( ((rLanguage.getLength() == 2 || rLanguage.getLength() == 3) &&
     580                 :            :                 (isUpperAscii( rLanguage[0]) || isUpperAscii( rLanguage[1]))) ||
     581                 :            :             (rLanguage.getLength() == 3 && isUpperAscii( rLanguage[2])), "i18npool.langtag",
     582                 :            :             "LanguageTag::isIsoLanguage: rejecting upper case");
     583                 :         25 :     return false;
     584                 :            : }
     585                 :            : 
     586                 :            : 
     587                 :            : // static
     588                 :         35 : bool LanguageTag::isIsoCountry( const rtl::OUString& rRegion )
     589                 :            : {
     590                 :            :     /* TODO: ignore case? For now let's see where rubbish is used. */
     591   [ +  +  +  -  :        110 :     if (rRegion.isEmpty() ||
             +  -  +  - ]
                 [ +  - ]
     592                 :         75 :             (rRegion.getLength() == 2 && isUpperAscii( rRegion[0]) && isUpperAscii( rRegion[1])))
     593                 :         35 :         return true;
     594                 :            :     SAL_WARN_IF( rRegion.getLength() == 2 && (isLowerAscii( rRegion[0]) || isLowerAscii( rRegion[1])),
     595                 :            :             "i18npool.langtag", "LanguageTag::isIsoCountry: rejecting lower case");
     596                 :         35 :     return false;
     597                 :            : }
     598                 :            : 
     599                 :            : 
     600                 :            : // static
     601                 :         30 : bool LanguageTag::isIsoScript( const rtl::OUString& rScript )
     602                 :            : {
     603                 :            :     /* TODO: ignore case? For now let's see where rubbish is used. */
     604   [ +  +  +  -  :         80 :     if (rScript.isEmpty() ||
          +  -  +  -  +  
                -  +  - ]
                 [ +  - ]
     605                 :         10 :             (rScript.getLength() == 4 &&
     606                 :         20 :              isUpperAscii( rScript[0]) && isLowerAscii( rScript[1]) &&
     607                 :         20 :              isLowerAscii( rScript[2]) && isLowerAscii( rScript[3])))
     608                 :         30 :         return true;
     609                 :            :     SAL_WARN_IF( rScript.getLength() == 4 &&
     610                 :            :             (isLowerAscii( rScript[0]) || isUpperAscii( rScript[1]) ||
     611                 :            :              isUpperAscii( rScript[2]) || isUpperAscii( rScript[3])),
     612                 :            :             "i18npool.langtag", "LanguageTag::isIsoScript: rejecting case mismatch");
     613                 :         30 :     return false;
     614                 :            : }
     615                 :            : 
     616                 :            : 
     617                 :         25 : rtl::OUString LanguageTag::getLanguage() const
     618                 :            : {
     619         [ +  - ]:         25 :     if (!mbCachedLanguage)
     620                 :            :     {
     621                 :         25 :         maCachedLanguage = getLanguageFromLangtag();
     622                 :         25 :         mbCachedLanguage = true;
     623                 :            :     }
     624                 :         25 :     return maCachedLanguage;
     625                 :            : }
     626                 :            : 
     627                 :            : 
     628                 :         30 : rtl::OUString LanguageTag::getScript() const
     629                 :            : {
     630         [ +  + ]:         30 :     if (!mbCachedScript)
     631                 :            :     {
     632                 :         25 :         maCachedScript = getScriptFromLangtag();
     633                 :         25 :         mbCachedScript = true;
     634                 :            :     }
     635                 :         30 :     return maCachedScript;
     636                 :            : }
     637                 :            : 
     638                 :            : 
     639                 :          0 : rtl::OUString LanguageTag::getLanguageAndScript() const
     640                 :            : {
     641         [ #  # ]:          0 :     OUString aLanguageScript( getLanguage());
     642         [ #  # ]:          0 :     OUString aScript( getScript());
     643         [ #  # ]:          0 :     if (!aScript.isEmpty())
     644                 :            :     {
     645                 :          0 :         OUStringBuffer aBuf( aLanguageScript.getLength() + 1 + aScript.getLength());
     646 [ #  # ][ #  # ]:          0 :         aBuf.append( aLanguageScript).append( '-').append( aScript);
                 [ #  # ]
     647         [ #  # ]:          0 :         aLanguageScript = aBuf.makeStringAndClear();
     648                 :            :     }
     649                 :          0 :     return aLanguageScript;
     650                 :            : }
     651                 :            : 
     652                 :            : 
     653                 :         10 : rtl::OUString LanguageTag::getCountry() const
     654                 :            : {
     655         [ +  - ]:         10 :     if (!mbCachedCountry)
     656                 :            :     {
     657                 :         10 :         maCachedCountry = getRegionFromLangtag();
     658         [ -  + ]:         10 :         if (!isIsoCountry( maCachedCountry))
     659                 :          0 :             maCachedCountry = OUString();
     660                 :         10 :         mbCachedCountry = true;
     661                 :            :     }
     662                 :         10 :     return maCachedCountry;
     663                 :            : }
     664                 :            : 
     665                 :            : 
     666                 :         25 : rtl::OUString LanguageTag::getRegion() const
     667                 :            : {
     668                 :         25 :     return getRegionFromLangtag();
     669                 :            : }
     670                 :            : 
     671                 :            : 
     672                 :         80 : bool LanguageTag::isIsoLocale() const
     673                 :            : {
     674         [ +  + ]:         80 :     if (meIsIsoLocale == DECISION_DONTKNOW)
     675                 :            :     {
     676         [ -  + ]:         30 :         if (!mpImplLangtag)
     677                 :          0 :             canonicalize();
     678                 :            :         // It must be at most ll-CC or lll-CC
     679                 :            :         // Do not use getCountry() here, use getRegion() instead.
     680                 :         30 :         meIsIsoLocale = ((maBcp47.isEmpty() ||
     681 [ +  - ][ +  - ]:        100 :                     (maBcp47.getLength() <= 6 && isIsoLanguage( getLanguage()) && isIsoCountry( getRegion()))) ?
         [ +  - ][ +  - ]
         [ +  + ][ +  + ]
           [ #  #  #  # ]
     682 [ +  - ][ +  - ]:        100 :                 DECISION_YES : DECISION_NO);
           [ +  -  +  + ]
     683                 :            :     }
     684                 :         80 :     return meIsIsoLocale == DECISION_YES;
     685                 :            : }
     686                 :            : 
     687                 :            : 
     688                 :         25 : bool LanguageTag::isIsoODF() const
     689                 :            : {
     690         [ +  - ]:         25 :     if (meIsIsoODF == DECISION_DONTKNOW)
     691                 :            :     {
     692         [ -  + ]:         25 :         if (!mpImplLangtag)
     693                 :          0 :             canonicalize();
     694 [ +  - ][ -  + ]:         25 :         if (!isIsoScript( getScript()))
     695                 :          0 :             return ((meIsIsoODF = DECISION_NO) == DECISION_YES);
     696                 :            :         // The usual case is lll-CC so simply check that first.
     697         [ +  + ]:         25 :         if (isIsoLocale())
     698                 :         15 :             return ((meIsIsoODF = DECISION_YES) == DECISION_YES);
     699                 :            :         // If this is not ISO locale for which script must not exist it can
     700                 :            :         // still be ISO locale plus ISO script lll-Ssss-CC
     701                 :         10 :         meIsIsoODF = ((maBcp47.getLength() <= 11 &&
     702 [ +  - ][ +  - ]:         25 :                     isIsoLanguage( getLanguage()) && isIsoCountry( getRegion()) && isIsoScript( getScript())) ?
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  + ][ +  + ]
         [ +  + ][ #  #  
             #  #  #  # ]
     703 [ +  - ][ +  - ]:         25 :                 DECISION_YES : DECISION_NO);
         [ +  - ][ +  + ]
     704                 :            :     }
     705                 :         25 :     return meIsIsoODF == DECISION_YES;
     706                 :            : }
     707                 :            : 
     708                 :            : 
     709                 :         25 : bool LanguageTag::isValidBcp47() const
     710                 :            : {
     711         [ -  + ]:         25 :     if (meIsValid == DECISION_DONTKNOW)
     712                 :            :     {
     713         [ #  # ]:          0 :         if (!mpImplLangtag)
     714                 :          0 :            canonicalize();
     715                 :            :         SAL_WARN_IF( meIsValid == DECISION_DONTKNOW, "i18npool.langtag",
     716                 :            :                 "LanguageTag::isValidBcp47: canonicalize() doesn't set meIsValid");
     717                 :            :     }
     718                 :         25 :     return meIsValid == DECISION_YES;
     719 [ +  - ][ +  - ]:       5505 : }
     720                 :            : 
     721                 :            : 
     722                 :            : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10