Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <i18npool/languagetag.hxx>
21 : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
22 : #include <com/sun/star/util/TextSearch.hpp>
23 : #include <com/sun/star/util/SearchFlags.hpp>
24 : #include <com/sun/star/i18n/TransliterationModules.hpp>
25 : #include <unotools/charclass.hxx>
26 : #include <comphelper/processfactory.hxx>
27 : #include <unotools/textsearch.hxx>
28 : #include <rtl/instance.hxx>
29 :
30 : using namespace ::com::sun::star::util;
31 : using namespace ::com::sun::star::uno;
32 : using namespace ::com::sun::star::lang;
33 :
34 : // ............................................................................
35 : namespace utl
36 : {
37 : // ............................................................................
38 :
39 1 : SearchParam::SearchParam( const rtl::OUString &rText,
40 : SearchType eType,
41 : sal_Bool bCaseSensitive,
42 : sal_Bool bWrdOnly,
43 1 : sal_Bool bSearchInSel )
44 : {
45 1 : sSrchStr = rText;
46 1 : m_eSrchType = eType;
47 :
48 1 : m_bWordOnly = bWrdOnly;
49 1 : m_bSrchInSel = bSearchInSel;
50 1 : m_bCaseSense = bCaseSensitive;
51 :
52 1 : nTransliterationFlags = 0;
53 :
54 : // Parameters for weighted Levenshtein distance
55 1 : bLEV_Relaxed = sal_True;
56 1 : nLEV_OtherX = 2;
57 1 : nLEV_ShorterY = 1;
58 1 : nLEV_LongerZ = 3;
59 1 : }
60 :
61 0 : SearchParam::SearchParam( const SearchParam& rParam )
62 : {
63 0 : sSrchStr = rParam.sSrchStr;
64 0 : sReplaceStr = rParam.sReplaceStr;
65 0 : m_eSrchType = rParam.m_eSrchType;
66 :
67 0 : m_bWordOnly = rParam.m_bWordOnly;
68 0 : m_bSrchInSel = rParam.m_bSrchInSel;
69 0 : m_bCaseSense = rParam.m_bCaseSense;
70 :
71 0 : bLEV_Relaxed = rParam.bLEV_Relaxed;
72 0 : nLEV_OtherX = rParam.nLEV_OtherX;
73 0 : nLEV_ShorterY = rParam.nLEV_ShorterY;
74 0 : nLEV_LongerZ = rParam.nLEV_LongerZ;
75 :
76 0 : nTransliterationFlags = rParam.nTransliterationFlags;
77 0 : }
78 :
79 1 : SearchParam::~SearchParam() {}
80 :
81 1 : static bool lcl_Equals( const SearchOptions& rSO1, const SearchOptions& rSO2 )
82 : {
83 : return rSO1.algorithmType == rSO2.algorithmType &&
84 : rSO1.searchFlag == rSO2.searchFlag &&
85 0 : rSO1.searchString.equals(rSO2.searchString) &&
86 0 : rSO1.replaceString.equals(rSO2.replaceString) &&
87 : rSO1.changedChars == rSO2.changedChars &&
88 : rSO1.deletedChars == rSO2.deletedChars &&
89 : rSO1.insertedChars == rSO2.insertedChars &&
90 0 : rSO1.Locale.Language == rSO2.Locale.Language &&
91 0 : rSO1.Locale.Country == rSO2.Locale.Country &&
92 0 : rSO1.Locale.Variant == rSO2.Locale.Variant &&
93 1 : rSO1.transliterateFlags == rSO2.transliterateFlags;
94 : }
95 :
96 : namespace
97 : {
98 2 : struct CachedTextSearch
99 : {
100 : ::osl::Mutex mutex;
101 : ::com::sun::star::util::SearchOptions Options;
102 : ::com::sun::star::uno::Reference< ::com::sun::star::util::XTextSearch > xTextSearch;
103 : };
104 :
105 : struct theCachedTextSearch
106 : : public rtl::Static< CachedTextSearch, theCachedTextSearch > {};
107 : }
108 :
109 1 : Reference<XTextSearch> TextSearch::getXTextSearch( const SearchOptions& rPara )
110 : {
111 1 : CachedTextSearch &rCache = theCachedTextSearch::get();
112 :
113 1 : osl::MutexGuard aGuard(rCache.mutex);
114 :
115 1 : if ( lcl_Equals(rCache.Options, rPara) )
116 0 : return rCache.xTextSearch;
117 :
118 1 : Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
119 1 : rCache.xTextSearch.set( ::TextSearch::create(xContext) );
120 1 : rCache.xTextSearch->setOptions( rPara );
121 1 : rCache.Options = rPara;
122 :
123 1 : return rCache.xTextSearch;
124 : }
125 :
126 0 : TextSearch::TextSearch(const SearchParam & rParam, LanguageType eLang )
127 : {
128 0 : if( LANGUAGE_NONE == eLang )
129 0 : eLang = LANGUAGE_SYSTEM;
130 0 : ::com::sun::star::lang::Locale aLocale( LanguageTag( eLang ).getLocale() );
131 :
132 0 : Init( rParam, aLocale);
133 0 : }
134 :
135 1 : TextSearch::TextSearch(const SearchParam & rParam, const CharClass& rCClass )
136 : {
137 1 : Init( rParam, rCClass.getLanguageTag().getLocale() );
138 1 : }
139 :
140 0 : TextSearch::TextSearch( const SearchOptions& rPara )
141 : {
142 0 : xTextSearch = getXTextSearch( rPara );
143 0 : }
144 :
145 1 : void TextSearch::Init( const SearchParam & rParam,
146 : const ::com::sun::star::lang::Locale& rLocale )
147 : {
148 : // convert SearchParam to the UNO SearchOptions
149 1 : SearchOptions aSOpt;
150 :
151 1 : switch( rParam.GetSrchType() )
152 : {
153 : case SearchParam::SRCH_REGEXP:
154 1 : aSOpt.algorithmType = SearchAlgorithms_REGEXP;
155 1 : if( rParam.IsSrchInSelection() )
156 : aSOpt.searchFlag |= SearchFlags::REG_NOT_BEGINOFLINE |
157 0 : SearchFlags::REG_NOT_ENDOFLINE;
158 1 : break;
159 :
160 : case SearchParam::SRCH_LEVDIST:
161 0 : aSOpt.algorithmType = SearchAlgorithms_APPROXIMATE;
162 0 : aSOpt.changedChars = rParam.GetLEVOther();
163 0 : aSOpt.deletedChars = rParam.GetLEVLonger();
164 0 : aSOpt.insertedChars = rParam.GetLEVShorter();
165 0 : if( rParam.IsSrchRelaxed() )
166 0 : aSOpt.searchFlag |= SearchFlags::LEV_RELAXED;
167 0 : break;
168 :
169 : // case SearchParam::SRCH_NORMAL:
170 : default:
171 0 : aSOpt.algorithmType = SearchAlgorithms_ABSOLUTE;
172 0 : if( rParam.IsSrchWordOnly() )
173 0 : aSOpt.searchFlag |= SearchFlags::NORM_WORD_ONLY;
174 0 : break;
175 : }
176 1 : aSOpt.searchString = rParam.GetSrchStr();
177 1 : aSOpt.replaceString = rParam.GetReplaceStr();
178 1 : aSOpt.Locale = rLocale;
179 1 : aSOpt.transliterateFlags = rParam.GetTransliterationFlags();
180 1 : if( !rParam.IsCaseSensitive() )
181 : {
182 1 : aSOpt.searchFlag |= SearchFlags::ALL_IGNORE_CASE;
183 1 : aSOpt.transliterateFlags |= ::com::sun::star::i18n::TransliterationModules_IGNORE_CASE;
184 : }
185 :
186 1 : xTextSearch = getXTextSearch( aSOpt );
187 1 : }
188 :
189 0 : void TextSearch::SetLocale( const ::com::sun::star::util::SearchOptions& rOptions,
190 : const ::com::sun::star::lang::Locale& rLocale )
191 : {
192 : // convert SearchParam to the UNO SearchOptions
193 0 : SearchOptions aSOpt( rOptions );
194 0 : aSOpt.Locale = rLocale;
195 :
196 0 : xTextSearch = getXTextSearch( aSOpt );
197 0 : }
198 :
199 :
200 1 : TextSearch::~TextSearch()
201 : {
202 1 : }
203 :
204 : /*
205 : * General search methods. These methods will call the respective
206 : * methods, such as ordinary string searching or regular expression
207 : * matching, using the method pointer.
208 : */
209 : #if defined _MSC_VER
210 : #pragma optimize("", off)
211 : #pragma warning(push)
212 : #pragma warning(disable: 4748)
213 : #endif
214 8 : int TextSearch::SearchFrwrd( const String & rStr, xub_StrLen* pStart,
215 : xub_StrLen* pEnde, SearchResult* pRes )
216 : {
217 8 : int nRet = 0;
218 : try
219 : {
220 8 : if( xTextSearch.is() )
221 : {
222 8 : SearchResult aRet( xTextSearch->searchForward(
223 8 : rStr, *pStart, *pEnde ));
224 8 : if( aRet.subRegExpressions > 0 )
225 : {
226 1 : nRet = 1;
227 : // the XTextsearch returns in startOffset the higher position
228 : // and the endposition is allways exclusive.
229 : // The caller of this function will have in startPos the
230 : // lower pos. and end
231 1 : *pStart = (xub_StrLen)aRet.startOffset[ 0 ];
232 1 : *pEnde = (xub_StrLen)aRet.endOffset[ 0 ];
233 1 : if( pRes )
234 0 : *pRes = aRet;
235 8 : }
236 : }
237 : }
238 0 : catch ( Exception& )
239 : {
240 : SAL_WARN( "unotools.i18n", "SearchForward: Exception caught!" );
241 : }
242 8 : return nRet;
243 : }
244 :
245 0 : sal_Bool TextSearch::SearchForward( const ::rtl::OUString &rStr,
246 : sal_Int32* pStart, sal_Int32* pEnd,
247 : ::com::sun::star::util::SearchResult* pRes)
248 : {
249 0 : sal_Bool nRet = sal_False;
250 : try
251 : {
252 0 : if( xTextSearch.is() )
253 : {
254 0 : SearchResult aRet( xTextSearch->searchForward(
255 0 : rStr, *pStart, *pEnd ));
256 0 : if( aRet.subRegExpressions > 0 )
257 : {
258 0 : nRet = sal_True;
259 : // the XTextsearch returns in startOffset the higher position
260 : // and the endposition is allways exclusive.
261 : // The caller of this function will have in startPos the
262 : // lower pos. and end
263 0 : *pStart = aRet.startOffset[ 0 ];
264 0 : *pEnd = aRet.endOffset[ 0 ];
265 0 : if( pRes )
266 0 : *pRes = aRet;
267 0 : }
268 : }
269 : }
270 0 : catch ( Exception& )
271 : {
272 : SAL_WARN( "unotools.i18n", "SearchForward: Exception caught!" );
273 : }
274 0 : return nRet;
275 : }
276 :
277 :
278 0 : int TextSearch::SearchBkwrd( const String & rStr, xub_StrLen* pStart,
279 : xub_StrLen* pEnde, SearchResult* pRes )
280 : {
281 0 : int nRet = 0;
282 : try
283 : {
284 0 : if( xTextSearch.is() )
285 : {
286 0 : SearchResult aRet( xTextSearch->searchBackward(
287 0 : rStr, *pStart, *pEnde ));
288 0 : if( aRet.subRegExpressions )
289 : {
290 0 : nRet = 1;
291 : // the XTextsearch returns in startOffset the higher position
292 : // and the endposition is allways exclusive.
293 : // The caller of this function will have in startPos the
294 : // lower pos. and end
295 0 : *pEnde = (xub_StrLen)aRet.startOffset[ 0 ];
296 0 : *pStart = (xub_StrLen)aRet.endOffset[ 0 ];
297 0 : if( pRes )
298 0 : *pRes = aRet;
299 0 : }
300 : }
301 : }
302 0 : catch ( Exception& )
303 : {
304 : SAL_WARN( "unotools.i18n", "SearchBackward: Exception caught!" );
305 : }
306 0 : return nRet;
307 : }
308 :
309 0 : void TextSearch::ReplaceBackReferences( String& rReplaceStr, const String &rStr, const SearchResult& rResult )
310 : {
311 0 : if( rResult.subRegExpressions > 0 )
312 : {
313 0 : rtl::OUString sTab( '\t' );
314 0 : sal_Unicode sSrchChrs[] = {'\\', '&', '$', 0};
315 0 : String sTmp;
316 0 : xub_StrLen nPos = 0;
317 : sal_Unicode sFndChar;
318 0 : while( STRING_NOTFOUND != ( nPos = rReplaceStr.SearchChar( sSrchChrs, nPos )) )
319 : {
320 0 : if( rReplaceStr.GetChar( nPos ) == '&')
321 : {
322 0 : sal_uInt16 nStart = (sal_uInt16)(rResult.startOffset[0]);
323 0 : sal_uInt16 nLength = (sal_uInt16)(rResult.endOffset[0] - rResult.startOffset[0]);
324 0 : rReplaceStr.Erase( nPos, 1 ); // delete ampersand
325 : // replace by found string
326 0 : rReplaceStr.Insert( rStr, nStart, nLength, nPos );
327 : // jump over
328 0 : nPos = nPos + nLength;
329 : }
330 0 : else if( rReplaceStr.GetChar( nPos ) == '$')
331 : {
332 0 : if( nPos + 1 < rReplaceStr.Len())
333 : {
334 0 : sFndChar = rReplaceStr.GetChar( nPos + 1 );
335 0 : switch(sFndChar)
336 : { // placeholder for a backward reference?
337 : case '0':
338 : case '1':
339 : case '2':
340 : case '3':
341 : case '4':
342 : case '5':
343 : case '6':
344 : case '7':
345 : case '8':
346 : case '9':
347 : {
348 0 : rReplaceStr.Erase( nPos, 2 ); // delete both
349 0 : int i = sFndChar - '0'; // index
350 0 : if(i < rResult.subRegExpressions)
351 : {
352 0 : sal_uInt16 nSttReg = (sal_uInt16)(rResult.startOffset[i]);
353 0 : sal_uInt16 nRegLen = (sal_uInt16)(rResult.endOffset[i]);
354 0 : if( nRegLen > nSttReg )
355 0 : nRegLen = nRegLen - nSttReg;
356 : else
357 : {
358 0 : nRegLen = nSttReg - nRegLen;
359 0 : nSttReg = (sal_uInt16)(rResult.endOffset[i]);
360 : }
361 : // Copy reference from found string
362 0 : sTmp = rStr.Copy((sal_uInt16)nSttReg, (sal_uInt16)nRegLen);
363 : // insert
364 0 : rReplaceStr.Insert( sTmp, nPos );
365 : // and step over
366 0 : nPos = nPos + sTmp.Len();
367 : }
368 : }
369 0 : break;
370 : default:
371 0 : nPos += 2; // leave both chars unchanged
372 0 : break;
373 : }
374 : }
375 : else
376 0 : ++nPos;
377 : }
378 : else
379 : {
380 : // at least another character?
381 0 : if( nPos + 1 < rReplaceStr.Len())
382 : {
383 0 : sFndChar = rReplaceStr.GetChar( nPos + 1 );
384 0 : switch(sFndChar)
385 : {
386 : case '\\':
387 : case '&':
388 : case '$':
389 0 : rReplaceStr.Erase( nPos, 1 );
390 0 : nPos++;
391 0 : break;
392 : case 't':
393 0 : rReplaceStr.Erase( nPos, 2 ); // delete both
394 0 : rReplaceStr.Insert( sTab, nPos ); // insert tabulator
395 0 : nPos++; // step over
396 0 : break;
397 : default:
398 0 : nPos += 2; // ignore both characters
399 0 : break;
400 : }
401 : }
402 : else
403 0 : ++nPos;
404 : }
405 0 : }
406 : }
407 0 : }
408 :
409 :
410 : #if defined _MSC_VER
411 : #pragma optimize("", on)
412 : #pragma warning(pop)
413 : #endif
414 :
415 : // ............................................................................
416 : } // namespace utl
417 : // ............................................................................
418 :
419 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|