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