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