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_unotools.hxx"
26 #include <i18npool/mslangid.hxx>
27 #include <tools/debug.hxx>
28 #ifndef _INTN_HXX //autogen
29 //#include <tools/intn.hxx>
30 #endif
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #ifndef _COM_SUN_STAR_UTIL_SEARCHFLAGS_HDL_
33 #include <com/sun/star/util/SearchFlags.hdl>
34 #endif
35 #include <com/sun/star/i18n/TransliterationModules.hpp>
36 #include <unotools/charclass.hxx>
37 #include <comphelper/processfactory.hxx>
38 #include <unotools/textsearch.hxx>
39 #include <rtl/instance.hxx>
40 
41 using namespace ::com::sun::star::util;
42 using namespace ::com::sun::star::uno;
43 using namespace ::com::sun::star::lang;
44 
45 // ............................................................................
46 namespace utl
47 {
48 // ............................................................................
49 
SearchParam(const String & rText,SearchType eType,sal_Bool bCaseSensitive,sal_Bool bWrdOnly,sal_Bool bSearchInSel)50 SearchParam::SearchParam( const String &rText,
51 								SearchType eType,
52 								sal_Bool bCaseSensitive,
53 								sal_Bool bWrdOnly,
54 								sal_Bool bSearchInSel )
55 {
56 	sSrchStr        = rText;
57 	eSrchType       = eType;
58 
59 	bWordOnly       = bWrdOnly;
60 	bSrchInSel      = bSearchInSel;
61 	bCaseSense      = bCaseSensitive;
62 
63 	nTransliterationFlags = 0;
64 
65 	// Werte fuer "Gewichtete Levenshtein-Distanz"
66 	bLEV_Relaxed    = sal_True;
67 	nLEV_OtherX     = 2;
68 	nLEV_ShorterY   = 1;
69 	nLEV_LongerZ    = 3;
70 }
71 
SearchParam(const SearchParam & rParam)72 SearchParam::SearchParam( const SearchParam& rParam )
73 {
74 	sSrchStr        = rParam.sSrchStr;
75 	sReplaceStr     = rParam.sReplaceStr;
76 	eSrchType       = rParam.eSrchType;
77 
78 	bWordOnly       = rParam.bWordOnly;
79 	bSrchInSel      = rParam.bSrchInSel;
80 	bCaseSense      = rParam.bCaseSense;
81 
82 	bLEV_Relaxed    = rParam.bLEV_Relaxed;
83 	nLEV_OtherX     = rParam.nLEV_OtherX;
84 	nLEV_ShorterY   = rParam.nLEV_ShorterY;
85 	nLEV_LongerZ    = rParam.nLEV_LongerZ;
86 
87 	nTransliterationFlags = rParam.nTransliterationFlags;
88 }
89 
lcl_Equals(const SearchOptions & rSO1,const SearchOptions & rSO2)90 static bool lcl_Equals( const SearchOptions& rSO1, const SearchOptions& rSO2 )
91 {
92     return rSO1.algorithmType == rSO2.algorithmType &&
93         rSO1.searchFlag == rSO2.searchFlag &&
94         rSO1.searchString.equals(rSO2.searchString) &&
95         rSO1.replaceString.equals(rSO2.replaceString) &&
96         rSO1.changedChars == rSO2.changedChars &&
97         rSO1.deletedChars == rSO2.deletedChars &&
98         rSO1.insertedChars == rSO2.insertedChars &&
99         rSO1.Locale.Language == rSO2.Locale.Language &&
100         rSO1.Locale.Country == rSO2.Locale.Country &&
101         rSO1.Locale.Variant == rSO2.Locale.Variant &&
102         rSO1.transliterateFlags == rSO2.transliterateFlags;
103 }
104 
105 namespace
106 {
107     struct CachedTextSearch
108     {
109         ::osl::Mutex mutex;
110         ::com::sun::star::util::SearchOptions Options;
111         ::com::sun::star::uno::Reference< ::com::sun::star::util::XTextSearch > xTextSearch;
112     };
113 
114     struct theCachedTextSearch
115         : public rtl::Static< CachedTextSearch, theCachedTextSearch > {};
116 }
117 
getXTextSearch(const SearchOptions & rPara)118 Reference<XTextSearch> TextSearch::getXTextSearch( const SearchOptions& rPara )
119 {
120     CachedTextSearch &rCache = theCachedTextSearch::get();
121 
122     osl::MutexGuard aGuard(rCache.mutex);
123 
124     if ( lcl_Equals(rCache.Options, rPara) )
125         return rCache.xTextSearch;
126 
127     try
128     {
129         Reference< XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
130         rCache.xTextSearch.set( xMSF->createInstance(
131             ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
132                         "com.sun.star.util.TextSearch" ) ) ), UNO_QUERY_THROW );
133         rCache.xTextSearch->setOptions( rPara );
134         rCache.Options = rPara;
135     }
136     catch ( Exception& )
137     {
138         DBG_ERRORFILE( "TextSearch ctor: Exception caught!" );
139     }
140     return rCache.xTextSearch;
141 }
142 
TextSearch(const SearchParam & rParam,LanguageType eLang)143 TextSearch::TextSearch(const SearchParam & rParam, LanguageType eLang )
144 {
145 	if( LANGUAGE_NONE == eLang )
146 		eLang = LANGUAGE_SYSTEM;
147     ::com::sun::star::lang::Locale aLocale(
148             MsLangId::convertLanguageToLocale( LanguageType(eLang)));
149 
150 	Init( rParam, aLocale);
151 }
152 
TextSearch(const SearchParam & rParam,const CharClass & rCClass)153 TextSearch::TextSearch(const SearchParam & rParam, const CharClass& rCClass )
154 {
155 	Init( rParam, rCClass.getLocale() );
156 }
157 
TextSearch(const SearchOptions & rPara)158 TextSearch::TextSearch( const SearchOptions& rPara )
159 {
160     xTextSearch = getXTextSearch( rPara );
161 }
162 
Init(const SearchParam & rParam,const::com::sun::star::lang::Locale & rLocale)163 void TextSearch::Init( const SearchParam & rParam,
164 						const ::com::sun::star::lang::Locale& rLocale )
165 {
166 	// convert SearchParam to the UNO SearchOptions
167 	SearchOptions aSOpt;
168 
169 	switch( rParam.GetSrchType() )
170 	{
171 	case SearchParam::SRCH_REGEXP:
172 		aSOpt.algorithmType = SearchAlgorithms_REGEXP;
173 		if( rParam.IsSrchInSelection() )
174 			aSOpt.searchFlag |= SearchFlags::REG_NOT_BEGINOFLINE |
175 								SearchFlags::REG_NOT_ENDOFLINE;
176 		break;
177 
178 	case SearchParam::SRCH_LEVDIST:
179 		aSOpt.algorithmType = SearchAlgorithms_APPROXIMATE;
180 		aSOpt.changedChars = rParam.GetLEVOther();
181 		aSOpt.deletedChars = rParam.GetLEVLonger();
182 		aSOpt.insertedChars = rParam.GetLEVShorter();
183 		if( rParam.IsSrchRelaxed() )
184 			aSOpt.searchFlag |= SearchFlags::LEV_RELAXED;
185 		break;
186 
187 //	case SearchParam::SRCH_NORMAL:
188 	default:
189 		aSOpt.algorithmType = SearchAlgorithms_ABSOLUTE;
190 		if( rParam.IsSrchWordOnly() )
191 			aSOpt.searchFlag |= SearchFlags::NORM_WORD_ONLY;
192 		break;
193 	}
194 	aSOpt.searchString = rParam.GetSrchStr();
195 	aSOpt.replaceString = rParam.GetReplaceStr();
196 	aSOpt.Locale = rLocale;
197 	aSOpt.transliterateFlags = rParam.GetTransliterationFlags();
198 	if( !rParam.IsCaseSensitive() )
199     {
200 		aSOpt.searchFlag |= SearchFlags::ALL_IGNORE_CASE;
201         aSOpt.transliterateFlags |= ::com::sun::star::i18n::TransliterationModules_IGNORE_CASE;
202     }
203 
204     xTextSearch = getXTextSearch( aSOpt );
205 }
206 
SetLocale(const::com::sun::star::util::SearchOptions & rOptions,const::com::sun::star::lang::Locale & rLocale)207 void TextSearch::SetLocale( const ::com::sun::star::util::SearchOptions& rOptions,
208                             const ::com::sun::star::lang::Locale& rLocale )
209 {
210 	// convert SearchParam to the UNO SearchOptions
211     SearchOptions aSOpt( rOptions );
212     aSOpt.Locale = rLocale;
213 
214     xTextSearch = getXTextSearch( aSOpt );
215 }
216 
217 
~TextSearch()218 TextSearch::~TextSearch()
219 {
220 }
221 
222 /*
223  * Die allgemeinen Methoden zu Suchen. Diese rufen dann die entpsrecheden
224  * Methoden fuer die normale Suche oder der Suche nach Regular-Expressions
225  * ueber die MethodenPointer auf.
226  */
227 #if defined _MSC_VER
228 #pragma optimize("", off)
229 #pragma warning(push)
230 #pragma warning(disable: 4748)
231 #endif
SearchFrwrd(const String & rStr,xub_StrLen * pStart,xub_StrLen * pEnde,SearchResult * pRes)232 int TextSearch::SearchFrwrd( const String & rStr, xub_StrLen* pStart,
233 							xub_StrLen* pEnde, SearchResult* pRes )
234 {
235 	int nRet = 0;
236 	try
237 	{
238 		if( xTextSearch.is() )
239 		{
240 			SearchResult aRet( xTextSearch->searchForward(
241 													rStr, *pStart, *pEnde ));
242 			if( aRet.subRegExpressions > 0 )
243 			{
244 				nRet = 1;
245 				// the XTextsearch returns in startOffset the higher position
246 				// and the endposition is allways exclusive.
247 				// The caller of this function will have in startPos the
248 				// lower pos. and end
249 				*pStart = (xub_StrLen)aRet.startOffset[ 0 ];
250 				*pEnde = (xub_StrLen)aRet.endOffset[ 0 ];
251 				if( pRes )
252 					*pRes = aRet;
253 			}
254 		}
255 	}
256 	catch ( Exception& )
257 	{
258 		DBG_ERRORFILE( "SearchForward: Exception caught!" );
259 	}
260 	return nRet;
261 }
262 
SearchBkwrd(const String & rStr,xub_StrLen * pStart,xub_StrLen * pEnde,SearchResult * pRes)263 int TextSearch::SearchBkwrd( const String & rStr, xub_StrLen* pStart,
264 							xub_StrLen* pEnde, SearchResult* pRes )
265 {
266 	int nRet = 0;
267 	try
268 	{
269 		if( xTextSearch.is() )
270 		{
271 			SearchResult aRet( xTextSearch->searchBackward(
272 													rStr, *pStart, *pEnde ));
273 			if( aRet.subRegExpressions )
274 			{
275 				nRet = 1;
276 				// the XTextsearch returns in startOffset the higher position
277 				// and the endposition is allways exclusive.
278 				// The caller of this function will have in startPos the
279 				// lower pos. and end
280 				*pEnde = (xub_StrLen)aRet.startOffset[ 0 ];
281 				*pStart = (xub_StrLen)aRet.endOffset[ 0 ];
282 				if( pRes )
283 					*pRes = aRet;
284 			}
285 		}
286 	}
287 	catch ( Exception& )
288 	{
289 		DBG_ERRORFILE( "SearchBackward: Exception caught!" );
290 	}
291 	return nRet;
292 }
293 
ReplaceBackReferences(String & rReplaceStr,const String & rStr,const SearchResult & rResult)294 void TextSearch::ReplaceBackReferences( String& rReplaceStr, const String &rStr, const SearchResult& rResult )
295 {
296     if( rResult.subRegExpressions > 0 )
297     {
298         String sTab( '\t' );
299         sal_Unicode sSrchChrs[] = {'\\', '&', '$', 0};
300         String sTmp;
301         xub_StrLen nPos = 0;
302         sal_Unicode sFndChar;
303         while( STRING_NOTFOUND != ( nPos = rReplaceStr.SearchChar( sSrchChrs, nPos )) )
304         {
305             if( rReplaceStr.GetChar( nPos ) == '&')
306             {
307                 sal_uInt16 nStart = (sal_uInt16)(rResult.startOffset[0]);
308                 sal_uInt16 nLength = (sal_uInt16)(rResult.endOffset[0] - rResult.startOffset[0]);
309                 rReplaceStr.Erase( nPos, 1 );	// delete ampersand
310                 // replace by found string
311                 rReplaceStr.Insert( rStr, nStart, nLength, nPos );
312                 // jump over
313                 nPos = nPos + nLength;
314             }
315             else if( rReplaceStr.GetChar( nPos ) == '$')
316             {
317                 if( nPos + 1 < rReplaceStr.Len())
318                 {
319                     sFndChar = rReplaceStr.GetChar( nPos + 1 );
320                     switch(sFndChar)
321                     {   // placeholder for a backward reference?
322                         case '0':
323                         case '1':
324                         case '2':
325                         case '3':
326                         case '4':
327                         case '5':
328                         case '6':
329                         case '7':
330                         case '8':
331                         case '9':
332                         {
333                             rReplaceStr.Erase( nPos, 2 );	// delete both
334                             int i = sFndChar - '0';	// index
335                             if(i < rResult.subRegExpressions)
336                             {
337                                 sal_uInt16 nSttReg = (sal_uInt16)(rResult.startOffset[i]);
338                                 sal_uInt16 nRegLen = (sal_uInt16)(rResult.endOffset[i]);
339                                 if( nRegLen > nSttReg )
340                                     nRegLen = nRegLen - nSttReg;
341                                 else
342                                 {
343                                     nRegLen = nSttReg - nRegLen;
344                                     nSttReg = (sal_uInt16)(rResult.endOffset[i]);
345                                 }
346                                 // Copy reference from found string
347                                 sTmp = rStr.Copy((sal_uInt16)nSttReg, (sal_uInt16)nRegLen);
348                                 // insert
349                                 rReplaceStr.Insert( sTmp, nPos );
350                                 // and step over
351                                 nPos = nPos + sTmp.Len();
352                             }
353                         }
354                         break;
355                         default:
356                             nPos += 2; // leave both chars unchanged
357                             break;
358                     }
359                 }
360                 else
361                     ++nPos;
362             }
363             else
364             {
365                 // at least another character?
366                 if( nPos + 1 < rReplaceStr.Len())
367                 {
368                     sFndChar = rReplaceStr.GetChar( nPos + 1 );
369                     switch(sFndChar)
370                     {
371                         case '\\':
372                         case '&':
373                         case '$':
374                             rReplaceStr.Erase( nPos, 1 );
375                             nPos++;
376                         break;
377                         case 't':
378                             rReplaceStr.Erase( nPos, 2 ); // delete both
379                             rReplaceStr.Insert( sTab, nPos ); // insert tabulator
380                             nPos++;	// step over
381                         break;
382                         default:
383                             nPos += 2; // ignore both characters
384                         break;
385                     }
386                 }
387                 else
388                     ++nPos;
389             }
390         }
391     }
392 }
393 
394 
395 #if defined _MSC_VER
396 #pragma optimize("", on)
397 #pragma warning(pop)
398 #endif
399 
400 // ............................................................................
401 }	// namespace utl
402 // ............................................................................
403 
404