LibreOffice Module filter (master) 1
filterfactory.cxx
Go to the documentation of this file.
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>
27#include <officecfg/Setup.hxx>
28#include <officecfg/TypeDetection/UISort.hxx>
29
30
31namespace filter::config{
32
34{
35 static FilterCache CACHE;
36 return CACHE;
37}
38
55FilterFactory::FilterFactory(const css::uno::Reference< css::uno::XComponentContext >& rxContext)
56 : m_xContext(rxContext)
57{
58 static const css::uno::Sequence<OUString> sServiceNames { "com.sun.star.document.FilterFactory" };
59 BaseContainer::init("com.sun.star.comp.filter.config.FilterFactory" ,
60 sServiceNames,
62}
63
64
66{
67}
68
69
70css::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
76css::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
116css::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 {
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
141css::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 "Use of deprecated and now unsupported query!",
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 OUString sPatchedQuery(OUString::Concat("matchByDocumentService=") + sNewQuery.subView(7));
156 sNewQuery = sPatchedQuery;
157 }
158
159 // analyze query and split it into its tokens
160 QueryTokenizer lTokens(sNewQuery);
161 std::vector<OUString> lEnumSet;
162
163 // start query
164 // (see attention comment below!)
165 if (lTokens.valid())
166 {
167 // SAFE -> ----------------------
168 {
169 std::unique_lock aLock(m_aMutex);
170 // May be not all filters was loaded ...
171 // But we need it now!
172 impl_loadOnDemand(aLock);
173 }
174 // <- SAFE ----------------------
175
176 if (lTokens.find(QUERY_IDENTIFIER_GETPREFERREDFILTERFORTYPE) != lTokens.end())
177 OSL_FAIL("DEPRECATED!\nPlease use prop search at the TypeDetection container!");
178 else
179 if (lTokens.find(QUERY_IDENTIFIER_MATCHBYDOCUMENTSERVICE) != lTokens.end())
180 lEnumSet = impl_queryMatchByDocumentService(lTokens);
181 else
182 if (lTokens.find(QUERY_IDENTIFIER_GET_SORTED_FILTERLIST) != lTokens.end())
183 lEnumSet = impl_getSortedFilterList(lTokens);
184 }
185
186 // pack list of item names as an enum list
187 // Attention: Do not return empty reference for empty list!
188 // The outside check "hasMoreElements()" should be enough, to detect this state :-)
189 return new ::comphelper::OEnumerationByName(this, std::move(lEnumSet));
190}
191
192
193std::vector<OUString> FilterFactory::impl_queryMatchByDocumentService(const QueryTokenizer& lTokens) const
194{
195 // analyze query
196 QueryTokenizer::const_iterator pIt;
197
198 OUString sDocumentService;
199 sal_Int32 nIFlags = 0;
200 sal_Int32 nEFlags = 0;
201
202 pIt = lTokens.find(QUERY_IDENTIFIER_MATCHBYDOCUMENTSERVICE);
203 if (pIt != lTokens.end())
204 sDocumentService = pIt->second;
205
206#define COMP_HACK
207#ifdef COMP_HACK
208 if ( sDocumentService == "writer" )
209 {
210 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
211 sDocumentService = "com.sun.star.text.TextDocument";
212 }
213 else if ( sDocumentService == "web" )
214 {
215 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
216 sDocumentService = "com.sun.star.text.WebDocument";
217 }
218 else if ( sDocumentService == "global" )
219 {
220 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
221 sDocumentService = "com.sun.star.text.GlobalDocument";
222 }
223 else if ( sDocumentService == "calc" )
224 {
225 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
226 sDocumentService = "com.sun.star.sheet.SpreadsheetDocument";
227 }
228 else if ( sDocumentService == "draw" )
229 {
230 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
231 sDocumentService = "com.sun.star.drawing.DrawingDocument";
232 }
233 else if ( sDocumentService == "impress" )
234 {
235 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
236 sDocumentService = "com.sun.star.presentation.PresentationDocument";
237 }
238 else if ( sDocumentService == "math" )
239 {
240 OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!");
241 sDocumentService = "com.sun.star.formula.FormulaProperties";
242 }
243#endif
244
245 pIt = lTokens.find(QUERY_PARAM_IFLAGS);
246 if (pIt != lTokens.end())
247 nIFlags = pIt->second.toInt32();
248
249 pIt = lTokens.find(QUERY_PARAM_EFLAGS);
250 if (pIt != lTokens.end())
251 nEFlags = pIt->second.toInt32();
252
253 // SAFE -> ----------------------
254 std::unique_lock aLock(m_aMutex);
255
256 // search suitable filters
257 FilterCache* pCache = impl_getWorkingCache(aLock);
258 std::vector<OUString> lFilterNames = pCache->getItemNames(FilterCache::E_FILTER);
259 std::vector<OUString> lResult ;
260
261 for (auto const& filterName : lFilterNames)
262 {
263 try
264 {
265 const CacheItem aFilter = pCache->getItem(FilterCache::E_FILTER, filterName);
266
267 // "matchByDocumentService=" => any filter will be addressed here
268 // "matchByDocumentService=all" => any filter will be addressed here
269 // "matchByDocumentService=com.sun.star..." => only filter matching this document service will be addressed
270 OUString sCheckValue = aFilter.getUnpackedValueOrDefault(PROPNAME_DOCUMENTSERVICE, OUString());
271 if (
272 (!sDocumentService.isEmpty() ) &&
274 (sCheckValue != sDocumentService )
275 )
276 {
277 continue; // ignore filter -> try next one!
278 }
279
280 // "iflags=" => not allowed
281 // "iflags=-1" => not allowed
282 // "iflags=0" => not useful
283 // "iflags=283648" => only filter, which has set these flag field will be addressed
284 sal_Int32 nCheckValue = aFilter.getUnpackedValueOrDefault(PROPNAME_FLAGS, sal_Int32(0));
285 if (
286 (nIFlags > 0 ) &&
287 ((nCheckValue & nIFlags) != nIFlags)
288 )
289 {
290 continue; // ignore filter -> try next one!
291 }
292
293 // "eflags=" => not allowed
294 // "eflags=-1" => not allowed
295 // "eflags=0" => not useful
296 // "eflags=283648" => only filter, which has not set these flag field will be addressed
297 if (
298 (nEFlags > 0 ) &&
299 ((nCheckValue & nEFlags) == nEFlags)
300 )
301 {
302 continue; // ignore filter -> try next one!
303 }
304
305 // OK - this filter passed all checks.
306 // It match the query ...
307 lResult.push_back(filterName);
308 }
309 catch(const css::uno::RuntimeException&)
310 { throw; }
311 catch(const css::uno::Exception&)
312 { continue; }
313 }
314
315 aLock.unlock();
316 // <- SAFE ----------------------
317
318 return lResult;
319}
320
321namespace {
322
323class stlcomp_removeIfMatchFlags
324{
325 private:
326 FilterCache* m_pCache ;
327 sal_Int32 m_nFlags ;
329
330 public:
331 stlcomp_removeIfMatchFlags(FilterCache* pCache ,
332 sal_Int32 nFlags ,
333 bool bIFlags)
334 : m_pCache (pCache )
335 , m_nFlags (nFlags )
336 , m_bIFlags(bIFlags)
337 {}
338
339 bool operator() (const OUString& sFilter) const
340 {
341 try
342 {
343 const CacheItem aFilter = m_pCache->getItem(FilterCache::E_FILTER, sFilter);
344 sal_Int32 nFlags = aFilter.getUnpackedValueOrDefault(PROPNAME_FLAGS, (sal_Int32(0)));
345
346 bool bMatch = false;
347 if (m_bIFlags)
348 // IFlags are interpreted as ALL_FLAGS_MUST_MATCH !
349 bMatch = ((nFlags & m_nFlags) == m_nFlags);
350 else
351 // EFlags are interpreted as AT_LEAST_ONE_FLAG_MUST_MATCH !
352 bMatch = !(nFlags & m_nFlags);
353 // We are asked for bRemove ! And bMatch = !bRemove => so bRemove = !bMatch .-)
354 return !bMatch;
355 }
356 catch(const css::container::NoSuchElementException &)
357 {
358 return true;
359 }
360 }
361};
362
363}
364
365std::vector<OUString> FilterFactory::impl_getSortedFilterList(const QueryTokenizer& lTokens) const
366{
367 // analyze the given query parameter
368 QueryTokenizer::const_iterator pIt1;
369
370 OUString sModule;
371 sal_Int32 nIFlags = -1;
372 sal_Int32 nEFlags = -1;
373
374 pIt1 = lTokens.find(QUERY_PARAM_MODULE);
375 if (pIt1 != lTokens.end())
376 sModule = pIt1->second;
377 pIt1 = lTokens.find(QUERY_PARAM_IFLAGS);
378 if (pIt1 != lTokens.end())
379 nIFlags = pIt1->second.toInt32();
380 pIt1 = lTokens.find(QUERY_PARAM_EFLAGS);
381 if (pIt1 != lTokens.end())
382 nEFlags = pIt1->second.toInt32();
383
384 // simple search for filters of one specific module.
385 std::vector<OUString> lFilterList;
386 if (!sModule.isEmpty())
387 lFilterList = impl_getSortedFilterListForModule(sModule, nIFlags, nEFlags);
388 else
389 {
390 // more complex search for all filters
391 // We check first, which office modules are installed...
392 const css::uno::Sequence<OUString> lModules = impl_getListOfInstalledModules();
393 for (auto const& module : lModules)
394 {
395 std::vector<OUString> lFilters4Module = impl_getSortedFilterListForModule(module, nIFlags, nEFlags);
396 for (auto const& filter4Module : lFilters4Module)
397 {
398 lFilterList.push_back(filter4Module);
399 }
400 }
401 }
402
403 return lFilterList;
404}
405
406
408{
409 css::uno::Reference< css::container::XNameAccess > xModuleConfig = officecfg::Setup::Office::Factories::get();
410 return xModuleConfig->getElementNames();
411}
412
413
414std::vector<OUString> FilterFactory::impl_getSortedFilterListForModule(const OUString& sModule,
415 sal_Int32 nIFlags,
416 sal_Int32 nEFlags) const
417{
418 std::vector<OUString> lSortedFilters = impl_readSortedFilterListFromConfig(sModule);
419
420 // get all filters for the requested module
421 css::beans::NamedValue lIProps[] { { PROPNAME_DOCUMENTSERVICE, css::uno::Any(sModule) } };
422
423 // SAFE -> ----------------------
424 std::unique_lock aLock(m_aMutex);
425 FilterCache* pCache = impl_getWorkingCache(aLock);
426 std::vector<OUString> lOtherFilters = pCache->getMatchingItemsByProps(FilterCache::E_FILTER, lIProps);
427 aLock.unlock();
428 // <- SAFE ----------------------
429
430 // bring "other" filters in an alphabetical order
431 // It's needed below.
432 ::std::sort(lOtherFilters.begin(), lOtherFilters.end());
433
434 // merge both lists together
435 std::vector<OUString> lMergedFilters = lSortedFilters;
436 const auto itlSortedFiltersEnd = lSortedFilters.cend();
437 for (auto const& otherFilter : lOtherFilters)
438 {
439 if (::std::find(lSortedFilters.cbegin(), lSortedFilters.cend(), otherFilter) == itlSortedFiltersEnd)
440 lMergedFilters.push_back(otherFilter);
441 }
442
443 // remove all filters from this merged list, which does not fit the flag specification
444 if (nIFlags != -1)
445 {
446 auto pItToErase = ::std::remove_if(lMergedFilters.begin(), lMergedFilters.end(), stlcomp_removeIfMatchFlags(pCache, nIFlags, true));
447 lMergedFilters.erase(pItToErase, lMergedFilters.end());
448 }
449 if (nEFlags != -1)
450 {
451 auto pItToErase = ::std::remove_if(lMergedFilters.begin(), lMergedFilters.end(), stlcomp_removeIfMatchFlags(pCache, nEFlags, false));
452 lMergedFilters.erase(pItToErase, lMergedFilters.end());
453 }
454
455 // sort the default filter to the front of this list
456 // TODO
457
458 return lMergedFilters;
459}
460
461
462std::vector<OUString> FilterFactory::impl_readSortedFilterListFromConfig(const OUString& sModule)
463{
464 try
465 {
466 css::uno::Reference< css::container::XNameAccess > xUISortConfig = officecfg::TypeDetection::UISort::ModuleDependendFilterOrder::get();
467 // don't check the module name here. If it does not exists, an exception is thrown and caught below.
468 // We return an empty list as result then.
469 css::uno::Reference< css::container::XNameAccess > xModule;
470 xUISortConfig->getByName(sModule) >>= xModule;
471 if (xModule.is()) // only to be on the safe side of life if the exception was not thrown .-)
472 {
473 // Note: conversion of the returned Any to std::vector<OUString> throws
474 // an IllegalArgumentException if the type does not match ...
475 // but it resets the std::vector<OUString> to a length of 0 if the Any is empty!
476 std::vector<OUString> lSortedFilters(
477 comphelper::sequenceToContainer< std::vector<OUString> >(xModule->getByName(PROPNAME_SORTEDFILTERLIST).get<css::uno::Sequence<OUString> >()));
478 return lSortedFilters;
479 }
480 }
481 catch(const css::uno::RuntimeException&)
482 { throw; }
483 catch(const css::uno::Exception&)
484 {}
485
486 return std::vector<OUString>();
487}
488
489} // namespace filter
490
491extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
493 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
494{
495 return cppu::acquire(new filter::config::FilterFactory(context));
496}
497
498/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XComponentContext > m_xContext
Definition: OdfFlatXml.cxx:62
constexpr OUStringLiteral sDocumentService
TValueType getUnpackedValueOrDefault(const OUString &sKey, const TValueType &aDefault) const
void init(const OUString &sImplementationName, const css::uno::Sequence< OUString > &lServiceNames, FilterCache::EItemType eType)
initialize this generic instance with some specialized values from our derived object.
represent an item of a FilterCache instance.
Definition: cacheitem.hxx:40
implements a cache, which contains all elements of our filter and type detection configuration.
Definition: filtercache.hxx:61
std::vector< OUString > getMatchingItemsByProps(EItemType eType, o3tl::span< const css::beans::NamedValue > lIProps, o3tl::span< const css::beans::NamedValue > lEProps={}) const
return a list of key names for items, which match the specified criteria.
std::vector< OUString > getItemNames(EItemType eType) const
return a list of all key names, which represent an item inside the specified sub container.
CacheItem getItem(EItemType eType, const OUString &sItem)
return an item, which match the specified type and name.
implements the service <type scope="com.sun.star.document">FilterFactory</type>.
virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByQuery(const OUString &sQuery) override
std::vector< OUString > impl_getSortedFilterList(const QueryTokenizer &lTokens) const
implement the container string query: "getSortedFilterList()[:module=<xxx>]:[iflags=<xxx>][:eflags=<x...
std::vector< OUString > impl_queryMatchByDocumentService(const QueryTokenizer &lTokens) const
implement the container string query: "matchByDocumentService=:iflags=:eflags=:......
virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance(const OUString &sFilter) override
virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments(const OUString &sFilter, const css::uno::Sequence< css::uno::Any > &lArguments) override
virtual ~FilterFactory() override
standard dtor.
static css::uno::Sequence< OUString > impl_getListOfInstalledModules()
TODO document me.
css::uno::Reference< css::uno::XComponentContext > m_xContext
std::vector< OUString > impl_getSortedFilterListForModule(const OUString &sModule, sal_Int32 nIFlags, sal_Int32 nEFlags) const
TODO document me.
static std::vector< OUString > impl_readSortedFilterListFromConfig(const OUString &sModule)
read a specialized and sorted list of filter names from the configuration (matching the specified mod...
FilterFactory(const css::uno::Reference< css::uno::XComponentContext > &rxContext)
standard ctor to connect this interface wrapper to the global filter cache instance ....
virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames() override
It can be used to split any query string (which can be used at the related interface <type scope="css...
bool valid() const
can be used to check if analyzing of given query was successful or not.
constexpr OUStringLiteral PROPNAME_SORTEDFILTERLIST
used to identify the list of sorted filters for a specific office module
Definition: constant.hxx:67
constexpr OUStringLiteral PROPNAME_FLAGS
Definition: constant.hxx:51
constexpr OUStringLiteral QUERY_IDENTIFIER_GET_SORTED_FILTERLIST
Definition: constant.hxx:140
constexpr OUStringLiteral PROPNAME_FILTERSERVICE
Definition: constant.hxx:49
constexpr OUStringLiteral QUERY_PARAM_IFLAGS
Definition: constant.hxx:142
constexpr OUStringLiteral QUERY_PARAM_EFLAGS
Definition: constant.hxx:143
constexpr OUStringLiteral PROPNAME_DOCUMENTSERVICE
Definition: constant.hxx:48
constexpr OUStringLiteral QUERY_IDENTIFIER_MATCHBYDOCUMENTSERVICE
used for the queries of the FilterFactory service.
Definition: constant.hxx:138
constexpr OUStringLiteral QUERY_IDENTIFIER_GETPREFERREDFILTERFORTYPE
Definition: constant.hxx:139
constexpr OUStringLiteral QUERY_PARAM_MODULE
Definition: constant.hxx:144
#define QUERY_CONSTVALUE_ALL
Definition: constant.hxx:145
FilterCache * m_pCache
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * filter_FilterFactory_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
sal_Int32 m_nFlags
bool m_bIFlags
std::mutex m_aMutex
DstType sequenceToContainer(const css::uno::Sequence< SrcType > &i_Sequence)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
FilterCache & GetTheFilterCache()
module
size_t pos