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
21 #include "filterfactory.hxx"
22 #include "constant.hxx"
23
24 #include <com/sun/star/lang/XInitialization.hpp>
25 #include <comphelper/enumhelper.hxx>
26 #include <comphelper/sequence.hxx>
27 #include <officecfg/Setup.hxx>
28 #include <officecfg/TypeDetection/UISort.hxx>
29
30
31 namespace filter::config{
32
GetTheFilterCache()33 FilterCache& GetTheFilterCache()
34 {
35 static FilterCache CACHE;
36 return CACHE;
37 }
38
39 /** @short define all possible parts of a filter query.
40
41 @descr syntax: "<query>[:<param>[=<value>]]"
42 e.g.: "_query_writer:default_first:use_order:sort_prop=uiname"
43
44 argument description default
45 -----------------------------------------------------------------------------------------------
46 iflags=<mask> include filters by given mask 0
47 eflags=<mask> exclude filters by given mask 0
48 sort_prop=<[name,uiname]> sort by internal name or uiname name
49 descending sort descending false
50 use_order use order flag of filters for sorting false
51 default_first set default filter on top of return list false
52 case_sensitive compare "sort_prop" case sensitive false
53 */
54
FilterFactory(const css::uno::Reference<css::uno::XComponentContext> & rxContext)55 FilterFactory::FilterFactory(const css::uno::Reference< css::uno::XComponentContext >& rxContext)
56 : m_xContext(rxContext)
57 {
58 static const css::uno::Sequence<OUString> sServiceNames { u"com.sun.star.document.FilterFactory"_ustr };
59 BaseContainer::init(u"com.sun.star.comp.filter.config.FilterFactory"_ustr ,
60 sServiceNames,
61 FilterCache::E_FILTER );
62 }
63
64
~FilterFactory()65 FilterFactory::~FilterFactory()
66 {
67 }
68
69
createInstance(const OUString & sFilter)70 css::uno::Reference< css::uno::XInterface > SAL_CALL FilterFactory::createInstance(const OUString& sFilter)
71 {
72 return createInstanceWithArguments(sFilter, css::uno::Sequence< css::uno::Any >());
73 }
74
75
createInstanceWithArguments(const OUString & sFilter,const css::uno::Sequence<css::uno::Any> & lArguments)76 css::uno::Reference< css::uno::XInterface > SAL_CALL FilterFactory::createInstanceWithArguments(const OUString& sFilter ,
77 const css::uno::Sequence< css::uno::Any >& lArguments)
78 {
79 // SAFE ->
80 std::unique_lock aLock(m_aMutex);
81
82 auto & cache = GetTheFilterCache();
83
84 // search filter on cache
85 CacheItem aFilter = cache.getItem(FilterCache::E_FILTER, sFilter);
86 OUString sFilterService;
87 aFilter[PROPNAME_FILTERSERVICE] >>= sFilterService;
88
89 // create service instance
90 css::uno::Reference< css::uno::XInterface > xFilter;
91 if (!sFilterService.isEmpty())
92 xFilter = m_xContext->getServiceManager()->createInstanceWithContext(sFilterService, m_xContext);
93
94 // initialize filter
95 css::uno::Reference< css::lang::XInitialization > xInit(xFilter, css::uno::UNO_QUERY);
96 if (xInit.is())
97 {
98 // format: lInitData[0] = seq<PropertyValue>, which contains all configuration properties of this filter
99 // lInitData[1] = lArguments[0]
100 // ...
101 // lInitData[n] = lArguments[n-1]
102 css::uno::Sequence< css::beans::PropertyValue > lConfig;
103 aFilter >> lConfig;
104
105 ::std::vector< css::uno::Any > stlArguments(comphelper::sequenceToContainer< ::std::vector< css::uno::Any > >(lArguments));
106 stlArguments.insert(stlArguments.begin(), css::uno::Any(lConfig));
107
108 xInit->initialize(comphelper::containerToSequence(stlArguments));
109 }
110
111 return xFilter;
112 // <- SAFE
113 }
114
115
getAvailableServiceNames()116 css::uno::Sequence< OUString > SAL_CALL FilterFactory::getAvailableServiceNames()
117 {
118 /* Attention: Instead of getElementNames() this method have to return only filter names,
119 which can be created as UNO Services really. That's why we search for filters,
120 which don't have a valid value for the property "FilterService".
121 Of course we can't check for corrupted service names here. We can check
122 for empty strings only...
123 */
124 css::beans::NamedValue lEProps[] {
125 { PROPNAME_FILTERSERVICE, css::uno::Any(OUString()) } };
126
127 std::vector<OUString> lUNOFilters;
128 try
129 {
130 lUNOFilters = GetTheFilterCache().getMatchingItemsByProps(FilterCache::E_FILTER, {}, lEProps);
131 }
132 catch(const css::uno::RuntimeException&)
133 { throw; }
134 catch(const css::uno::Exception&)
135 { lUNOFilters.clear(); }
136
137 return comphelper::containerToSequence(lUNOFilters);
138 }
139
140
createSubSetEnumerationByQuery(const OUString & sQuery)141 css::uno::Reference< css::container::XEnumeration > SAL_CALL FilterFactory::createSubSetEnumerationByQuery(const OUString& sQuery)
142 {
143 // reject old deprecated queries ...
144 if (sQuery.startsWith("_filterquery_"))
145 throw css::uno::RuntimeException(
146 u"Use of deprecated and now unsupported query!"_ustr,
147 static_cast< css::container::XContainerQuery* >(this));
148
149 // convert "_query_xxx:..." to "getByDocService=xxx:..."
150 OUString sNewQuery(sQuery);
151 sal_Int32 pos = sNewQuery.indexOf("_query_");
152 if (pos != -1)
153 {
154 OSL_FAIL("DEPRECATED!\nPlease use new query format: 'matchByDocumentService=...'");
155 sNewQuery = OUString::Concat("matchByDocumentService=") + sNewQuery.subView(7);
156 }
157
158 // analyze query and split it into its tokens
159 QueryTokenizer lTokens(sNewQuery);
160 std::vector<OUString> lEnumSet;
161
162 // start query
163 // (see attention comment below!)
164 if (lTokens.valid())
165 {
166 // SAFE -> ----------------------
167 {
168 std::unique_lock aLock(m_aMutex);
169 // May be not all filters was loaded ...
170 // But we need it now!
171 impl_loadOnDemand(aLock);
172 }
173 // <- SAFE ----------------------
174
175 if (lTokens.find(QUERY_IDENTIFIER_GETPREFERREDFILTERFORTYPE) != lTokens.end())
176 OSL_FAIL("DEPRECATED!\nPlease use prop search at the TypeDetection container!");
177 else
178 if (lTokens.find(QUERY_IDENTIFIER_MATCHBYDOCUMENTSERVICE) != lTokens.end())
179 lEnumSet = impl_queryMatchByDocumentService(lTokens);
180 else
181 if (lTokens.find(QUERY_IDENTIFIER_GET_SORTED_FILTERLIST) != lTokens.end())
182 lEnumSet = impl_getSortedFilterList(lTokens);
183 }
184
185 // pack list of item names as an enum list
186 // Attention: Do not return empty reference for empty list!
187 // The outside check "hasMoreElements()" should be enough, to detect this state :-)
188 return new ::comphelper::OEnumerationByName(this, std::move(lEnumSet));
189 }
190
191
impl_queryMatchByDocumentService(const QueryTokenizer & lTokens) const192 std::vector<OUString> FilterFactory::impl_queryMatchByDocumentService(const QueryTokenizer& lTokens) const
193 {
194 // analyze query
195 QueryTokenizer::const_iterator pIt;
196
197 OUString sDocumentService;
198 sal_Int32 nIFlags = 0;
199 sal_Int32 nEFlags = 0;
200
201 pIt = lTokens.find(QUERY_IDENTIFIER_MATCHBYDOCUMENTSERVICE);
202 if (pIt != lTokens.end())
203 sDocumentService = pIt->second;
204
205 #define COMP_HACK
206 #ifdef COMP_HACK
207 if ( sDocumentService == "writer" )
208 {
209 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
210 sDocumentService = "com.sun.star.text.TextDocument";
211 }
212 else if ( sDocumentService == "web" )
213 {
214 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
215 sDocumentService = "com.sun.star.text.WebDocument";
216 }
217 else if ( sDocumentService == "global" )
218 {
219 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
220 sDocumentService = "com.sun.star.text.GlobalDocument";
221 }
222 else if ( sDocumentService == "calc" )
223 {
224 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
225 sDocumentService = "com.sun.star.sheet.SpreadsheetDocument";
226 }
227 else if ( sDocumentService == "draw" )
228 {
229 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
230 sDocumentService = "com.sun.star.drawing.DrawingDocument";
231 }
232 else if ( sDocumentService == "impress" )
233 {
234 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
235 sDocumentService = "com.sun.star.presentation.PresentationDocument";
236 }
237 else if ( sDocumentService == "math" )
238 {
239 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
240 sDocumentService = "com.sun.star.formula.FormulaProperties";
241 }
242 #endif
243
244 pIt = lTokens.find(QUERY_PARAM_IFLAGS);
245 if (pIt != lTokens.end())
246 nIFlags = pIt->second.toInt32();
247
248 pIt = lTokens.find(QUERY_PARAM_EFLAGS);
249 if (pIt != lTokens.end())
250 nEFlags = pIt->second.toInt32();
251
252 // SAFE -> ----------------------
253 std::unique_lock aLock(m_aMutex);
254
255 // search suitable filters
256 FilterCache* pCache = impl_getWorkingCache(aLock);
257 std::vector<OUString> lFilterNames = pCache->getItemNames(FilterCache::E_FILTER);
258 std::vector<OUString> lResult ;
259
260 for (auto const& filterName : lFilterNames)
261 {
262 try
263 {
264 const CacheItem aFilter = pCache->getItem(FilterCache::E_FILTER, filterName);
265
266 // "matchByDocumentService=" => any filter will be addressed here
267 // "matchByDocumentService=all" => any filter will be addressed here
268 // "matchByDocumentService=com.sun.star..." => only filter matching this document service will be addressed
269 OUString sCheckValue = aFilter.getUnpackedValueOrDefault(PROPNAME_DOCUMENTSERVICE, OUString());
270 if (
271 (!sDocumentService.isEmpty() ) &&
272 (sDocumentService != QUERY_CONSTVALUE_ALL ) &&
273 (sCheckValue != sDocumentService )
274 )
275 {
276 continue; // ignore filter -> try next one!
277 }
278
279 // "iflags=" => not allowed
280 // "iflags=-1" => not allowed
281 // "iflags=0" => not useful
282 // "iflags=283648" => only filter, which has set these flag field will be addressed
283 sal_Int32 nCheckValue = aFilter.getUnpackedValueOrDefault(PROPNAME_FLAGS, sal_Int32(0));
284 if (
285 (nIFlags > 0 ) &&
286 ((nCheckValue & nIFlags) != nIFlags)
287 )
288 {
289 continue; // ignore filter -> try next one!
290 }
291
292 // "eflags=" => not allowed
293 // "eflags=-1" => not allowed
294 // "eflags=0" => not useful
295 // "eflags=283648" => only filter, which has not set these flag field will be addressed
296 if (
297 (nEFlags > 0 ) &&
298 ((nCheckValue & nEFlags) == nEFlags)
299 )
300 {
301 continue; // ignore filter -> try next one!
302 }
303
304 // OK - this filter passed all checks.
305 // It match the query ...
306 lResult.push_back(filterName);
307 }
308 catch(const css::uno::RuntimeException&)
309 { throw; }
310 catch(const css::uno::Exception&)
311 { continue; }
312 }
313
314 aLock.unlock();
315 // <- SAFE ----------------------
316
317 return lResult;
318 }
319
320 namespace {
321
322 class stlcomp_removeIfMatchFlags
323 {
324 private:
325 FilterCache* m_pCache ;
326 sal_Int32 m_nFlags ;
327 bool m_bIFlags;
328
329 public:
stlcomp_removeIfMatchFlags(FilterCache * pCache,sal_Int32 nFlags,bool bIFlags)330 stlcomp_removeIfMatchFlags(FilterCache* pCache ,
331 sal_Int32 nFlags ,
332 bool bIFlags)
333 : m_pCache (pCache )
334 , m_nFlags (nFlags )
335 , m_bIFlags(bIFlags)
336 {}
337
operator ()(const OUString & sFilter) const338 bool operator() (const OUString& sFilter) const
339 {
340 try
341 {
342 const CacheItem aFilter = m_pCache->getItem(FilterCache::E_FILTER, sFilter);
343 sal_Int32 nFlags = aFilter.getUnpackedValueOrDefault(PROPNAME_FLAGS, (sal_Int32(0)));
344
345 bool bMatch = false;
346 if (m_bIFlags)
347 // IFlags are interpreted as ALL_FLAGS_MUST_MATCH !
348 bMatch = ((nFlags & m_nFlags) == m_nFlags);
349 else
350 // EFlags are interpreted as AT_LEAST_ONE_FLAG_MUST_MATCH !
351 bMatch = !(nFlags & m_nFlags);
352 // We are asked for bRemove ! And bMatch = !bRemove => so bRemove = !bMatch .-)
353 return !bMatch;
354 }
355 catch(const css::container::NoSuchElementException &)
356 {
357 return true;
358 }
359 }
360 };
361
362 }
363
impl_getSortedFilterList(const QueryTokenizer & lTokens) const364 std::vector<OUString> FilterFactory::impl_getSortedFilterList(const QueryTokenizer& lTokens) const
365 {
366 // analyze the given query parameter
367 QueryTokenizer::const_iterator pIt1;
368
369 OUString sModule;
370 sal_Int32 nIFlags = -1;
371 sal_Int32 nEFlags = -1;
372
373 pIt1 = lTokens.find(QUERY_PARAM_MODULE);
374 if (pIt1 != lTokens.end())
375 sModule = pIt1->second;
376 pIt1 = lTokens.find(QUERY_PARAM_IFLAGS);
377 if (pIt1 != lTokens.end())
378 nIFlags = pIt1->second.toInt32();
379 pIt1 = lTokens.find(QUERY_PARAM_EFLAGS);
380 if (pIt1 != lTokens.end())
381 nEFlags = pIt1->second.toInt32();
382
383 // simple search for filters of one specific module.
384 std::vector<OUString> lFilterList;
385 if (!sModule.isEmpty())
386 lFilterList = impl_getSortedFilterListForModule(sModule, nIFlags, nEFlags);
387 else
388 {
389 // more complex search for all filters
390 // We check first, which office modules are installed...
391 const css::uno::Sequence<OUString> lModules = impl_getListOfInstalledModules();
392 for (auto const& module : lModules)
393 {
394 std::vector<OUString> lFilters4Module = impl_getSortedFilterListForModule(module, nIFlags, nEFlags);
395 for (auto const& filter4Module : lFilters4Module)
396 {
397 lFilterList.push_back(filter4Module);
398 }
399 }
400 }
401
402 return lFilterList;
403 }
404
405
impl_getListOfInstalledModules()406 css::uno::Sequence<OUString> FilterFactory::impl_getListOfInstalledModules()
407 {
408 css::uno::Reference< css::container::XNameAccess > xModuleConfig = officecfg::Setup::Office::Factories::get();
409 return xModuleConfig->getElementNames();
410 }
411
412
impl_getSortedFilterListForModule(const OUString & sModule,sal_Int32 nIFlags,sal_Int32 nEFlags) const413 std::vector<OUString> FilterFactory::impl_getSortedFilterListForModule(const OUString& sModule,
414 sal_Int32 nIFlags,
415 sal_Int32 nEFlags) const
416 {
417 std::vector<OUString> lSortedFilters = impl_readSortedFilterListFromConfig(sModule);
418
419 // get all filters for the requested module
420 css::beans::NamedValue lIProps[] { { PROPNAME_DOCUMENTSERVICE, css::uno::Any(sModule) } };
421
422 // SAFE -> ----------------------
423 std::unique_lock aLock(m_aMutex);
424 FilterCache* pCache = impl_getWorkingCache(aLock);
425 std::vector<OUString> lOtherFilters = pCache->getMatchingItemsByProps(FilterCache::E_FILTER, lIProps);
426 aLock.unlock();
427 // <- SAFE ----------------------
428
429 // bring "other" filters in an alphabetical order
430 // It's needed below.
431 ::std::sort(lOtherFilters.begin(), lOtherFilters.end());
432
433 // merge both lists together
434 std::vector<OUString> lMergedFilters = lSortedFilters;
435 const auto itlSortedFiltersEnd = lSortedFilters.cend();
436 for (auto const& otherFilter : lOtherFilters)
437 {
438 if (::std::find(lSortedFilters.cbegin(), lSortedFilters.cend(), otherFilter) == itlSortedFiltersEnd)
439 lMergedFilters.push_back(otherFilter);
440 }
441
442 // remove all filters from this merged list, which does not fit the flag specification
443 if (nIFlags != -1)
444 {
445 std::erase_if(lMergedFilters, stlcomp_removeIfMatchFlags(pCache, nIFlags, true));
446 }
447 if (nEFlags != -1)
448 {
449 std::erase_if(lMergedFilters, stlcomp_removeIfMatchFlags(pCache, nEFlags, false));
450 }
451
452 // sort the default filter to the front of this list
453 // TODO
454
455 return lMergedFilters;
456 }
457
458
impl_readSortedFilterListFromConfig(const OUString & sModule)459 std::vector<OUString> FilterFactory::impl_readSortedFilterListFromConfig(const OUString& sModule)
460 {
461 try
462 {
463 css::uno::Reference< css::container::XNameAccess > xUISortConfig = officecfg::TypeDetection::UISort::ModuleDependendFilterOrder::get();
464 // don't check the module name here. If it does not exists, an exception is thrown and caught below.
465 // We return an empty list as result then.
466 css::uno::Reference< css::container::XNameAccess > xModule;
467 xUISortConfig->getByName(sModule) >>= xModule;
468 if (xModule.is()) // only to be on the safe side of life if the exception was not thrown .-)
469 {
470 // Note: conversion of the returned Any to std::vector<OUString> throws
471 // an IllegalArgumentException if the type does not match ...
472 // but it resets the std::vector<OUString> to a length of 0 if the Any is empty!
473 std::vector<OUString> lSortedFilters(
474 comphelper::sequenceToContainer< std::vector<OUString> >(xModule->getByName(PROPNAME_SORTEDFILTERLIST).get<css::uno::Sequence<OUString> >()));
475 return lSortedFilters;
476 }
477 }
478 catch(const css::uno::RuntimeException&)
479 { throw; }
480 catch(const css::uno::Exception&)
481 {}
482
483 return std::vector<OUString>();
484 }
485
486 } // namespace filter
487
488 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
filter_FilterFactory_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)489 filter_FilterFactory_get_implementation(
490 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
491 {
492 return cppu::acquire(new filter::config::FilterFactory(context));
493 }
494
495 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
496