1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26 
27 #include "cellkeytranslator.hxx"
28 #include "comphelper/processfactory.hxx"
29 #include "i18npool/mslangid.hxx"
30 #include "i18npool/lang.h"
31 #include "rtl/ustring.hxx"
32 
33 #include <com/sun/star/i18n/TransliterationModules.hpp>
34 
35 using ::com::sun::star::lang::Locale;
36 using ::com::sun::star::uno::Sequence;
37 using ::std::list;
38 using ::std::hash_map;
39 using ::rtl::OUString;
40 
41 using namespace ::com::sun::star;
42 
43 enum LocaleMatch
44 {
45     LOCALE_MATCH_NONE = 0,
46     LOCALE_MATCH_LANG,
47     LOCALE_MATCH_LANG_COUNTRY,
48     LOCALE_MATCH_ALL
49 };
50 
lclLocaleCompare(const Locale & rLocale1,const Locale & rLocale2)51 static LocaleMatch lclLocaleCompare(const Locale& rLocale1, const Locale& rLocale2)
52 {
53     LocaleMatch eMatchLevel = LOCALE_MATCH_NONE;
54     if ( !rLocale1.Language.compareTo(rLocale1.Language) )
55         eMatchLevel = LOCALE_MATCH_LANG;
56     else
57         return eMatchLevel;
58 
59     if ( !rLocale1.Country.compareTo(rLocale2.Country) )
60         eMatchLevel = LOCALE_MATCH_LANG_COUNTRY;
61     else
62         return eMatchLevel;
63 
64     if ( !rLocale1.Variant.compareTo(rLocale2.Variant) )
65         eMatchLevel = LOCALE_MATCH_ALL;
66 
67     return eMatchLevel;
68 }
69 
ScCellKeyword(const sal_Char * pName,OpCode eOpCode,const Locale & rLocale)70 ScCellKeyword::ScCellKeyword(const sal_Char* pName, OpCode eOpCode, const Locale& rLocale) :
71     mpName(pName),
72     meOpCode(eOpCode),
73     mrLocale(rLocale)
74 {
75 }
76 
77 ::std::auto_ptr<ScCellKeywordTranslator> ScCellKeywordTranslator::spInstance(NULL);
78 
lclMatchKeyword(String & rName,const ScCellKeywordHashMap & aMap,OpCode eOpCode=ocNone,const Locale * pLocale=NULL)79 static void lclMatchKeyword(String& rName, const ScCellKeywordHashMap& aMap,
80                             OpCode eOpCode = ocNone, const Locale* pLocale = NULL)
81 {
82     ScCellKeywordHashMap::const_iterator itrEnd = aMap.end();
83     ScCellKeywordHashMap::const_iterator itr = aMap.find(rName);
84 
85     if ( itr == itrEnd || itr->second.empty() )
86         // No candidate strings exist.  Bail out.
87         return;
88 
89     if ( eOpCode == ocNone && !pLocale )
90     {
91         // Since no locale nor opcode matching is needed, simply return
92         // the first item on the list.
93         rName = String::CreateFromAscii( itr->second.front().mpName );
94         return;
95     }
96 
97     const sal_Char* aBestMatchName = itr->second.front().mpName;
98     LocaleMatch eLocaleMatchLevel = LOCALE_MATCH_NONE;
99     bool bOpCodeMatched = false;
100 
101     list<ScCellKeyword>::const_iterator itrListEnd = itr->second.end();
102     list<ScCellKeyword>::const_iterator itrList = itr->second.begin();
103     for ( ; itrList != itrListEnd; ++itrList )
104     {
105         if ( eOpCode != ocNone && pLocale )
106         {
107             if ( itrList->meOpCode == eOpCode )
108             {
109                 LocaleMatch eLevel = lclLocaleCompare(itrList->mrLocale, *pLocale);
110                 if ( eLevel == LOCALE_MATCH_ALL )
111                 {
112                     // Name with matching opcode and locale found.
113                     rName = String::CreateFromAscii( itrList->mpName );
114                     return;
115                 }
116                 else if ( eLevel > eLocaleMatchLevel )
117                 {
118                     // Name with a better matching locale.
119                     eLocaleMatchLevel = eLevel;
120                     aBestMatchName = itrList->mpName;
121                 }
122                 else if ( !bOpCodeMatched )
123                     // At least the opcode matches.
124                     aBestMatchName = itrList->mpName;
125 
126                 bOpCodeMatched = true;
127             }
128         }
129         else if ( eOpCode != ocNone && !pLocale )
130         {
131             if ( itrList->meOpCode == eOpCode )
132             {
133                 // Name with a matching opcode preferred.
134                 rName = String::CreateFromAscii( itrList->mpName );
135                 return;
136             }
137         }
138         else if ( !eOpCode && pLocale )
139         {
140             LocaleMatch eLevel = lclLocaleCompare(itrList->mrLocale, *pLocale);
141             if ( eLevel == LOCALE_MATCH_ALL )
142             {
143                 // Name with matching locale preferred.
144                 rName = String::CreateFromAscii( itrList->mpName );
145                 return;
146             }
147             else if ( eLevel > eLocaleMatchLevel )
148             {
149                 // Name with a better matching locale.
150                 eLocaleMatchLevel = eLevel;
151                 aBestMatchName = itrList->mpName;
152             }
153         }
154     }
155 
156     // No preferred strings found.  Return the best matching name.
157     rName = String::CreateFromAscii(aBestMatchName);
158 }
159 
transKeyword(String & rName,const Locale * pLocale,OpCode eOpCode)160 void ScCellKeywordTranslator::transKeyword(String& rName, const Locale* pLocale, OpCode eOpCode)
161 {
162     if ( !spInstance.get() )
163         spInstance.reset( new ScCellKeywordTranslator );
164 
165     LanguageType eLang = pLocale ? MsLangId::convertLocaleToLanguageWithFallback(*pLocale) : LANGUAGE_SYSTEM;
166     Sequence<sal_Int32> aOffsets;
167     rName = spInstance->maTransWrapper.transliterate(rName, eLang, 0, rName.Len(), &aOffsets);
168     lclMatchKeyword(rName, spInstance->maStringNameMap, eOpCode, pLocale);
169 }
170 
ScCellKeywordTranslator()171 ScCellKeywordTranslator::ScCellKeywordTranslator() :
172     maTransWrapper( ::comphelper::getProcessServiceFactory(),
173                     i18n::TransliterationModules_LOWERCASE_UPPERCASE )
174 {
175     init();
176 }
177 
~ScCellKeywordTranslator()178 ScCellKeywordTranslator::~ScCellKeywordTranslator()
179 {
180 }
181 
182 struct TransItem
183 {
184     const sal_Unicode*  from;
185     const sal_Char*     to;
186     OpCode              func;
187 };
188 
init()189 void ScCellKeywordTranslator::init()
190 {
191     ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
192 
193     // The file below has been autogenerated by sc/workben/celltrans/parse.py.
194     // To add new locale keywords, edit sc/workben/celltrans/keywords_utf16.txt
195     // and re-run the parse.py script.
196     //
197     // All keywords must be uppercase, and the mapping must be from the
198     // localized keyword to the English keyword.
199     //
200     // Make sure that the original keyword file (keywords_utf16.txt) is
201     // encoded in UCS-2/UTF-16!
202 
203     #include "cellkeywords.inl"
204 }
205 
addToMap(const String & rKey,const sal_Char * pName,const Locale & rLocale,OpCode eOpCode)206 void ScCellKeywordTranslator::addToMap(const String& rKey, const sal_Char* pName, const Locale& rLocale, OpCode eOpCode)
207 {
208     ScCellKeyword aKeyItem( pName, eOpCode, rLocale );
209 
210     ScCellKeywordHashMap::iterator itrEnd = maStringNameMap.end();
211     ScCellKeywordHashMap::iterator itr = maStringNameMap.find(rKey);
212 
213     if ( itr == itrEnd )
214     {
215         // New keyword.
216         list<ScCellKeyword> aList;
217         aList.push_back(aKeyItem);
218         maStringNameMap.insert( ScCellKeywordHashMap::value_type(rKey, aList) );
219     }
220     else
221         itr->second.push_back(aKeyItem);
222 }
223 
addToMap(const TransItem * pItems,const Locale & rLocale)224 void ScCellKeywordTranslator::addToMap(const TransItem* pItems, const Locale& rLocale)
225 {
226     for (sal_uInt16 i = 0; pItems[i].from != NULL; ++i)
227         addToMap(String(pItems[i].from), pItems[i].to, rLocale, pItems[i].func);
228 }
229