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 <i18nlangtag/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 : #include <rtl/ustrbuf.hxx>
30 :
31 : using namespace ::com::sun::star::util;
32 : using namespace ::com::sun::star::uno;
33 : using namespace ::com::sun::star::lang;
34 :
35 : namespace utl
36 : {
37 :
38 12 : SearchParam::SearchParam( const OUString &rText,
39 : SearchType eType,
40 : bool bCaseSensitive,
41 : bool bWrdOnly,
42 12 : bool bSearchInSel )
43 : {
44 12 : sSrchStr = rText;
45 12 : m_eSrchType = eType;
46 :
47 12 : m_bWordOnly = bWrdOnly;
48 12 : m_bSrchInSel = bSearchInSel;
49 12 : m_bCaseSense = bCaseSensitive;
50 :
51 12 : nTransliterationFlags = 0;
52 :
53 : // Parameters for weighted Levenshtein distance
54 12 : bLEV_Relaxed = true;
55 12 : nLEV_OtherX = 2;
56 12 : nLEV_ShorterY = 1;
57 12 : nLEV_LongerZ = 3;
58 12 : }
59 :
60 0 : SearchParam::SearchParam( const SearchParam& rParam )
61 : {
62 0 : sSrchStr = rParam.sSrchStr;
63 0 : sReplaceStr = rParam.sReplaceStr;
64 0 : m_eSrchType = rParam.m_eSrchType;
65 :
66 0 : m_bWordOnly = rParam.m_bWordOnly;
67 0 : m_bSrchInSel = rParam.m_bSrchInSel;
68 0 : m_bCaseSense = rParam.m_bCaseSense;
69 :
70 0 : bLEV_Relaxed = rParam.bLEV_Relaxed;
71 0 : nLEV_OtherX = rParam.nLEV_OtherX;
72 0 : nLEV_ShorterY = rParam.nLEV_ShorterY;
73 0 : nLEV_LongerZ = rParam.nLEV_LongerZ;
74 :
75 0 : nTransliterationFlags = rParam.nTransliterationFlags;
76 0 : }
77 :
78 12 : SearchParam::~SearchParam() {}
79 :
80 178 : static bool lcl_Equals( const SearchOptions& rSO1, const SearchOptions& rSO2 )
81 : {
82 350 : return rSO1.algorithmType == rSO2.algorithmType &&
83 330 : rSO1.searchFlag == rSO2.searchFlag &&
84 236 : rSO1.searchString.equals(rSO2.searchString) &&
85 150 : rSO1.replaceString.equals(rSO2.replaceString) &&
86 144 : rSO1.changedChars == rSO2.changedChars &&
87 144 : rSO1.deletedChars == rSO2.deletedChars &&
88 144 : rSO1.insertedChars == rSO2.insertedChars &&
89 144 : rSO1.Locale.Language == rSO2.Locale.Language &&
90 144 : rSO1.Locale.Country == rSO2.Locale.Country &&
91 322 : rSO1.Locale.Variant == rSO2.Locale.Variant &&
92 250 : rSO1.transliterateFlags == rSO2.transliterateFlags;
93 : }
94 :
95 : namespace
96 : {
97 32 : struct CachedTextSearch
98 : {
99 : ::osl::Mutex mutex;
100 : ::com::sun::star::util::SearchOptions Options;
101 : ::com::sun::star::uno::Reference< ::com::sun::star::util::XTextSearch > xTextSearch;
102 : };
103 :
104 : struct theCachedTextSearch
105 : : public rtl::Static< CachedTextSearch, theCachedTextSearch > {};
106 : }
107 :
108 178 : Reference<XTextSearch> TextSearch::getXTextSearch( const SearchOptions& rPara )
109 : {
110 178 : CachedTextSearch &rCache = theCachedTextSearch::get();
111 :
112 178 : osl::MutexGuard aGuard(rCache.mutex);
113 :
114 178 : if ( lcl_Equals(rCache.Options, rPara) )
115 72 : return rCache.xTextSearch;
116 :
117 212 : Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
118 106 : rCache.xTextSearch.set( ::TextSearch::create(xContext) );
119 106 : rCache.xTextSearch->setOptions( rPara );
120 106 : rCache.Options = rPara;
121 :
122 284 : return rCache.xTextSearch;
123 : }
124 :
125 0 : TextSearch::TextSearch(const SearchParam & rParam, LanguageType eLang )
126 : {
127 0 : if( LANGUAGE_NONE == eLang )
128 0 : eLang = LANGUAGE_SYSTEM;
129 0 : ::com::sun::star::lang::Locale aLocale( LanguageTag::convertToLocale( eLang ) );
130 :
131 0 : Init( rParam, aLocale);
132 0 : }
133 :
134 12 : TextSearch::TextSearch(const SearchParam & rParam, const CharClass& rCClass )
135 : {
136 12 : Init( rParam, rCClass.getLanguageTag().getLocale() );
137 12 : }
138 :
139 166 : TextSearch::TextSearch( const SearchOptions& rPara )
140 : {
141 166 : xTextSearch = getXTextSearch( rPara );
142 166 : }
143 :
144 12 : void TextSearch::Init( const SearchParam & rParam,
145 : const ::com::sun::star::lang::Locale& rLocale )
146 : {
147 : // convert SearchParam to the UNO SearchOptions
148 12 : SearchOptions aSOpt;
149 :
150 12 : switch( rParam.GetSrchType() )
151 : {
152 : case SearchParam::SRCH_REGEXP:
153 10 : aSOpt.algorithmType = SearchAlgorithms_REGEXP;
154 10 : if( rParam.IsSrchInSelection() )
155 : aSOpt.searchFlag |= SearchFlags::REG_NOT_BEGINOFLINE |
156 0 : SearchFlags::REG_NOT_ENDOFLINE;
157 10 : break;
158 :
159 : case SearchParam::SRCH_LEVDIST:
160 0 : aSOpt.algorithmType = SearchAlgorithms_APPROXIMATE;
161 0 : aSOpt.changedChars = rParam.GetLEVOther();
162 0 : aSOpt.deletedChars = rParam.GetLEVLonger();
163 0 : aSOpt.insertedChars = rParam.GetLEVShorter();
164 0 : if( rParam.IsSrchRelaxed() )
165 0 : aSOpt.searchFlag |= SearchFlags::LEV_RELAXED;
166 0 : break;
167 :
168 : // case SearchParam::SRCH_NORMAL:
169 : default:
170 2 : aSOpt.algorithmType = SearchAlgorithms_ABSOLUTE;
171 2 : if( rParam.IsSrchWordOnly() )
172 0 : aSOpt.searchFlag |= SearchFlags::NORM_WORD_ONLY;
173 2 : break;
174 : }
175 12 : aSOpt.searchString = rParam.GetSrchStr();
176 12 : aSOpt.replaceString = rParam.GetReplaceStr();
177 12 : aSOpt.Locale = rLocale;
178 12 : aSOpt.transliterateFlags = rParam.GetTransliterationFlags();
179 12 : if( !rParam.IsCaseSensitive() )
180 : {
181 12 : aSOpt.searchFlag |= SearchFlags::ALL_IGNORE_CASE;
182 12 : aSOpt.transliterateFlags |= ::com::sun::star::i18n::TransliterationModules_IGNORE_CASE;
183 : }
184 :
185 12 : xTextSearch = getXTextSearch( aSOpt );
186 12 : }
187 :
188 0 : void TextSearch::SetLocale( const ::com::sun::star::util::SearchOptions& rOptions,
189 : const ::com::sun::star::lang::Locale& rLocale )
190 : {
191 : // convert SearchParam to the UNO SearchOptions
192 0 : SearchOptions aSOpt( rOptions );
193 0 : aSOpt.Locale = rLocale;
194 :
195 0 : xTextSearch = getXTextSearch( aSOpt );
196 0 : }
197 :
198 178 : TextSearch::~TextSearch()
199 : {
200 178 : }
201 :
202 : /*
203 : * General search methods. These methods will call the respective
204 : * methods, such as ordinary string searching or regular expression
205 : * matching, using the method pointer.
206 : */
207 2372 : bool TextSearch::SearchForward( const OUString &rStr,
208 : sal_Int32* pStart, sal_Int32* pEnd,
209 : ::com::sun::star::util::SearchResult* pRes)
210 : {
211 2372 : bool nRet = false;
212 : try
213 : {
214 2372 : if( xTextSearch.is() )
215 : {
216 2372 : SearchResult aRet( xTextSearch->searchForward( rStr, *pStart, *pEnd ));
217 2372 : if( aRet.subRegExpressions > 0 )
218 : {
219 1560 : nRet = true;
220 : // the XTextsearch returns in startOffset the higher position
221 : // and the endposition is always exclusive.
222 : // The caller of this function will have in startPos the
223 : // lower pos. and end
224 1560 : *pStart = aRet.startOffset[ 0 ];
225 1560 : *pEnd = aRet.endOffset[ 0 ];
226 1560 : if( pRes )
227 1372 : *pRes = aRet;
228 2372 : }
229 : }
230 : }
231 0 : catch ( Exception& )
232 : {
233 : SAL_WARN( "unotools.i18n", "SearchForward: Exception caught!" );
234 : }
235 2372 : return nRet;
236 : }
237 :
238 0 : bool TextSearch::SearchBackward( const OUString & rStr, sal_Int32* pStart,
239 : sal_Int32* pEnde, SearchResult* pRes )
240 : {
241 0 : bool nRet = false;
242 : try
243 : {
244 0 : if( xTextSearch.is() )
245 : {
246 0 : SearchResult aRet( xTextSearch->searchBackward( rStr, *pStart, *pEnde ));
247 0 : if( aRet.subRegExpressions )
248 : {
249 0 : nRet = true;
250 : // the XTextsearch returns in startOffset the higher position
251 : // and the endposition is always exclusive.
252 : // The caller of this function will have in startPos the
253 : // lower pos. and end
254 0 : *pEnde = aRet.startOffset[ 0 ];
255 0 : *pStart = aRet.endOffset[ 0 ];
256 0 : if( pRes )
257 0 : *pRes = aRet;
258 0 : }
259 : }
260 : }
261 0 : catch ( Exception& )
262 : {
263 : SAL_WARN( "unotools.i18n", "SearchBackward: Exception caught!" );
264 : }
265 0 : return nRet;
266 : }
267 :
268 538 : void TextSearch::ReplaceBackReferences( OUString& rReplaceStr, const OUString &rStr, const SearchResult& rResult )
269 : {
270 538 : if( rResult.subRegExpressions > 0 )
271 : {
272 : sal_Unicode sFndChar;
273 : sal_Int32 i;
274 538 : OUStringBuffer sBuff(rReplaceStr.getLength()*4);
275 4466 : for(i = 0; i < rReplaceStr.getLength(); i++)
276 : {
277 3928 : if( rReplaceStr[i] == '&')
278 : {
279 0 : sal_Int32 nStart = rResult.startOffset[0];
280 0 : sal_Int32 nLength = rResult.endOffset[0] - rResult.startOffset[0];
281 0 : sBuff.append(rStr.getStr() + nStart, nLength);
282 : }
283 3928 : else if((i < rReplaceStr.getLength() - 1) && rReplaceStr[i] == '$')
284 : {
285 0 : sFndChar = rReplaceStr[ i + 1 ];
286 0 : switch(sFndChar)
287 : { // placeholder for a backward reference?
288 : case '0':
289 : case '1':
290 : case '2':
291 : case '3':
292 : case '4':
293 : case '5':
294 : case '6':
295 : case '7':
296 : case '8':
297 : case '9':
298 : {
299 0 : int j = sFndChar - '0'; // index
300 0 : if(j < rResult.subRegExpressions)
301 : {
302 0 : sal_Int32 nSttReg = rResult.startOffset[j];
303 0 : sal_Int32 nRegLen = rResult.endOffset[j];
304 0 : if( nRegLen > nSttReg )
305 : {
306 0 : nRegLen = nRegLen - nSttReg;
307 : }
308 : else
309 : {
310 0 : nRegLen = nSttReg - nRegLen;
311 0 : nSttReg = rResult.endOffset[j];
312 : }
313 : // Copy reference from found string
314 0 : sBuff.append(rStr.getStr() + nSttReg, nRegLen);
315 : }
316 0 : i += 1;
317 : }
318 0 : break;
319 : default:
320 0 : sBuff.append(rReplaceStr[i]);
321 0 : sBuff.append(rReplaceStr[i+1]);
322 0 : i += 1;
323 0 : break;
324 : }
325 : }
326 3928 : else if((i < rReplaceStr.getLength() - 1) && rReplaceStr[i] == '\\')
327 : {
328 0 : sFndChar = rReplaceStr[ i+1 ];
329 0 : switch(sFndChar)
330 : {
331 : case '\\':
332 : case '&':
333 : case '$':
334 0 : sBuff.append(sFndChar);
335 0 : i+=1;
336 0 : break;
337 : case 't':
338 0 : sBuff.append('\t');
339 0 : i += 1;
340 0 : break;
341 : default:
342 0 : sBuff.append(rReplaceStr[i]);
343 0 : sBuff.append(rReplaceStr[i+1]);
344 0 : i += 1;
345 0 : break;
346 : }
347 : }
348 : else
349 : {
350 3928 : sBuff.append(rReplaceStr[i]);
351 : }
352 : }
353 538 : rReplaceStr = sBuff.makeStringAndClear();
354 : }
355 538 : }
356 :
357 : } // namespace utl
358 :
359 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|