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