/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include "filterfactory.hxx" #include "constant.hxx" #include #include #include #include #include namespace filter::config{ FilterCache& GetTheFilterCache() { static FilterCache CACHE; return CACHE; } /** @short define all possible parts of a filter query. @descr syntax: "[:[=]]" e.g.: "_query_writer:default_first:use_order:sort_prop=uiname" argument description default ----------------------------------------------------------------------------------------------- iflags= include filters by given mask 0 eflags= exclude filters by given mask 0 sort_prop=<[name,uiname]> sort by internal name or uiname name descending sort descending false use_order use order flag of filters for sorting false default_first set default filter on top of return list false case_sensitive compare "sort_prop" case sensitive false */ FilterFactory::FilterFactory(const css::uno::Reference< css::uno::XComponentContext >& rxContext) : m_xContext(rxContext) { static const css::uno::Sequence sServiceNames { "com.sun.star.document.FilterFactory" }; BaseContainer::init("com.sun.star.comp.filter.config.FilterFactory" , sServiceNames, FilterCache::E_FILTER ); } FilterFactory::~FilterFactory() { } css::uno::Reference< css::uno::XInterface > SAL_CALL FilterFactory::createInstance(const OUString& sFilter) { return createInstanceWithArguments(sFilter, css::uno::Sequence< css::uno::Any >()); } css::uno::Reference< css::uno::XInterface > SAL_CALL FilterFactory::createInstanceWithArguments(const OUString& sFilter , const css::uno::Sequence< css::uno::Any >& lArguments) { // SAFE -> osl::MutexGuard aLock(m_aMutex); auto & cache = GetTheFilterCache(); // search filter on cache CacheItem aFilter = cache.getItem(FilterCache::E_FILTER, sFilter); OUString sFilterService; aFilter[PROPNAME_FILTERSERVICE] >>= sFilterService; // create service instance css::uno::Reference< css::uno::XInterface > xFilter; if (!sFilterService.isEmpty()) xFilter = m_xContext->getServiceManager()->createInstanceWithContext(sFilterService, m_xContext); // initialize filter css::uno::Reference< css::lang::XInitialization > xInit(xFilter, css::uno::UNO_QUERY); if (xInit.is()) { // format: lInitData[0] = seq, which contains all configuration properties of this filter // lInitData[1] = lArguments[0] // ... // lInitData[n] = lArguments[n-1] css::uno::Sequence< css::beans::PropertyValue > lConfig; aFilter >> lConfig; ::std::vector< css::uno::Any > stlArguments(comphelper::sequenceToContainer< ::std::vector< css::uno::Any > >(lArguments)); stlArguments.insert(stlArguments.begin(), css::uno::Any(lConfig)); xInit->initialize(comphelper::containerToSequence(stlArguments)); } return xFilter; // <- SAFE } css::uno::Sequence< OUString > SAL_CALL FilterFactory::getAvailableServiceNames() { /* Attention: Instead of getElementNames() this method have to return only filter names, which can be created as UNO Services really. That's why we search for filters, which don't have a valid value for the property "FilterService". Of course we can't check for corrupted service names here. We can check for empty strings only... */ css::beans::NamedValue lEProps[] { { PROPNAME_FILTERSERVICE, css::uno::Any(OUString()) } }; std::vector lUNOFilters; try { lUNOFilters = GetTheFilterCache().getMatchingItemsByProps(FilterCache::E_FILTER, {}, lEProps); } catch(const css::uno::RuntimeException&) { throw; } catch(const css::uno::Exception&) { lUNOFilters.clear(); } return comphelper::containerToSequence(lUNOFilters); } css::uno::Reference< css::container::XEnumeration > SAL_CALL FilterFactory::createSubSetEnumerationByQuery(const OUString& sQuery) { // reject old deprecated queries ... if (sQuery.startsWith("_filterquery_")) throw css::uno::RuntimeException( "Use of deprecated and now unsupported query!", static_cast< css::container::XContainerQuery* >(this)); // convert "_query_xxx:..." to "getByDocService=xxx:..." OUString sNewQuery(sQuery); sal_Int32 pos = sNewQuery.indexOf("_query_"); if (pos != -1) { OSL_FAIL("DEPRECATED!\nPlease use new query format: 'matchByDocumentService=...'"); OUString sPatchedQuery(OUString::Concat("matchByDocumentService=") + sNewQuery.subView(7)); sNewQuery = sPatchedQuery; } // analyze query and split it into its tokens QueryTokenizer lTokens(sNewQuery); std::vector lEnumSet; // start query // (see attention comment below!) if (lTokens.valid()) { // SAFE -> ---------------------- { osl::MutexGuard aLock(m_aMutex); // May be not all filters was loaded ... // But we need it now! impl_loadOnDemand(); } // <- SAFE ---------------------- if (lTokens.find(QUERY_IDENTIFIER_GETPREFERREDFILTERFORTYPE) != lTokens.end()) OSL_FAIL("DEPRECATED!\nPlease use prop search at the TypeDetection container!"); else if (lTokens.find(QUERY_IDENTIFIER_MATCHBYDOCUMENTSERVICE) != lTokens.end()) lEnumSet = impl_queryMatchByDocumentService(lTokens); else if (lTokens.find(QUERY_IDENTIFIER_GET_SORTED_FILTERLIST) != lTokens.end()) lEnumSet = impl_getSortedFilterList(lTokens); } // pack list of item names as an enum list // Attention: Do not return empty reference for empty list! // The outside check "hasMoreElements()" should be enough, to detect this state :-) return new ::comphelper::OEnumerationByName(this, std::move(lEnumSet)); } std::vector FilterFactory::impl_queryMatchByDocumentService(const QueryTokenizer& lTokens) const { // analyze query QueryTokenizer::const_iterator pIt; OUString sDocumentService; sal_Int32 nIFlags = 0; sal_Int32 nEFlags = 0; pIt = lTokens.find(QUERY_IDENTIFIER_MATCHBYDOCUMENTSERVICE); if (pIt != lTokens.end()) sDocumentService = pIt->second; #define COMP_HACK #ifdef COMP_HACK if ( sDocumentService == "writer" ) { OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!"); sDocumentService = "com.sun.star.text.TextDocument"; } else if ( sDocumentService == "web" ) { OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!"); sDocumentService = "com.sun.star.text.WebDocument"; } else if ( sDocumentService == "global" ) { OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!"); sDocumentService = "com.sun.star.text.GlobalDocument"; } else if ( sDocumentService == "calc" ) { OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!"); sDocumentService = "com.sun.star.sheet.SpreadsheetDocument"; } else if ( sDocumentService == "draw" ) { OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!"); sDocumentService = "com.sun.star.drawing.DrawingDocument"; } else if ( sDocumentService == "impress" ) { OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!"); sDocumentService = "com.sun.star.presentation.PresentationDocument"; } else if ( sDocumentService == "math" ) { OSL_FAIL("DEPRECATED!\nPlease use right document service for filter query!"); sDocumentService = "com.sun.star.formula.FormulaProperties"; } #endif pIt = lTokens.find(QUERY_PARAM_IFLAGS); if (pIt != lTokens.end()) nIFlags = pIt->second.toInt32(); pIt = lTokens.find(QUERY_PARAM_EFLAGS); if (pIt != lTokens.end()) nEFlags = pIt->second.toInt32(); // SAFE -> ---------------------- osl::ClearableMutexGuard aLock(m_aMutex); // search suitable filters FilterCache* pCache = impl_getWorkingCache(); std::vector lFilterNames = pCache->getItemNames(FilterCache::E_FILTER); std::vector lResult ; for (auto const& filterName : lFilterNames) { try { const CacheItem aFilter = pCache->getItem(FilterCache::E_FILTER, filterName); // "matchByDocumentService=" => any filter will be addressed here // "matchByDocumentService=all" => any filter will be addressed here // "matchByDocumentService=com.sun.star..." => only filter matching this document service will be addressed OUString sCheckValue = aFilter.getUnpackedValueOrDefault(PROPNAME_DOCUMENTSERVICE, OUString()); if ( (!sDocumentService.isEmpty() ) && (sDocumentService != QUERY_CONSTVALUE_ALL ) && (sCheckValue != sDocumentService ) ) { continue; // ignore filter -> try next one! } // "iflags=" => not allowed // "iflags=-1" => not allowed // "iflags=0" => not useful // "iflags=283648" => only filter, which has set these flag field will be addressed sal_Int32 nCheckValue = aFilter.getUnpackedValueOrDefault(PROPNAME_FLAGS, sal_Int32(0)); if ( (nIFlags > 0 ) && ((nCheckValue & nIFlags) != nIFlags) ) { continue; // ignore filter -> try next one! } // "eflags=" => not allowed // "eflags=-1" => not allowed // "eflags=0" => not useful // "eflags=283648" => only filter, which has not set these flag field will be addressed if ( (nEFlags > 0 ) && ((nCheckValue & nEFlags) == nEFlags) ) { continue; // ignore filter -> try next one! } // OK - this filter passed all checks. // It match the query ... lResult.push_back(filterName); } catch(const css::uno::RuntimeException&) { throw; } catch(const css::uno::Exception&) { continue; } } aLock.clear(); // <- SAFE ---------------------- return lResult; } namespace { class stlcomp_removeIfMatchFlags { private: FilterCache* m_pCache ; sal_Int32 m_nFlags ; bool m_bIFlags; public: stlcomp_removeIfMatchFlags(FilterCache* pCache , sal_Int32 nFlags , bool bIFlags) : m_pCache (pCache ) , m_nFlags (nFlags ) , m_bIFlags(bIFlags) {} bool operator() (const OUString& sFilter) const { try { const CacheItem aFilter = m_pCache->getItem(FilterCache::E_FILTER, sFilter); sal_Int32 nFlags = aFilter.getUnpackedValueOrDefault(PROPNAME_FLAGS, (sal_Int32(0))); bool bMatch = false; if (m_bIFlags) // IFlags are interpreted as ALL_FLAGS_MUST_MATCH ! bMatch = ((nFlags & m_nFlags) == m_nFlags); else // EFlags are interpreted as AT_LEAST_ONE_FLAG_MUST_MATCH ! bMatch = !(nFlags & m_nFlags); // We are asked for bRemove ! And bMatch = !bRemove => so bRemove = !bMatch .-) return !bMatch; } catch(const css::container::NoSuchElementException &) { return true; } } }; } std::vector FilterFactory::impl_getSortedFilterList(const QueryTokenizer& lTokens) const { // analyze the given query parameter QueryTokenizer::const_iterator pIt1; OUString sModule; sal_Int32 nIFlags = -1; sal_Int32 nEFlags = -1; pIt1 = lTokens.find(QUERY_PARAM_MODULE); if (pIt1 != lTokens.end()) sModule = pIt1->second; pIt1 = lTokens.find(QUERY_PARAM_IFLAGS); if (pIt1 != lTokens.end()) nIFlags = pIt1->second.toInt32(); pIt1 = lTokens.find(QUERY_PARAM_EFLAGS); if (pIt1 != lTokens.end()) nEFlags = pIt1->second.toInt32(); // simple search for filters of one specific module. std::vector lFilterList; if (!sModule.isEmpty()) lFilterList = impl_getSortedFilterListForModule(sModule, nIFlags, nEFlags); else { // more complex search for all filters // We check first, which office modules are installed... const css::uno::Sequence lModules = impl_getListOfInstalledModules(); for (auto const& module : lModules) { std::vector lFilters4Module = impl_getSortedFilterListForModule(module, nIFlags, nEFlags); for (auto const& filter4Module : lFilters4Module) { lFilterList.push_back(filter4Module); } } } return lFilterList; } css::uno::Sequence FilterFactory::impl_getListOfInstalledModules() { css::uno::Reference< css::container::XNameAccess > xModuleConfig = officecfg::Setup::Office::Factories::get(); return xModuleConfig->getElementNames(); } std::vector FilterFactory::impl_getSortedFilterListForModule(const OUString& sModule, sal_Int32 nIFlags, sal_Int32 nEFlags) const { std::vector lSortedFilters = impl_readSortedFilterListFromConfig(sModule); // get all filters for the requested module css::beans::NamedValue lIProps[] { { PROPNAME_DOCUMENTSERVICE, css::uno::Any(sModule) } }; // SAFE -> ---------------------- osl::ClearableMutexGuard aLock(m_aMutex); FilterCache* pCache = impl_getWorkingCache(); std::vector lOtherFilters = pCache->getMatchingItemsByProps(FilterCache::E_FILTER, lIProps); aLock.clear(); // <- SAFE ---------------------- // bring "other" filters in an alphabetical order // It's needed below. ::std::sort(lOtherFilters.begin(), lOtherFilters.end()); // merge both lists together std::vector lMergedFilters = lSortedFilters; const auto itlSortedFiltersEnd = lSortedFilters.cend(); for (auto const& otherFilter : lOtherFilters) { if (::std::find(lSortedFilters.cbegin(), lSortedFilters.cend(), otherFilter) == itlSortedFiltersEnd) lMergedFilters.push_back(otherFilter); } // remove all filters from this merged list, which does not fit the flag specification if (nIFlags != -1) { auto pItToErase = ::std::remove_if(lMergedFilters.begin(), lMergedFilters.end(), stlcomp_removeIfMatchFlags(pCache, nIFlags, true)); lMergedFilters.erase(pItToErase, lMergedFilters.end()); } if (nEFlags != -1) { auto pItToErase = ::std::remove_if(lMergedFilters.begin(), lMergedFilters.end(), stlcomp_removeIfMatchFlags(pCache, nEFlags, false)); lMergedFilters.erase(pItToErase, lMergedFilters.end()); } // sort the default filter to the front of this list // TODO return lMergedFilters; } std::vector FilterFactory::impl_readSortedFilterListFromConfig(const OUString& sModule) { try { css::uno::Reference< css::container::XNameAccess > xUISortConfig = officecfg::TypeDetection::UISort::ModuleDependendFilterOrder::get(); // don't check the module name here. If it does not exists, an exception is thrown and caught below. // We return an empty list as result then. css::uno::Reference< css::container::XNameAccess > xModule; xUISortConfig->getByName(sModule) >>= xModule; if (xModule.is()) // only to be on the safe side of life if the exception was not thrown .-) { // Note: conversion of the returned Any to std::vector throws // an IllegalArgumentException if the type does not match ... // but it resets the std::vector to a length of 0 if the Any is empty! std::vector lSortedFilters( comphelper::sequenceToContainer< std::vector >(xModule->getByName(PROPNAME_SORTEDFILTERLIST).get >())); return lSortedFilters; } } catch(const css::uno::RuntimeException&) { throw; } catch(const css::uno::Exception&) {} return std::vector(); } } // namespace filter extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* filter_FilterFactory_get_implementation( css::uno::XComponentContext* context, css::uno::Sequence const&) { return cppu::acquire(new filter::config::FilterFactory(context)); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */