xref: /trunk/main/i18npool/source/breakiterator/breakiterator_unicode.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 #include <breakiterator_unicode.hxx>
27 #include <localedata.hxx>
28 #include <unicode/uchar.h>
29 #include <unicode/locid.h>
30 #include <unicode/rbbi.h>
31 #include <unicode/udata.h>
32 #include <rtl/strbuf.hxx>
33 #include <rtl/ustring.hxx>
34 
35 U_CDECL_BEGIN
36 extern const char OpenOffice_dat[];
37 U_CDECL_END
38 
39 using namespace ::com::sun::star;
40 using namespace ::com::sun::star::lang;
41 using namespace ::rtl;
42 
43 namespace com { namespace sun { namespace star { namespace i18n {
44 
45 #define ERROR ::com::sun::star::uno::RuntimeException()
46 
47 //#define ImplementName "com.sun.star.i18n.BreakIterator_Unicode";
48 
49 
BreakIterator_Unicode()50 BreakIterator_Unicode::BreakIterator_Unicode() :
51     cBreakIterator( "com.sun.star.i18n.BreakIterator_Unicode" ),    // implementation name
52     wordRule( "word" ),
53     lineRule( "line" ),
54     result(),
55     character(),
56     word(),
57     sentence(),
58     line(),
59     icuBI( NULL ),
60     aLocale(),
61     aBreakType(),
62     aWordType()
63 {
64 }
65 
66 
~BreakIterator_Unicode()67 BreakIterator_Unicode::~BreakIterator_Unicode()
68 {
69         if (icuBI && icuBI->aBreakIterator) {
70             delete icuBI->aBreakIterator;
71             icuBI->aBreakIterator=NULL;
72         }
73         if (character.aBreakIterator) delete character.aBreakIterator;
74         if (word.aBreakIterator) delete word.aBreakIterator;
75         if (sentence.aBreakIterator) delete sentence.aBreakIterator;
76         if (line.aBreakIterator) delete line.aBreakIterator;
77 }
78 
79 /*
80     Wrapper class to provide public access to the RuleBasedBreakIterator's
81     setbreakType method.
82 */
83 class OOoRuleBasedBreakIterator : public RuleBasedBreakIterator {
84     public:
publicSetBreakType(int32_t type)85         inline void publicSetBreakType(int32_t type) {
86             setBreakType(type);
87         };
OOoRuleBasedBreakIterator(UDataMemory * image,UErrorCode & status)88         OOoRuleBasedBreakIterator(UDataMemory* image,
89                 UErrorCode &status) :
90             RuleBasedBreakIterator(image, status) { };
91 
92 };
93 
94 // loading ICU breakiterator on demand.
loadICUBreakIterator(const com::sun::star::lang::Locale & rLocale,sal_Int16 rBreakType,sal_Int16 rWordType,const sal_Char * rule,const OUString & rText)95 void SAL_CALL BreakIterator_Unicode::loadICUBreakIterator(const com::sun::star::lang::Locale& rLocale,
96         sal_Int16 rBreakType, sal_Int16 rWordType, const sal_Char *rule, const OUString& rText)
97 {
98     sal_Bool newBreak = sal_False;
99     UErrorCode status = U_ZERO_ERROR;
100     sal_Int16 breakType = 0;
101     switch (rBreakType) {
102         case LOAD_CHARACTER_BREAKITERATOR: icuBI=&character; breakType = 3; break;
103         case LOAD_WORD_BREAKITERATOR: icuBI=&word;
104             switch (rWordType) {
105                 case WordType::ANYWORD_IGNOREWHITESPACES: breakType = 0; rule=wordRule = "edit_word"; break;
106                 case WordType::DICTIONARY_WORD: breakType = 1; rule=wordRule = "dict_word"; break;
107                 case WordType::WORD_COUNT: breakType = 2; rule=wordRule = "count_word"; break;
108             }
109             break;
110         case LOAD_SENTENCE_BREAKITERATOR: icuBI=&sentence; breakType = 5; break;
111         case LOAD_LINE_BREAKITERATOR: icuBI=&line; breakType = 4; break;
112     }
113     if (!icuBI->aBreakIterator || rWordType != aWordType ||
114             rLocale.Language != aLocale.Language || rLocale.Country != aLocale.Country ||
115             rLocale.Variant != aLocale.Variant) {
116         if (icuBI->aBreakIterator) {
117             delete icuBI->aBreakIterator;
118             icuBI->aBreakIterator=NULL;
119         }
120         if (rule) {
121             uno::Sequence< OUString > breakRules = LocaleData().getBreakIteratorRules(rLocale);
122 
123             status = U_ZERO_ERROR;
124             udata_setAppData("OpenOffice", OpenOffice_dat, &status);
125             if ( !U_SUCCESS(status) ) throw ERROR;
126 
127             OOoRuleBasedBreakIterator *rbi = NULL;
128 
129             if (breakRules.getLength() > breakType && breakRules[breakType].getLength() > 0) {
130                 rbi = new OOoRuleBasedBreakIterator(udata_open("OpenOffice", "brk",
131                     OUStringToOString(breakRules[breakType], RTL_TEXTENCODING_ASCII_US).getStr(), &status), status);
132             } else {
133                 status = U_ZERO_ERROR;
134                 OStringBuffer aUDName(64);
135                 aUDName.append(rule);
136                 aUDName.append('_');
137                 aUDName.append( OUStringToOString(rLocale.Language, RTL_TEXTENCODING_ASCII_US));
138                 UDataMemory* pUData = udata_open("OpenOffice", "brk", aUDName.getStr(), &status);
139                 if( U_SUCCESS(status) )
140                     rbi = new OOoRuleBasedBreakIterator( pUData, status);
141                 if (!U_SUCCESS(status) ) {
142                     status = U_ZERO_ERROR;
143                     pUData = udata_open("OpenOffice", "brk", rule, &status);
144                     if( U_SUCCESS(status) )
145                         rbi = new OOoRuleBasedBreakIterator( pUData, status);
146                     if (!U_SUCCESS(status) ) icuBI->aBreakIterator=NULL;
147                 }
148             }
149             if (rbi) {
150                 switch (rBreakType) {
151                     case LOAD_CHARACTER_BREAKITERATOR: rbi->publicSetBreakType(UBRK_CHARACTER); break;
152                     case LOAD_WORD_BREAKITERATOR: rbi->publicSetBreakType(UBRK_WORD); break;
153                     case LOAD_SENTENCE_BREAKITERATOR: rbi->publicSetBreakType(UBRK_SENTENCE); break;
154                     case LOAD_LINE_BREAKITERATOR: rbi->publicSetBreakType(UBRK_LINE); break;
155                 }
156                 icuBI->aBreakIterator = rbi;
157             }
158         }
159 
160         if (!icuBI->aBreakIterator) {
161             icu::Locale icuLocale(
162                     OUStringToOString(rLocale.Language, RTL_TEXTENCODING_ASCII_US).getStr(),
163                     OUStringToOString(rLocale.Country, RTL_TEXTENCODING_ASCII_US).getStr(),
164                     OUStringToOString(rLocale.Variant, RTL_TEXTENCODING_ASCII_US).getStr());
165 
166             status = U_ZERO_ERROR;
167             switch (rBreakType) {
168                 case LOAD_CHARACTER_BREAKITERATOR:
169                     icuBI->aBreakIterator =  icu::BreakIterator::createCharacterInstance(icuLocale, status);
170                     break;
171                 case LOAD_WORD_BREAKITERATOR:
172                     icuBI->aBreakIterator =  icu::BreakIterator::createWordInstance(icuLocale, status);
173                     break;
174                 case LOAD_SENTENCE_BREAKITERATOR:
175                     icuBI->aBreakIterator = icu::BreakIterator::createSentenceInstance(icuLocale, status);
176                     break;
177                 case LOAD_LINE_BREAKITERATOR:
178                     icuBI->aBreakIterator = icu::BreakIterator::createLineInstance(icuLocale, status);
179                     break;
180             }
181             if ( !U_SUCCESS(status) ) {
182                 icuBI->aBreakIterator=NULL;
183                 throw ERROR;
184             }
185         }
186         if (icuBI->aBreakIterator) {
187             aLocale=rLocale;
188             aWordType=rWordType;
189             aBreakType=rBreakType;
190             newBreak=sal_True;
191         } else {
192             throw ERROR;
193         }
194     }
195 
196     if (newBreak || icuBI->aICUText.compare(UnicodeString(reinterpret_cast<const UChar *>(rText.getStr()), rText.getLength()))) {   // UChar != sal_Unicode in MinGW
197         icuBI->aICUText=UnicodeString(reinterpret_cast<const UChar *>(rText.getStr()), rText.getLength());
198         icuBI->aBreakIterator->setText(icuBI->aICUText);
199     }
200 }
201 
202 
nextCharacters(const OUString & Text,sal_Int32 nStartPos,const lang::Locale & rLocale,sal_Int16 nCharacterIteratorMode,sal_Int32 nCount,sal_Int32 & nDone)203 sal_Int32 SAL_CALL BreakIterator_Unicode::nextCharacters( const OUString& Text,
204         sal_Int32 nStartPos, const lang::Locale &rLocale,
205         sal_Int16 nCharacterIteratorMode, sal_Int32 nCount, sal_Int32& nDone )
206 {
207         if (nCharacterIteratorMode == CharacterIteratorMode::SKIPCELL ) { // for CELL mode
208             loadICUBreakIterator(rLocale, LOAD_CHARACTER_BREAKITERATOR, 0, "char", Text);
209             for (nDone = 0; nDone < nCount; nDone++) {
210                 nStartPos = character.aBreakIterator->following(nStartPos);
211                 if (nStartPos == BreakIterator::DONE)
212                     return Text.getLength();
213             }
214         } else { // for CHARACTER mode
215             for (nDone = 0; nDone < nCount && nStartPos < Text.getLength(); nDone++)
216                 Text.iterateCodePoints(&nStartPos, 1);
217         }
218         return nStartPos;
219 }
220 
previousCharacters(const OUString & Text,sal_Int32 nStartPos,const lang::Locale & rLocale,sal_Int16 nCharacterIteratorMode,sal_Int32 nCount,sal_Int32 & nDone)221 sal_Int32 SAL_CALL BreakIterator_Unicode::previousCharacters( const OUString& Text,
222         sal_Int32 nStartPos, const lang::Locale& rLocale,
223         sal_Int16 nCharacterIteratorMode, sal_Int32 nCount, sal_Int32& nDone )
224 {
225         if (nCharacterIteratorMode == CharacterIteratorMode::SKIPCELL ) { // for CELL mode
226             loadICUBreakIterator(rLocale, LOAD_CHARACTER_BREAKITERATOR, 0, "char", Text);
227             for (nDone = 0; nDone < nCount; nDone++) {
228                 nStartPos = character.aBreakIterator->preceding(nStartPos);
229                 if (nStartPos == BreakIterator::DONE)
230                     return 0;
231             }
232         } else { // for BS to delete one char and CHARACTER mode.
233             for (nDone = 0; nDone < nCount && nStartPos > 0; nDone++)
234                 Text.iterateCodePoints(&nStartPos, -1);
235         }
236         return nStartPos;
237 }
238 
239 
nextWord(const OUString & Text,sal_Int32 nStartPos,const lang::Locale & rLocale,sal_Int16 rWordType)240 Boundary SAL_CALL BreakIterator_Unicode::nextWord( const OUString& Text, sal_Int32 nStartPos,
241     const lang::Locale& rLocale, sal_Int16 rWordType )
242 {
243         loadICUBreakIterator(rLocale, LOAD_WORD_BREAKITERATOR, rWordType, NULL, Text);
244 
245         result.startPos = word.aBreakIterator->following(nStartPos);
246         if( result.startPos >= Text.getLength() || result.startPos == BreakIterator::DONE )
247             result.endPos = result.startPos;
248         else {
249             if ( (rWordType == WordType::ANYWORD_IGNOREWHITESPACES ||
250                     rWordType == WordType::DICTIONARY_WORD ) &&
251                         u_isWhitespace(Text.iterateCodePoints(&result.startPos, 0)) )
252                 result.startPos = word.aBreakIterator->following(result.startPos);
253 
254             result.endPos = word.aBreakIterator->following(result.startPos);
255             if(result.endPos == BreakIterator::DONE)
256                 result.endPos = result.startPos;
257         }
258         return result;
259 }
260 
261 
previousWord(const OUString & Text,sal_Int32 nStartPos,const lang::Locale & rLocale,sal_Int16 rWordType)262 Boundary SAL_CALL BreakIterator_Unicode::previousWord(const OUString& Text, sal_Int32 nStartPos,
263         const lang::Locale& rLocale, sal_Int16 rWordType)
264 {
265         loadICUBreakIterator(rLocale, LOAD_WORD_BREAKITERATOR, rWordType, NULL, Text);
266 
267         result.startPos = word.aBreakIterator->preceding(nStartPos);
268         if( result.startPos < 0 || result.startPos == BreakIterator::DONE)
269             result.endPos = result.startPos;
270         else {
271             if ( (rWordType == WordType::ANYWORD_IGNOREWHITESPACES ||
272                     rWordType == WordType::DICTIONARY_WORD) &&
273                         u_isWhitespace(Text.iterateCodePoints(&result.startPos, 0)) )
274                 result.startPos = word.aBreakIterator->preceding(result.startPos);
275 
276             result.endPos = word.aBreakIterator->following(result.startPos);
277             if(result.endPos == BreakIterator::DONE)
278                 result.endPos = result.startPos;
279         }
280         return result;
281 }
282 
283 
getWordBoundary(const OUString & Text,sal_Int32 nPos,const lang::Locale & rLocale,sal_Int16 rWordType,sal_Bool bDirection)284 Boundary SAL_CALL BreakIterator_Unicode::getWordBoundary( const OUString& Text, sal_Int32 nPos, const lang::Locale& rLocale,
285         sal_Int16 rWordType, sal_Bool bDirection )
286 {
287         loadICUBreakIterator(rLocale, LOAD_WORD_BREAKITERATOR, rWordType, NULL, Text);
288         sal_Int32 len = Text.getLength();
289 
290         if(word.aBreakIterator->isBoundary(nPos)) {
291             result.startPos = result.endPos = nPos;
292             if((bDirection || nPos == 0) && nPos < len) //forward
293                 result.endPos = word.aBreakIterator->following(nPos);
294             else
295                 result.startPos = word.aBreakIterator->preceding(nPos);
296         } else {
297             if(nPos <= 0) {
298                 result.startPos = 0;
299                 result.endPos = len ? word.aBreakIterator->following((sal_Int32)0) : 0;
300             } else if(nPos >= len) {
301                 result.startPos = word.aBreakIterator->preceding(len);
302                 result.endPos = len;
303             } else {
304                 result.startPos = word.aBreakIterator->preceding(nPos);
305                 result.endPos = word.aBreakIterator->following(nPos);
306             }
307         }
308         if (result.startPos == BreakIterator::DONE)
309             result.startPos = result.endPos;
310         else if (result.endPos == BreakIterator::DONE)
311             result.endPos = result.startPos;
312 
313         return result;
314 }
315 
316 
beginOfSentence(const OUString & Text,sal_Int32 nStartPos,const lang::Locale & rLocale)317 sal_Int32 SAL_CALL BreakIterator_Unicode::beginOfSentence( const OUString& Text, sal_Int32 nStartPos,
318         const lang::Locale &rLocale )
319 {
320         loadICUBreakIterator(rLocale, LOAD_SENTENCE_BREAKITERATOR, 0, "sent", Text);
321 
322         sal_Int32 len = Text.getLength();
323         if (len > 0 && nStartPos == len)
324             Text.iterateCodePoints(&nStartPos, -1); // issue #i27703# treat end position as part of last sentence
325         if (!sentence.aBreakIterator->isBoundary(nStartPos))
326             nStartPos = sentence.aBreakIterator->preceding(nStartPos);
327 
328         // skip preceding space.
329         sal_uInt32 ch = Text.iterateCodePoints(&nStartPos, 1);
330         while (nStartPos < len && u_isWhitespace(ch)) ch = Text.iterateCodePoints(&nStartPos, 1);
331         Text.iterateCodePoints(&nStartPos, -1);
332 
333         return nStartPos;
334 }
335 
endOfSentence(const OUString & Text,sal_Int32 nStartPos,const lang::Locale & rLocale)336 sal_Int32 SAL_CALL BreakIterator_Unicode::endOfSentence( const OUString& Text, sal_Int32 nStartPos,
337         const lang::Locale &rLocale )
338 {
339         loadICUBreakIterator(rLocale, LOAD_SENTENCE_BREAKITERATOR, 0, "sent", Text);
340 
341         sal_Int32 len = Text.getLength();
342         if (len > 0 && nStartPos == len)
343             Text.iterateCodePoints(&nStartPos, -1); // issue #i27703# treat end position as part of last sentence
344         nStartPos = sentence.aBreakIterator->following(nStartPos);
345 
346         sal_Int32 nPos=nStartPos;
347         while (nPos > 0 && u_isWhitespace(Text.iterateCodePoints(&nPos, -1))) nStartPos=nPos;
348 
349         return nStartPos;
350 }
351 
getLineBreak(const OUString & Text,sal_Int32 nStartPos,const lang::Locale & rLocale,sal_Int32 nMinBreakPos,const LineBreakHyphenationOptions & hOptions,const LineBreakUserOptions &)352 LineBreakResults SAL_CALL BreakIterator_Unicode::getLineBreak(
353         const OUString& Text, sal_Int32 nStartPos,
354         const lang::Locale& rLocale, sal_Int32 nMinBreakPos,
355         const LineBreakHyphenationOptions& hOptions,
356         const LineBreakUserOptions& /*rOptions*/ )
357 {
358         LineBreakResults lbr;
359 
360         if (nStartPos >= Text.getLength()) {
361             lbr.breakIndex = Text.getLength();
362             lbr.breakType = BreakType::WORDBOUNDARY;
363             return lbr;
364         }
365 
366         loadICUBreakIterator(rLocale, LOAD_LINE_BREAKITERATOR, 0, lineRule, Text);
367 
368         sal_Bool GlueSpace=sal_True;
369         while (GlueSpace) {
370             if (line.aBreakIterator->preceding(nStartPos + 1) == nStartPos) { //Line boundary break
371                 lbr.breakIndex = nStartPos;
372                 lbr.breakType = BreakType::WORDBOUNDARY;
373             } else if (hOptions.rHyphenator.is()) { //Hyphenation break
374                 Boundary wBoundary = getWordBoundary( Text, nStartPos, rLocale,
375                                                 WordType::DICTIONARY_WORD, false);
376                 uno::Reference< linguistic2::XHyphenatedWord > aHyphenatedWord;
377                 aHyphenatedWord = hOptions.rHyphenator->hyphenate(Text.copy(wBoundary.startPos,
378                     wBoundary.endPos - wBoundary.startPos), rLocale,
379                     (sal_Int16) (hOptions.hyphenIndex - wBoundary.startPos), hOptions.aHyphenationOptions);
380                 if (aHyphenatedWord.is()) {
381                     lbr.rHyphenatedWord = aHyphenatedWord;
382                     if(wBoundary.startPos + aHyphenatedWord->getHyphenationPos() + 1 < nMinBreakPos )
383                         lbr.breakIndex = -1;
384                     else
385                         lbr.breakIndex = wBoundary.startPos; //aHyphenatedWord->getHyphenationPos();
386                     lbr.breakType = BreakType::HYPHENATION;
387                 } else {
388                     lbr.breakIndex = line.aBreakIterator->preceding(nStartPos);
389                     lbr.breakType = BreakType::WORDBOUNDARY;
390                 }
391             } else { //word boundary break
392                 lbr.breakIndex = line.aBreakIterator->preceding(nStartPos);
393                 lbr.breakType = BreakType::WORDBOUNDARY;
394             }
395 
396 #define WJ 0x2060   // Word Joiner
397             GlueSpace=sal_False;
398             if (lbr.breakType == BreakType::WORDBOUNDARY) {
399                 nStartPos = lbr.breakIndex;
400                 if (Text[nStartPos--] == WJ)
401                     GlueSpace=sal_True;
402                 while (nStartPos >= 0 &&
403                     (u_isWhitespace(Text.iterateCodePoints(&nStartPos, 0)) || Text[nStartPos] == WJ)) {
404                     if (Text[nStartPos--] == WJ)
405                         GlueSpace=sal_True;
406                 }
407                 if (GlueSpace && nStartPos < 0)  {
408                     lbr.breakIndex = 0;
409                     break;
410                 }
411             }
412         }
413 
414         return lbr;
415 }
416 
417 
418 
419 OUString SAL_CALL
getImplementationName(void)420 BreakIterator_Unicode::getImplementationName(void)
421 {
422         return OUString::createFromAscii(cBreakIterator);
423 }
424 
425 sal_Bool SAL_CALL
supportsService(const OUString & rServiceName)426 BreakIterator_Unicode::supportsService(const OUString& rServiceName)
427 {
428         return !rServiceName.compareToAscii(cBreakIterator);
429 }
430 
431 uno::Sequence< OUString > SAL_CALL
getSupportedServiceNames(void)432 BreakIterator_Unicode::getSupportedServiceNames(void)
433 {
434         uno::Sequence< OUString > aRet(1);
435         aRet[0] = OUString::createFromAscii(cBreakIterator);
436         return aRet;
437 }
438 
439 } } } }
440