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