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 #include <functional>
25 #include <algorithm>
26 #include <vos/mutex.hxx>
27 #include <vcl/svapp.hxx>
28 
29 // #ifndef _OSL_DIAGNOSE_H_
30 // #include <osl/diagnose.h>
31 // #endif
32 #include "CFStringUtilities.hxx"
33 #include "NSString_OOoAdditions.hxx"
34 #include "NSURL_OOoAdditions.hxx"
35 
36 #include "FilterHelper.hxx"
37 
38 #pragma mark DEFINES
39 #define CLASS_NAME "FilterEntry"
40 
41 #pragma mark FilterEntry
42 //---------------------------------------------------------------------
FilterEntry(const rtl::OUString & _rTitle,const UnoFilterList & _rSubFilters)43 FilterEntry::FilterEntry( const rtl::OUString& _rTitle, const UnoFilterList& _rSubFilters )
44 :m_sTitle( _rTitle )
45 ,m_aSubFilters( _rSubFilters )
46 {
47     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", _rTitle);
48     DBG_PRINT_EXIT(CLASS_NAME, __func__);
49 }
50 
51 //---------------------------------------------------------------------
hasSubFilters() const52 sal_Bool FilterEntry::hasSubFilters() const
53 {
54 //    OSL_TRACE(">>> FilterEntry::%s", __func__);
55     sal_Bool bReturn = ( 0 < m_aSubFilters.getLength() );
56 //    OSL_TRACE("<<< FilterEntry::%s retVal: %d", __func__, bReturn);
57     return bReturn;
58 }
59 
60 //---------------------------------------------------------------------
getSubFilters(UnoFilterList & _rSubFilterList)61 sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
62 {
63     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
64 
65     _rSubFilterList = m_aSubFilters;
66     sal_Int32 nReturn = m_aSubFilters.getLength();
67 
68     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn);
69 
70     return nReturn;
71 }
72 
73 #pragma mark statics
74 static bool
isFilterString(const rtl::OUString & rFilterString,const char * pMatch)75 isFilterString( const rtl::OUString& rFilterString, const char *pMatch )
76 {
77     sal_Int32 nIndex = 0;
78     rtl::OUString aToken;
79     bool bIsFilter = true;
80 
81     rtl::OUString aMatch(rtl::OUString::createFromAscii(pMatch));
82 
83     do
84     {
85         aToken = rFilterString.getToken( 0, ';', nIndex );
86         if( !aToken.match( aMatch ) )
87         {
88             bIsFilter = false;
89             break;
90         }
91     }
92     while( nIndex >= 0 );
93 
94     return bIsFilter;
95 }
96 
97 //=====================================================================
98 
99 static rtl::OUString
shrinkFilterName(const rtl::OUString aFilterName,bool bAllowNoStar=false)100 shrinkFilterName( const rtl::OUString aFilterName, bool bAllowNoStar = false )
101 {
102     // DBG_PRINT_ENTRY(CLASS_NAME, "shrinkFilterName", "filterName", aFilterName);
103 
104     int i;
105     int nBracketLen = -1;
106     int nBracketEnd = -1;
107     rtl::OUString rFilterName = aFilterName;
108     const sal_Unicode* pStr = rFilterName.getStr();
109     rtl::OUString aRealName = rFilterName;
110 
111     for( i = aRealName.getLength() - 1; i > 0; i-- )
112     {
113         if( pStr[i] == ')' )
114             nBracketEnd = i;
115         else if( pStr[i] == '(' )
116         {
117             nBracketLen = nBracketEnd - i;
118             if( nBracketEnd <= 0 )
119                 continue;
120             if( isFilterString( rFilterName.copy( i + 1, nBracketLen - 1 ), "*." ) )
121                 aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
122             else if (bAllowNoStar)
123             {
124                 if( isFilterString( rFilterName.copy( i + 1, nBracketLen - 1 ), ".") )
125                     aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
126             }
127         }
128     }
129 
130     return aRealName;
131 }
132 
133 //------------------------------------------------------------------------------------
134 namespace {
135     //................................................................................
136     struct FilterTitleMatch : public ::std::unary_function< FilterEntry, bool >
137     {
138 protected:
139         const rtl::OUString rTitle;
140 
141 public:
FilterTitleMatch__anon2b3e87a70111::FilterTitleMatch142         FilterTitleMatch( const rtl::OUString _rTitle ) : rTitle( _rTitle ) { }
143 
144         //............................................................................
operator ()__anon2b3e87a70111::FilterTitleMatch145         bool operator () ( const FilterEntry& _rEntry )
146         {
147             sal_Bool bMatch;
148             if( !_rEntry.hasSubFilters() ) {
149                 //first try the complete filter name
150                 rtl::OUString title = _rEntry.getTitle();
151                 bMatch = ( title.equals(rTitle) );
152                 if (!bMatch) {
153                     //we didn't find a match using the full name, let's give it another
154                     //try using the shrunk version
155                     rtl::OUString aShrunkName = shrinkFilterName( _rEntry.getTitle() ).trim();
156                     bMatch = ( aShrunkName.equals(rTitle) );
157                 }
158             }
159             else
160                 // a filter group -> search the sub filters
161                 bMatch =
162                     _rEntry.endSubFilters() != ::std::find_if(
163                                                               _rEntry.beginSubFilters(),
164                                                               _rEntry.endSubFilters(),
165                                                               *this
166                                                               );
167 
168             return bMatch ? true : false;
169         }
170 
operator ()__anon2b3e87a70111::FilterTitleMatch171         bool operator () ( const UnoFilterEntry& _rEntry )
172         {
173             rtl::OUString aShrunkName = shrinkFilterName( _rEntry.First );
174             bool retVal = aShrunkName.equals(rTitle);
175             return retVal;
176         }
177     };
178 }
179 
180 #undef CLASS_NAME
181 #define CLASS_NAME "FilterHelper"
182 
FilterHelper()183 FilterHelper::FilterHelper()
184 : m_pFilterList(NULL)
185 , m_pFilterNames(NULL)
186 {
187     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
188     DBG_PRINT_EXIT(CLASS_NAME, __func__);
189 }
190 
~FilterHelper()191 FilterHelper::~FilterHelper()
192 {
193     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
194 
195     NSAutoreleasePool *pool = [NSAutoreleasePool new];
196 
197     if (NULL != m_pFilterList) {
198         delete m_pFilterList;
199     }
200 
201     if (NULL != m_pFilterNames) {
202         //we called retain when we added the strings to the list, so we should release them now
203         for (NSStringList::iterator iter = m_pFilterNames->begin(); iter != m_pFilterNames->end(); iter++) {
204             [*iter release];
205         }
206         delete m_pFilterNames;
207     }
208 
209     [pool release];
210 
211     DBG_PRINT_EXIT(CLASS_NAME, __func__);
212 }
213 
214 //------------------------------------------------------------------------------------
FilterNameExists(const rtl::OUString rTitle)215 sal_Bool FilterHelper::FilterNameExists( const rtl::OUString rTitle )
216 {
217     sal_Bool bRet = sal_False;
218 
219     if( m_pFilterList )
220         bRet =
221             m_pFilterList->end() != ::std::find_if(
222                                                    m_pFilterList->begin(),
223                                                    m_pFilterList->end(),
224                                                    FilterTitleMatch( rTitle )
225                                                    );
226 
227     return bRet;
228 }
229 
230 //------------------------------------------------------------------------------------
FilterNameExists(const UnoFilterList & _rGroupedFilters)231 sal_Bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
232 {
233     sal_Bool bRet = sal_False;
234 
235     if( m_pFilterList )
236     {
237         const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
238         const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
239         for( ; pStart != pEnd; ++pStart )
240             if( m_pFilterList->end() != ::std::find_if(
241                                                         m_pFilterList->begin(),
242                                                         m_pFilterList->end(),
243                                                         FilterTitleMatch( pStart->First ) ) )
244                 break;
245 
246         bRet = (pStart != pEnd);
247     }
248 
249     return bRet;
250 }
251 
252 //------------------------------------------------------------------------------------
ensureFilterList(const::rtl::OUString & _rInitialCurrentFilter)253 void FilterHelper::ensureFilterList( const ::rtl::OUString& _rInitialCurrentFilter )
254 {
255     //OSL_TRACE(">>> FilterHelper::%s", __func__);
256     if( NULL == m_pFilterList )
257     {
258         m_pFilterList = new FilterList;
259 
260         // set the first filter to the current filter
261         m_aCurrentFilter = _rInitialCurrentFilter;
262         OSL_TRACE("ensureFilterList filter:%s", OUStringToOString(m_aCurrentFilter, RTL_TEXTENCODING_UTF8).getStr());
263     }
264     //OSL_TRACE("<<< FilterHelper::%s", __func__);
265 }
266 
SetCurFilter(const rtl::OUString & rFilter)267 void FilterHelper::SetCurFilter( const rtl::OUString& rFilter )
268 {
269     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "filter", rFilter);
270 
271     ::vos::OGuard aGuard( Application::GetSolarMutex() );
272 
273     if(m_aCurrentFilter.equals(rFilter) == false)
274     {
275         m_aCurrentFilter = rFilter;
276     }
277 
278     //only for output purposes
279 #if OSL_DEBUG_LEVEL > 1
280     FilterList::iterator aFilter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
281     if (aFilter != m_pFilterList->end()) {
282         OUStringList suffixes = aFilter->getFilterSuffixList();
283         if (!suffixes.empty()) {
284             OSL_TRACE("Current active suffixes: ");
285             OUStringList::iterator suffIter = suffixes.begin();
286             while(suffIter != suffixes.end()) {
287                 OSL_TRACE("%s", OUStringToOString((*suffIter), RTL_TEXTENCODING_UTF8).getStr());
288                 suffIter++;
289             }
290         }
291     } else {
292         OSL_TRACE("No filter entry was found for that name!");
293     }
294 #endif
295 
296     DBG_PRINT_EXIT(CLASS_NAME, __func__);
297 }
298 
SetFilters()299 void FilterHelper::SetFilters()
300 {
301     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
302 
303     // set the default filter
304     if( m_aCurrentFilter.getLength() > 0 )
305     {
306         OSL_TRACE( "Setting current filter to %s", OUStringToOString(m_aCurrentFilter, RTL_TEXTENCODING_UTF8).getStr());
307 
308         SetCurFilter( m_aCurrentFilter );
309     }
310 
311     DBG_PRINT_EXIT(CLASS_NAME, __func__);
312 }
313 
appendFilter(const::rtl::OUString & aTitle,const::rtl::OUString & aFilterString)314 void FilterHelper::appendFilter(const ::rtl::OUString& aTitle, const ::rtl::OUString& aFilterString)
315 throw( ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException ) {
316     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", aTitle, "filter", aFilterString);
317 
318     ::vos::OGuard aGuard( Application::GetSolarMutex() );
319 
320     if( FilterNameExists( aTitle ) ) {
321         throw com::sun::star::lang::IllegalArgumentException();
322     }
323 
324     // ensure that we have a filter list
325     ensureFilterList( aTitle );
326 
327     // append the filter
328     OUStringList suffixList;
329     fillSuffixList(suffixList, aFilterString);
330     m_pFilterList->push_back(FilterEntry( aTitle, suffixList ) );
331 
332     DBG_PRINT_EXIT(CLASS_NAME, __func__);
333 }
334 
setCurrentFilter(const::rtl::OUString & aTitle)335 void FilterHelper::setCurrentFilter( const ::rtl::OUString& aTitle )
336 throw( ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException ) {
337     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aTitle", OUStringToOString(aTitle, RTL_TEXTENCODING_UTF8).getStr());
338 
339     SetCurFilter(aTitle);
340 
341     DBG_PRINT_EXIT(CLASS_NAME, __func__);
342 }
343 
getCurrentFilter()344 ::rtl::OUString SAL_CALL FilterHelper::getCurrentFilter(  )
345 throw( ::com::sun::star::uno::RuntimeException ) {
346     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
347 
348     ::rtl::OUString sReturn = (m_aCurrentFilter);
349 
350     DBG_PRINT_EXIT(CLASS_NAME, __func__, OUStringToOString(sReturn, RTL_TEXTENCODING_UTF8).getStr());
351 
352     return sReturn;
353 }
354 
appendFilterGroup(const::rtl::OUString & sGroupTitle,const::com::sun::star::uno::Sequence<::com::sun::star::beans::StringPair> & aFilters)355 void SAL_CALL FilterHelper::appendFilterGroup( const ::rtl::OUString& sGroupTitle, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::StringPair >& aFilters )
356 throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException) {
357 
358     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", OUStringToOString(sGroupTitle, RTL_TEXTENCODING_UTF8).getStr());
359 
360     ::vos::OGuard aGuard( Application::GetSolarMutex() );
361 
362     //add a separator if this is not the first group to be added
363     sal_Bool bPrependSeparator = m_pFilterList != NULL;
364 
365     // ensure that we have a filter list
366     ::rtl::OUString sInitialCurrentFilter;
367     if( aFilters.getLength() > 0)
368         sInitialCurrentFilter = aFilters[0].First;
369     ensureFilterList( sInitialCurrentFilter );
370 
371     // append the filter
372     if (bPrependSeparator) {
373         rtl::OUString dash = rtl::OUString::createFromAscii("-");
374         OUStringList emptyList;
375         m_pFilterList->push_back(FilterEntry(dash, emptyList));
376     }
377 
378     const com::sun::star::beans::StringPair* pSubFilters   = aFilters.getConstArray();
379     const com::sun::star::beans::StringPair* pSubFiltersEnd = pSubFilters + aFilters.getLength();
380     for( ; pSubFilters != pSubFiltersEnd; ++pSubFilters ) {
381         appendFilter(pSubFilters->First, pSubFilters->Second);
382     }
383 
384     DBG_PRINT_EXIT(CLASS_NAME, __func__);
385 }
386 
filenameMatchesFilter(NSString * sFilename)387 sal_Bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
388 {
389     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
390 
391     if (m_aCurrentFilter == NULL) {
392         OSL_TRACE("filter name is null");
393         return sal_True;
394     }
395 
396     NSFileManager *manager = [NSFileManager defaultManager];
397     NSDictionary* pAttribs = [manager fileAttributesAtPath: sFilename traverseLink: NO];
398     if( pAttribs )
399     {
400         NSObject* pType = [pAttribs objectForKey: NSFileType];
401         if( pType && [pType isKindOfClass: [NSString class]] )
402         {
403             NSString* pT = (NSString*)pType;
404             if( [pT isEqualToString: NSFileTypeDirectory]    ||
405                 [pT isEqualToString: NSFileTypeSymbolicLink] )
406                 return sal_True;
407         }
408     }
409 
410     FilterList::iterator filter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
411     if (filter == m_pFilterList->end()) {
412         OSL_TRACE("filter not found in list");
413         return sal_True;
414     }
415 
416     OUStringList suffixList = filter->getFilterSuffixList();
417 
418     {
419         rtl::OUString aName = [sFilename OUString];
420         rtl::OUString allMatcher = rtl::OUString::createFromAscii(".*");
421         for(OUStringList::iterator iter = suffixList.begin(); iter != suffixList.end(); iter++) {
422             if (aName.matchIgnoreAsciiCase(*iter, aName.getLength() - (*iter).getLength()) || ((*iter).equals(allMatcher))) {
423                 return sal_True;
424             }
425         }
426     }
427 
428     // might be an alias
429     NSString* pResolved = resolveAlias( sFilename );
430     if( pResolved )
431     {
432         sal_Bool bResult = filenameMatchesFilter( pResolved );
433         [pResolved autorelease];
434         if( bResult )
435             return sal_True;
436     }
437 
438     DBG_PRINT_EXIT(CLASS_NAME, __func__);
439 
440     return sal_False;
441 }
442 
getFilterList()443 FilterList* FilterHelper::getFilterList() {
444     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
445     DBG_PRINT_EXIT(CLASS_NAME, __func__);
446 
447     return m_pFilterList;
448 }
449 
getFilterNames()450 NSStringList* FilterHelper::getFilterNames() {
451     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
452 
453     if (NULL == m_pFilterList)
454         return NULL;
455     if (NULL == m_pFilterNames) {
456         //build filter names list
457         m_pFilterNames = new NSStringList;
458         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++) {
459             m_pFilterNames->push_back([[NSString stringWithOUString:iter->getTitle()] retain]);
460         }
461     }
462 
463     DBG_PRINT_EXIT(CLASS_NAME, __func__);
464 
465     return m_pFilterNames;
466 }
467 
SetFilterAtIndex(unsigned index)468 void FilterHelper::SetFilterAtIndex(unsigned index) {
469     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "index", index);
470 
471     if (m_pFilterList->size() <= index) {
472         index = 0;
473     }
474     FilterEntry entry = m_pFilterList->at(index);
475     SetCurFilter(entry.getTitle());
476 
477     DBG_PRINT_EXIT(CLASS_NAME, __func__);
478 }
479 
fillSuffixList(OUStringList & aSuffixList,const::rtl::OUString & suffixString)480 void FilterHelper::fillSuffixList(OUStringList& aSuffixList, const ::rtl::OUString& suffixString) {
481     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aSuffixList", suffixString);
482 
483     sal_Int32 nIndex = 0;
484     do {
485         rtl::OUString aToken = suffixString.getToken( 0, ';', nIndex );
486         aSuffixList.push_back(aToken.copy(1));
487     } while ( nIndex >= 0 );
488 
489     DBG_PRINT_EXIT(CLASS_NAME, __func__);
490 }
491 
getCurrentFilterIndex()492 int FilterHelper::getCurrentFilterIndex() {
493     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
494 
495     int result = 0;//default to first filter
496     if (m_aCurrentFilter.getLength() > 0) {
497         int i = 0;
498         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++, i++) {
499             rtl::OUString aTitle = iter->getTitle();
500             if (m_aCurrentFilter.equals(aTitle)) {
501                 result = i;
502                 break;
503             } else {
504                 aTitle = shrinkFilterName(aTitle).trim();
505                 if (m_aCurrentFilter.equals(aTitle)) {
506                     result = i;
507                     break;
508                 }
509             }
510         }
511     }
512 
513     DBG_PRINT_EXIT(CLASS_NAME, __func__, result);
514 
515     return result;
516 }
517 
getCurrentFilterSuffixList()518 OUStringList FilterHelper::getCurrentFilterSuffixList() {
519     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
520 
521     OUStringList retVal;
522     if (m_aCurrentFilter.getLength() > 0) {
523         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++) {
524             rtl::OUString aTitle = iter->getTitle();
525             if (m_aCurrentFilter.equals(aTitle)) {
526                 retVal = iter->getFilterSuffixList();
527                 break;
528             } else {
529                 aTitle = shrinkFilterName(aTitle).trim();
530                 if (m_aCurrentFilter.equals(aTitle)) {
531                     retVal = iter->getFilterSuffixList();
532                     break;
533                 }
534             }
535         }
536     }
537 
538     DBG_PRINT_EXIT(CLASS_NAME, __func__);
539 
540     return retVal;
541 }
542