xref: /trunk/main/i18npool/source/breakiterator/breakiteratorImpl.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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_i18npool.hxx"
26 
27 #include <breakiteratorImpl.hxx>
28 #include <unicode/uchar.h>
29 #include <rtl/ustrbuf.hxx>
30 
31 using namespace ::com::sun::star::uno;
32 using namespace ::com::sun::star::lang;
33 using namespace ::rtl;
34 
35 namespace com { namespace sun { namespace star { namespace i18n {
36 
BreakIteratorImpl(const Reference<XMultiServiceFactory> & rxMSF)37 BreakIteratorImpl::BreakIteratorImpl( const Reference < XMultiServiceFactory >& rxMSF ) : xMSF( rxMSF )
38 {
39 }
40 
BreakIteratorImpl()41 BreakIteratorImpl::BreakIteratorImpl()
42 {
43 }
44 
~BreakIteratorImpl()45 BreakIteratorImpl::~BreakIteratorImpl()
46 {
47         // Clear lookuptable
48         for (size_t l = 0; l < lookupTable.size(); l++)
49             delete lookupTable[l];
50         lookupTable.clear();
51 }
52 
53 #define LBI getLocaleSpecificBreakIterator(rLocale)
54 
nextCharacters(const OUString & Text,sal_Int32 nStartPos,const Locale & rLocale,sal_Int16 nCharacterIteratorMode,sal_Int32 nCount,sal_Int32 & nDone)55 sal_Int32 SAL_CALL BreakIteratorImpl::nextCharacters( const OUString& Text, sal_Int32 nStartPos,
56         const Locale &rLocale, sal_Int16 nCharacterIteratorMode, sal_Int32 nCount, sal_Int32& nDone )
57 {
58         if (nCount < 0) throw RuntimeException();
59 
60         return LBI->nextCharacters( Text, nStartPos, rLocale, nCharacterIteratorMode, nCount, nDone);
61 }
62 
previousCharacters(const OUString & Text,sal_Int32 nStartPos,const Locale & rLocale,sal_Int16 nCharacterIteratorMode,sal_Int32 nCount,sal_Int32 & nDone)63 sal_Int32 SAL_CALL BreakIteratorImpl::previousCharacters( const OUString& Text, sal_Int32 nStartPos,
64         const Locale& rLocale, sal_Int16 nCharacterIteratorMode, sal_Int32 nCount, sal_Int32& nDone )
65 {
66         if (nCount < 0) throw RuntimeException();
67 
68         return LBI->previousCharacters( Text, nStartPos, rLocale, nCharacterIteratorMode, nCount, nDone);
69 }
70 
71 #define isZWSP(c) (ch == 0x200B)
72 
skipSpace(const OUString & Text,sal_Int32 nPos,sal_Int32 len,sal_Int16 rWordType,sal_Bool bDirection)73 static sal_Int32 skipSpace(const OUString& Text, sal_Int32 nPos, sal_Int32 len, sal_Int16 rWordType, sal_Bool bDirection)
74 {
75         sal_uInt32 ch=0;
76         sal_Int32 pos=nPos;
77         switch (rWordType) {
78             case WordType::ANYWORD_IGNOREWHITESPACES:
79                 if (bDirection)
80                     while (nPos < len && (u_isWhitespace(ch = Text.iterateCodePoints(&pos, 1)) || isZWSP(ch))) nPos=pos;
81                 else
82                     while (nPos > 0 && (u_isWhitespace(ch = Text.iterateCodePoints(&pos, -1)) || isZWSP(ch))) nPos=pos;
83             break;
84             case WordType::DICTIONARY_WORD:
85                 if (bDirection)
86                     while (nPos < len && (u_isWhitespace(ch = Text.iterateCodePoints(&pos, 1)) || isZWSP(ch) ||
87                             ! (ch == 0x002E || u_isalnum(ch)))) nPos=pos;
88                 else
89                     while (nPos > 0 && (u_isWhitespace(ch = Text.iterateCodePoints(&pos, -1)) || isZWSP(ch) ||
90                             ! (ch == 0x002E || u_isalnum(ch)))) nPos=pos;
91             break;
92             case WordType::WORD_COUNT:
93                 if (bDirection)
94                     while (nPos < len && (u_isUWhiteSpace(ch = Text.iterateCodePoints(&pos, 1)) || isZWSP(ch))) nPos=pos;
95                 else
96                     while (nPos > 0 && (u_isUWhiteSpace(ch = Text.iterateCodePoints(&pos, -1)) || isZWSP(ch))) nPos=pos;
97             break;
98         }
99         return nPos;
100 }
101 
nextWord(const OUString & Text,sal_Int32 nStartPos,const Locale & rLocale,sal_Int16 rWordType)102 Boundary SAL_CALL BreakIteratorImpl::nextWord( const OUString& Text, sal_Int32 nStartPos,
103         const Locale& rLocale, sal_Int16 rWordType )
104 {
105         sal_Int32 len = Text.getLength();
106         if( nStartPos < 0 || len == 0 )
107             result.endPos = result.startPos = 0;
108         else if (nStartPos >= len)
109             result.endPos = result.startPos = len;
110         else {
111             result = LBI->nextWord(Text, nStartPos, rLocale, rWordType);
112 
113             nStartPos = skipSpace(Text, result.startPos, len, rWordType, sal_True);
114 
115             if ( nStartPos != result.startPos) {
116                 if( nStartPos >= len )
117                     result.startPos = result.endPos = len;
118                 else {
119                     result = LBI->getWordBoundary(Text, nStartPos, rLocale, rWordType, sal_True);
120                     // i88041: avoid startPos goes back to nStartPos when switching between Latin and CJK scripts
121                     if (result.startPos < nStartPos) result.startPos = nStartPos;
122                 }
123             }
124         }
125         return result;
126 }
127 
isCJK(const Locale & rLocale)128 static inline sal_Bool SAL_CALL isCJK( const Locale& rLocale ) {
129         return rLocale.Language.equalsAscii("zh") || rLocale.Language.equalsAscii("ja") || rLocale.Language.equalsAscii("ko");
130 }
131 
previousWord(const OUString & Text,sal_Int32 nStartPos,const Locale & rLocale,sal_Int16 rWordType)132 Boundary SAL_CALL BreakIteratorImpl::previousWord( const OUString& Text, sal_Int32 nStartPos,
133         const Locale& rLocale, sal_Int16 rWordType)
134 {
135         sal_Int32 len = Text.getLength();
136         if( nStartPos <= 0 || len == 0 ) {
137             result.endPos = result.startPos = 0;
138             return result;
139         } else if (nStartPos > len) {
140             result.endPos = result.startPos = len;
141             return result;
142         }
143 
144         sal_Int32 nPos = skipSpace(Text, nStartPos, len, rWordType, sal_False);
145 
146         // if some spaces are skipped, and the script type is Asian with no CJK rLocale, we have to return
147         // (nStartPos, -1) for caller to send correct rLocale for loading correct dictionary.
148         result.startPos = nPos;
149         if (nPos != nStartPos && nPos > 0 && !isCJK(rLocale) && getScriptClass(Text.iterateCodePoints(&nPos, -1)) == ScriptType::ASIAN) {
150             result.endPos = -1;
151             return result;
152         }
153 
154         return LBI->previousWord(Text, result.startPos, rLocale, rWordType);
155 }
156 
157 
getWordBoundary(const OUString & Text,sal_Int32 nPos,const Locale & rLocale,sal_Int16 rWordType,sal_Bool bDirection)158 Boundary SAL_CALL BreakIteratorImpl::getWordBoundary( const OUString& Text, sal_Int32 nPos, const Locale& rLocale,
159         sal_Int16 rWordType, sal_Bool bDirection )
160 {
161         sal_Int32 len = Text.getLength();
162         if( nPos < 0 || len == 0 )
163             result.endPos = result.startPos = 0;
164         else if (nPos > len)
165             result.endPos = result.startPos = len;
166         else {
167             sal_Int32 next, prev;
168             next = skipSpace(Text, nPos, len, rWordType, sal_True);
169             prev = skipSpace(Text, nPos, len, rWordType, sal_False);
170             if (prev == 0 && next == len) {
171                 result.endPos = result.startPos = nPos;
172             } else if (prev == 0 && ! bDirection) {
173                 result.endPos = result.startPos = 0;
174             } else if (next == len && bDirection) {
175                 result.endPos = result.startPos = len;
176             } else {
177                 if (next != prev) {
178                     if (next == nPos && next != len)
179                         bDirection = sal_True;
180                     else if (prev == nPos && prev != 0)
181                         bDirection = sal_False;
182                     else
183                         nPos = bDirection ? next : prev;
184                 }
185                 result = LBI->getWordBoundary(Text, nPos, rLocale, rWordType, bDirection);
186             }
187         }
188         return result;
189 }
190 
isBeginWord(const OUString & Text,sal_Int32 nPos,const Locale & rLocale,sal_Int16 rWordType)191 sal_Bool SAL_CALL BreakIteratorImpl::isBeginWord( const OUString& Text, sal_Int32 nPos,
192         const Locale& rLocale, sal_Int16 rWordType )
193 {
194         sal_Int32 len = Text.getLength();
195 
196         if (nPos < 0 || nPos >= len) return sal_False;
197 
198         sal_Int32 tmp = skipSpace(Text, nPos, len, rWordType, sal_True);
199 
200         if (tmp != nPos) return sal_False;
201 
202         result = getWordBoundary(Text, nPos, rLocale, rWordType, sal_True);
203 
204         return result.startPos == nPos;
205 }
206 
isEndWord(const OUString & Text,sal_Int32 nPos,const Locale & rLocale,sal_Int16 rWordType)207 sal_Bool SAL_CALL BreakIteratorImpl::isEndWord( const OUString& Text, sal_Int32 nPos,
208         const Locale& rLocale, sal_Int16 rWordType )
209 {
210         sal_Int32 len = Text.getLength();
211 
212         if (nPos <= 0 || nPos > len) return sal_False;
213 
214         sal_Int32 tmp = skipSpace(Text, nPos, len, rWordType, sal_False);
215 
216         if (tmp != nPos) return sal_False;
217 
218         result = getWordBoundary(Text, nPos, rLocale, rWordType, sal_False);
219 
220         return result.endPos == nPos;
221 }
222 
beginOfSentence(const OUString & Text,sal_Int32 nStartPos,const Locale & rLocale)223 sal_Int32 SAL_CALL BreakIteratorImpl::beginOfSentence( const OUString& Text, sal_Int32 nStartPos,
224         const Locale &rLocale )
225 {
226         if (nStartPos < 0 || nStartPos > Text.getLength())
227             return -1;
228         if (Text.getLength() == 0) return 0;
229         return LBI->beginOfSentence(Text, nStartPos, rLocale);
230 }
231 
endOfSentence(const OUString & Text,sal_Int32 nStartPos,const Locale & rLocale)232 sal_Int32 SAL_CALL BreakIteratorImpl::endOfSentence( const OUString& Text, sal_Int32 nStartPos,
233         const Locale &rLocale )
234 {
235         if (nStartPos < 0 || nStartPos > Text.getLength())
236             return -1;
237         if (Text.getLength() == 0) return 0;
238         return LBI->endOfSentence(Text, nStartPos, rLocale);
239 }
240 
getLineBreak(const OUString & Text,sal_Int32 nStartPos,const Locale & rLocale,sal_Int32 nMinBreakPos,const LineBreakHyphenationOptions & hOptions,const LineBreakUserOptions & bOptions)241 LineBreakResults SAL_CALL BreakIteratorImpl::getLineBreak( const OUString& Text, sal_Int32 nStartPos,
242         const Locale& rLocale, sal_Int32 nMinBreakPos, const LineBreakHyphenationOptions& hOptions,
243         const LineBreakUserOptions& bOptions )
244 {
245         return LBI->getLineBreak(Text, nStartPos, rLocale, nMinBreakPos, hOptions, bOptions);
246 }
247 
getScriptType(const OUString & Text,sal_Int32 nPos)248 sal_Int16 SAL_CALL BreakIteratorImpl::getScriptType( const OUString& Text, sal_Int32 nPos )
249 {
250         return (nPos < 0 || nPos >= Text.getLength()) ? ScriptType::WEAK :
251                             getScriptClass(Text.iterateCodePoints(&nPos, 0));
252 }
253 
254 
255 /** Increments/decrements position first, then obtains character.
256     @return current position, may be -1 or text length if string was consumed.
257  */
iterateCodePoints(const OUString & Text,sal_Int32 & nStartPos,sal_Int32 inc,sal_uInt32 & ch)258 static sal_Int32 SAL_CALL iterateCodePoints(const OUString& Text, sal_Int32 &nStartPos, sal_Int32 inc, sal_uInt32& ch) {
259         sal_Int32 nLen = Text.getLength();
260         if (nStartPos + inc < 0 || nStartPos + inc >= nLen) {
261             ch = 0;
262             nStartPos = nStartPos + inc < 0 ? -1 : nLen;
263         } else {
264             ch = Text.iterateCodePoints(&nStartPos, inc);
265             // Fix for #i80436#.
266             // erAck: 2009-06-30T21:52+0200  This logic looks somewhat
267             // suspicious as if it cures a symptom.. anyway, had to add
268             // nStartPos < Text.getLength() to silence the (correct) assertion
269             // in rtl_uString_iterateCodePoints() if Text was one character
270             // (codepoint) only, made up of a surrogate pair.
271             //if (inc > 0 && nStartPos < Text.getLength())
272             //    ch = Text.iterateCodePoints(&nStartPos, 0);
273             // With surrogates, nStartPos may actually point behind string
274             // now, even if inc is only +1
275             if (inc > 0)
276                 ch = (nStartPos < nLen ? Text.iterateCodePoints(&nStartPos, 0) : 0);
277         }
278         return nStartPos;
279 }
280 
281 
beginOfScript(const OUString & Text,sal_Int32 nStartPos,sal_Int16 ScriptType)282 sal_Int32 SAL_CALL BreakIteratorImpl::beginOfScript( const OUString& Text,
283         sal_Int32 nStartPos, sal_Int16 ScriptType )
284 {
285         if (nStartPos < 0 || nStartPos >= Text.getLength())
286             return -1;
287 
288         if(ScriptType != getScriptClass(Text.iterateCodePoints(&nStartPos, 0)))
289             return -1;
290 
291         if (nStartPos == 0) return 0;
292         sal_uInt32 ch=0;
293         while (iterateCodePoints(Text, nStartPos, -1, ch) >= 0 && ScriptType == getScriptClass(ch)) {
294             if (nStartPos == 0) return 0;
295         }
296 
297         return  iterateCodePoints(Text, nStartPos, 1, ch);
298 }
299 
endOfScript(const OUString & Text,sal_Int32 nStartPos,sal_Int16 ScriptType)300 sal_Int32 SAL_CALL BreakIteratorImpl::endOfScript( const OUString& Text,
301         sal_Int32 nStartPos, sal_Int16 ScriptType )
302 {
303         if (nStartPos < 0 || nStartPos >= Text.getLength())
304             return -1;
305 
306         if(ScriptType != getScriptClass(Text.iterateCodePoints(&nStartPos, 0)))
307             return -1;
308 
309         sal_Int32 strLen = Text.getLength();
310         sal_uInt32 ch=0;
311         while(iterateCodePoints(Text, nStartPos, 1, ch) < strLen ) {
312             sal_Int16 currentCharScriptType = getScriptClass(ch);
313             if(ScriptType != currentCharScriptType && currentCharScriptType != ScriptType::WEAK)
314                 break;
315         }
316         return  nStartPos;
317 }
318 
previousScript(const OUString & Text,sal_Int32 nStartPos,sal_Int16 ScriptType)319 sal_Int32  SAL_CALL BreakIteratorImpl::previousScript( const OUString& Text,
320         sal_Int32 nStartPos, sal_Int16 ScriptType )
321 {
322         if (nStartPos < 0)
323             return -1;
324         if (nStartPos > Text.getLength())
325             nStartPos = Text.getLength();
326 
327         sal_Int16 numberOfChange = (ScriptType == getScriptClass(Text.iterateCodePoints(&nStartPos, 0))) ? 3 : 2;
328 
329         sal_uInt32 ch=0;
330         while (numberOfChange > 0 && iterateCodePoints(Text, nStartPos, -1, ch) >= 0) {
331             if ((((numberOfChange % 2) == 0) ^ (ScriptType != getScriptClass(ch))))
332                 numberOfChange--;
333             else if (nStartPos == 0) {
334                 if (numberOfChange > 0)
335                     numberOfChange--;
336                 if (nStartPos > 0)
337                     Text.iterateCodePoints(&nStartPos, -1);
338                 else
339                     return -1;
340             }
341         }
342         return numberOfChange == 0 ? iterateCodePoints(Text, nStartPos, 1, ch) : -1;
343 }
344 
nextScript(const OUString & Text,sal_Int32 nStartPos,sal_Int16 ScriptType)345 sal_Int32 SAL_CALL BreakIteratorImpl::nextScript( const OUString& Text, sal_Int32 nStartPos,
346         sal_Int16 ScriptType )
347 
348 {
349         if (nStartPos < 0)
350             nStartPos = 0;
351         sal_Int32 strLen = Text.getLength();
352         if (nStartPos > strLen)
353             return -1;
354 
355         sal_Int16 numberOfChange = (ScriptType == getScriptClass(Text.iterateCodePoints(&nStartPos, 0))) ? 2 : 1;
356 
357         sal_uInt32 ch=0;
358         while (numberOfChange > 0 && iterateCodePoints(Text, nStartPos, 1, ch) < strLen) {
359             sal_Int16 currentCharScriptType = getScriptClass(ch);
360             if ((numberOfChange == 1) ? (ScriptType == currentCharScriptType) :
361                     (ScriptType != currentCharScriptType && currentCharScriptType != ScriptType::WEAK))
362                 numberOfChange--;
363         }
364         return numberOfChange == 0 ? nStartPos : -1;
365 }
366 
beginOfCharBlock(const OUString & Text,sal_Int32 nStartPos,const Locale &,sal_Int16 CharType)367 sal_Int32 SAL_CALL BreakIteratorImpl::beginOfCharBlock( const OUString& Text, sal_Int32 nStartPos,
368         const Locale& /*rLocale*/, sal_Int16 CharType )
369 {
370         if (CharType == CharType::ANY_CHAR) return 0;
371         if (nStartPos < 0 || nStartPos >= Text.getLength()) return -1;
372         if (CharType != (sal_Int16)u_charType( Text.iterateCodePoints(&nStartPos, 0))) return -1;
373 
374         sal_Int32 nPos=nStartPos;
375         while(nStartPos > 0 && CharType == (sal_Int16)u_charType(Text.iterateCodePoints(&nPos, -1))) { nStartPos=nPos; }
376         return nStartPos; // begin of char block is inclusive
377 }
378 
endOfCharBlock(const OUString & Text,sal_Int32 nStartPos,const Locale &,sal_Int16 CharType)379 sal_Int32 SAL_CALL BreakIteratorImpl::endOfCharBlock( const OUString& Text, sal_Int32 nStartPos,
380         const Locale& /*rLocale*/, sal_Int16 CharType )
381 {
382         sal_Int32 strLen = Text.getLength();
383 
384         if (CharType == CharType::ANY_CHAR) return strLen; // end of char block is exclusive
385         if (nStartPos < 0 || nStartPos >= strLen) return -1;
386         if (CharType != (sal_Int16)u_charType(Text.iterateCodePoints(&nStartPos, 0))) return -1;
387 
388         sal_uInt32 ch=0;
389         while(iterateCodePoints(Text, nStartPos, 1, ch) < strLen && CharType == (sal_Int16)u_charType(ch)) {}
390         return nStartPos; // end of char block is exclusive
391 }
392 
nextCharBlock(const OUString & Text,sal_Int32 nStartPos,const Locale &,sal_Int16 CharType)393 sal_Int32 SAL_CALL BreakIteratorImpl::nextCharBlock( const OUString& Text, sal_Int32 nStartPos,
394         const Locale& /*rLocale*/, sal_Int16 CharType )
395 {
396         if (CharType == CharType::ANY_CHAR) return -1;
397         if (nStartPos < 0 || nStartPos >= Text.getLength()) return -1;
398 
399         sal_Int16 numberOfChange = (CharType == (sal_Int16)u_charType(Text.iterateCodePoints(&nStartPos, 0))) ? 2 : 1;
400         sal_Int32 strLen = Text.getLength();
401 
402     sal_uInt32 ch=0;
403     while (numberOfChange > 0 && iterateCodePoints(Text, nStartPos, 1, ch) < strLen) {
404         if ((CharType != (sal_Int16)u_charType(ch)) ^ (numberOfChange == 1))
405             numberOfChange--;
406     }
407     return numberOfChange == 0 ? nStartPos : -1;
408 }
409 
previousCharBlock(const OUString & Text,sal_Int32 nStartPos,const Locale &,sal_Int16 CharType)410 sal_Int32 SAL_CALL BreakIteratorImpl::previousCharBlock( const OUString& Text, sal_Int32 nStartPos,
411         const Locale& /*rLocale*/, sal_Int16 CharType )
412 {
413         if(CharType == CharType::ANY_CHAR) return -1;
414         if (nStartPos < 0 || nStartPos >= Text.getLength()) return -1;
415 
416         sal_Int16 numberOfChange = (CharType == (sal_Int16)u_charType(Text.iterateCodePoints(&nStartPos, 0))) ? 3 : 2;
417 
418         sal_uInt32 ch=0;
419         while (numberOfChange > 0 && iterateCodePoints(Text, nStartPos, -1, ch) >= 0) {
420             if (((numberOfChange % 2) == 0) ^ (CharType != (sal_Int16)u_charType(ch)))
421                 numberOfChange--;
422             if (nStartPos == 0 && numberOfChange > 0) {
423                 numberOfChange--;
424                 if (numberOfChange == 0) return nStartPos;
425             }
426         }
427         return numberOfChange == 0 ? iterateCodePoints(Text, nStartPos, 1, ch) : -1;
428 }
429 
430 
431 
getWordType(const OUString &,sal_Int32,const Locale &)432 sal_Int16 SAL_CALL BreakIteratorImpl::getWordType( const OUString& /*Text*/,
433         sal_Int32 /*nPos*/, const Locale& /*rLocale*/ )
434 {
435         return 0;
436 }
437 
438 typedef struct {
439     UBlockCode from;
440     UBlockCode to;
441     sal_Int16 script;
442 } UBlock2Script;
443 
444 // for a list of the UBLOCK_... values see:
445 // http://icu-project.org/apiref/icu4c/uchar_8h.html
446 // where enum UBlockCode is defined.
447 // See also http://www.unicode.org/charts/ for general reference
448 static UBlock2Script scriptList[] = {
449     {UBLOCK_NO_BLOCK, UBLOCK_NO_BLOCK, ScriptType::WEAK},
450     {UBLOCK_BASIC_LATIN, UBLOCK_ARMENIAN, ScriptType::LATIN},
451     {UBLOCK_HEBREW, UBLOCK_MYANMAR, ScriptType::COMPLEX},
452     {UBLOCK_GEORGIAN, UBLOCK_GEORGIAN, ScriptType::LATIN},
453     {UBLOCK_HANGUL_JAMO, UBLOCK_HANGUL_JAMO, ScriptType::ASIAN},
454     {UBLOCK_ETHIOPIC, UBLOCK_ETHIOPIC, ScriptType::COMPLEX},
455     {UBLOCK_CHEROKEE, UBLOCK_RUNIC, ScriptType::LATIN},
456     {UBLOCK_KHMER, UBLOCK_MONGOLIAN, ScriptType::COMPLEX},
457     {UBLOCK_LATIN_EXTENDED_ADDITIONAL, UBLOCK_GREEK_EXTENDED, ScriptType::LATIN},
458     {UBLOCK_CJK_RADICALS_SUPPLEMENT, UBLOCK_HANGUL_SYLLABLES, ScriptType::ASIAN},
459     {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS, ScriptType::ASIAN},
460     {UBLOCK_ARABIC_PRESENTATION_FORMS_A, UBLOCK_ARABIC_PRESENTATION_FORMS_A, ScriptType::COMPLEX},
461     {UBLOCK_CJK_COMPATIBILITY_FORMS, UBLOCK_CJK_COMPATIBILITY_FORMS, ScriptType::ASIAN},
462     {UBLOCK_ARABIC_PRESENTATION_FORMS_B, UBLOCK_ARABIC_PRESENTATION_FORMS_B, ScriptType::COMPLEX},
463     {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS, ScriptType::ASIAN},
464     {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT, ScriptType::ASIAN},
465     {UBLOCK_CJK_STROKES, UBLOCK_CJK_STROKES, ScriptType::ASIAN},
466     {UBLOCK_LATIN_EXTENDED_C, UBLOCK_LATIN_EXTENDED_D, ScriptType::LATIN}
467 };
468 
469 #define scriptListCount sizeof (scriptList) / sizeof (UBlock2Script)
470 
getScriptClass(sal_uInt32 currentChar)471 sal_Int16  BreakIteratorImpl::getScriptClass(sal_uInt32 currentChar)
472 {
473         static sal_uInt32 lastChar = 0;
474         static sal_Int16 nRet = 0;
475 
476         if (currentChar != lastChar) {
477             lastChar = currentChar;
478 
479             //JP 21.9.2001: handle specific characters - always as weak
480             //                  definition of 1 - this breaks a word
481             //                  2 - this can be inside a word
482             //                  0x20 & 0xA0 - Bug 102975, declare western space and non-break space as WEAK char.
483             if( 1 == currentChar || 2 == currentChar || 0x20 == currentChar || 0xA0 == currentChar)
484                 nRet = ScriptType::WEAK;
485             // workaround for Coptic
486             else if ( 0x2C80 <= currentChar && 0x2CE3 >= currentChar)
487                 nRet = ScriptType::LATIN;
488             // work-around for ligatures (see http://www.unicode.org/charts/PDF/UFB00.pdf)
489             else if ((0xFB00 <= currentChar && currentChar <= 0xFB06) ||
490                      (0xFB13 <= currentChar && currentChar <= 0xFB17))
491                 nRet = ScriptType::LATIN;
492             else {
493                 UBlockCode block=ublock_getCode(currentChar);
494                 sal_uInt16 i;
495                 for ( i = 0; i < scriptListCount; i++) {
496                     if (block <= scriptList[i].to) break;
497                 }
498                 nRet=(i < scriptListCount && block >= scriptList[i].from) ? scriptList[i].script : ScriptType::WEAK;
499             }
500         }
501         return nRet;
502 }
503 
operator ==(const Locale & l1,const Locale & l2)504 static inline sal_Bool operator == (const Locale& l1, const Locale& l2) {
505         return l1.Language == l2.Language && l1.Country == l2.Country && l1.Variant == l2.Variant;
506 }
507 
createLocaleSpecificBreakIterator(const OUString & aLocaleName)508 sal_Bool SAL_CALL BreakIteratorImpl::createLocaleSpecificBreakIterator(const OUString& aLocaleName)
509 {
510         // to share service between same Language but different Country code, like zh_CN and zh_TW
511         for (size_t l = 0; l < lookupTable.size(); l++) {
512             lookupTableItem *listItem = lookupTable[l];
513             if (aLocaleName == listItem->aLocale.Language) {
514                 xBI = listItem->xBI;
515                 return sal_True;
516             }
517         }
518 
519         Reference < uno::XInterface > xI = xMSF->createInstance(
520             OUString::createFromAscii("com.sun.star.i18n.BreakIterator_") + aLocaleName);
521 
522         if ( xI.is() ) {
523             xI->queryInterface( getCppuType((const Reference< XBreakIterator>*)0) ) >>= xBI;
524             if (xBI.is()) {
525                 lookupTable.push_back(new lookupTableItem(Locale(aLocaleName, aLocaleName, aLocaleName), xBI));
526                 return sal_True;
527             }
528         }
529         return sal_False;
530 }
531 
532 Reference < XBreakIterator > SAL_CALL
getLocaleSpecificBreakIterator(const Locale & rLocale)533 BreakIteratorImpl::getLocaleSpecificBreakIterator(const Locale& rLocale)
534 {
535         if (xBI.is() && rLocale == aLocale)
536             return xBI;
537         else if (xMSF.is()) {
538             aLocale = rLocale;
539 
540             for (size_t i = 0; i < lookupTable.size(); i++) {
541                 lookupTableItem *listItem = lookupTable[i];
542                 if (rLocale == listItem->aLocale)
543                     return xBI = listItem->xBI;
544             }
545 
546             sal_Unicode under = (sal_Unicode)'_';
547 
548             sal_Int32 l = rLocale.Language.getLength();
549             sal_Int32 c = rLocale.Country.getLength();
550             sal_Int32 v = rLocale.Variant.getLength();
551             OUStringBuffer aBuf(l+c+v+3);
552 
553             if ((l > 0 && c > 0 && v > 0 &&
554                     // load service with name <base>_<lang>_<country>_<varian>
555                     createLocaleSpecificBreakIterator(aBuf.append(rLocale.Language).append(under).append(
556                                     rLocale.Country).append(under).append(rLocale.Variant).makeStringAndClear())) ||
557                 (l > 0 && c > 0 &&
558                     // load service with name <base>_<lang>_<country>
559                     createLocaleSpecificBreakIterator(aBuf.append(rLocale.Language).append(under).append(
560                                     rLocale.Country).makeStringAndClear())) ||
561                 (l > 0 && c > 0 && rLocale.Language.compareToAscii("zh") == 0 &&
562                                     (rLocale.Country.compareToAscii("HK") == 0 ||
563                                     rLocale.Country.compareToAscii("MO") == 0) &&
564                     // if the country code is HK or MO, one more step to try TW.
565                     createLocaleSpecificBreakIterator(aBuf.append(rLocale.Language).append(under).appendAscii(
566                                     "TW").makeStringAndClear())) ||
567                 (l > 0 &&
568                     // load service with name <base>_<lang>
569                     createLocaleSpecificBreakIterator(rLocale.Language)) ||
570                     // load default service with name <base>_Unicode
571                     createLocaleSpecificBreakIterator(OUString::createFromAscii("Unicode"))) {
572                 lookupTable.push_back( new lookupTableItem(aLocale, xBI) );
573                 return xBI;
574             }
575         }
576         throw RuntimeException();
577 }
578 
579 const sal_Char cBreakIterator[] = "com.sun.star.i18n.BreakIterator";
580 
581 OUString SAL_CALL
getImplementationName(void)582 BreakIteratorImpl::getImplementationName(void)
583 {
584         return OUString::createFromAscii(cBreakIterator);
585 }
586 
587 sal_Bool SAL_CALL
supportsService(const OUString & rServiceName)588 BreakIteratorImpl::supportsService(const OUString& rServiceName)
589 {
590         return !rServiceName.compareToAscii(cBreakIterator);
591 }
592 
593 Sequence< OUString > SAL_CALL
getSupportedServiceNames(void)594 BreakIteratorImpl::getSupportedServiceNames(void)
595 {
596         Sequence< OUString > aRet(1);
597         aRet[0] = OUString::createFromAscii(cBreakIterator);
598         return aRet;
599 }
600 
601 } } } }
602