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 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 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: 85 inline void publicSetBreakType(int32_t type) { 86 setBreakType(type); 87 }; 88 OOoRuleBasedBreakIterator(UDataMemory* image, 89 UErrorCode &status) : 90 RuleBasedBreakIterator(image, status) { }; 91 92 }; 93 94 // loading ICU breakiterator on demand. 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 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 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 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 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 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 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 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 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 420 BreakIterator_Unicode::getImplementationName(void) 421 { 422 return OUString::createFromAscii(cBreakIterator); 423 } 424 425 sal_Bool SAL_CALL 426 BreakIterator_Unicode::supportsService(const OUString& rServiceName) 427 { 428 return !rServiceName.compareToAscii(cBreakIterator); 429 } 430 431 uno::Sequence< OUString > SAL_CALL 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