diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /filter/source/config/cache | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'filter/source/config/cache')
22 files changed, 7850 insertions, 0 deletions
diff --git a/filter/source/config/cache/basecontainer.cxx b/filter/source/config/cache/basecontainer.cxx new file mode 100644 index 000000000..0dee9d4ba --- /dev/null +++ b/filter/source/config/cache/basecontainer.cxx @@ -0,0 +1,476 @@ +/* -*- 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 "basecontainer.hxx" + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/uno/Type.h> +#include <comphelper/enumhelper.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> + +#define LOAD_IMPLICIT + +namespace filter::config{ + +BaseContainer::BaseContainer() + : m_eType() + , m_lListener (m_aMutex) +{ + GetTheFilterCache().load(FilterCache::E_CONTAINS_STANDARD); +} + + +BaseContainer::~BaseContainer() +{ +} + + +void BaseContainer::init(const OUString& sImplementationName, + const css::uno::Sequence< OUString >& lServiceNames , + FilterCache::EItemType eType ) +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + m_sImplementationName = sImplementationName; + m_lServiceNames = lServiceNames ; + m_eType = eType ; + // <- SAFE +} + + +void BaseContainer::impl_loadOnDemand() +{ +#ifdef LOAD_IMPLICIT + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + // A generic container needs all items of a set of our cache! + // Of course it can block for a while, till the cache is really filled. + // Note: don't load all sets supported by the cache here! + + FilterCache::EFillState eRequiredState = FilterCache::E_CONTAINS_NOTHING; + switch(m_eType) + { + case FilterCache::E_TYPE : + eRequiredState = FilterCache::E_CONTAINS_TYPES; + break; + + case FilterCache::E_FILTER : + eRequiredState = FilterCache::E_CONTAINS_FILTERS; + break; + + case FilterCache::E_FRAMELOADER : + eRequiredState = FilterCache::E_CONTAINS_FRAMELOADERS; + break; + + case FilterCache::E_CONTENTHANDLER : + eRequiredState = FilterCache::E_CONTAINS_CONTENTHANDLERS; + break; + } + + GetTheFilterCache().load(eRequiredState); + // <- SAFE +#endif +} + + +void BaseContainer::impl_initFlushMode() +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + if (!m_pFlushCache) + m_pFlushCache = GetTheFilterCache().clone(); + if (!m_pFlushCache) + throw css::uno::RuntimeException( "Can not create write copy of internal used cache on demand.", + static_cast< OWeakObject* >(this)); + // <- SAFE +} + + +FilterCache* BaseContainer::impl_getWorkingCache() const +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + if (m_pFlushCache) + return m_pFlushCache.get(); + else + return &GetTheFilterCache(); + // <- SAFE +} + + +OUString SAL_CALL BaseContainer::getImplementationName() +{ + return m_sImplementationName; +} + + +sal_Bool SAL_CALL BaseContainer::supportsService(const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL BaseContainer::getSupportedServiceNames() +{ + return m_lServiceNames; +} + + +void SAL_CALL BaseContainer::insertByName(const OUString& sItem , + const css::uno::Any& aValue) +{ + if (sItem.isEmpty()) + throw css::lang::IllegalArgumentException("empty value not allowed as item name.", + static_cast< css::container::XNameContainer* >(this), + 1); + + CacheItem aItem; + try + { + aItem << aValue; + } + catch(const css::uno::Exception& ex) + { + throw css::lang::IllegalArgumentException(ex.Message, static_cast< css::container::XNameContainer* >(this), 2); + } + + impl_loadOnDemand(); + + // SAFE -> ---------------------------------- + osl::MutexGuard aLock(m_aMutex); + + // create write copy of used cache on demand ... + impl_initFlushMode(); + + FilterCache* pCache = impl_getWorkingCache(); + if (pCache->hasItem(m_eType, sItem)) + throw css::container::ElementExistException(OUString(), static_cast< css::container::XNameContainer* >(this)); + pCache->setItem(m_eType, sItem, aItem); + // <- SAFE ---------------------------------- +} + + +void SAL_CALL BaseContainer::removeByName(const OUString& sItem) +{ + impl_loadOnDemand(); + + // SAFE -> ---------------------------------- + osl::MutexGuard aLock(m_aMutex); + + // create write copy of used cache on demand ... + impl_initFlushMode(); + + FilterCache* pCache = impl_getWorkingCache(); + pCache->removeItem(m_eType, sItem); // throw exceptions automatically + // <- SAFE ---------------------------------- +} + + +void SAL_CALL BaseContainer::replaceByName(const OUString& sItem , + const css::uno::Any& aValue) +{ + if (sItem.isEmpty()) + throw css::lang::IllegalArgumentException("empty value not allowed as item name.", + static_cast< css::container::XNameContainer* >(this), + 1); + + CacheItem aItem; + try + { + aItem << aValue; + } + catch(const css::uno::Exception& ex) + { + throw css::lang::IllegalArgumentException(ex.Message, static_cast< css::container::XNameContainer* >(this), 2); + } + + impl_loadOnDemand(); + + // SAFE -> ---------------------------------- + osl::MutexGuard aLock(m_aMutex); + + // create write copy of used cache on demand ... + impl_initFlushMode(); + + FilterCache* pCache = impl_getWorkingCache(); + if (!pCache->hasItem(m_eType, sItem)) + throw css::container::NoSuchElementException(OUString(), static_cast< css::container::XNameContainer* >(this)); + pCache->setItem(m_eType, sItem, aItem); + // <- SAFE ---------------------------------- +} + + +css::uno::Any SAL_CALL BaseContainer::getByName(const OUString& sItem) +{ + if (sItem.isEmpty()) + throw css::container::NoSuchElementException( "An empty item can't be part of this cache!", + static_cast< css::container::XNameAccess* >(this)); + + css::uno::Any aValue; + + impl_loadOnDemand(); + + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + try + { + FilterCache* pCache = impl_getWorkingCache(); + aValue = pCache->getItemWithStateProps(m_eType, sItem); + } + catch(const css::container::NoSuchElementException&) + { + throw; + } + catch(const css::uno::Exception&) + { + // TODO invalid cache!? How should it be handled right? + } + + // <- SAFE + + return aValue; +} + + +css::uno::Sequence< OUString > SAL_CALL BaseContainer::getElementNames() +{ + css::uno::Sequence< OUString > lNames; + + impl_loadOnDemand(); + + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + try + { + FilterCache* pCache = impl_getWorkingCache(); + std::vector<OUString> lKeys = pCache->getItemNames(m_eType); + lNames = comphelper::containerToSequence(lKeys); + } + catch(const css::uno::Exception&) + { + // invalid cache!? + lNames.realloc(0); + } + + // <- SAFE + + return lNames; +} + + +sal_Bool SAL_CALL BaseContainer::hasByName(const OUString& sItem) +{ + bool bHasOne = false; + + impl_loadOnDemand(); + + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + try + { + FilterCache* pCache = impl_getWorkingCache(); + bHasOne = pCache->hasItem(m_eType, sItem); + } + catch(const css::uno::Exception&) + { + // invalid cache!? + bHasOne = false; + } + + // <- SAFE + + return bHasOne; +} + + +css::uno::Type SAL_CALL BaseContainer::getElementType() +{ + // no lock necessary - because the type of our items + // is fix! no internal call or member needed ... + return cppu::UnoType<css::uno::Sequence< css::beans::PropertyValue >>::get(); +} + + +sal_Bool SAL_CALL BaseContainer::hasElements() +{ + bool bHasSome = false; + + impl_loadOnDemand(); + + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + try + { + FilterCache* pCache = impl_getWorkingCache(); + bHasSome = pCache->hasItems(m_eType); + } + catch(const css::uno::Exception&) + { + // invalid cache?! + bHasSome = false; + } + + // <- SAFE + + return bHasSome; +} + + +css::uno::Reference< css::container::XEnumeration > SAL_CALL BaseContainer::createSubSetEnumerationByQuery(const OUString& /* sQuery */ ) +{ + OSL_FAIL("not pure virtual ... but not really implemented .-)"); + + return new ::comphelper::OEnumerationByName(this, {}); +} + + +css::uno::Reference< css::container::XEnumeration > SAL_CALL BaseContainer::createSubSetEnumerationByProperties(const css::uno::Sequence< css::beans::NamedValue >& lProperties) +{ + std::vector<OUString> lKeys; + + impl_loadOnDemand(); + + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + try + { + // search the key names of all items, where its properties match + // the given ones in its minimum + FilterCache* pCache = impl_getWorkingCache(); + lKeys = pCache->getMatchingItemsByProps(m_eType, o3tl::span<const css::beans::NamedValue>( lProperties.getConstArray(), lProperties.getLength() )); + } + catch(const css::uno::Exception&) + { + // invalid cache, internal failure, wrong conversion ...!? + // doesn't matter + lKeys.clear(); + } + + // <- SAFE + + // create a specialized enumeration helper, which + // provides the collected information outside. + // It hold a reference to us ... and call our container interface directly. + // be aware of some direct callbacks if it will be created :-) + + /* Note: It's not allowed to return NULL. Because an empty enumeration + transport the same information but make no trouble outside. + Further its easier to work directly with the return value + instead of checking of NULL returns! */ + + return new ::comphelper::OEnumerationByName(this, std::move(lKeys)); +} + + +void SAL_CALL BaseContainer::flush() +{ + // SAFE -> + osl::ClearableMutexGuard aLock(m_aMutex); + + if (!m_pFlushCache) + throw css::lang::WrappedTargetRuntimeException( + "Can not guarantee cache consistency. Special flush container does not exists!", + static_cast< OWeakObject* >(this), + css::uno::Any()); + + try + { + m_pFlushCache->flush(); + // Take over all changes into the global cache and + // forget the clone. + /* TODO + -think about me + If the global cache gets this information via listener, + we should remove this method! + */ + GetTheFilterCache().takeOver(*m_pFlushCache); + } + catch(const css::uno::Exception& ex) + { + // Don't remove the clone. May be the outside + // user wish to repair it now and calls flush() + // later again ... + + throw css::lang::WrappedTargetRuntimeException( "Flush rejected by internal container.", + static_cast< OWeakObject* >(this), + css::uno::Any(ex)); + } + + m_pFlushCache.reset(); + + aLock.clear(); + // <- SAFE + + // notify listener outside the lock! + // The used listener helper lives if we live + // and is threadsafe by itself. + // Further it's not a good idea to hold the own lock + // if an outside object is called :-) + css::lang::EventObject aSource (static_cast< css::util::XFlushable* >(this)); + comphelper::OInterfaceContainerHelper2* pContainer = m_lListener.getContainer(cppu::UnoType<css::util::XFlushListener>::get()); + if (!pContainer) + return; + + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + // ... this pointer can be interesting to find out, where will be called as listener + // Don't optimize it to a direct iterator cast :-) + css::util::XFlushListener* pListener = static_cast<css::util::XFlushListener*>(pIterator.next()); + pListener->flushed(aSource); + } + catch(const css::uno::Exception&) + { + // ignore any "damaged" flush listener! + // May its remote reference is broken ... + pIterator.remove(); + } + } +} + + +void SAL_CALL BaseContainer::addFlushListener(const css::uno::Reference< css::util::XFlushListener >& xListener) +{ + // no locks necessary + // used helper lives if we live and is threadsafe by itself ... + m_lListener.addInterface(cppu::UnoType<css::util::XFlushListener>::get(), xListener); +} + + +void SAL_CALL BaseContainer::removeFlushListener(const css::uno::Reference< css::util::XFlushListener >& xListener) +{ + // no locks necessary + // used helper lives if we live and is threadsafe by itself ... + m_lListener.removeInterface(cppu::UnoType<css::util::XFlushListener>::get(), xListener); +} + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/basecontainer.hxx b/filter/source/config/cache/basecontainer.hxx new file mode 100644 index 000000000..6691c1b49 --- /dev/null +++ b/filter/source/config/cache/basecontainer.hxx @@ -0,0 +1,256 @@ +/* -*- 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 . + */ +#pragma once + +#include <memory> + +#include "filtercache.hxx" +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/util/XRefreshable.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XContainerQuery.hpp> +#include <com/sun/star/util/XFlushable.hpp> +#include <comphelper/multicontainer2.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weakref.hxx> +#include <rtl/ustring.hxx> + + +namespace filter::config { + + +/** @short implements the interface css::container::XNameContainer + on top of a FilterCache reference. + + @descr This class can be used as base for own service implementations, + which must provide read/write access to the filter configuration. + Parameters regulate read/write access, which sub set of information + should be available etc. + + @attention The base class BaseLock must be the first of declared ones. + Otherwise we can't be sure, that our own mutex member (which is + present by this base class!) was full initialized inside our own + ctor as first! + */ +class BaseContainer : public cppu::BaseMutex + , public ::cppu::WeakImplHelper< css::lang::XServiceInfo , + css::container::XNameContainer , // => XNameReplace => XNameAccess => XElementAccess + css::container::XContainerQuery , + css::util::XFlushable > +{ + + // member + + protected: + + /** @short the implementation name of our derived class, which we provide + at the interface XServiceInfo of our class... */ + OUString m_sImplementationName; + + /** @short the list of supported uno service names of our derived class, which we provide + at the interface XServiceInfo of our class... */ + css::uno::Sequence< OUString > m_lServiceNames; + + /** @short local filter cache, which is used to collect changes on the + filter configuration first and flush it later. + + @descr Normally this member isn't used nor initialized. That's true, + if this container is used for reading only. The first write access + (e.g. by calling insertByName()) creates a copy of the current + global cache m_rCache to initialize the m_pFlushCache member. + + Afterwards only the flush cache copy is used. Inside flush() this + copy will be removed and m_rCache can be used again. + + m_pFlushCache and m_rCache must not be synchronized manually here. + m_rCache listen on the global configuration, where m_pFlushCache + write its data. m_rCache update itself automatically. + */ + std::unique_ptr<FilterCache> m_pFlushCache; + + /** @short specify, which sub container of the used filter cache + must be wrapped by this container interface. */ + FilterCache::EItemType m_eType; + + /** @short holds all listener, which are registered at this instance. */ + comphelper::OMultiTypeInterfaceContainerHelper2 m_lListener; + + + // native interface + + public: + + + // ctor/dtor + + /** @short standard ctor. + + @descr Because mostly this class is used as base class for own service + implementations in combination with an ImplInheritanceHelper template... + there is no way to provide some initializing data through the ctor :-( + This base class will be created inside its default ctor and must be + initialized with its needed parameters explicitly by calling: "init()". + + @see init() + */ + BaseContainer(); + + + /** @short standard dtor. + */ + virtual ~BaseContainer() override; + + + /** @short initialize this generic instance with some specialized values + from our derived object. + + @descr Because an outside class must use ImplInheritanceHelper template to + use us a base class... and there is no way to pass such initializing + parameters through a required default ctor... we must be initialized + by this special method. Of course this method must be called first before + any other interface method is used. + + @param rxContext + reference to the uno service manager, which created this service instance. + + @param sImplementationName + the implementation name of our derived class, which we provide + at the interface XServiceInfo of our class... + + @param lServiceNames + the list of supported uno service names of our derived class, which we provide + at the interface XServiceInfo of our class... + + @param eType + specify, which sub container of the used filter cache + must be wrapped by this container interface. + */ + void init( const OUString& sImplementationName, + const css::uno::Sequence< OUString >& lServiceNames , + FilterCache::EItemType eType ); + + + // helper + + protected: + + + /** @short check if the underlying configuration data was already loaded + and do it if necessary automatically. + */ + void impl_loadOnDemand(); + + + /** @short it creates the global instance m_pFilterCache, which is a copy + of the global instance m_rCache, and will be used to change the + configuration. + + @descr If no exception occurs, its guaranteed, that the member m_rFlushCache + was initialized right and can be used further. + + @throws css::uno::RuntimeException + */ + void impl_initFlushMode(); + + + /** @short returns a pointer to the current used cache member. + + @descr It's a point to the FilterCache instance behind m_pFlushCache + or m_rCache. + + @note The lifetime of this pointer is restricted to the time, where + the mutex of this BaseContainer instance is locked. + Otherwise may be the interface method flush() will destroy + m_pFlushCache and the here returned pointer will be invalid! + + Use: + + Guard aLock(m_aLock); + FilterCache* p = impl_getWorkingCache(); + p->doSomething(); + aLock.clear(); + // after this point p can't b e guaranteed any longer! + */ + FilterCache* impl_getWorkingCache() const; + + + // uno interface + + public: + + + // XServiceInfo + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + + // XNameContainer + + virtual void SAL_CALL insertByName(const OUString& sItem , + const css::uno::Any& aValue) override; + + virtual void SAL_CALL removeByName(const OUString& sItem) override; + + + // XNameReplace + + virtual void SAL_CALL replaceByName(const OUString& sItem , + const css::uno::Any& aValue) override; + + + // XElementAccess + + virtual css::uno::Any SAL_CALL getByName(const OUString& sItem) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + + virtual sal_Bool SAL_CALL hasByName(const OUString& sItem) override; + + virtual css::uno::Type SAL_CALL getElementType() override; + + virtual sal_Bool SAL_CALL hasElements() override; + + + // XContainerQuery + + // must be implemented really by derived class... + // We implement return of an empty result here only! + // But we show an assertion :-) + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByQuery(const OUString& sQuery) override; + + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByProperties(const css::uno::Sequence< css::beans::NamedValue >& lProperties) override; + + + // XFlushable + + virtual void SAL_CALL flush() override; + + virtual void SAL_CALL addFlushListener(const css::uno::Reference< css::util::XFlushListener >& xListener) override; + + virtual void SAL_CALL removeFlushListener(const css::uno::Reference< css::util::XFlushListener >& xListener) override; +}; + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/cacheitem.cxx b/filter/source/config/cache/cacheitem.cxx new file mode 100644 index 000000000..595d3891a --- /dev/null +++ b/filter/source/config/cache/cacheitem.cxx @@ -0,0 +1,325 @@ +/* -*- 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 "cacheitem.hxx" +#include "constant.hxx" + +#include <com/sun/star/uno/Sequence.h> + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <osl/diagnose.h> +#include <comphelper/sequence.hxx> + + +namespace filter::config{ + +CacheItem::CacheItem() +{ +} + + +void CacheItem::update(const CacheItem& rUpdateItem) +{ + for (auto const& elem : rUpdateItem) + (*this)[elem.first] = elem.second; +} + + +void CacheItem::validateUINames(const OUString& sActLocale) +{ + if (sActLocale.isEmpty()) + return; + + // 1) check UINames first + const_iterator pUINames = find(PROPNAME_UINAMES); + const_iterator pUIName = find(PROPNAME_UINAME ); + + ::comphelper::SequenceAsHashMap lUINames; + if (pUINames != end()) + lUINames << pUINames->second; + + OUString sUIName; + if (pUIName != end()) + pUIName->second >>= sUIName; + + if (!sUIName.isEmpty()) + { + // 1a) set UIName inside list of UINames for current locale + lUINames[sActLocale] <<= sUIName; + } + else if (!lUINames.empty()) + { + // 1b) or get it from this list, if it not exist! + lUINames[sActLocale] >>= sUIName; + } + + (*this)[PROPNAME_UINAMES] <<= lUINames.getAsConstPropertyValueList(); + (*this)[PROPNAME_UINAME ] <<= sUIName; +} + + +css::uno::Sequence< css::beans::PropertyValue > CacheItem::getAsPackedPropertyValueList(bool bFinalized, bool bMandatory) const +{ + sal_Int32 c = static_cast<sal_Int32>(size()); + sal_Int32 i = 0; + + css::uno::Sequence< css::beans::PropertyValue > lList(c+2); + css::beans::PropertyValue* pList = lList.getArray(); + + for (const_iterator pProp = begin(); + pProp != end() ; + ++pProp ) + { + const OUString& rName = pProp->first.maString; + const css::uno::Any& rValue = pProp->second; + + if (!rValue.hasValue()) + continue; + assert (rName != PROPNAME_FINALIZED && rName != PROPNAME_MANDATORY); + + pList[i].Name = rName ; + pList[i].Value = rValue; + ++i; + } + pList[i].Name = PROPNAME_FINALIZED ; + pList[i].Value <<= bFinalized; + ++i; + pList[i].Name = PROPNAME_MANDATORY ; + pList[i].Value <<= bMandatory; + ++i; + lList.realloc(i); + + return lList; +} + + +static bool isSubSet(const css::uno::Any& aSubSet, + const css::uno::Any& aSet ) +{ + const css::uno::Type& aT1 = aSubSet.getValueType(); + const css::uno::Type& aT2 = aSet.getValueType(); + + if (!aT1.equals(aT2)) + { + return false; + } + + if (aSubSet.hasValue() && aSet.hasValue()) + { + css::uno::TypeClass aTypeClass = aT1.getTypeClass(); + switch(aTypeClass) + { + + case css::uno::TypeClass_BOOLEAN : + case css::uno::TypeClass_BYTE : + case css::uno::TypeClass_SHORT : + case css::uno::TypeClass_UNSIGNED_SHORT : + case css::uno::TypeClass_LONG : + case css::uno::TypeClass_UNSIGNED_LONG : + case css::uno::TypeClass_HYPER : + case css::uno::TypeClass_UNSIGNED_HYPER : + case css::uno::TypeClass_FLOAT : + case css::uno::TypeClass_DOUBLE : + { + bool bIs = (aSubSet == aSet); + return bIs; + } + + + case css::uno::TypeClass_STRING : + return aSubSet == aSet; + break; + + + case css::uno::TypeClass_STRUCT : + { + css::beans::PropertyValue p1; + css::beans::PropertyValue p2; + + if ( + (aSubSet >>= p1) && + (aSet >>= p2) + ) + { + bool bIs = (p1.Name == p2.Name) && isSubSet(p1.Value, p2.Value); + return bIs; + } + + css::beans::NamedValue n1; + css::beans::NamedValue n2; + + if ( + (aSubSet >>= n1) && + (aSet >>= n2) + ) + { + bool bIs = (n1.Name == n2.Name) && isSubSet(n1.Value, n2.Value); + return bIs; + } + } + break; + + + case css::uno::TypeClass_SEQUENCE : + { + css::uno::Sequence< OUString > uno_s1; + css::uno::Sequence< OUString > uno_s2; + + if ( + (aSubSet >>= uno_s1) && + (aSet >>= uno_s2) + ) + { + auto s2Begin = uno_s2.getConstArray(); + auto s2End = uno_s2.getConstArray() + uno_s2.getLength(); + + for (auto const& elem : uno_s1) + { + if (::std::find(s2Begin, s2End, elem) == s2End) + { + return false; + } + } + return true; + } + + css::uno::Sequence< css::beans::PropertyValue > uno_p1; + css::uno::Sequence< css::beans::PropertyValue > uno_p2; + + if ( + (aSubSet >>= uno_p1) && + (aSet >>= uno_p2) + ) + { + ::comphelper::SequenceAsHashMap stl_p1(uno_p1); + ::comphelper::SequenceAsHashMap stl_p2(uno_p2); + + for (auto const& elem : stl_p1) + { + ::comphelper::SequenceAsHashMap::const_iterator it2 = stl_p2.find(elem.first); + if (it2 == stl_p2.end()) + { + return false; + } + if (!isSubSet(elem.second, it2->second)) + { + return false; + } + } + return true; + } + + css::uno::Sequence< css::beans::NamedValue > uno_n1; + css::uno::Sequence< css::beans::NamedValue > uno_n2; + + if ( + (aSubSet >>= uno_n1) && + (aSet >>= uno_n2) + ) + { + ::comphelper::SequenceAsHashMap stl_n1(uno_n1); + ::comphelper::SequenceAsHashMap stl_n2(uno_n2); + + for (auto const& elem : stl_n1) + { + ::comphelper::SequenceAsHashMap::const_iterator it2 = stl_n2.find(elem.first); + if (it2 == stl_n2.end()) + { + return false; + } + if (!isSubSet(elem.second, it2->second)) + { + return false; + } + } + return true; + } + } + break; + default: break; + } + } + OSL_FAIL("isSubSet() ... this point should not be reached!"); + return false; +} + + +bool CacheItem::haveProps(o3tl::span< const css::beans::NamedValue > lProps) const +{ + for (auto const& prop : lProps) + { + // i) one required property does not exist at this item => return false + const_iterator pItThis = find(prop.Name); + if (pItThis == end()) + { + return false; + } + + // ii) one item does not have the right value => return false + if (!isSubSet(prop.Value, pItThis->second)) + { + return false; + } + } + + // this method was not broken before => + // the given property set seems to match with our + // own properties in its minimum => return TRUE + return true; +} + + +bool CacheItem::dontHaveProps(o3tl::span< const css::beans::NamedValue > lProps) const +{ + for (auto const& prop : lProps) + { + // i) one item does not exist in general + // => continue with next one, because + // "excluding" means... "don't have it". + // And "not exists" matches to "don't have it". + const_iterator pItThis = find(prop.Name); + if (pItThis == end()) + { + continue; + } + + // ii) one item have the right value => return false + // because this item has the requested property... + // But we checked for "don't have it" here. + if (isSubSet(prop.Value, pItThis->second)) + { + return false; + } + } + + // this method was not broken before => + // That means: this item has no matching property + // of the given set. It "don't have" it ... => return true. + return true; +} + +FlatDetectionInfo::FlatDetectionInfo() : + bMatchByExtension(false), bMatchByPattern(false), bPreselectedByDocumentService(false) {} + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/cacheitem.hxx b/filter/source/config/cache/cacheitem.hxx new file mode 100644 index 000000000..b20bf72c8 --- /dev/null +++ b/filter/source/config/cache/cacheitem.hxx @@ -0,0 +1,177 @@ +/* -*- 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 . + */ + +#pragma once + +#include <unordered_map> +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <vector> +#include <comphelper/sequenceashashmap.hxx> +#include <o3tl/span.hxx> + +namespace filter::config { + + +/** @short represent an item of a FilterCache + instance. + + @descr This class is not threadsafe tp perform + operations, which use many instances of + this class! Synchronizations must be done outside. + */ +class CacheItem : public ::comphelper::SequenceAsHashMap +{ + + public: + + + /** @short creates an empty item. + */ + CacheItem(); + + + /** @short update only properties, which are given by the + specified rItem. + + @descr Update means: - add new properties + - change existing values + + @param rUpdateItem + another cache item, which contains some special + properties, which should by used for updating + this one. + */ + void update(const CacheItem& rUpdateItem); + + + /** @short check, if the given properties exist + at this item. + + @descr All properties are compared in its minimum. + E.g: string lists => only the requested items + are checked. Additional existing items are ignored. + + @param lProps + contains all properties, which must exist at this item. + + @return sal_True if all given properties exists + at this item; sal_False otherwise. + */ + bool haveProps(o3tl::span< const css::beans::NamedValue > lProps) const; + + + /** @short check, if the given properties don't exist + at this item. + + @descr All properties are compared in its minimum. + E.g: string lists => only the requested items + are checked. Additional existing items are ignored. + + @param lProps + contains all properties, which should not exist at this item. + + @return sal_False if at least on property exists at this item(!); + sal_True otherwise. + */ + bool dontHaveProps(o3tl::span< const css::beans::NamedValue > lProps) const; + + + /** @short because we know two UIName properties + (a list with all locales and the value + for the current locale only), we must be sure + that the correspond together. + + @param sActLocale + must specify the current office locale. + It's needed to address the UIName property inside + the list of possible ones. + */ + void validateUINames(const OUString& sActLocale); + + + /** @short convert this structure to a seq< PropertyValue > + and ignore all empty properties! + + @descr Normally the converter routines of the base class + SequenceAsHashMap do this job already. + But it doesn't provide a "pack" mechanism to + ignore properties with empty (means "void") values. + + @return css::uno::Sequence< css::beans::PropertyValue > + as a list of all properties of this cacheitem, + where empty properties was removed. + */ + css::uno::Sequence< css::beans::PropertyValue > getAsPackedPropertyValueList(bool bFinalized, bool bMandatory) const; +}; + + +/** @short represent an item list of a FilterCache + instance. + */ +typedef std::unordered_map< OUString, + CacheItem > CacheItemList; + + +/** @short supports registration of multiple key to + another string information. + + @descr E.g. a list of internal type names can be registered + to an extension. Organization as an hash makes it + faster than searching inside vectors. + + On the other side e.g. URLPattern can't be really addressed + by a hash value ... because the use wildcards. But + there we need key-value pairs too, which can't be provided + by a pure vector! + */ +typedef std::unordered_map< OUString, + std::vector<OUString> > CacheItemRegistration; + + +/** @short is used to collect all matching types of a URL + during type detection. + + @descr Every type in this list is combined with an information, + which property matched to the given URL. The user of this + structure can decide then, if a deep detection should be + suppressed e.g. if a URLPattern was used. + */ +struct FlatDetectionInfo +{ + // the internal type name + OUString sType; + + // this type was found by a matching the URL extension + bool bMatchByExtension; + + // this type was found by a matching URL Pattern + bool bMatchByPattern; + + // the user selected this type implicit by selecting a corresponding office module + bool bPreselectedByDocumentService; + + FlatDetectionInfo(); +}; + +typedef ::std::vector< FlatDetectionInfo > FlatDetection; + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/cacheupdatelistener.cxx b/filter/source/config/cache/cacheupdatelistener.cxx new file mode 100644 index 000000000..c613d0549 --- /dev/null +++ b/filter/source/config/cache/cacheupdatelistener.cxx @@ -0,0 +1,182 @@ +/* -*- 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 "cacheupdatelistener.hxx" +#include "configflush.hxx" + +#include <com/sun/star/util/XChangesNotifier.hpp> +#include <com/sun/star/util/XRefreshable.hpp> +#include <unotools/configpaths.hxx> +#include <rtl/ustring.hxx> +#include <comphelper/processfactory.hxx> +#include <utility> + + +namespace filter::config{ + +CacheUpdateListener::CacheUpdateListener(FilterCache &rFilterCache, + css::uno::Reference< css::uno::XInterface > xConfigAccess, + FilterCache::EItemType eConfigType) + : m_rCache(rFilterCache) + , m_xConfig(std::move(xConfigAccess)) + , m_eConfigType(eConfigType) +{ +} + +CacheUpdateListener::~CacheUpdateListener() +{ +} + +void CacheUpdateListener::startListening() +{ + // SAFE -> + std::unique_lock aLock(m_aMutex); + css::uno::Reference< css::util::XChangesNotifier > xNotifier(m_xConfig, css::uno::UNO_QUERY); + aLock.unlock(); + // <- SAFE + + if (!xNotifier.is()) + return; + + css::uno::Reference< css::util::XChangesListener > xThis(this); + xNotifier->addChangesListener(xThis); +} + + +void CacheUpdateListener::stopListening() +{ + // SAFE -> + std::unique_lock aLock(m_aMutex); + css::uno::Reference< css::util::XChangesNotifier > xNotifier(m_xConfig, css::uno::UNO_QUERY); + aLock.unlock(); + // <- SAFE + + if (!xNotifier.is()) + return; + + css::uno::Reference< css::util::XChangesListener > xThis(this); + xNotifier->removeChangesListener(xThis); +} + + +void SAL_CALL CacheUpdateListener::changesOccurred(const css::util::ChangesEvent& aEvent) +{ + // SAFE -> + std::unique_lock aLock(m_aMutex); + + // disposed ? + if ( ! m_xConfig.is()) + return; + + FilterCache::EItemType eType = m_eConfigType; + + aLock.unlock(); + // <- SAFE + + std::vector<OUString> lChangedItems; + sal_Int32 c = aEvent.Changes.getLength(); + sal_Int32 i = 0; + + for (i=0; i<c; ++i) + { + const css::util::ElementChange& aChange = aEvent.Changes[i]; + + OUString sOrgPath ; + OUString sTempPath; + + OUString sProperty; + OUString sNode ; + OUString sLocale ; + + /* at least we must be able to retrieve 2 path elements + But sometimes the original path can contain 3 of them ... in case + a localized value was changed. + => + 1) Filters/Filter["filtername"]/Property + 2) Filters/Filter["filtername"]/LocalizedProperty/Locale + */ + + aChange.Accessor >>= sOrgPath; + if ( ! ::utl::splitLastFromConfigurationPath(sOrgPath, sTempPath, sLocale)) + continue; + sOrgPath = sTempPath; + if ( ! ::utl::splitLastFromConfigurationPath(sOrgPath, sTempPath, sProperty)) + { + sNode = sLocale; + sProperty.clear(); + sLocale.clear(); + } + else + { + sOrgPath = sTempPath; + if ( ! ::utl::splitLastFromConfigurationPath(sOrgPath, sTempPath, sNode)) + { + sNode = sProperty; + sProperty = sLocale; + sLocale.clear(); + } + } + + if ( sNode.isEmpty() ) + continue; + + auto pIt = ::std::find(lChangedItems.cbegin(), lChangedItems.cend(), sNode); + if (pIt == lChangedItems.cend()) + lChangedItems.push_back(sNode); + } + + bool bNotifyRefresh = false; + for (auto const& changedItem : lChangedItems) + { + try + { + m_rCache.refreshItem(eType, changedItem); + } + catch(const css::container::NoSuchElementException&) + { + // can be ignored! Because we must be aware that + // sItem was removed from the configuration and we forced an update of the cache. + // But we know, that the cache is up-to-date know and has thrown this exception afterwards .-) + } + // NO FLUSH! Otherwise we start a never ending story here .-) + bNotifyRefresh = true; + } + + // notify sfx cache about the changed filter cache .-) + if (bNotifyRefresh) + { + rtl::Reference< ConfigFlush > xRefreshBroadcaster = new ConfigFlush(); + xRefreshBroadcaster->refresh(); + } +} + + +void SAL_CALL CacheUpdateListener::disposing(const css::lang::EventObject& aEvent) +{ + // SAFE -> + std::unique_lock aLock(m_aMutex); + if (aEvent.Source == m_xConfig) + m_xConfig.clear(); + // <- SAFE +} + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/cacheupdatelistener.hxx b/filter/source/config/cache/cacheupdatelistener.hxx new file mode 100644 index 000000000..3870b9916 --- /dev/null +++ b/filter/source/config/cache/cacheupdatelistener.hxx @@ -0,0 +1,113 @@ +/* -*- 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 . + */ +#pragma once + +#include "filtercache.hxx" +#include <com/sun/star/util/XChangesListener.hpp> +#include <cppuhelper/implbase.hxx> +#include <mutex> + + +namespace filter::config { + + +/** @short implements a listener, which will update the + global filter cache, if the underlying configuration + wa changed by other processes. + */ +class CacheUpdateListener : public ::cppu::WeakImplHelper< css::util::XChangesListener > +{ + + // member + + private: + + std::mutex m_aMutex; + + /** @short reference to the singleton(!) filter cache implementation, + which should be updated by this thread. */ + FilterCache &m_rCache; + + /** @short holds the configuration access, where we listen alive. */ + css::uno::Reference< css::uno::XInterface > m_xConfig; + + /** @short every instance of this update listener listen on + a special sub set of the filter configuration. + So it should know, which type of configuration entry + it must put into the filter cache, if the configuration notifies changes ... */ + FilterCache::EItemType m_eConfigType; + + + // native interface + + public: + + + // ctor/dtor + + /** @short initialize new instance of this class. + + @descr Listening won't be started here. It can be done + by calling startListening() on this instance. + + @see startListening() + + @param xConfigAccess + the configuration access, where this instance should listen for changes. + + @param eConfigType + specify the type of configuration. + */ + CacheUpdateListener(FilterCache &rFilterCache, + css::uno::Reference< css::uno::XInterface > xConfigAccess, + FilterCache::EItemType eConfigType); + + + /** @short standard dtor. + */ + virtual ~CacheUpdateListener() override; + + + /** @short starts listening. + */ + void startListening(); + + + /** @short stop listening. + */ + void stopListening(); + + + // uno interface + + public: + + + // XChangesListener + + virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& aEvent) override; + + + // lang.XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override; +}; + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/configflush.cxx b/filter/source/config/cache/configflush.cxx new file mode 100644 index 000000000..34ac28782 --- /dev/null +++ b/filter/source/config/cache/configflush.cxx @@ -0,0 +1,112 @@ +/* -*- 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 "configflush.hxx" +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> + + +namespace filter::config{ + +ConfigFlush::ConfigFlush() + : m_lListener(m_aMutex) +{ +} + +ConfigFlush::~ConfigFlush() +{ +} + +OUString SAL_CALL ConfigFlush::getImplementationName() +{ + return "com.sun.star.comp.filter.config.ConfigFlush"; + // <- SAFE +} + +sal_Bool SAL_CALL ConfigFlush::supportsService(const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ConfigFlush::getSupportedServiceNames() +{ + return { "com.sun.star.document.FilterConfigRefresh" }; +} + +void SAL_CALL ConfigFlush::refresh() +{ + // notify listener outside the lock! + // The used listener helper lives if we live + // and is threadsafe by itself. + // Further it's not a good idea to hold the own lock + // if an outside object is called :-) + css::lang::EventObject aSource (static_cast< css::util::XRefreshable* >(this)); + comphelper::OInterfaceContainerHelper2* pContainer = m_lListener.getContainer(cppu::UnoType<css::util::XRefreshListener>::get()); + if (!pContainer) + return; + + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + // ... this pointer can be interesting to find out, where will be called as listener + // Don't optimize it to a direct iterator cast :-) + css::util::XRefreshListener* pListener = static_cast<css::util::XRefreshListener*>(pIterator.next()); + pListener->refreshed(aSource); + } + catch(const css::uno::Exception&) + { + // ignore any "damaged" flush listener! + // May its remote reference is broken ... + pIterator.remove(); + } + } +} + + +void SAL_CALL ConfigFlush::addRefreshListener(const css::uno::Reference< css::util::XRefreshListener >& xListener) +{ + // no locks necessary + // used helper lives if we live and is threadsafe by itself ... + m_lListener.addInterface(cppu::UnoType<css::util::XRefreshListener>::get(), + xListener); +} + + +void SAL_CALL ConfigFlush::removeRefreshListener(const css::uno::Reference< css::util::XRefreshListener >& xListener) +{ + // no locks necessary + // used helper lives if we live and is threadsafe by itself ... + m_lListener.removeInterface(cppu::UnoType<css::util::XRefreshListener>::get(), + xListener); +} + + +} // namespace filter::config + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +filter_ConfigFlush_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new filter::config::ConfigFlush()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/configflush.hxx b/filter/source/config/cache/configflush.hxx new file mode 100644 index 000000000..d03351673 --- /dev/null +++ b/filter/source/config/cache/configflush.hxx @@ -0,0 +1,89 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/util/XRefreshable.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <comphelper/multicontainer2.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/basemutex.hxx> + + +namespace filter::config { + + +/** @short supports registration of XRefreshListener + on the global filter configuration. + + @descr Such refresh listener will be called in case the + type/filter configuration will be changed at runtime. + */ +class ConfigFlush final : public cppu::BaseMutex + , public ::cppu::WeakImplHelper< + css::util::XRefreshable, + css::lang::XServiceInfo + > +{ + /** @short holds all listener, which are registered at this instance. */ + comphelper::OMultiTypeInterfaceContainerHelper2 m_lListener; + + + // native interface + + public: + + + // ctor/dtor + + /** @short standard ctor. + */ + ConfigFlush(); + + + /** @short standard dtor. + */ + virtual ~ConfigFlush() override; + + + // uno interface + + public: + + + // XServiceInfo + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + + // XRefreshable + + virtual void SAL_CALL refresh() override; + + virtual void SAL_CALL addRefreshListener(const css::uno::Reference< css::util::XRefreshListener >& xListener) override; + + virtual void SAL_CALL removeRefreshListener(const css::uno::Reference< css::util::XRefreshListener >& xListener) override; +}; + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/constant.hxx b/filter/source/config/cache/constant.hxx new file mode 100644 index 000000000..86e6e6de9 --- /dev/null +++ b/filter/source/config/cache/constant.hxx @@ -0,0 +1,153 @@ +/* -*- 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 . + */ +#pragma once + +#include <rtl/ustring.hxx> + +/* disable impl_loadOnDemand function of BaseContainer for certain + functions, where it the feature "impl_loadItemOnDemand() of class FilterCache + can be used instead of loadAll()!*/ +// #define LOAD_IMPLICIT + + +/** @short used to identify a some generic item properties against the + configuration API and can be used at all name containers + (based on this filtercache) too. + */ +inline constexpr OUStringLiteral PROPNAME_NAME = u"Name"; + +/** @short used to identify a type item property against the + configuration API and can be used at all name containers + (based on this filtercache) too. + */ +inline constexpr OUStringLiteral PROPNAME_UINAME = u"UIName"; +inline constexpr OUStringLiteral PROPNAME_UINAMES = u"UINames"; +inline constexpr OUStringLiteral PROPNAME_PREFERRED = u"Preferred"; +inline constexpr OUStringLiteral PROPNAME_PREFERREDFILTER = u"PreferredFilter"; +inline constexpr OUStringLiteral PROPNAME_DETECTSERVICE = u"DetectService"; +inline constexpr OUStringLiteral PROPNAME_MEDIATYPE = u"MediaType"; +inline constexpr OUStringLiteral PROPNAME_CLIPBOARDFORMAT = u"ClipboardFormat"; +inline constexpr OUStringLiteral PROPNAME_URLPATTERN = u"URLPattern"; +inline constexpr OUStringLiteral PROPNAME_EXTENSIONS = u"Extensions"; + +/** @short used to identify a filter item property against the + configuration API and can be used at all name containers + (based on this filtercache) too. + */ +inline constexpr OUStringLiteral PROPNAME_TYPE = u"Type"; +inline constexpr OUStringLiteral PROPNAME_DOCUMENTSERVICE = u"DocumentService"; +inline constexpr OUStringLiteral PROPNAME_FILTERSERVICE = u"FilterService"; +inline constexpr OUStringLiteral PROPNAME_UICOMPONENT = u"UIComponent"; +inline constexpr OUStringLiteral PROPNAME_FLAGS = u"Flags"; +inline constexpr OUStringLiteral PROPNAME_USERDATA = u"UserData"; +inline constexpr OUStringLiteral PROPNAME_TEMPLATENAME = u"TemplateName"; +inline constexpr OUStringLiteral PROPNAME_FILEFORMATVERSION = u"FileFormatVersion"; +inline constexpr OUStringLiteral PROPNAME_EXPORTEXTENSION = u"ExportExtension"; +inline constexpr OUStringLiteral PROPNAME_ENABLED = u"Enabled"; + +/** @short used to identify a frame loader or detect service item + property against the configuration API and can be used + at all name containers (based on this filtercache) too. + */ +inline constexpr OUStringLiteral PROPNAME_TYPES = u"Types"; + +/** @short used to identify the list of sorted filters for a specific + office module + */ +inline constexpr OUStringLiteral PROPNAME_SORTEDFILTERLIST = u"SortedFilterList"; + +/** @short implicit properties. which are used at the container interface only. + */ +inline constexpr OUStringLiteral PROPNAME_FINALIZED = u"Finalized"; +inline constexpr OUStringLiteral PROPNAME_MANDATORY = u"Mandatory"; + +/** @short used to identify a set of items against the configuration API. */ +inline constexpr OUStringLiteral CFGSET_TYPES = u"Types"; +inline constexpr OUStringLiteral CFGSET_FILTERS = u"Filters"; +inline constexpr OUStringLiteral CFGSET_FRAMELOADERS = u"FrameLoaders"; +inline constexpr OUStringLiteral CFGSET_CONTENTHANDLERS = u"ContentHandlers"; + +/** @short used to address some configuration keys directly. + + @descr Such direct keys should be used with function + FilterCache::impl_getDirectCFGValue() only! + + @TODO define these direct keys ... + */ +inline constexpr OUStringLiteral CFGDIRECTKEY_OFFICELOCALE = u"/org.openoffice.Setup/L10N/ooLocale"; +inline constexpr OUStringLiteral CFGDIRECTKEY_DEFAULTFRAMELOADER = u"/org.openoffice.TypeDetection.Misc/Defaults/DefaultFrameLoader"; +#define CFGDIRECTKEY_PRODUCTNAME "/org.openoffice.Setup/Product/ooName" + +// Note that these flag bits have parallel names in +// comphelper/inc/comphelper/documentconstants.hxx . See that file for +// documentation on their meaning. + +/** @short names of filter flags, sorted in alphabetical order */ +inline constexpr OUStringLiteral FLAGNAME_3RDPARTYFILTER = u"3RDPARTYFILTER"; +inline constexpr OUStringLiteral FLAGNAME_ALIEN = u"ALIEN"; +inline constexpr OUStringLiteral FLAGNAME_CONSULTSERVICE = u"CONSULTSERVICE"; +inline constexpr OUStringLiteral FLAGNAME_DEFAULT = u"DEFAULT"; +inline constexpr OUStringLiteral FLAGNAME_ENCRYPTION = u"ENCRYPTION"; +inline constexpr OUStringLiteral FLAGNAME_EXPORT = u"EXPORT"; +inline constexpr OUStringLiteral FLAGNAME_GPGENCRYPTION = u"GPGENCRYPTION"; +inline constexpr OUStringLiteral FLAGNAME_IMPORT = u"IMPORT"; +inline constexpr OUStringLiteral FLAGNAME_INTERNAL = u"INTERNAL"; +inline constexpr OUStringLiteral FLAGNAME_NOTINFILEDIALOG = u"NOTINFILEDIALOG"; +inline constexpr OUStringLiteral FLAGNAME_NOTINSTALLED = u"NOTINSTALLED"; +inline constexpr OUStringLiteral FLAGNAME_OWN = u"OWN"; +inline constexpr OUStringLiteral FLAGNAME_PACKED = u"PACKED"; +inline constexpr OUStringLiteral FLAGNAME_PASSWORDTOMODIFY = u"PASSWORDTOMODIFY"; +inline constexpr OUStringLiteral FLAGNAME_PREFERRED = u"PREFERRED"; +inline constexpr OUStringLiteral FLAGNAME_STARTPRESENTATION = u"STARTPRESENTATION"; +inline constexpr OUStringLiteral FLAGNAME_READONLY = u"READONLY"; +inline constexpr OUStringLiteral FLAGNAME_SUPPORTSSELECTION = u"SUPPORTSSELECTION"; +inline constexpr OUStringLiteral FLAGNAME_TEMPLATE = u"TEMPLATE"; +inline constexpr OUStringLiteral FLAGNAME_TEMPLATEPATH = u"TEMPLATEPATH"; +inline constexpr OUStringLiteral FLAGNAME_COMBINED = u"COMBINED"; +inline constexpr OUStringLiteral FLAGNAME_SUPPORTSSIGNING = u"SUPPORTSSIGNING"; +inline constexpr OUStringLiteral FLAGNAME_EXOTIC = u"EXOTIC"; + +/** @short some uno service names. + */ +inline constexpr OUStringLiteral SERVICE_CONFIGURATIONUPDATEACCESS = u"com.sun.star.configuration.ConfigurationUpdateAccess"; +inline constexpr OUStringLiteral SERVICE_CONFIGURATIONACCESS = u"com.sun.star.configuration.ConfigurationAccess"; + +/** @short some configuration paths. + */ +inline constexpr OUStringLiteral CFGPACKAGE_TD_TYPES = u"/org.openoffice.TypeDetection.Types"; +inline constexpr OUStringLiteral CFGPACKAGE_TD_FILTERS = u"/org.openoffice.TypeDetection.Filter"; +inline constexpr OUStringLiteral CFGPACKAGE_TD_OTHERS = u"/org.openoffice.TypeDetection.Misc"; +inline constexpr OUStringLiteral CFGPACKAGE_TD_OLD = u"/org.openoffice.Office.TypeDetection"; + +/** @short some default values. + */ +inline constexpr OUStringLiteral DEFAULT_OFFICELOCALE = u"en-US"; + +/** @short used for the queries of the FilterFactory service. + */ +inline constexpr OUStringLiteral QUERY_IDENTIFIER_MATCHBYDOCUMENTSERVICE = u"matchByDocumentService"; +inline constexpr OUStringLiteral QUERY_IDENTIFIER_GETPREFERREDFILTERFORTYPE = u"getDefaultFilterForType"; +inline constexpr OUStringLiteral QUERY_IDENTIFIER_GET_SORTED_FILTERLIST = u"getSortedFilterList()"; + +inline constexpr OUStringLiteral QUERY_PARAM_IFLAGS = u"iflags"; +inline constexpr OUStringLiteral QUERY_PARAM_EFLAGS = u"eflags"; +inline constexpr OUStringLiteral QUERY_PARAM_MODULE = u"module"; +#define QUERY_CONSTVALUE_ALL "all" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/contenthandlerfactory.cxx b/filter/source/config/cache/contenthandlerfactory.cxx new file mode 100644 index 000000000..089f8db5f --- /dev/null +++ b/filter/source/config/cache/contenthandlerfactory.cxx @@ -0,0 +1,103 @@ +/* -*- 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 "contenthandlerfactory.hxx" + +#include <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/sequence.hxx> + + +namespace filter::config{ + +ContentHandlerFactory::ContentHandlerFactory(const css::uno::Reference< css::uno::XComponentContext >& rxContext) + : m_xContext(rxContext) +{ + BaseContainer::init("com.sun.star.comp.filter.config.ContentHandlerFactory" , + { "com.sun.star.frame.ContentHandlerFactory" }, + FilterCache::E_CONTENTHANDLER ); +} + + +ContentHandlerFactory::~ContentHandlerFactory() +{ +} + + +css::uno::Reference< css::uno::XInterface > SAL_CALL ContentHandlerFactory::createInstance(const OUString& sHandler) +{ + return createInstanceWithArguments(sHandler, css::uno::Sequence< css::uno::Any >()); +} + + +css::uno::Reference< css::uno::XInterface > SAL_CALL ContentHandlerFactory::createInstanceWithArguments(const OUString& sHandler , + const css::uno::Sequence< css::uno::Any >& lArguments) +{ + css::uno::Reference< css::uno::XInterface > xHandler; + + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + auto & cache = GetTheFilterCache(); + + // search handler on cache + CacheItem aHandler = cache.getItem(FilterCache::E_CONTENTHANDLER, sHandler); + + // create service instance + xHandler = m_xContext->getServiceManager()->createInstanceWithContext(sHandler, m_xContext); + + // initialize filter + css::uno::Reference< css::lang::XInitialization > xInit(xHandler, css::uno::UNO_QUERY); + if (xInit.is()) + { + // format: lInitData[0] = seq<PropertyValue>, which contains all configuration properties of this handler + // lInitData[1] = lArguments[0] + // ... + // lInitData[n] = lArguments[n-1] + css::uno::Sequence< css::beans::PropertyValue > lConfig; + aHandler >> 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 xHandler; + // <- SAFE +} + + +css::uno::Sequence< OUString > SAL_CALL ContentHandlerFactory::getAvailableServiceNames() +{ + // must be the same list as ((XNameAccess*)this)->getElementNames() return! + return BaseContainer::getElementNames(); +} + +} // namespace filter::config + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +filter_ContentHandlerFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new filter::config::ContentHandlerFactory(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/contenthandlerfactory.hxx b/filter/source/config/cache/contenthandlerfactory.hxx new file mode 100644 index 000000000..5c3ecd760 --- /dev/null +++ b/filter/source/config/cache/contenthandlerfactory.hxx @@ -0,0 +1,99 @@ +/* -*- 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 . + */ + +#pragma once + +#include "basecontainer.hxx" +#include <com/sun/star/frame/XLoaderFactory.hpp> +#include <cppuhelper/implbase.hxx> + + +namespace filter::config { + + +/** @short implements the service <type scope="com.sun.star.document">ContentHandlerFactory</type>. + */ +class ContentHandlerFactory : public ::cppu::ImplInheritanceHelper< BaseContainer , + css::frame::XLoaderFactory > +{ + + // native interface + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + public: + + + // ctor/dtor + + /** @short standard ctor to connect this interface wrapper to + the global filter cache instance ... + + @param rxContext + reference to the uno service manager, which created this service instance. + */ + explicit ContentHandlerFactory(const css::uno::Reference< css::uno::XComponentContext >& rxContext); + + + /** @short standard dtor. + */ + virtual ~ContentHandlerFactory() override; + + + // uno interface + + public: + + + // XMultiServiceFactory + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance(const OUString& sHandler) override; + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments(const OUString& sHandler , + const css::uno::Sequence< css::uno::Any >& lArguments) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames() override; + + + public: + + // Overrides to resolve ambiguity + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override + { return BaseContainer::getByName(aName); } + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override + { return BaseContainer::getElementNames(); } + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override + { return BaseContainer::hasByName(aName); } + + virtual css::uno::Type SAL_CALL getElementType() override + { return BaseContainer::getElementType(); } + virtual sal_Bool SAL_CALL hasElements() override + { return BaseContainer::hasElements(); } + + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByQuery( const OUString& Query ) override + { return BaseContainer::createSubSetEnumerationByQuery(Query); } + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByProperties( const css::uno::Sequence< css::beans::NamedValue >& Properties ) override + { return BaseContainer::createSubSetEnumerationByProperties(Properties); } + + +}; + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/filtercache.cxx b/filter/source/config/cache/filtercache.cxx new file mode 100644 index 000000000..b32586162 --- /dev/null +++ b/filter/source/config/cache/filtercache.cxx @@ -0,0 +1,2236 @@ +/* -*- 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 <memory> +#include <tools/diagnose_ex.h> +#include "filtercache.hxx" +#include "constant.hxx" +#include "cacheupdatelistener.hxx" + +/*TODO see using below ... */ +#define AS_ENABLE_FILTER_UINAMES + +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XMultiPropertySet.hpp> +#include <com/sun/star/beans/XProperty.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/Property.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/document/CorruptedFilterConfigurationException.hpp> +#include <comphelper/sequence.hxx> +#include <comphelper/processfactory.hxx> + +#include <unotools/configmgr.hxx> +#include <unotools/configpaths.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/uri.hxx> +#include <sal/log.hxx> +#include <tools/urlobj.hxx> +#include <tools/wldcrd.hxx> +#include <i18nlangtag/languagetag.hxx> + +#include <officecfg/Setup.hxx> +#include <o3tl/string_view.hxx> + + +namespace filter::config{ + +FilterCache::FilterCache() + : m_eFillState(E_CONTAINS_NOTHING ) +{ + int i = 0; + OUString sStandardProps[10]; + + sStandardProps[i++] = PROPNAME_USERDATA; + sStandardProps[i++] = PROPNAME_TEMPLATENAME; + sStandardProps[i++] = PROPNAME_ENABLED; + // E_READ_UPDATE only above + sStandardProps[i++] = PROPNAME_TYPE; + sStandardProps[i++] = PROPNAME_FILEFORMATVERSION; + sStandardProps[i++] = PROPNAME_UICOMPONENT; + sStandardProps[i++] = PROPNAME_FILTERSERVICE; + sStandardProps[i++] = PROPNAME_DOCUMENTSERVICE; + sStandardProps[i++] = PROPNAME_EXPORTEXTENSION; + sStandardProps[i++] = PROPNAME_FLAGS; // must be last. + assert(i == SAL_N_ELEMENTS(sStandardProps)); + + // E_READ_NOTHING -> creative nothingness. + m_aStandardProps[E_READ_STANDARD] = + css::uno::Sequence< OUString >(sStandardProps + 3, 7); + m_aStandardProps[E_READ_UPDATE] = + css::uno::Sequence< OUString >(sStandardProps, 3); + m_aStandardProps[E_READ_ALL] = + css::uno::Sequence< OUString >(sStandardProps, + SAL_N_ELEMENTS(sStandardProps)); + + i = 0; + OUString sTypeProps[7]; + sTypeProps[i++] = PROPNAME_MEDIATYPE; + // E_READ_UPDATE only above + sTypeProps[i++] = PROPNAME_PREFERREDFILTER; + sTypeProps[i++] = PROPNAME_DETECTSERVICE; + sTypeProps[i++] = PROPNAME_URLPATTERN; + sTypeProps[i++] = PROPNAME_EXTENSIONS; + sTypeProps[i++] = PROPNAME_PREFERRED; + sTypeProps[i++] = PROPNAME_CLIPBOARDFORMAT; + assert(i == SAL_N_ELEMENTS(sTypeProps)); + + // E_READ_NOTHING -> more creative nothingness. + m_aTypeProps[E_READ_STANDARD] = + css::uno::Sequence< OUString >(sTypeProps + 1, 6); + m_aTypeProps[E_READ_UPDATE] = + css::uno::Sequence< OUString >(sTypeProps, 1); + m_aTypeProps[E_READ_ALL] = + css::uno::Sequence< OUString >(sTypeProps, + SAL_N_ELEMENTS(sTypeProps)); +} + + +FilterCache::~FilterCache() +{ + if (m_xTypesChglisteners.is()) + m_xTypesChglisteners->stopListening(); + if (m_xFiltersChgListener.is()) + m_xFiltersChgListener->stopListening(); +} + + +std::unique_ptr<FilterCache> FilterCache::clone() const +{ + // SAFE -> ---------------------------------- + osl::MutexGuard aLock(m_aMutex); + + auto pClone = std::make_unique<FilterCache>(); + + // Don't copy the configuration access points here. + // They will be created on demand inside the cloned instance, + // if they are needed. + + pClone->m_lTypes = m_lTypes; + pClone->m_lFilters = m_lFilters; + pClone->m_lFrameLoaders = m_lFrameLoaders; + pClone->m_lContentHandlers = m_lContentHandlers; + pClone->m_lExtensions2Types = m_lExtensions2Types; + pClone->m_lURLPattern2Types = m_lURLPattern2Types; + + pClone->m_sActLocale = m_sActLocale; + + pClone->m_eFillState = m_eFillState; + + pClone->m_lChangedTypes = m_lChangedTypes; + pClone->m_lChangedFilters = m_lChangedFilters; + pClone->m_lChangedFrameLoaders = m_lChangedFrameLoaders; + pClone->m_lChangedContentHandlers = m_lChangedContentHandlers; + + return pClone; + // <- SAFE ---------------------------------- +} + + +void FilterCache::takeOver(const FilterCache& rClone) +{ + // SAFE -> ---------------------------------- + osl::MutexGuard aLock(m_aMutex); + + // a) + // Don't copy the configuration access points here! + // We must use our own ones... + + // b) + // Further we can ignore the uno service manager. + // We should already have a valid instance. + + // c) + // Take over only changed items! + // Otherwise we risk the following scenario: + // c1) clone_1 contains changed filters + // c2) clone_2 container changed types + // c3) clone_1 take over changed filters and unchanged types + // c4) clone_2 take over unchanged filters(!) and changed types(!) + // c5) c4 overwrites c3! + + if (!rClone.m_lChangedTypes.empty()) + m_lTypes = rClone.m_lTypes; + if (!rClone.m_lChangedFilters.empty()) + m_lFilters = rClone.m_lFilters; + if (!rClone.m_lChangedFrameLoaders.empty()) + m_lFrameLoaders = rClone.m_lFrameLoaders; + if (!rClone.m_lChangedContentHandlers.empty()) + m_lContentHandlers = rClone.m_lContentHandlers; + + m_lChangedTypes.clear(); + m_lChangedFilters.clear(); + m_lChangedFrameLoaders.clear(); + m_lChangedContentHandlers.clear(); + + m_sActLocale = rClone.m_sActLocale; + + m_eFillState = rClone.m_eFillState; + + // renew all dependencies and optimizations + // Because we can't be sure, that changed filters on one clone + // and changed types of another clone work together. + // But here we can check against the later changes... + impl_validateAndOptimize(); + // <- SAFE ---------------------------------- +} + +void FilterCache::load(EFillState eRequired) +{ + // SAFE -> ---------------------------------- + osl::MutexGuard aLock(m_aMutex); + + // check if required fill state is already reached ... + // There is nothing to do then. + if ((m_eFillState & eRequired) == eRequired) + return; + + // Otherwise load the missing items. + + + // a) load some const values from configuration. + // These values are needed there for loading + // config items ... + // Further we load some std items from the + // configuration so we can try to load the first + // office document with a minimal set of values. + if (m_eFillState == E_CONTAINS_NOTHING) + { + impl_getDirectCFGValue(CFGDIRECTKEY_OFFICELOCALE) >>= m_sActLocale; + if (m_sActLocale.isEmpty()) + { + m_sActLocale = DEFAULT_OFFICELOCALE; + } + + // Support the old configuration support. Read it only one times during office runtime! + impl_readOldFormat(); + } + + + // b) If the required fill state was not reached + // but std values was already loaded ... + // we must load some further missing items. + impl_load(eRequired); + // <- SAFE +} + +bool FilterCache::isFillState(FilterCache::EFillState eState) const +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + return ((m_eFillState & eState) == eState); + // <- SAFE +} + + +std::vector<OUString> FilterCache::getMatchingItemsByProps( EItemType eType , + o3tl::span< const css::beans::NamedValue > lIProps, + o3tl::span< const css::beans::NamedValue > lEProps) const +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + // search for right list + // An exception is thrown - "eType" is unknown. + // => rList will be valid everytimes next line is reached. + const CacheItemList& rList = impl_getItemList(eType); + + std::vector<OUString> lKeys; + lKeys.reserve(rList.size()); + + // search items, which provides all needed properties of set "lIProps" + // but not of set "lEProps"! + for (auto const& elem : rList) + { + if ( + (elem.second.haveProps(lIProps) ) && + (elem.second.dontHaveProps(lEProps)) + ) + { + lKeys.push_back(elem.first); + } + } + + return lKeys; + // <- SAFE +} + + +bool FilterCache::hasItems(EItemType eType) const +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + // search for right list + // An exception is thrown - "eType" is unknown. + // => rList will be valid everytimes next line is reached. + const CacheItemList& rList = impl_getItemList(eType); + + return !rList.empty(); + // <- SAFE +} + + +std::vector<OUString> FilterCache::getItemNames(EItemType eType) const +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + // search for right list + // An exception is thrown - "eType" is unknown. + // => rList will be valid everytimes next line is reached. + const CacheItemList& rList = impl_getItemList(eType); + + std::vector<OUString> lKeys; + for (auto const& elem : rList) + { + lKeys.push_back(elem.first); + } + return lKeys; + // <- SAFE +} + + +bool FilterCache::hasItem( EItemType eType, + const OUString& sItem) +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + // search for right list + // An exception is thrown - "eType" is unknown. + // => rList will be valid everytimes next line is reached. + const CacheItemList& rList = impl_getItemList(eType); + + // if item could not be found - check if it can be loaded + // from the underlying configuration layer. Might it was not already + // loaded into this FilterCache object before. + CacheItemList::const_iterator pIt = rList.find(sItem); + if (pIt != rList.end()) + return true; + + try + { + impl_loadItemOnDemand(eType, sItem); + // no exception => item could be loaded! + return true; + } + catch(const css::container::NoSuchElementException&) + {} + + return false; + // <- SAFE +} + + +CacheItem FilterCache::getItem( EItemType eType, + const OUString& sItem) +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + CacheItem aItem = impl_getItem(eType, sItem); + // <- SAFE + return aItem; +} + + +CacheItem& FilterCache::impl_getItem( EItemType eType, + const OUString& sItem) +{ + // search for right list + // An exception is thrown if "eType" is unknown. + // => rList will be valid everytimes next line is reached. + CacheItemList& rList = impl_getItemList(eType); + + // check if item exists ... + CacheItemList::iterator pIt = rList.find(sItem); + if (pIt == rList.end()) + { + // ... or load it on demand from the + // underlying configuration layer. + // Note: NoSuchElementException is thrown automatically here if + // item could not be loaded! + pIt = impl_loadItemOnDemand(eType, sItem); + } + + /* Workaround for #137955# + Draw types and filters are installed ... but draw was disabled during setup. + We must suppress accessing these filters. Otherwise the office can crash. + Solution for the next major release: do not install those filters ! + */ + if (eType == E_FILTER) + { + CacheItem& rFilter = pIt->second; + OUString sDocService; + rFilter[PROPNAME_DOCUMENTSERVICE] >>= sDocService; + + // In Standalone-Impress the module WriterWeb is not installed + // but it is there to load help pages + bool bIsHelpFilter = sItem == "writer_web_HTML_help"; + + if ( !bIsHelpFilter && !impl_isModuleInstalled(sDocService) ) + { + OUString sMsg("The requested filter '" + sItem + + "' exists ... but it should not; because the corresponding LibreOffice module was not installed."); + throw css::container::NoSuchElementException(sMsg, css::uno::Reference< css::uno::XInterface >()); + } + } + + return pIt->second; +} + + +void FilterCache::removeItem( EItemType eType, + const OUString& sItem) +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + // search for right list + // An exception is thrown - "eType" is unknown. + // => rList will be valid everytimes next line is reached. + CacheItemList& rList = impl_getItemList(eType); + + CacheItemList::iterator pItem = rList.find(sItem); + if (pItem == rList.end()) + pItem = impl_loadItemOnDemand(eType, sItem); // throws NoSuchELementException! + rList.erase(pItem); + + impl_addItem2FlushList(eType, sItem); +} + + +void FilterCache::setItem( EItemType eType , + const OUString& sItem , + const CacheItem& aValue) +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + // search for right list + // An exception is thrown - "eType" is unknown. + // => rList will be valid everytimes next line is reached. + CacheItemList& rList = impl_getItemList(eType); + + // name must be part of the property set too ... otherwise our + // container query can't work correctly + CacheItem aItem = aValue; + aItem[PROPNAME_NAME] <<= sItem; + aItem.validateUINames(m_sActLocale); + + // remove implicit properties as e.g. FINALIZED or MANDATORY + // They can't be saved here and must be read on demand later, if they are needed. + removeStatePropsFromItem(aItem); + + rList[sItem] = aItem; + + impl_addItem2FlushList(eType, sItem); +} + + +void FilterCache::refreshItem( EItemType eType, + const OUString& sItem) +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + impl_loadItemOnDemand(eType, sItem); +} + + +css::uno::Any FilterCache::getItemWithStateProps( EItemType eType, + const OUString& sItem) +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + const CacheItem& rItem = impl_getItem(eType, sItem); + + // Note: Opening of the configuration layer throws some exceptions + // if it failed. So we mustn't check any reference here... + css::uno::Reference< css::container::XNameAccess > xPackage; + css::uno::Reference< css::container::XNameAccess > xSet; + switch(eType) + { + case E_TYPE : + { + xPackage.set(impl_openConfig(E_PROVIDER_TYPES), css::uno::UNO_QUERY_THROW); + xPackage->getByName(CFGSET_TYPES) >>= xSet; + } + break; + + case E_FILTER : + { + xPackage.set(impl_openConfig(E_PROVIDER_FILTERS), css::uno::UNO_QUERY_THROW); + xPackage->getByName(CFGSET_FILTERS) >>= xSet; + } + break; + + case E_FRAMELOADER : + { + /* TODO + Hack --> + The default frame loader can't be located inside the normal set of frame loaders. + It's an atomic property inside the misc cfg package. So we can't retrieve the information + about FINALIZED and MANDATORY very easy ... :-( + => set it to readonly/required everytimes :-) + */ + css::uno::Any aDirectValue = impl_getDirectCFGValue(CFGDIRECTKEY_DEFAULTFRAMELOADER); + OUString sDefaultFrameLoader; + if ( + (aDirectValue >>= sDefaultFrameLoader) && + (!sDefaultFrameLoader.isEmpty() ) && + (sItem == sDefaultFrameLoader ) + ) + { + css::uno::Sequence aProps = rItem.getAsPackedPropertyValueList(true, true); + return css::uno::Any(aProps); + } + /* <-- HACK */ + + xPackage.set(impl_openConfig(E_PROVIDER_OTHERS), css::uno::UNO_QUERY_THROW); + xPackage->getByName(CFGSET_FRAMELOADERS) >>= xSet; + } + break; + + case E_CONTENTHANDLER : + { + xPackage.set(impl_openConfig(E_PROVIDER_OTHERS), css::uno::UNO_QUERY_THROW); + xPackage->getByName(CFGSET_CONTENTHANDLERS) >>= xSet; + } + break; + default: break; + } + + bool bFinalized, bMandatory; + try + { + css::uno::Reference< css::beans::XProperty > xItem; + xSet->getByName(sItem) >>= xItem; + css::beans::Property aDescription = xItem->getAsProperty(); + + bFinalized = ((aDescription.Attributes & css::beans::PropertyAttribute::READONLY ) == css::beans::PropertyAttribute::READONLY ); + bMandatory = ((aDescription.Attributes & css::beans::PropertyAttribute::REMOVABLE) != css::beans::PropertyAttribute::REMOVABLE); + + } + catch(const css::container::NoSuchElementException&) + { + /* Ignore exceptions for missing elements inside configuration. + May by the following reason exists: + - The item does not exists inside the new configuration package org.openoffice.TypeDetection - but + we got it from the old package org.openoffice.Office/TypeDetection. We don't migrate such items + automatically to the new format. Because it will disturb e.g. the deinstallation of an external filter + package. Because such external filter can remove the old file - but not the automatically created new one ... + + => mark item as FINALIZED / MANDATORY, we don't support writing to the old format + */ + bFinalized = true; + bMandatory = true; + } + + css::uno::Sequence<css::beans::PropertyValue> aProps = rItem.getAsPackedPropertyValueList(bFinalized, bMandatory); + + return css::uno::Any(aProps); + // <- SAFE +} + + +void FilterCache::removeStatePropsFromItem(CacheItem& rItem) +{ + CacheItem::iterator pIt = rItem.find(PROPNAME_FINALIZED); + if (pIt != rItem.end()) + rItem.erase(pIt); + pIt = rItem.find(PROPNAME_MANDATORY); + if (pIt != rItem.end()) + rItem.erase(pIt); +} + + +void FilterCache::flush() +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + // renew all dependencies and optimizations + impl_validateAndOptimize(); + + if (!m_lChangedTypes.empty()) + { + css::uno::Reference< css::container::XNameAccess > xConfig(impl_openConfig(E_PROVIDER_TYPES), css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XNameAccess > xSet ; + + xConfig->getByName(CFGSET_TYPES) >>= xSet; + impl_flushByList(xSet, E_TYPE, m_lTypes, m_lChangedTypes); + + css::uno::Reference< css::util::XChangesBatch > xFlush(xConfig, css::uno::UNO_QUERY); + xFlush->commitChanges(); + } + + if (!m_lChangedFilters.empty()) + { + css::uno::Reference< css::container::XNameAccess > xConfig(impl_openConfig(E_PROVIDER_FILTERS), css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XNameAccess > xSet ; + + xConfig->getByName(CFGSET_FILTERS) >>= xSet; + impl_flushByList(xSet, E_FILTER, m_lFilters, m_lChangedFilters); + + css::uno::Reference< css::util::XChangesBatch > xFlush(xConfig, css::uno::UNO_QUERY); + xFlush->commitChanges(); + } + + /*TODO FrameLoader/ContentHandler must be flushed here too ... */ +} + + +void FilterCache::impl_flushByList(const css::uno::Reference< css::container::XNameAccess >& xSet , + EItemType eType , + const CacheItemList& rCache, + const std::vector<OUString>& lItems) +{ + css::uno::Reference< css::container::XNameContainer > xAddRemoveSet(xSet, css::uno::UNO_QUERY); + css::uno::Reference< css::lang::XSingleServiceFactory > xFactory(xSet, css::uno::UNO_QUERY); + + for (auto const& item : lItems) + { + EItemFlushState eState = impl_specifyFlushOperation(xSet, rCache, item); + switch(eState) + { + case E_ITEM_REMOVED : + { + xAddRemoveSet->removeByName(item); + } + break; + + case E_ITEM_ADDED : + { + css::uno::Reference< css::container::XNameReplace > xItem (xFactory->createInstance(), css::uno::UNO_QUERY); + + // special case. no exception - but not a valid item => set must be finalized or mandatory! + // Reject flush operation by throwing an exception. At least one item couldn't be flushed. + if (!xItem.is()) + throw css::uno::Exception("Can not add item. Set is finalized or mandatory!", + css::uno::Reference< css::uno::XInterface >()); + + CacheItemList::const_iterator pItem = rCache.find(item); + impl_saveItem(xItem, eType, pItem->second); + xAddRemoveSet->insertByName(item, css::uno::Any(xItem)); + } + break; + + case E_ITEM_CHANGED : + { + css::uno::Reference< css::container::XNameReplace > xItem; + xSet->getByName(item) >>= xItem; + + // special case. no exception - but not a valid item => it must be finalized or mandatory! + // Reject flush operation by throwing an exception. At least one item couldn't be flushed. + if (!xItem.is()) + throw css::uno::Exception("Can not change item. It's finalized or mandatory!", + css::uno::Reference< css::uno::XInterface >()); + + CacheItemList::const_iterator pItem = rCache.find(item); + impl_saveItem(xItem, eType, pItem->second); + } + break; + default: break; + } + } +} + + +void FilterCache::detectFlatForURL(const css::util::URL& aURL , + FlatDetection& rFlatTypes) const +{ + // extract extension from URL, so it can be used directly as key into our hash map! + // Note further: It must be converted to lower case, because the optimize hash + // (which maps extensions to types) work with lower case key strings! + INetURLObject aParser (aURL.Main); + OUString sExtension = aParser.getExtension(INetURLObject::LAST_SEGMENT , + true , + INetURLObject::DecodeMechanism::WithCharset); + sExtension = sExtension.toAsciiLowerCase(); + + // SAFE -> ---------------------------------- + osl::MutexGuard aLock(m_aMutex); + + + // i) Step over all well known URL pattern + // and add registered types to the return list too + // Do it as first one - because: if a type match by a + // pattern a following deep detection can be suppressed! + // Further we can stop after first match ... + for (auto const& pattern : m_lURLPattern2Types) + { + WildCard aPatternCheck(pattern.first); + if (aPatternCheck.Matches(aURL.Main)) + { + const std::vector<OUString>& rTypesForPattern = pattern.second; + + FlatDetectionInfo aInfo; + aInfo.sType = *(rTypesForPattern.begin()); + aInfo.bMatchByPattern = true; + + rFlatTypes.push_back(aInfo); +// return; + } + } + + + // ii) search types matching to the given extension. + // Copy every matching type without changing its order! + // Because preferred types was added as first one during + // loading configuration. + CacheItemRegistration::const_iterator pExtReg = m_lExtensions2Types.find(sExtension); + if (pExtReg != m_lExtensions2Types.end()) + { + const std::vector<OUString>& rTypesForExtension = pExtReg->second; + for (auto const& elem : rTypesForExtension) + { + FlatDetectionInfo aInfo; + aInfo.sType = elem; + aInfo.bMatchByExtension = true; + + rFlatTypes.push_back(aInfo); + } + } + + // <- SAFE ---------------------------------- +} + +const CacheItemList& FilterCache::impl_getItemList(EItemType eType) const +{ + // SAFE -> ---------------------------------- + osl::MutexGuard aLock(m_aMutex); + + switch(eType) + { + case E_TYPE : return m_lTypes ; + case E_FILTER : return m_lFilters ; + case E_FRAMELOADER : return m_lFrameLoaders ; + case E_CONTENTHANDLER : return m_lContentHandlers; + + } + + throw css::uno::RuntimeException("unknown sub container requested.", + css::uno::Reference< css::uno::XInterface >()); + // <- SAFE ---------------------------------- +} + +CacheItemList& FilterCache::impl_getItemList(EItemType eType) +{ + // SAFE -> ---------------------------------- + osl::MutexGuard aLock(m_aMutex); + + switch(eType) + { + case E_TYPE : return m_lTypes ; + case E_FILTER : return m_lFilters ; + case E_FRAMELOADER : return m_lFrameLoaders ; + case E_CONTENTHANDLER : return m_lContentHandlers; + + } + + throw css::uno::RuntimeException("unknown sub container requested.", + css::uno::Reference< css::uno::XInterface >()); + // <- SAFE ---------------------------------- +} + +css::uno::Reference< css::uno::XInterface > FilterCache::impl_openConfig(EConfigProvider eProvider) +{ + osl::MutexGuard aLock(m_aMutex); + + OUString sPath ; + css::uno::Reference< css::uno::XInterface >* pConfig = nullptr; + css::uno::Reference< css::uno::XInterface > xOld ; + OString sRtlLog ; + + switch(eProvider) + { + case E_PROVIDER_TYPES : + { + if (m_xConfigTypes.is()) + return m_xConfigTypes; + sPath = CFGPACKAGE_TD_TYPES; + pConfig = &m_xConfigTypes; + sRtlLog = "impl_openconfig(E_PROVIDER_TYPES)"; + } + break; + + case E_PROVIDER_FILTERS : + { + if (m_xConfigFilters.is()) + return m_xConfigFilters; + sPath = CFGPACKAGE_TD_FILTERS; + pConfig = &m_xConfigFilters; + sRtlLog = "impl_openconfig(E_PROVIDER_FILTERS)"; + } + break; + + case E_PROVIDER_OTHERS : + { + if (m_xConfigOthers.is()) + return m_xConfigOthers; + sPath = CFGPACKAGE_TD_OTHERS; + pConfig = &m_xConfigOthers; + sRtlLog = "impl_openconfig(E_PROVIDER_OTHERS)"; + } + break; + + case E_PROVIDER_OLD : + { + // This special provider is used to work with + // the old configuration format only. It's not cached! + sPath = CFGPACKAGE_TD_OLD; + pConfig = &xOld; + sRtlLog = "impl_openconfig(E_PROVIDER_OLD)"; + } + break; + + default : throw css::uno::RuntimeException("These configuration node is not supported here for open!", nullptr); + } + + { + SAL_INFO( "filter.config", "" << sRtlLog); + *pConfig = impl_createConfigAccess(sPath , + false, // bReadOnly + true ); // bLocalesMode + } + + + // Start listening for changes on that configuration access. + switch(eProvider) + { + case E_PROVIDER_TYPES: + { + m_xTypesChglisteners.set(new CacheUpdateListener(*this, *pConfig, FilterCache::E_TYPE)); + m_xTypesChglisteners->startListening(); + } + break; + case E_PROVIDER_FILTERS: + { + m_xFiltersChgListener.set(new CacheUpdateListener(*this, *pConfig, FilterCache::E_FILTER)); + m_xFiltersChgListener->startListening(); + } + break; + default: + break; + } + + return *pConfig; +} + +css::uno::Any FilterCache::impl_getDirectCFGValue(const OUString& sDirectKey) +{ + OUString sRoot; + OUString sKey ; + + if ( + (!::utl::splitLastFromConfigurationPath(sDirectKey, sRoot, sKey)) || + (sRoot.isEmpty() ) || + (sKey.isEmpty() ) + ) + return css::uno::Any(); + + css::uno::Reference< css::uno::XInterface > xCfg = impl_createConfigAccess(sRoot , + true , // bReadOnly + false); // bLocalesMode + if (!xCfg.is()) + return css::uno::Any(); + + css::uno::Reference< css::container::XNameAccess > xAccess(xCfg, css::uno::UNO_QUERY); + if (!xAccess.is()) + return css::uno::Any(); + + css::uno::Any aValue; + try + { + aValue = xAccess->getByName(sKey); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "filter.config", ""); + aValue.clear(); + } + + return aValue; +} + + +css::uno::Reference< css::uno::XInterface > FilterCache::impl_createConfigAccess(const OUString& sRoot , + bool bReadOnly , + bool bLocalesMode) +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + css::uno::Reference< css::uno::XInterface > xCfg; + + if (!utl::ConfigManager::IsFuzzing()) + { + try + { + css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider( + css::configuration::theDefaultProvider::get( comphelper::getProcessComponentContext() ) ); + + ::std::vector< css::uno::Any > lParams; + css::beans::NamedValue aParam; + + // set root path + aParam.Name = "nodepath"; + aParam.Value <<= sRoot; + lParams.push_back(css::uno::Any(aParam)); + + // enable "all locales mode" ... if required + if (bLocalesMode) + { + aParam.Name = "locale"; + aParam.Value <<= OUString("*"); + lParams.push_back(css::uno::Any(aParam)); + } + + // open it + if (bReadOnly) + xCfg = xConfigProvider->createInstanceWithArguments(SERVICE_CONFIGURATIONACCESS, + comphelper::containerToSequence(lParams)); + else + xCfg = xConfigProvider->createInstanceWithArguments(SERVICE_CONFIGURATIONUPDATEACCESS, + comphelper::containerToSequence(lParams)); + + // If configuration could not be opened... but factory method did not throw an exception + // trigger throwing of our own CorruptedFilterConfigurationException. + // Let message empty. The normal exception text show enough information to the user. + if (! xCfg.is()) + throw css::uno::Exception( + "Got NULL reference on opening configuration file ... but no exception.", + css::uno::Reference< css::uno::XInterface >()); + } + catch(const css::uno::Exception& ex) + { + throw css::document::CorruptedFilterConfigurationException( + "filter configuration, caught: " + ex.Message, + css::uno::Reference< css::uno::XInterface >(), + ex.Message); + } + } + + return xCfg; + // <- SAFE +} + + +void FilterCache::impl_validateAndOptimize() +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + // First check if any filter or type could be read + // from the underlying configuration! + bool bSomeTypesShouldExist = ((m_eFillState & E_CONTAINS_STANDARD ) == E_CONTAINS_STANDARD ); + bool bAllFiltersShouldExist = ((m_eFillState & E_CONTAINS_FILTERS ) == E_CONTAINS_FILTERS ); + +#if OSL_DEBUG_LEVEL > 0 + + sal_Int32 nWarnings = 0; + +// sal_Bool bAllTypesShouldExist = ((m_eFillState & E_CONTAINS_TYPES ) == E_CONTAINS_TYPES ); + bool bAllLoadersShouldExist = ((m_eFillState & E_CONTAINS_FRAMELOADERS ) == E_CONTAINS_FRAMELOADERS ); + bool bAllHandlersShouldExist = ((m_eFillState & E_CONTAINS_CONTENTHANDLERS) == E_CONTAINS_CONTENTHANDLERS); +#endif + + if ( + ( + bSomeTypesShouldExist && m_lTypes.empty() + ) || + ( + bAllFiltersShouldExist && m_lFilters.empty() + ) + ) + { + throw css::document::CorruptedFilterConfigurationException( + "filter configuration: the list of types or filters is empty", + css::uno::Reference< css::uno::XInterface >(), + "The list of types or filters is empty." ); + } + + // Create a log for all detected problems, which + // occur in the next few lines. + // If there are some real errors throw a RuntimException! + // If there are some warnings only, show an assertion. + sal_Int32 nErrors = 0; + OUStringBuffer sLog(256); + + for (auto const& elem : m_lTypes) + { + const OUString & sType = elem.first; + const CacheItem & aType = elem.second; + + // get its registration for file Extensions AND(!) URLPattern ... + // It doesn't matter if these items exists or if our + // used index access create some default ones ... + // only in case there is no filled set of Extensions AND + // no filled set of URLPattern -> we must try to remove this invalid item + // from this cache! + css::uno::Sequence< OUString > lExtensions; + css::uno::Sequence< OUString > lURLPattern; + auto it = aType.find(PROPNAME_EXTENSIONS); + if (it != aType.end()) + it->second >>= lExtensions; + it = aType.find(PROPNAME_URLPATTERN); + if (it != aType.end()) + it->second >>= lURLPattern; + sal_Int32 ce = lExtensions.getLength(); + sal_Int32 cu = lURLPattern.getLength(); + +#if OSL_DEBUG_LEVEL > 0 + + OUString sInternalTypeNameCheck; + it = aType.find(PROPNAME_NAME); + if (it != aType.end()) + it->second >>= sInternalTypeNameCheck; + if (sInternalTypeNameCheck != sType) + { + sLog.append("Warning\t:\t" "The type \"" + sType + "\" does support the property \"Name\" correctly.\n"); + ++nWarnings; + } + + if (!ce && !cu) + { + sLog.append("Warning\t:\t" "The type \"" + sType + "\" does not contain any URL pattern nor any extensions.\n"); + ++nWarnings; + } +#endif + + // create an optimized registration for this type to + // its set list of extensions/url pattern. If it's a "normal" type + // set it at the end of this optimized list. But if it's + // a "Preferred" one - set it to the front of this list. + // Of course multiple "Preferred" registrations can occur + // (they shouldn't - but they can!) ... Ignore it. The last + // preferred type is usable in the same manner then every + // other type! + bool bPreferred = false; + it = aType.find(PROPNAME_PREFERRED); + if (it != aType.end()) + it->second >>= bPreferred; + + const OUString* pExtensions = lExtensions.getConstArray(); + for (sal_Int32 e=0; e<ce; ++e) + { + // Note: We must be sure that address the right hash entry + // does not depend from any upper/lower case problems ... + OUString sNormalizedExtension = pExtensions[e].toAsciiLowerCase(); + + std::vector<OUString>& lTypesForExtension = m_lExtensions2Types[sNormalizedExtension]; + if (::std::find(lTypesForExtension.begin(), lTypesForExtension.end(), sType) != lTypesForExtension.end()) + continue; + + if (bPreferred) + lTypesForExtension.insert(lTypesForExtension.begin(), sType); + else + lTypesForExtension.push_back(sType); + } + + const OUString* pURLPattern = lURLPattern.getConstArray(); + for (sal_Int32 u=0; u<cu; ++u) + { + std::vector<OUString>& lTypesForURLPattern = m_lURLPattern2Types[pURLPattern[u]]; + if (::std::find(lTypesForURLPattern.begin(), lTypesForURLPattern.end(), sType) != lTypesForURLPattern.end()) + continue; + + if (bPreferred) + lTypesForURLPattern.insert(lTypesForURLPattern.begin(), sType); + else + lTypesForURLPattern.push_back(sType); + } + +#if OSL_DEBUG_LEVEL > 0 + + // Don't check cross references between types and filters, if + // not all filters read from disk! + // OK - this cache can read single filters on demand too ... + // but then the fill state of this cache should not be set to E_CONTAINS_FILTERS! + if (!bAllFiltersShouldExist) + continue; + + OUString sPrefFilter; + it = aType.find(PROPNAME_PREFERREDFILTER); + if (it != aType.end()) + it->second >>= sPrefFilter; + if (sPrefFilter.isEmpty()) + { + // OK - there is no filter for this type. But that's not an error. + // Maybe it can be handled by a ContentHandler... + // But at this time it's not guaranteed that there is any ContentHandler + // or FrameLoader inside this cache... but on disk... + bool bReferencedByLoader = true; + bool bReferencedByHandler = true; + if (bAllLoadersShouldExist) + bReferencedByLoader = !impl_searchFrameLoaderForType(sType).isEmpty(); + + if (bAllHandlersShouldExist) + bReferencedByHandler = !impl_searchContentHandlerForType(sType).isEmpty(); + + if ( + (!bReferencedByLoader ) && + (!bReferencedByHandler) + ) + { + sLog.append("Warning\t:\t" "The type \"" + sType + "\" is not used by any filter, loader or content handler.\n"); + ++nWarnings; + } + } + + if (!sPrefFilter.isEmpty()) + { + CacheItemList::const_iterator pIt2 = m_lFilters.find(sPrefFilter); + if (pIt2 == m_lFilters.end()) + { + if (bAllFiltersShouldExist) + { + ++nWarnings; // preferred filters can point to a non-installed office module ! no error ... it's a warning only .-( + sLog.append("error\t:\t"); + } + else + { + ++nWarnings; + sLog.append("warning\t:\t"); + } + + sLog.append("The type \"" + sType + "\" points to an invalid filter \"" + sPrefFilter + "\".\n"); + continue; + } + + CacheItem aPrefFilter = pIt2->second; + OUString sFilterTypeReg; + aPrefFilter[PROPNAME_TYPE] >>= sFilterTypeReg; + if (sFilterTypeReg != sType) + { + sLog.append("error\t:\t" "The preferred filter \"" + + sPrefFilter + "\" of type \"" + sType + + "\" is registered for another type \"" + sFilterTypeReg + + "\".\n"); + ++nErrors; + } + + sal_Int32 nFlags = 0; + aPrefFilter[PROPNAME_FLAGS] >>= nFlags; + if (!(static_cast<SfxFilterFlags>(nFlags) & SfxFilterFlags::IMPORT)) + { + sLog.append("error\t:\t" "The preferred filter \"" + sPrefFilter + "\" of type \"" + + sType + "\" is not an IMPORT filter!\n"); + ++nErrors; + } + + OUString sInternalFilterNameCheck; + aPrefFilter[PROPNAME_NAME] >>= sInternalFilterNameCheck; + if (sInternalFilterNameCheck != sPrefFilter) + { + sLog.append("Warning\t:\t" "The filter \"" + sPrefFilter + + "\" does support the property \"Name\" correctly.\n"); + ++nWarnings; + } + } +#endif + } + + // create dependencies between the global default frame loader + // and all types (and of course if registered filters), which + // does not registered for any other loader. + css::uno::Any aDirectValue = impl_getDirectCFGValue(CFGDIRECTKEY_DEFAULTFRAMELOADER); + OUString sDefaultFrameLoader; + + if ( + (!(aDirectValue >>= sDefaultFrameLoader)) || + (sDefaultFrameLoader.isEmpty() ) + ) + { + sLog.append("error\t:\t" "There is no valid default frame loader!?\n"); + ++nErrors; + } + + // a) get list of all well known types + // b) step over all well known frame loader services + // and remove all types from list a), which already + // referenced by a loader b) + std::vector<OUString> lTypes = getItemNames(E_TYPE); + for (auto & frameLoader : m_lFrameLoaders) + { + // Note: of course the default loader must be ignored here. + // Because we replace its registration later completely with all + // types, which are not referenced by any other loader. + // So we can avoid our code against the complexity of a diff! + OUString sLoader = frameLoader.first; + if (sLoader == sDefaultFrameLoader) + continue; + + CacheItem& rLoader = frameLoader.second; + css::uno::Any& rTypesReg = rLoader[PROPNAME_TYPES]; + const css::uno::Sequence<OUString> lTypesReg = rTypesReg.get<css::uno::Sequence<OUString> >(); + + for (auto const& typeReg : lTypesReg) + { + auto pTypeCheck = ::std::find(lTypes.begin(), lTypes.end(), typeReg); + if (pTypeCheck != lTypes.end()) + lTypes.erase(pTypeCheck); + } + } + + CacheItem& rDefaultLoader = m_lFrameLoaders[sDefaultFrameLoader]; + rDefaultLoader[PROPNAME_NAME ] <<= sDefaultFrameLoader; + rDefaultLoader[PROPNAME_TYPES] <<= comphelper::containerToSequence(lTypes); + + OUString sLogOut = sLog.makeStringAndClear(); + OSL_ENSURE(!nErrors, OUStringToOString(sLogOut,RTL_TEXTENCODING_UTF8).getStr()); + if (nErrors>0) + throw css::document::CorruptedFilterConfigurationException( + "filter configuration: " + sLogOut, + css::uno::Reference< css::uno::XInterface >(), + sLogOut); +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE(!nWarnings, OUStringToOString(sLogOut,RTL_TEXTENCODING_UTF8).getStr()); +#endif + + // <- SAFE +} + +void FilterCache::impl_addItem2FlushList( EItemType eType, + const OUString& sItem) +{ + std::vector<OUString>* pList = nullptr; + switch(eType) + { + case E_TYPE : + pList = &m_lChangedTypes; + break; + + case E_FILTER : + pList = &m_lChangedFilters; + break; + + case E_FRAMELOADER : + pList = &m_lChangedFrameLoaders; + break; + + case E_CONTENTHANDLER : + pList = &m_lChangedContentHandlers; + break; + + default : throw css::uno::RuntimeException("unsupported item type", nullptr); + } + + auto pItem = ::std::find(pList->cbegin(), pList->cend(), sItem); + if (pItem == pList->cend()) + pList->push_back(sItem); +} + +FilterCache::EItemFlushState FilterCache::impl_specifyFlushOperation(const css::uno::Reference< css::container::XNameAccess >& xSet , + const CacheItemList& rList, + const OUString& sItem) +{ + bool bExistsInConfigLayer = xSet->hasByName(sItem); + bool bExistsInMemory = (rList.find(sItem) != rList.end()); + + EItemFlushState eState( E_ITEM_UNCHANGED ); + + // !? ... such situation can occur, if an item was added and(!) removed before it was flushed :-) + if (!bExistsInConfigLayer && !bExistsInMemory) + eState = E_ITEM_UNCHANGED; + else if (!bExistsInConfigLayer && bExistsInMemory) + eState = E_ITEM_ADDED; + else if (bExistsInConfigLayer && bExistsInMemory) + eState = E_ITEM_CHANGED; + else if (bExistsInConfigLayer && !bExistsInMemory) + eState = E_ITEM_REMOVED; + + return eState; +} + +void FilterCache::impl_load(EFillState eRequiredState) +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + // Attention: Detect services are part of the standard set! + // So there is no need to handle it separately. + + + // a) The standard set of config value is needed. + if ( + ((eRequiredState & E_CONTAINS_STANDARD) == E_CONTAINS_STANDARD) && + ((m_eFillState & E_CONTAINS_STANDARD) != E_CONTAINS_STANDARD) + ) + { + // Attention! If config couldn't be opened successfully + // and exception is thrown automatically and must be forwarded + // to our caller... + css::uno::Reference< css::container::XNameAccess > xTypes(impl_openConfig(E_PROVIDER_TYPES), css::uno::UNO_QUERY_THROW); + { + SAL_INFO( "filter.config", "FilterCache::load std"); + impl_loadSet(xTypes, E_TYPE, E_READ_STANDARD, &m_lTypes); + } + } + + + // b) We need all type information ... + if ( + ((eRequiredState & E_CONTAINS_TYPES) == E_CONTAINS_TYPES) && + ((m_eFillState & E_CONTAINS_TYPES) != E_CONTAINS_TYPES) + ) + { + // Attention! If config couldn't be opened successfully + // and exception is thrown automatically and must be forwarded + // to our call... + css::uno::Reference< css::container::XNameAccess > xTypes(impl_openConfig(E_PROVIDER_TYPES), css::uno::UNO_QUERY_THROW); + { + SAL_INFO( "filter.config", "FilterCache::load all types"); + impl_loadSet(xTypes, E_TYPE, E_READ_UPDATE, &m_lTypes); + } + } + + + // c) We need all filter information ... + if ( + ((eRequiredState & E_CONTAINS_FILTERS) == E_CONTAINS_FILTERS) && + ((m_eFillState & E_CONTAINS_FILTERS) != E_CONTAINS_FILTERS) + ) + { + // Attention! If config couldn't be opened successfully + // and exception is thrown automatically and must be forwarded + // to our call... + css::uno::Reference< css::container::XNameAccess > xFilters(impl_openConfig(E_PROVIDER_FILTERS), css::uno::UNO_QUERY_THROW); + { + SAL_INFO( "filter.config", "FilterCache::load all filters"); + impl_loadSet(xFilters, E_FILTER, E_READ_ALL, &m_lFilters); + } + } + + + // c) We need all frame loader information ... + if ( + ((eRequiredState & E_CONTAINS_FRAMELOADERS) == E_CONTAINS_FRAMELOADERS) && + ((m_eFillState & E_CONTAINS_FRAMELOADERS) != E_CONTAINS_FRAMELOADERS) + ) + { + // Attention! If config couldn't be opened successfully + // and exception is thrown automatically and must be forwarded + // to our call... + css::uno::Reference< css::container::XNameAccess > xLoaders(impl_openConfig(E_PROVIDER_OTHERS), css::uno::UNO_QUERY_THROW); + { + SAL_INFO( "filter.config", "FilterCache::load all frame loader"); + impl_loadSet(xLoaders, E_FRAMELOADER, E_READ_ALL, &m_lFrameLoaders); + } + } + + + // d) We need all content handler information... + if ( + ((eRequiredState & E_CONTAINS_CONTENTHANDLERS) == E_CONTAINS_CONTENTHANDLERS) && + ((m_eFillState & E_CONTAINS_CONTENTHANDLERS) != E_CONTAINS_CONTENTHANDLERS) + ) + { + // Attention! If config couldn't be opened successfully + // and exception is thrown automatically and must be forwarded + // to our call... + css::uno::Reference< css::container::XNameAccess > xHandlers(impl_openConfig(E_PROVIDER_OTHERS), css::uno::UNO_QUERY_THROW); + { + SAL_INFO( "filter.config", "FilterCache::load all content handler"); + impl_loadSet(xHandlers, E_CONTENTHANDLER, E_READ_ALL, &m_lContentHandlers); + } + } + + // update fill state. Note: it's a bit field, which combines different parts. + m_eFillState = static_cast<EFillState>(static_cast<sal_Int32>(m_eFillState) | static_cast<sal_Int32>(eRequiredState)); + + // any data read? + // yes! => validate it and update optimized structures. + impl_validateAndOptimize(); + + // <- SAFE +} + +void FilterCache::impl_loadSet(const css::uno::Reference< css::container::XNameAccess >& xConfig, + EItemType eType , + EReadOption eOption, + CacheItemList* pCache ) +{ + // get access to the right configuration set + OUString sSetName; + switch(eType) + { + case E_TYPE : + sSetName = CFGSET_TYPES; + break; + + case E_FILTER : + sSetName = CFGSET_FILTERS; + break; + + case E_FRAMELOADER : + sSetName = CFGSET_FRAMELOADERS; + break; + + case E_CONTENTHANDLER : + sSetName = CFGSET_CONTENTHANDLERS; + break; + default: break; + } + + css::uno::Reference< css::container::XNameAccess > xSet; + css::uno::Sequence< OUString > lItems; + + try + { + css::uno::Any aVal = xConfig->getByName(sSetName); + if (!(aVal >>= xSet) || !xSet.is()) + { + OUString sMsg("Could not open configuration set \"" + sSetName + "\"."); + throw css::uno::Exception(sMsg, css::uno::Reference< css::uno::XInterface >()); + } + lItems = xSet->getElementNames(); + } + catch(const css::uno::Exception& ex) + { + throw css::document::CorruptedFilterConfigurationException( + "filter configuration, caught: " + ex.Message, + css::uno::Reference< css::uno::XInterface >(), + ex.Message); + } + + // get names of all existing sub items of this set + // step over it and fill internal cache structures. + + // But don't update optimized structures like e.g. hash + // for mapping extensions to its types! + + const OUString* pItems = lItems.getConstArray(); + sal_Int32 c = lItems.getLength(); + for (sal_Int32 i=0; i<c; ++i) + { + CacheItemList::iterator pItem = pCache->find(pItems[i]); + switch(eOption) + { + // a) read a standard set of properties only or read all + case E_READ_STANDARD : + case E_READ_ALL : + { + try + { + (*pCache)[pItems[i]] = impl_loadItem(xSet, eType, pItems[i], eOption); + } + catch(const css::uno::Exception& ex) + { + throw css::document::CorruptedFilterConfigurationException( + "filter configuration, caught: " + ex.Message, + css::uno::Reference< css::uno::XInterface >(), + ex.Message); + } + } + break; + + // b) read optional properties only! + // All items must already exist inside our cache. + // But they must be updated. + case E_READ_UPDATE : + { + if (pItem == pCache->end()) + { + OUString sMsg("item \"" + pItems[i] + "\" not found for update!"); + throw css::uno::Exception(sMsg, css::uno::Reference< css::uno::XInterface >()); + } + try + { + CacheItem aItem = impl_loadItem(xSet, eType, pItems[i], eOption); + pItem->second.update(aItem); + } + catch(const css::uno::Exception& ex) + { + throw css::document::CorruptedFilterConfigurationException( + "filter configuration, caught: " + ex.Message, + css::uno::Reference< css::uno::XInterface >(), + ex.Message); + } + } + break; + default: break; + } + } +} + +void FilterCache::impl_readPatchUINames(const css::uno::Reference< css::container::XNameAccess >& xNode, + CacheItem& rItem) +{ + + // SAFE -> ---------------------------------- + osl::ClearableMutexGuard aLock(m_aMutex); + OUString sActLocale = m_sActLocale ; + aLock.clear(); + // <- SAFE ---------------------------------- + + css::uno::Any aVal = xNode->getByName(PROPNAME_UINAME); + css::uno::Reference< css::container::XNameAccess > xUIName; + if (!(aVal >>= xUIName) && !xUIName.is()) + return; + + const ::std::vector< OUString > lLocales(comphelper::sequenceToContainer< ::std::vector< OUString >>( + xUIName->getElementNames())); + ::std::vector< OUString >::const_iterator pLocale ; + ::comphelper::SequenceAsHashMap lUINames; + + for (auto const& locale : lLocales) + { + OUString sValue; + xUIName->getByName(locale) >>= sValue; + + lUINames[locale] <<= sValue; + } + + aVal <<= lUINames.getAsConstPropertyValueList(); + rItem[PROPNAME_UINAMES] = aVal; + + // find right UIName for current office locale + // Use fallbacks too! + pLocale = LanguageTag::getFallback(lLocales, sActLocale); + if (pLocale == lLocales.end()) + { +#if OSL_DEBUG_LEVEL > 0 + if ( sActLocale == "en-US" ) + return; + OUString sName = rItem.getUnpackedValueOrDefault(PROPNAME_NAME, OUString()); + + SAL_WARN("filter.config", "Fallback scenario for filter or type '" << sName << "' and locale '" << + sActLocale << "' failed. Please check your filter configuration."); +#endif + return; + } + + const OUString& sLocale = *pLocale; + ::comphelper::SequenceAsHashMap::const_iterator pUIName = lUINames.find(sLocale); + if (pUIName != lUINames.end()) + rItem[PROPNAME_UINAME] = pUIName->second; +} + +void FilterCache::impl_savePatchUINames(const css::uno::Reference< css::container::XNameReplace >& xNode, + const CacheItem& rItem) +{ + css::uno::Reference< css::container::XNameContainer > xAdd (xNode, css::uno::UNO_QUERY); + + css::uno::Sequence< css::beans::PropertyValue > lUINames = rItem.getUnpackedValueOrDefault(PROPNAME_UINAMES, css::uno::Sequence< css::beans::PropertyValue >()); + sal_Int32 c = lUINames.getLength(); + const css::beans::PropertyValue* pUINames = lUINames.getConstArray(); + + for (sal_Int32 i=0; i<c; ++i) + { + if (xNode->hasByName(pUINames[i].Name)) + xNode->replaceByName(pUINames[i].Name, pUINames[i].Value); + else + xAdd->insertByName(pUINames[i].Name, pUINames[i].Value); + } +} + +/*----------------------------------------------- + TODO + clarify, how the real problem behind the + wrong constructed CacheItem instance (which + will force a crash during destruction) + can be solved ... +-----------------------------------------------*/ +CacheItem FilterCache::impl_loadItem(const css::uno::Reference< css::container::XNameAccess >& xSet , + EItemType eType , + const OUString& sItem , + EReadOption eOption) +{ + // try to get an API object, which points directly to the + // requested item. If it fail an exception should occur and + // break this operation. Of course returned API object must be + // checked too. + css::uno::Reference< css::container::XNameAccess > xItem; + css::uno::Any aVal = xSet->getByName(sItem); + if (!(aVal >>= xItem) || !xItem.is()) + { + throw css::uno::RuntimeException("found corrupted item \"" + sItem + "\".", + css::uno::Reference< css::uno::XInterface >()); + } + + // set too. Of course it's already used as key into the e.g. outside + // used hash map... but some of our API methods provide + // this property set as result only. But the user of this CacheItem + // should know, which value the key names has :-) IT'S IMPORTANT! + CacheItem aItem; + aItem[PROPNAME_NAME] <<= sItem; + switch(eType) + { + case E_TYPE : + { + assert(eOption >= 0 && eOption <= E_READ_ALL); + css::uno::Sequence< OUString > &rNames = m_aTypeProps[eOption]; + + // read standard properties of a filter + if (rNames.hasElements()) + { + css::uno::Reference< css::beans::XMultiPropertySet > + xPropSet( xItem, css::uno::UNO_QUERY_THROW); + css::uno::Sequence< css::uno::Any > aValues = xPropSet->getPropertyValues(rNames); + + for (sal_Int32 i = 0; i < aValues.getLength(); i++) + aItem[rNames[i]] = aValues[i]; + } + + // read optional properties of a type + // no else here! Is an additional switch ... + if (eOption == E_READ_UPDATE || eOption == E_READ_ALL) + impl_readPatchUINames(xItem, aItem); + } + break; + + + case E_FILTER : + { + assert(eOption >= 0 && eOption <= E_READ_ALL); + css::uno::Sequence< OUString > &rNames = m_aStandardProps[eOption]; + + // read standard properties of a filter + if (rNames.hasElements()) + { + css::uno::Reference< css::beans::XMultiPropertySet > + xPropSet( xItem, css::uno::UNO_QUERY_THROW); + css::uno::Sequence< css::uno::Any > aValues = xPropSet->getPropertyValues(rNames); + + for (sal_Int32 i = 0; i < rNames.getLength(); i++) + { + const OUString &rPropName = rNames[i]; + if (i != rNames.getLength() - 1 || rPropName != PROPNAME_FLAGS) + aItem[rPropName] = aValues[i]; + else + { + assert(rPropName == PROPNAME_FLAGS); + // special handling for flags! Convert it from a list of names to its + // int representation ... + css::uno::Sequence< OUString > lFlagNames; + if (aValues[i] >>= lFlagNames) + aItem[rPropName] <<= static_cast<sal_Int32>(FilterCache::impl_convertFlagNames2FlagField(lFlagNames)); + } + } + } +//TODO remove it if moving of filter uinames to type uinames +// will be finished really +#ifdef AS_ENABLE_FILTER_UINAMES + if (eOption == E_READ_UPDATE || eOption == E_READ_ALL) + impl_readPatchUINames(xItem, aItem); +#endif // AS_ENABLE_FILTER_UINAMES + } + break; + + case E_FRAMELOADER : + case E_CONTENTHANDLER : + aItem[PROPNAME_TYPES] = xItem->getByName(PROPNAME_TYPES); + break; + default: break; + } + + return aItem; +} + +CacheItemList::iterator FilterCache::impl_loadItemOnDemand( EItemType eType, + const OUString& sItem) +{ + CacheItemList* pList = nullptr; + css::uno::Reference< css::uno::XInterface > xConfig ; + OUString sSet ; + + switch(eType) + { + case E_TYPE : + { + pList = &m_lTypes; + xConfig = impl_openConfig(E_PROVIDER_TYPES); + sSet = CFGSET_TYPES; + } + break; + + case E_FILTER : + { + pList = &m_lFilters; + xConfig = impl_openConfig(E_PROVIDER_FILTERS); + sSet = CFGSET_FILTERS; + } + break; + + case E_FRAMELOADER : + { + pList = &m_lFrameLoaders; + xConfig = impl_openConfig(E_PROVIDER_OTHERS); + sSet = CFGSET_FRAMELOADERS; + } + break; + + case E_CONTENTHANDLER : + { + pList = &m_lContentHandlers; + xConfig = impl_openConfig(E_PROVIDER_OTHERS); + sSet = CFGSET_CONTENTHANDLERS; + } + break; + } + + if (!pList) + throw css::container::NoSuchElementException(); + + css::uno::Reference< css::container::XNameAccess > xRoot(xConfig, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XNameAccess > xSet ; + xRoot->getByName(sSet) >>= xSet; + + CacheItemList::iterator pItemInCache = pList->find(sItem); + bool bItemInConfig = xSet->hasByName(sItem); + + if (bItemInConfig) + { + (*pList)[sItem] = impl_loadItem(xSet, eType, sItem, E_READ_ALL); + } + else + { + if (pItemInCache != pList->end()) + pList->erase(pItemInCache); + // OK - this item does not exists inside configuration. + // And we already updated our internal cache. + // But the outside code needs this NoSuchElementException + // to know, that this item does notexists. + // Nobody checks the iterator! + throw css::container::NoSuchElementException(); + } + + return pList->find(sItem); +} + +void FilterCache::impl_saveItem(const css::uno::Reference< css::container::XNameReplace >& xItem, + EItemType eType, + const CacheItem & aItem) +{ + // This function changes the properties of aItem one-by-one; but it also + // listens to the configuration changes and reloads the whole item from the + // configuration on change, so use a copy of aItem throughout: + CacheItem copiedItem(aItem); + + CacheItem::const_iterator pIt; + switch(eType) + { + + case E_TYPE : + { + pIt = copiedItem.find(PROPNAME_PREFERREDFILTER); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_PREFERREDFILTER, pIt->second); + pIt = copiedItem.find(PROPNAME_DETECTSERVICE); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_DETECTSERVICE, pIt->second); + pIt = copiedItem.find(PROPNAME_URLPATTERN); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_URLPATTERN, pIt->second); + pIt = copiedItem.find(PROPNAME_EXTENSIONS); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_EXTENSIONS, pIt->second); + pIt = copiedItem.find(PROPNAME_PREFERRED); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_PREFERRED, pIt->second); + pIt = copiedItem.find(PROPNAME_MEDIATYPE); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_MEDIATYPE, pIt->second); + pIt = copiedItem.find(PROPNAME_CLIPBOARDFORMAT); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_CLIPBOARDFORMAT, pIt->second); + + css::uno::Reference< css::container::XNameReplace > xUIName; + xItem->getByName(PROPNAME_UINAME) >>= xUIName; + impl_savePatchUINames(xUIName, copiedItem); + } + break; + + + case E_FILTER : + { + pIt = copiedItem.find(PROPNAME_TYPE); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_TYPE, pIt->second); + pIt = copiedItem.find(PROPNAME_FILEFORMATVERSION); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_FILEFORMATVERSION, pIt->second); + pIt = copiedItem.find(PROPNAME_UICOMPONENT); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_UICOMPONENT, pIt->second); + pIt = copiedItem.find(PROPNAME_FILTERSERVICE); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_FILTERSERVICE, pIt->second); + pIt = copiedItem.find(PROPNAME_DOCUMENTSERVICE); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_DOCUMENTSERVICE, pIt->second); + pIt = copiedItem.find(PROPNAME_USERDATA); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_USERDATA, pIt->second); + pIt = copiedItem.find(PROPNAME_TEMPLATENAME); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_TEMPLATENAME, pIt->second); + + // special handling for flags! Convert it from an integer flag field back + // to a list of names ... + pIt = copiedItem.find(PROPNAME_FLAGS); + if (pIt != copiedItem.end()) + { + sal_Int32 nFlags = 0; + pIt->second >>= nFlags; + css::uno::Any aFlagNameList; + aFlagNameList <<= FilterCache::impl_convertFlagField2FlagNames(static_cast<SfxFilterFlags>(nFlags)); + xItem->replaceByName(PROPNAME_FLAGS, aFlagNameList); + } + +//TODO remove it if moving of filter uinames to type uinames +// will be finished really +#ifdef AS_ENABLE_FILTER_UINAMES + css::uno::Reference< css::container::XNameReplace > xUIName; + xItem->getByName(PROPNAME_UINAME) >>= xUIName; + impl_savePatchUINames(xUIName, copiedItem); +#endif // AS_ENABLE_FILTER_UINAMES + } + break; + + + case E_FRAMELOADER : + case E_CONTENTHANDLER : + { + pIt = copiedItem.find(PROPNAME_TYPES); + if (pIt != copiedItem.end()) + xItem->replaceByName(PROPNAME_TYPES, pIt->second); + } + break; + default: break; + } +} + +/*----------------------------------------------- + static! => no locks necessary +-----------------------------------------------*/ +css::uno::Sequence< OUString > FilterCache::impl_convertFlagField2FlagNames(SfxFilterFlags nFlags) +{ + std::vector<OUString> lFlagNames; + + if (nFlags & SfxFilterFlags::STARONEFILTER ) lFlagNames.emplace_back(FLAGNAME_3RDPARTYFILTER ); + if (nFlags & SfxFilterFlags::ALIEN ) lFlagNames.emplace_back(FLAGNAME_ALIEN ); + if (nFlags & SfxFilterFlags::CONSULTSERVICE ) lFlagNames.emplace_back(FLAGNAME_CONSULTSERVICE ); + if (nFlags & SfxFilterFlags::DEFAULT ) lFlagNames.emplace_back(FLAGNAME_DEFAULT ); + if (nFlags & SfxFilterFlags::ENCRYPTION ) lFlagNames.emplace_back(FLAGNAME_ENCRYPTION ); + if (nFlags & SfxFilterFlags::EXPORT ) lFlagNames.emplace_back(FLAGNAME_EXPORT ); + if (nFlags & SfxFilterFlags::IMPORT ) lFlagNames.emplace_back(FLAGNAME_IMPORT ); + if (nFlags & SfxFilterFlags::INTERNAL ) lFlagNames.emplace_back(FLAGNAME_INTERNAL ); + if (nFlags & SfxFilterFlags::NOTINFILEDLG ) lFlagNames.emplace_back(FLAGNAME_NOTINFILEDIALOG ); + if (nFlags & SfxFilterFlags::MUSTINSTALL ) lFlagNames.emplace_back(FLAGNAME_NOTINSTALLED ); + if (nFlags & SfxFilterFlags::OWN ) lFlagNames.emplace_back(FLAGNAME_OWN ); + if (nFlags & SfxFilterFlags::PACKED ) lFlagNames.emplace_back(FLAGNAME_PACKED ); + if (nFlags & SfxFilterFlags::PASSWORDTOMODIFY ) lFlagNames.emplace_back(FLAGNAME_PASSWORDTOMODIFY ); + if (nFlags & SfxFilterFlags::PREFERED ) lFlagNames.emplace_back(FLAGNAME_PREFERRED ); + if (nFlags & SfxFilterFlags::STARTPRESENTATION) lFlagNames.emplace_back(FLAGNAME_STARTPRESENTATION); + if (nFlags & SfxFilterFlags::OPENREADONLY ) lFlagNames.emplace_back(FLAGNAME_READONLY ); + if (nFlags & SfxFilterFlags::SUPPORTSSELECTION) lFlagNames.emplace_back(FLAGNAME_SUPPORTSSELECTION); + if (nFlags & SfxFilterFlags::TEMPLATE ) lFlagNames.emplace_back(FLAGNAME_TEMPLATE ); + if (nFlags & SfxFilterFlags::TEMPLATEPATH ) lFlagNames.emplace_back(FLAGNAME_TEMPLATEPATH ); + if (nFlags & SfxFilterFlags::COMBINED ) lFlagNames.emplace_back(FLAGNAME_COMBINED ); + if (nFlags & SfxFilterFlags::SUPPORTSSIGNING) lFlagNames.emplace_back(FLAGNAME_SUPPORTSSIGNING); + if (nFlags & SfxFilterFlags::GPGENCRYPTION) lFlagNames.emplace_back(FLAGNAME_GPGENCRYPTION); + if (nFlags & SfxFilterFlags::EXOTIC) lFlagNames.emplace_back(FLAGNAME_EXOTIC); + + return comphelper::containerToSequence(lFlagNames); +} + +/*----------------------------------------------- + static! => no locks necessary +-----------------------------------------------*/ +SfxFilterFlags FilterCache::impl_convertFlagNames2FlagField(const css::uno::Sequence< OUString >& lNames) +{ + SfxFilterFlags nField = SfxFilterFlags::NONE; + + const OUString* pNames = lNames.getConstArray(); + sal_Int32 c = lNames.getLength(); + for (sal_Int32 i=0; i<c; ++i) + { + if (pNames[i] == FLAGNAME_3RDPARTYFILTER) + { + nField |= SfxFilterFlags::STARONEFILTER; + continue; + } + if (pNames[i] == FLAGNAME_ALIEN) + { + nField |= SfxFilterFlags::ALIEN; + continue; + } + if (pNames[i] == FLAGNAME_CONSULTSERVICE) + { + nField |= SfxFilterFlags::CONSULTSERVICE; + continue; + } + if (pNames[i] == FLAGNAME_DEFAULT) + { + nField |= SfxFilterFlags::DEFAULT; + continue; + } + if (pNames[i] == FLAGNAME_ENCRYPTION) + { + nField |= SfxFilterFlags::ENCRYPTION; + continue; + } + if (pNames[i] == FLAGNAME_EXOTIC) + { + nField |= SfxFilterFlags::EXOTIC; + continue; + } + if (pNames[i] == FLAGNAME_EXPORT) + { + nField |= SfxFilterFlags::EXPORT; + continue; + } + if (pNames[i] == FLAGNAME_GPGENCRYPTION) + { + nField |= SfxFilterFlags::GPGENCRYPTION; + continue; + } + if (pNames[i] == FLAGNAME_IMPORT) + { + nField |= SfxFilterFlags::IMPORT; + continue; + } + if (pNames[i] == FLAGNAME_INTERNAL) + { + nField |= SfxFilterFlags::INTERNAL; + continue; + } + if (pNames[i] == FLAGNAME_NOTINFILEDIALOG) + { + nField |= SfxFilterFlags::NOTINFILEDLG; + continue; + } + if (pNames[i] == FLAGNAME_NOTINSTALLED) + { + nField |= SfxFilterFlags::MUSTINSTALL; + continue; + } + if (pNames[i] == FLAGNAME_OWN) + { + nField |= SfxFilterFlags::OWN; + continue; + } + if (pNames[i] == FLAGNAME_PACKED) + { + nField |= SfxFilterFlags::PACKED; + continue; + } + if (pNames[i] == FLAGNAME_PASSWORDTOMODIFY) + { + nField |= SfxFilterFlags::PASSWORDTOMODIFY; + continue; + } + if (pNames[i] == FLAGNAME_PREFERRED) + { + nField |= SfxFilterFlags::PREFERED; + continue; + } + if (pNames[i] == FLAGNAME_STARTPRESENTATION) + { + nField |= SfxFilterFlags::STARTPRESENTATION; + continue; + } + if (pNames[i] == FLAGNAME_SUPPORTSSIGNING) + { + nField |= SfxFilterFlags::SUPPORTSSIGNING; + continue; + } + if (pNames[i] == FLAGNAME_READONLY) + { + nField |= SfxFilterFlags::OPENREADONLY; + continue; + } + if (pNames[i] == FLAGNAME_SUPPORTSSELECTION) + { + nField |= SfxFilterFlags::SUPPORTSSELECTION; + continue; + } + if (pNames[i] == FLAGNAME_TEMPLATE) + { + nField |= SfxFilterFlags::TEMPLATE; + continue; + } + if (pNames[i] == FLAGNAME_TEMPLATEPATH) + { + nField |= SfxFilterFlags::TEMPLATEPATH; + continue; + } + if (pNames[i] == FLAGNAME_COMBINED) + { + nField |= SfxFilterFlags::COMBINED; + continue; + } + } + + return nField; +} + + +void FilterCache::impl_interpretDataVal4Type(const OUString& sValue, + sal_Int32 nProp , + CacheItem& rItem ) +{ + switch(nProp) + { + // Preferred + case 0: rItem[PROPNAME_PREFERRED] <<= (sValue.toInt32() == 1); + break; + // MediaType + case 1: rItem[PROPNAME_MEDIATYPE] <<= ::rtl::Uri::decode(sValue, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8); + break; + // ClipboardFormat + case 2: rItem[PROPNAME_CLIPBOARDFORMAT] <<= ::rtl::Uri::decode(sValue, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8); + break; + // URLPattern + case 3: rItem[PROPNAME_URLPATTERN] <<= comphelper::containerToSequence(impl_tokenizeString(sValue, ';')); + break; + // Extensions + case 4: rItem[PROPNAME_EXTENSIONS] <<= comphelper::containerToSequence(impl_tokenizeString(sValue, ';')); + break; + } +} + + +void FilterCache::impl_interpretDataVal4Filter(const OUString& sValue, + sal_Int32 nProp , + CacheItem& rItem ) +{ + switch(nProp) + { + // Order + case 0: { + sal_Int32 nOrder = sValue.toInt32(); + if (nOrder > 0) + { + SAL_WARN( "filter.config", "FilterCache::impl_interpretDataVal4Filter()\nCan not move Order value from filter to type on demand!"); + } + } + break; + // Type + case 1: rItem[PROPNAME_TYPE] <<= ::rtl::Uri::decode(sValue, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8); + break; + // DocumentService + case 2: rItem[PROPNAME_DOCUMENTSERVICE] <<= ::rtl::Uri::decode(sValue, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8); + break; + // FilterService + case 3: rItem[PROPNAME_FILTERSERVICE] <<= ::rtl::Uri::decode(sValue, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8); + break; + // Flags + case 4: rItem[PROPNAME_FLAGS] <<= sValue.toInt32(); + break; + // UserData + case 5: rItem[PROPNAME_USERDATA] <<= comphelper::containerToSequence(impl_tokenizeString(sValue, ';')); + break; + // FileFormatVersion + case 6: rItem[PROPNAME_FILEFORMATVERSION] <<= sValue.toInt32(); + break; + // TemplateName + case 7: rItem[PROPNAME_TEMPLATENAME] <<= ::rtl::Uri::decode(sValue, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8); + break; + // [optional!] UIComponent + case 8: rItem[PROPNAME_UICOMPONENT] <<= ::rtl::Uri::decode(sValue, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8); + break; + } +} + +/*----------------------------------------------- + TODO work on a cache copy first, which can be flushed afterwards + That would be useful to guarantee a consistent cache. +-----------------------------------------------*/ +void FilterCache::impl_readOldFormat() +{ + // Attention: Opening/Reading of this old configuration format has to be handled gracefully. + // It's optional and should not disturb our normal work! + // E.g. we must check, if the package exists... + try + { + css::uno::Reference< css::uno::XInterface > xInt = impl_openConfig(E_PROVIDER_OLD); + css::uno::Reference< css::container::XNameAccess > xCfg(xInt, css::uno::UNO_QUERY_THROW); + + OUString TYPES_SET("Types"); + + // May be there is no type set ... + if (xCfg->hasByName(TYPES_SET)) + { + css::uno::Reference< css::container::XNameAccess > xSet; + xCfg->getByName(TYPES_SET) >>= xSet; + const css::uno::Sequence< OUString > lItems = xSet->getElementNames(); + for (const OUString& rName : lItems) + m_lTypes[rName] = impl_readOldItem(xSet, E_TYPE, rName); + } + + OUString FILTER_SET("Filters"); + // May be there is no filter set ... + if (xCfg->hasByName(FILTER_SET)) + { + css::uno::Reference< css::container::XNameAccess > xSet; + xCfg->getByName(FILTER_SET) >>= xSet; + const css::uno::Sequence< OUString > lItems = xSet->getElementNames(); + for (const OUString& rName : lItems) + m_lFilters[rName] = impl_readOldItem(xSet, E_FILTER, rName); + } + } + /* corrupt filter addon? Because it's external (optional) code... we can ignore it. Addon won't work then... + but that seems to be acceptable. + see #139088# for further information + */ + catch(const css::uno::Exception&) + { + } +} + +CacheItem FilterCache::impl_readOldItem(const css::uno::Reference< css::container::XNameAccess >& xSet , + EItemType eType, + const OUString& sItem) +{ + css::uno::Reference< css::container::XNameAccess > xItem; + xSet->getByName(sItem) >>= xItem; + if (!xItem.is()) + throw css::uno::Exception("Can not read old item.", css::uno::Reference< css::uno::XInterface >()); + + CacheItem aItem; + aItem[PROPNAME_NAME] <<= sItem; + + // Installed flag ... + // Isn't used any longer! + + // UIName + impl_readPatchUINames(xItem, aItem); + + // Data + OUString sData; + std::vector<OUString> lData; + xItem->getByName( "Data" ) >>= sData; + lData = impl_tokenizeString(sData, ','); + if ( + (sData.isEmpty()) || + (lData.empty() ) + ) + { + throw css::uno::Exception( "Can not read old item property DATA.", css::uno::Reference< css::uno::XInterface >()); + } + + sal_Int32 nProp = 0; + for (auto const& prop : lData) + { + switch(eType) + { + case E_TYPE : + impl_interpretDataVal4Type(prop, nProp, aItem); + break; + + case E_FILTER : + impl_interpretDataVal4Filter(prop, nProp, aItem); + break; + default: break; + } + ++nProp; + } + + return aItem; +} + + +std::vector<OUString> FilterCache::impl_tokenizeString(std::u16string_view sData , + sal_Unicode cSeparator) +{ + std::vector<OUString> lData ; + sal_Int32 nToken = 0; + do + { + OUString sToken( o3tl::getToken(sData, 0, cSeparator, nToken) ); + lData.push_back(sToken); + } + while(nToken >= 0); + return lData; +} + +#if OSL_DEBUG_LEVEL > 0 + + +OUString FilterCache::impl_searchFrameLoaderForType(const OUString& sType) const +{ + for (auto const& frameLoader : m_lFrameLoaders) + { + const OUString& sItem = frameLoader.first; + ::comphelper::SequenceAsHashMap lProps(frameLoader.second); + const css::uno::Sequence<OUString> lTypes = + lProps[PROPNAME_TYPES].get<css::uno::Sequence<OUString> >(); + + if (::std::find(lTypes.begin(), lTypes.end(), sType) != lTypes.end()) + return sItem; + } + + return OUString(); +} + + +OUString FilterCache::impl_searchContentHandlerForType(const OUString& sType) const +{ + for (auto const& contentHandler : m_lContentHandlers) + { + const OUString& sItem = contentHandler.first; + ::comphelper::SequenceAsHashMap lProps(contentHandler.second); + const css::uno::Sequence<OUString> lTypes = + lProps[PROPNAME_TYPES].get<css::uno::Sequence<OUString> >(); + if (::std::find(lTypes.begin(), lTypes.end(), sType) != lTypes.end()) + return sItem; + } + + return OUString(); +} +#endif + + +bool FilterCache::impl_isModuleInstalled(const OUString& sModule) +{ + css::uno::Reference< css::container::XNameAccess > xCfg; + + // SAFE -> + { + osl::MutexGuard aLock(m_aMutex); + if (!m_xModuleCfg.is()) + { + m_xModuleCfg = officecfg::Setup::Office::Factories::get(); + } + + xCfg = m_xModuleCfg; + } + // <- SAFE + + if (xCfg.is()) + return xCfg->hasByName(sModule); + + return false; +} + +} // namespace filter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/filtercache.hxx b/filter/source/config/cache/filtercache.hxx new file mode 100644 index 000000000..2f647c33e --- /dev/null +++ b/filter/source/config/cache/filtercache.hxx @@ -0,0 +1,939 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> + +#include "cacheitem.hxx" +#include <com/sun/star/util/URL.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameReplace.hpp> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/uno/Any.h> +#include <comphelper/documentconstants.hxx> +#include <cppuhelper/basemutex.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> + + +namespace filter::config { + +class CacheUpdateListener; + + +/** @short implements a cache, which contains all + elements of our filter and type detection + configuration. + + @descr The cache itself is threadsafe implemented. + Because it should be used as a singleton only. + To do so please use reference mechanism as wrapper + around this FilterCache class. + + @attention Because we use a trick to get a full initialized + mutex lock during initialization time (means during + the constructor runs), the base class FilterCacheLock + must be the first of all declared one! + Further we make it public. So any user of this class + can lock us from outside too. + */ +class FilterCache : public cppu::BaseMutex +{ + + // public types + + public: + + + /** @short identify the type of a container item. + + @descr Because the cache interface is a generic one + every group of container items must be specified. + */ + enum EItemType + { + E_TYPE , + E_FILTER , + E_FRAMELOADER , + E_CONTENTHANDLER + }; + + + /** @short indicates, which items already exists inside this cache + and which not. + + @descr This cache supports a 2-step load mechanism. + First only types (and only some special properties of every type!) + but no filters/frame loaders/content handlers will be read. + That should be enough to work with this cache e.g. for loading + the first document. After this first document was loaded successfully, + a special "load-on-demand-thread" will be started to fill this cache + with ALL other information, which was not read before. + That's the second step. All operations on top of this cache will be + blocked then. + */ + enum EFillState + { + E_CONTAINS_NOTHING = 0, + E_CONTAINS_STANDARD = 1, + E_CONTAINS_TYPES = 2, + E_CONTAINS_FILTERS = 4, + E_CONTAINS_FRAMELOADERS = 8, + E_CONTAINS_CONTENTHANDLERS = 16 + }; + + + // private types + + private: + + + /** @short regulate, which properties of a configured item + will be read. + + @descr To perform reading of all configuration items, + only standard properties will be handled. At a second + step all optional properties will be read and added to + our internal structures. Of course the combination of + both options can be used too, to get all properties + at the same time. + */ + enum EReadOption + { + E_READ_STANDARD = 1, + E_READ_UPDATE = 2, + E_READ_ALL = 3 + }; + + + /** @short indicates the state of a configuration set item. + + @descr Inside method flush we check: + <ul> + <li>if the item exists inside config layer but not inside our cache => REMOVED</li> + <li>if the item exists inside config layer and inside our cache => CHANGED</li> + <li>if the item does not exists inside config layer but inside our cache => ADDED.</li> + </ul> + */ + enum EItemFlushState + { + /// indicates an unchanged item (can occur e.g. if an item was added and(!) removed before it was flushed ... + E_ITEM_UNCHANGED = 0, + /// indicates an item, which exists inside config layer but not inside our own cache + E_ITEM_REMOVED = 1, + /// indicates an item, which exists inside config layer and inside our own cache + E_ITEM_CHANGED = 2, + /// indicates an item, which does not exists inside config layer but inside our own cache + E_ITEM_ADDED = 3 + }; + + + /** TODO document me */ + enum EConfigProvider + { + E_PROVIDER_TYPES = 0, + E_PROVIDER_FILTERS = 1, + E_PROVIDER_OTHERS = 2, + E_PROVIDER_OLD = 3 + }; + + + // member + + private: + + + /** @short holds the used configuration provider alive, which + provides access to the list of types. */ + mutable css::uno::Reference< css::uno::XInterface > m_xConfigTypes; + + + /** @short holds the used configuration provider alive, which + provides access to the list of filters. */ + mutable css::uno::Reference< css::uno::XInterface > m_xConfigFilters; + + + /** @short holds the used configuration provider alive, which + provides access to the list of other values needed + by our type detection framework. */ + mutable css::uno::Reference< css::uno::XInterface > m_xConfigOthers; + + + /** @short contains all loaded types with its properties. */ + mutable CacheItemList m_lTypes; + + + /** @short contains all loaded filters with its properties. */ + mutable CacheItemList m_lFilters; + + + /** @short contains all loaded frame loader with its properties. */ + mutable CacheItemList m_lFrameLoaders; + + + /** @short contains all loaded content handler with its properties. */ + mutable CacheItemList m_lContentHandlers; + + + /** @short optimize mapping of URL extensions to a type representation, + by using extensions as key and a list of internal + type names as value. */ + mutable CacheItemRegistration m_lExtensions2Types; + + + /** @short optimize mapping of URL pattern to a type representation, + by using patterns as key and a list of internal + type names as value. */ + mutable CacheItemRegistration m_lURLPattern2Types; + + + /** @short contains the current locale of the office and will be + used to work with localized configuration values. */ + OUString m_sActLocale; + + + /** @short contains status, which cache items/properties + was already loaded from the underlying configuration. + + @descr This information can be used to detect missing + information and load it on demand. + + @see EFillState + @see load() + */ + EFillState m_eFillState; + + + /** TODO document me ... */ + std::vector<OUString> m_lChangedTypes; + std::vector<OUString> m_lChangedFilters; + std::vector<OUString> m_lChangedFrameLoaders; + std::vector<OUString> m_lChangedContentHandlers; + + /// standard property names for filter config keyed by EReadOption + css::uno::Sequence< OUString > m_aStandardProps[4]; + + /// type property names for filter config keyed by EReadOption + css::uno::Sequence< OUString > m_aTypeProps[4]; + + /// readonly access to the module configuration of OOo + css::uno::Reference< css::container::XNameAccess > m_xModuleCfg; + + rtl::Reference< CacheUpdateListener > m_xTypesChglisteners; + rtl::Reference< CacheUpdateListener > m_xFiltersChgListener; + + + // interface + + public: + + + // ctor/dtor + + /** @short standard ctor + + @descr It's not allowed to do anything here... + especially is forbidden to start operations, + which needs a FilterCache instance too! + Why? Because this FilterCache instance will be + used as a singleton! And if during this ctor any + action related to this FilterCache singleton is + started... a race will be the result. + + The first method after construction of a new + singleton reference should be "load()". There + a special fill state of this cache can be forced. + */ + FilterCache(); + + + /** @short standard dtor. + */ + ~FilterCache(); + + + /** @short creates a copy of this container. + + @descr Such copy can be used then to modify items (add/change/remove) + without the risk to damage the original container. + After its changed data was flushed to the configuration it can be + removed. + + The original container will get these new data automatically + because it listen for changes on the internal used configuration layer. + If the new data are needed immediately inside the original container, + the method takeOver() can be used to copy all changes back. + The may be following notifications of the configuration will be superfluous then. + But they can't be stopped... + + All internal structures will be copied here. But the internal used + configuration (update) access won't be copied. The cloned instance contains + a different one. + */ + std::unique_ptr<FilterCache> clone() const; + + + /** @short copy the cache content or rClone back to this instance. + */ + void takeOver(const FilterCache& rClone); + + + /** @short force special fill state of this cache. + + @descr This method checks, if all requested items/properties already + exist. Only missing information will be read. + Otherwise this method does nothing! + + This method must be called from every user of this cache + every time it needs a filled cache. Normally we load + only standard information into this cache on startup. + + @throw An exception if the cache could not be filled really + or seems to be invalid afterwards. But there is no reaction + at all if this method does nothing inside, because the cache + is already fully filled! + */ + void load(EFillState eRequired); + + + /** @short return the current fill state of this cache. + + @descr This information can be used e.g. to start + a search on top of this cache with a minimum on + information ... and do it again, if some other + cache items seems to be available after calling of "loadAll()" + on this cache and first search does not had any valid results. + + @return sal_True if the required fill state exists for this cache; FALSE + otherwise. + + @throws css::uno::Exception + */ + bool isFillState(EFillState eRequired) const; + + + /** @short return a list of key names for items, which match + the specified criteria. + + @descr The returned key names can be used at another method "getItem()" + of this cache to get further information about this item. + + @attention Please note: because this cache can be used inside multithreaded + environments, such returned key name can point to an already removed + item! Please be aware of some "NoSuchElementExceptions" if you try to + call any other method of this cache in relation to this key name. + + @param eType + specify the sub container of this cache, which should be used for + searching. see also EItemType. + + @param lIProps + specify the property set, which must exist at the searched items + as minimum. + + @param lEProps + specify the property set, which must not(!) exist at the searched items + as minimum. + + @return [std::vector<OUString>] + a list of key names, which identify items of the queried sub container. + May be an empty list. + + @throw [css::uno::Exception] + if some input parameter are wrong or the cache itself is not valid + any longer, because any operation before damage it. + */ + std::vector<OUString> getMatchingItemsByProps( EItemType eType, + o3tl::span< const css::beans::NamedValue > lIProps, + o3tl::span< const css::beans::NamedValue > lEProps = {}) const; + + + /** @short indicates if the requested sub container + contains some items. + + @descr We don't provide any information about the count + of such items. Because we don't implement any index + based interface! The information "we have items or not" + must be enough for the outside code ... till somewhere + give us a good reason. :-) + + @param eType + specify the sub container of this cache, which should be used. + see also EItemType. + + @return [sal_Bool] + True, if the requested sub container contains some items; + False otherwise. + + @throw [css::uno::Exception] + if some input parameter are wrong or the cache itself is not valid + any longer, because any operation before damage it. + */ + bool hasItems(EItemType eType) const; + + + /** @short return a list of all key names, which represent + an item inside the specified sub container. + + @attention Please note: because this cache can be used inside multithreaded + environments, such returned key names can point to some already removed + items! Please be aware of some "NoSuchElementExceptions" if you try to + call any other method of this cache in relation to this key names. + + @param eType + specify the sub container of this cache, which should be used for + searching. see also EItemType. + + @return [std::vector<OUString>] + a list of key names, which can be used to access the item properties + using some other methods of this cache. + + @throw [css::uno::Exception] + if some input parameter are wrong or the cache itself is not valid + any longer, because any operation before damage it. + */ + std::vector<OUString> getItemNames(EItemType eType) const; + + + /** @short check if the required item exist inside this container. + + @attention This method exists to supports some UNO container interfaces + only. (e.g. XNameAccess.hasByName()). But inside multithreaded + environments there is no guarantee, that this item still exists, if + it's really requested e.g. by calling getItem()! + Be aware of some NoSuchElementExistExceptions ... + + @param eType + specify the sub container of this cache, which should be used. + see also EItemType. + + @param sItem + the key name of the requested item inside the specified sub container. + + @throw [css::uno::Exception] + if some input parameter are wrong or the cache itself is not valid + any longer, because any operation before damage it. + */ + bool hasItem( EItemType eType, + const OUString& sItem); + + + /** @short return an item, which match the specified type and name. + + @descr Because this cache can be used inside multithreaded environments + the caller must be aware of some exceptions - especially a "NoSuchElementExcepotion". + May another thread already removed the required item before ... + + @param eType + specify the sub container of this cache, which should be used for + searching. see also EItemType. + + @param sItem + specify the requested item by its key name. + + @return [CacheItem] + the required item if it could be located ... + But we throw an exception if the required item does not exist! + + @throw [css::container::NoSuchElementException] + if the required item does not still exist. + + @throw [css::uno::Exception] + if some input parameter are wrong or the cache itself is not valid + any longer, because any operation before damage it. + */ + CacheItem getItem( EItemType eType, + const OUString& sItem); + + + /** TODO document me ... + + @throws css::uno::Exception + */ + void removeItem( EItemType eType, + const OUString& sItem); + + + /** TODO document me ... + + @throws css::uno::Exception + */ + void setItem( EItemType eType , + const OUString& sItem , + const CacheItem& aValue); + + + /** TODO document me ... + + @throws css::uno::Exception + */ + void refreshItem( EItemType eType, + const OUString& sItem); + + + /** @short add some implicit properties to the given + cache item reference. + + @descr Such properties can e.g. finalized or mandatory. + They are not persistent and not really part of e.g. a + filter not. But they are attributes of a configuration + entry and can influence our container interface. + + @attention These properties are not part of the normal CacheItem + returned by the method getItem(). Because getItem() is + used internally too but these specialized properties + are needed at our container services only. So these + function sets are different to allow different handling. + + @param eType + specify the sub container of this cache, which should be used for + searching. see also EItemType. + + @param sItem + specify the requested item by its key name. + + @param rItem + contains already the normal properties of this item, + and will be used as out parameter to add the implicit + attributes there. + + @throw [css::uno::Exception] + if an internal error occurred. + Note: if the item is missing inside the underlying configuration + no exception will be thrown. In such case the item is marked as + finalized/mandatory automatically + Reason: maybe the item comes from the old configuration package and + was not migrated to the new one. So we can't provide write access + to such items... + */ + css::uno::Any getItemWithStateProps( EItemType eType, + const OUString& sItem); + + /** TODO document me + + @throws css::uno::Exception + */ + static void removeStatePropsFromItem(CacheItem& aValue); + + + /** @short force writing of all changes (which was made after + last flush was called) back to the configuration. + + @descr TODO + + @throw [css::uno::Exception] + if the cache itself is not valid + any longer, because any operation before damage it. + */ + void flush(); + + + /** @short supports a flat type detection for given URL. + + @descr Because such detection works on our optimized internal + structures (e.g. mapping from extensions/pattern to type names), + it should be made inside this cache. + + @param aURL + URL of the content, which type should be detected. + It's already parsed and split into its different parts, + like e.g.: main, jump marks etcpp. + + @param rFlatTypes + used as [out] parameter to add all types, which match to the given + URL. Further an information is added for every type. It indicates, how + this type is related to the specified URL (means e.g. if it matches + by extension or URLPattern...). + + @attention Please note: because this cache can be used inside multithreaded + environments, such returned key names can point to some already removed + items! Please be aware of some "NoSuchElementExceptions" if you try to + call any other method of this cache in relation to this key names. + + @throw [css::uno::Exception] + if the cache itself is not valid + any longer, because any operation before damage it. + */ + void detectFlatForURL(const css::util::URL& aURL , + FlatDetection& rFlatTypes) const; + + + // private helper + + private: + + + /** @short return a reference to one of our internal + sub container, which contains items of the + requested type. + + @param eType + specify, which sub container is needed outside. + + @return [CacheItemList&] + a reference(!) to the right sub container member. + + @throw [css::uno::Exception] + if the required list does not exist. + */ + const CacheItemList& impl_getItemList(EItemType eType) const; + + CacheItemList& impl_getItemList(EItemType eType); + + CacheItem& impl_getItem( EItemType eType, const OUString& sItem); + + /** @short return a valid configuration update access + to the underlying configuration package, which + is fix for this cache. + + @descr It checks first, if the internal member m_xConfig already + points to an open update access. If not - it opens a new one. + Doing so this method can be called every time a configuration + access is needed. + + @param eProvider + specify the needed configuration provider. + see EConfigProvider for further information ... + + @throws css::uno::Exception + + @attention If a configuration access was opened successfully + all necessary listener connections will be established + too. So this cache will be informed about outside updates. + */ + css::uno::Reference< css::uno::XInterface > impl_openConfig(EConfigProvider eProvide); + + + /** @short tries to open the requested configuration root + using the specified modi. + + @param sRoot + specify the configuration root, which should be opened. + + @param bReadOnly + enable/disable write access on the returned configuration + object. + + @param bLocalesMode + enable/disable special handling of localized configuration + items by the returned configuration object. + + @return A valid reference, if the configuration access could be opened + and initialized within the requested modes successfully; + a NULL reference otherwise. + */ + css::uno::Reference< css::uno::XInterface > impl_createConfigAccess(const OUString& sRoot , + bool bReadOnly , + bool bLocalesMode); + + + /** @short reads the specified configuration key + and return its value. + + @descr The specified key must be an absolute configuration path, + which can be split into its package and relative path tokens. + + @attention Because this function might opens a new configuration + read access for reading one key value only, it should + be used in rare cases only. It's an easy way... but an + expensive one. + + @param sDirectKey + the absolute configuration path, which should be read. + + @return [css::uno::Any] + the value of the requested key. + Can be empty if an internal error occurred or if the requested + key does not exists! + */ + css::uno::Any impl_getDirectCFGValue(const OUString& sDirectKey); + + + /** @short load the underlying configuration into this cache. + + @descr Which items should be read can be regulate by the + parameter eRequiredState. That provides the possibility + to load standard values on startup only and update this + cache later on demand with all available information. + + @param eRequiredState + indicates, which fill state this cache should have afterwards. + + @throws css::uno::Exception + */ + void impl_load(EFillState eRequiredState); + + + /** @short validate the whole cache and create + structures for optimized items access. + + @descr Wrong cache items will be removed automatically. + Wrong dependencies will be corrected automatically. + If something could not be repaired - an exception + is thrown. + Further some optimized structures will be created. + E.g.: a hash to map extensions to her types. + + @attention There is no exception, if the cache could be repaired + but contained wrong elements before! + + @throw [css::uno::Exception] + if cache is invalid and could not be repaired. + */ + void impl_validateAndOptimize(); + + private: + + + /** @short read the specified config set into the cache. + + @descr This method provides the following mechanism for reading: + a) read only standard properties of set items + b) read anything + c) read only optional properties and update already existing + items of the specified cache + + @param xConfig + API which provides access to the required configuration set. + + @param eType + specify the type of config item, which must be interpreted. + Of course this information can be used to locate the right set + at the given xConfig API object. + + @param eOption + regulate reading of standard/optional or all properties. + + @param pCache + points to the cache member, which should be filled or updated. + + @throw [css::uno::Exception] + if an unrecoverable error occurs inside this operation. + */ + void impl_loadSet(const css::uno::Reference< css::container::XNameAccess >& xConfig, + EItemType eType , + EReadOption eOption, + CacheItemList* pCache ); + + + /** @short read the specified container item from the given configuration set. + + @descr It's not added to any internal structures here. That must be done + outside this method. + + @param xSet + provides access to the configuration set, which includes all items. + + @param eType + specify, which container item type must be read. + + @param sItem + means the internal name, which can be used to address the item + properties relative to the given configuration set. + + @param eOption + regulate, which properties of the requested item should be read. + See definition of EReadOption for further information. + + @throw [css::uno::Exception] + if an unrecoverable error occurs inside this operation. + */ + CacheItem impl_loadItem(const css::uno::Reference< css::container::XNameAccess >& xSet , + EItemType eType , + const OUString& sItem , + EReadOption eOption); + + + /** @short try to load the requested item on demand from the underlying configuration + layer. + + @descr The outside code has to be sure, that the item does not already exists + inside this cache. Otherwise it will be loaded twice. This method + doesn't check such constellations! + + @param eType + specify the type of config item, which must be interpreted. + Of course this information can be used to locate the right set + at the given xConfig API object. + + @param sItem + the set node name of the requested item. + + @return An iterator, which points directly to the new cached item. + Is a valid iterator if no exception occurred here! + But to improve robustness - it should be checked :-) + + @throw [css::container::NoSuchElementException] + if the item does not exists inside the configuration layer too! + + @throw [css::uno::Exception] + if an unrecoverable error occurs inside this operation. + */ + CacheItemList::iterator impl_loadItemOnDemand( EItemType eType, + const OUString& sItem); + + + /** TODO + + @throws css::uno::Exception + */ + static void impl_saveItem(const css::uno::Reference< css::container::XNameReplace >& xSet , + EItemType eType , + const CacheItem & aValue); + + + /** TODO + + @throws css::uno::Exception + */ + void impl_addItem2FlushList( EItemType eType, + const OUString& sItem); + + + /** TODO + + @throws css::uno::Exception + */ + static void impl_flushByList(const css::uno::Reference< css::container::XNameAccess >& xSet , + EItemType eType , + const CacheItemList& rCache, + const std::vector<OUString>& lItems); + + + /** @short specify, which save operation is necessary for the specified item. + + @desrc If an item of this cache will be added/removed or modified it will + be changed inside memory only first. But we save its name inside a special + list of changed items. If at least the method flush() is called, we use + this list to check if the item was changed/added or removed. This method + checks the exist state of the requested item inside our own cache + and inside the underlying configuration layer to find out, if the item + must be removed/added or modified inside the configuration layer. + + @param xSet + points directly to the configuration set, where the item should resist + (if it exists!). + + @param rList + points to our internal cache list, where the item should resist + (if it exists!). + + @param sItem + the internal name of the item, which should be checked. + + @return An enum value of type EItemFlushState, which indicates the needed + API operation for updating the underlying configuration layer. + + @throws An exception if anything failed inside this operation. + e.g. the given configuration set was not open. + */ + static EItemFlushState impl_specifyFlushOperation(const css::uno::Reference< css::container::XNameAccess >& xSet , + const CacheItemList& rList, + const OUString& sItem); + + + /** TODO + + @throws css::uno::Exception + */ + void impl_readPatchUINames(const css::uno::Reference< css::container::XNameAccess >& xNode, + CacheItem& rItem); + + + /** TODO + + @throws css::uno::Exception + */ + static void impl_savePatchUINames(const css::uno::Reference< css::container::XNameReplace >& xNode, + const CacheItem& rItem); + + /** TODO */ + void impl_readOldFormat(); + + /** TODO + + @throws css::uno::Exception + */ + CacheItem impl_readOldItem(const css::uno::Reference< css::container::XNameAccess >& xSet , + EItemType eType, + const OUString& sItem); + + + /** TODO */ + static void impl_interpretDataVal4Type(const OUString& sValue, + sal_Int32 nProp , + CacheItem& rItem ); + + + /** TODO */ + static void impl_interpretDataVal4Filter(const OUString& sValue, + sal_Int32 nProp , + CacheItem& rItem ); + + + /** TODO */ + static std::vector<OUString> impl_tokenizeString(std::u16string_view sData , + sal_Unicode cSeparator); + + +#if OSL_DEBUG_LEVEL > 0 + /** TODO */ + OUString impl_searchFrameLoaderForType(const OUString& sType) const; + OUString impl_searchContentHandlerForType(const OUString& sType) const; +#endif + + + /** @short check if the specified OOo module is installed. + + @param sModule + the long name of the module (e.g. "com.sun.star.text.TextDocument"). + + @return sal_True if the requested module is installed; sal_False otherwise. + */ + bool impl_isModuleInstalled(const OUString& sModule); + + + /** @short convert a list of flag names to its int representation. + + @param lNames + the list of flag names. + + @return the converted flag field. + */ + static SfxFilterFlags impl_convertFlagNames2FlagField(const css::uno::Sequence< OUString >& lNames); + + + /** @short convert a flag field value to its list representation of flag names. + + @param nFlags + the flag field value + + @return [seq< string >] + the converted flag name list. + */ + static css::uno::Sequence< OUString > impl_convertFlagField2FlagNames(SfxFilterFlags nFlags); +}; + +FilterCache& GetTheFilterCache(); + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/filterconfig1.component b/filter/source/config/cache/filterconfig1.component new file mode 100644 index 000000000..711c0e38f --- /dev/null +++ b/filter/source/config/cache/filterconfig1.component @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.filter.config.ConfigFlush" + constructor="filter_ConfigFlush_get_implementation" single-instance="true"> + <service name="com.sun.star.document.FilterConfigRefresh"/> + </implementation> + <implementation name="com.sun.star.comp.filter.config.ContentHandlerFactory" + constructor="filter_ContentHandlerFactory_get_implementation"> + <service name="com.sun.star.frame.ContentHandlerFactory"/> + </implementation> + <implementation name="com.sun.star.comp.filter.config.FilterFactory" + constructor="filter_FilterFactory_get_implementation"> + <service name="com.sun.star.document.FilterFactory"/> + </implementation> + <implementation name="com.sun.star.comp.filter.config.FrameLoaderFactory" + constructor="filter_FrameLoaderFactory_get_implementation"> + <service name="com.sun.star.frame.FrameLoaderFactory"/> + </implementation> + <implementation name="com.sun.star.comp.filter.config.TypeDetection" + constructor="filter_TypeDetection_get_implementation"> + <service name="com.sun.star.document.TypeDetection"/> + </implementation> +</component> diff --git a/filter/source/config/cache/filterfactory.cxx b/filter/source/config/cache/filterfactory.cxx new file mode 100644 index 000000000..e083614c0 --- /dev/null +++ b/filter/source/config/cache/filterfactory.cxx @@ -0,0 +1,498 @@ +/* -*- 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 <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/enumhelper.hxx> +#include <comphelper/sequence.hxx> +#include <officecfg/Setup.hxx> +#include <officecfg/TypeDetection/UISort.hxx> + + +namespace filter::config{ + +FilterCache& GetTheFilterCache() +{ + static FilterCache CACHE; + return CACHE; +} + +/** @short define all possible parts of a filter query. + + @descr syntax: "<query>[:<param>[=<value>]]" + e.g.: "_query_writer:default_first:use_order:sort_prop=uiname" + + argument description default + ----------------------------------------------------------------------------------------------- + iflags=<mask> include filters by given mask 0 + eflags=<mask> 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<OUString> 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<PropertyValue>, 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<OUString> 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<OUString> 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<OUString> 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<OUString> lFilterNames = pCache->getItemNames(FilterCache::E_FILTER); + std::vector<OUString> 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<OUString> 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<OUString> 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<OUString> lModules = impl_getListOfInstalledModules(); + for (auto const& module : lModules) + { + std::vector<OUString> lFilters4Module = impl_getSortedFilterListForModule(module, nIFlags, nEFlags); + for (auto const& filter4Module : lFilters4Module) + { + lFilterList.push_back(filter4Module); + } + } + } + + return lFilterList; +} + + +css::uno::Sequence<OUString> FilterFactory::impl_getListOfInstalledModules() +{ + css::uno::Reference< css::container::XNameAccess > xModuleConfig = officecfg::Setup::Office::Factories::get(); + return xModuleConfig->getElementNames(); +} + + +std::vector<OUString> FilterFactory::impl_getSortedFilterListForModule(const OUString& sModule, + sal_Int32 nIFlags, + sal_Int32 nEFlags) const +{ + std::vector<OUString> 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<OUString> 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<OUString> 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<OUString> 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<OUString> throws + // an IllegalArgumentException if the type does not match ... + // but it resets the std::vector<OUString> to a length of 0 if the Any is empty! + std::vector<OUString> lSortedFilters( + comphelper::sequenceToContainer< std::vector<OUString> >(xModule->getByName(PROPNAME_SORTEDFILTERLIST).get<css::uno::Sequence<OUString> >())); + return lSortedFilters; + } + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + {} + + return std::vector<OUString>(); +} + +} // namespace filter + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +filter_FilterFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new filter::config::FilterFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/filterfactory.hxx b/filter/source/config/cache/filterfactory.hxx new file mode 100644 index 000000000..9be370f4d --- /dev/null +++ b/filter/source/config/cache/filterfactory.hxx @@ -0,0 +1,135 @@ +/* -*- 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 . + */ + +#pragma once + +#include "basecontainer.hxx" +#include "querytokenizer.hxx" +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <cppuhelper/implbase.hxx> + + +namespace filter::config { + + +/** @short implements the service <type scope="com.sun.star.document">FilterFactory</type>. + */ +class FilterFactory : public ::cppu::ImplInheritanceHelper< BaseContainer , + css::lang::XMultiServiceFactory > +{ + + // native interface + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + public: + + + // ctor/dtor + + /** @short standard ctor to connect this interface wrapper to + the global filter cache instance ... + + @param rxContext + reference to the uno service manager, which created this service instance. + */ + explicit FilterFactory(const css::uno::Reference< css::uno::XComponentContext >& rxContext); + + + /** @short standard dtor. + */ + virtual ~FilterFactory() override; + + + // uno interface + + public: + + + // XMultiServiceFactory + + 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 css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames() override; + + + // XContainerQuery + + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByQuery(const OUString& sQuery) override; + + + // internal helper! + + private: + + + /** @short implement the container string query: "matchByDocumentService=:iflags=:eflags=:..." + + @param lTokens + the list of query tokens and its values. + + @return A string list of internal filter names, including + all filters, which match this query. + */ + std::vector<OUString> impl_queryMatchByDocumentService(const QueryTokenizer& lTokens) const; + + + /** TODO document me + */ + static css::uno::Sequence<OUString> impl_getListOfInstalledModules(); + + + /** @short implement the container string query: + "getSortedFilterList()[:module=<xxx>]:[iflags=<xxx>][:eflags=<xxx>]" + + @param lTokens + the list of query tokens and its values. + + @return A string list of internal filter names, including + all filters, which match this query. + */ + std::vector<OUString> impl_getSortedFilterList(const QueryTokenizer& lTokens) const; + + + /** TODO document me + */ + std::vector<OUString> impl_getSortedFilterListForModule(const OUString& sModule, + sal_Int32 nIFlags, + sal_Int32 nEFlags) const; + + + /** @short read a specialized and sorted list of filter names from + the configuration (matching the specified module) + + @param sModule + the module for which the sorted list should be retrieved for. + + @return A string list of internal filter names. + Can be empty. + */ + static std::vector<OUString> impl_readSortedFilterListFromConfig(const OUString& sModule); + +}; + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/frameloaderfactory.cxx b/filter/source/config/cache/frameloaderfactory.cxx new file mode 100644 index 000000000..ebe482366 --- /dev/null +++ b/filter/source/config/cache/frameloaderfactory.cxx @@ -0,0 +1,102 @@ +/* -*- 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 "frameloaderfactory.hxx" + +#include <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/sequence.hxx> + + +namespace filter::config{ + +FrameLoaderFactory::FrameLoaderFactory(const css::uno::Reference< css::uno::XComponentContext >& rxContext) + : m_xContext(rxContext) +{ + BaseContainer::init("com.sun.star.comp.filter.config.FrameLoaderFactory" , + { "com.sun.star.frame.FrameLoaderFactory" }, + FilterCache::E_FRAMELOADER ); +} + + +FrameLoaderFactory::~FrameLoaderFactory() +{ +} + + +css::uno::Reference< css::uno::XInterface > SAL_CALL FrameLoaderFactory::createInstance(const OUString& sLoader) +{ + return createInstanceWithArguments(sLoader, css::uno::Sequence< css::uno::Any >()); +} + + +css::uno::Reference< css::uno::XInterface > SAL_CALL FrameLoaderFactory::createInstanceWithArguments(const OUString& sLoader , + const css::uno::Sequence< css::uno::Any >& lArguments) +{ + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + auto & cache = GetTheFilterCache(); + + // search loader on cache + CacheItem aLoader = cache.getItem(m_eType, sLoader); + + // create service instance + css::uno::Reference< css::uno::XInterface > xLoader = m_xContext->getServiceManager()->createInstanceWithContext(sLoader, m_xContext); + + // initialize filter + css::uno::Reference< css::lang::XInitialization > xInit(xLoader, css::uno::UNO_QUERY); + if (xInit.is()) + { + // format: lInitData[0] = seq<PropertyValue>, which contains all configuration properties of this loader + // lInitData[1] = lArguments[0] + // ... + // lInitData[n] = lArguments[n-1] + css::uno::Sequence< css::beans::PropertyValue > lConfig; + aLoader >> 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 xLoader; + // <- SAFE +} + + +css::uno::Sequence< OUString > SAL_CALL FrameLoaderFactory::getAvailableServiceNames() +{ + // must be the same list as ((XNameAccess*)this)->getElementNames() return! + return BaseContainer::getElementNames(); +} + +} // namespace filter::config + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +filter_FrameLoaderFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new filter::config::FrameLoaderFactory(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/frameloaderfactory.hxx b/filter/source/config/cache/frameloaderfactory.hxx new file mode 100644 index 000000000..2b9098b1a --- /dev/null +++ b/filter/source/config/cache/frameloaderfactory.hxx @@ -0,0 +1,98 @@ +/* -*- 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 . + */ + +#pragma once + +#include "basecontainer.hxx" +#include <com/sun/star/frame/XLoaderFactory.hpp> +#include <cppuhelper/implbase.hxx> + + +namespace filter::config { + + +/** @short implements the service <type scope="com.sun.star.document">FrameLoaderFactory</type>. + */ +class FrameLoaderFactory : public ::cppu::ImplInheritanceHelper< BaseContainer , + css::frame::XLoaderFactory > +{ + + // native interface + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + public: + + + // ctor/dtor + + /** @short standard ctor to connect this interface wrapper to + the global filter cache instance ... + + @param rxContext + reference to the uno service manager, which created this service instance. + */ + explicit FrameLoaderFactory(const css::uno::Reference< css::uno::XComponentContext >& rxContext); + + + /** @short standard dtor. + */ + virtual ~FrameLoaderFactory() override; + + + // uno interface + + public: + + + // XMultiServiceFactory + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance(const OUString& sLoader) override; + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments(const OUString& sLoader , + const css::uno::Sequence< css::uno::Any >& lArguments) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames() override; + + + public: + + // Overrides to resolve ambiguity + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override + { return BaseContainer::getByName(aName); } + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override + { return BaseContainer::getElementNames(); } + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override + { return BaseContainer::hasByName(aName); } + + virtual css::uno::Type SAL_CALL getElementType() override + { return BaseContainer::getElementType(); } + virtual sal_Bool SAL_CALL hasElements() override + { return BaseContainer::hasElements(); } + + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByQuery( const OUString& Query ) override + { return BaseContainer::createSubSetEnumerationByQuery(Query); } + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByProperties( const css::uno::Sequence< css::beans::NamedValue >& Properties ) override + { return BaseContainer::createSubSetEnumerationByProperties(Properties); } + +}; + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/querytokenizer.cxx b/filter/source/config/cache/querytokenizer.cxx new file mode 100644 index 000000000..f5b021f53 --- /dev/null +++ b/filter/source/config/cache/querytokenizer.cxx @@ -0,0 +1,69 @@ +/* -*- 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 "querytokenizer.hxx" + +#include <osl/diagnose.h> +#include <o3tl/string_view.hxx> + + +namespace filter::config{ + + +QueryTokenizer::QueryTokenizer(std::u16string_view sQuery) + : m_bValid(true) +{ + sal_Int32 token = 0; + while(token != -1) + { + std::u16string_view sToken = o3tl::getToken(sQuery,0, ':', token); + if (!sToken.empty()) + { + sal_Int32 nIdx{ 0 }; + const OUString sKey{ o3tl::getToken(sToken, 0, '=', nIdx) }; + const OUString sVal{ o3tl::getToken(sToken, 0, ':', nIdx) }; + + if (sKey.isEmpty()) + m_bValid = false; + OSL_ENSURE(m_bValid, "QueryTokenizer::QueryTokenizer() Found non boolean query parameter ... but its key is empty. Will be ignored!"); + + if (find(sKey) != end()) + m_bValid = false; + OSL_ENSURE(m_bValid, "QueryTokenizer::QueryTokenizer() Query contains same param more than once. Last one wins :-)"); + + (*this)[sKey] = sVal; + } + } +} + + +QueryTokenizer::~QueryTokenizer() +{ + /*TODO*/ +} + + +bool QueryTokenizer::valid() const +{ + return m_bValid; +} + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/querytokenizer.hxx b/filter/source/config/cache/querytokenizer.hxx new file mode 100644 index 000000000..0c7e79b3d --- /dev/null +++ b/filter/source/config/cache/querytokenizer.hxx @@ -0,0 +1,92 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <unordered_map> + + +namespace filter::config { + + +/** @short It can be used to split any query string (which can be used at the + related interface <type scope="css::container">XContainerQuery</type>) + into its different tokens using a fix schema. + + @descr All queries implemented of the services + <ul> + <li><type scope="css::document">TypeDetection</type></li> + <li><type scope="css::document">FilterFactory</type></li> + <li><type scope="css::document">ExtendedTypeDetectionFactory</type></li> + <li><type scope="css::frame">FrameLoaderFactory</type></li> + <li><type scope="css::frame">ContentHandlerFactory</type></li> + </ul> + uses this schema. + + @attention This class is not threadsafe implemented. Because it's not necessary. + But you have to make sure that it's not used as such :-) + */ +class QueryTokenizer : public std::unordered_map< OUString, OUString > +{ + + // member + + private: + + /** @short Because the given query can contain errors, + it should be checked outside. + + TODO May it's a good idea to describe the real problem + more detailed ... + */ + bool m_bValid; + + + // interface + + public: + + /** @short create a new tokenizer instance with a + a new query. + + @descr The given query is immediately analyzed + and separated into its token, which can + be access by some specialized method later. + + @param sQuery + the query string. + */ + explicit QueryTokenizer(std::u16string_view sQuery); + + + /** @short destruct an instance of this class. + */ + virtual ~QueryTokenizer(); + + + /** @short can be used to check if analyzing of given query + was successful or not. + */ + bool valid() const; +}; + +} // namespace filter::config + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/typedetection.cxx b/filter/source/config/cache/typedetection.cxx new file mode 100644 index 000000000..ae8c194fa --- /dev/null +++ b/filter/source/config/cache/typedetection.cxx @@ -0,0 +1,1204 @@ +/* -*- 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 "typedetection.hxx" +#include "constant.hxx" + +#include <com/sun/star/document/XExtendedFilterDetection.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <o3tl/string_view.hxx> +#include <tools/wldcrd.hxx> +#include <sal/log.hxx> +#include <framework/interaction.hxx> +#include <tools/diagnose_ex.h> +#include <tools/urlobj.hxx> +#include <comphelper/fileurl.hxx> +#include <comphelper/sequence.hxx> +#include <utility> + +#define DEBUG_TYPE_DETECTION 0 + +#if DEBUG_TYPE_DETECTION +#include <iostream> +using std::cout; +using std::endl; +#endif + +using namespace com::sun::star; + +namespace filter::config{ + +TypeDetection::TypeDetection(const css::uno::Reference< css::uno::XComponentContext >& rxContext) + : m_xContext(rxContext) + , m_xTerminateListener(new TerminateDetection(this)) + , m_bCancel(false) +{ + css::frame::Desktop::create(m_xContext)->addTerminateListener(m_xTerminateListener); + BaseContainer::init("com.sun.star.comp.filter.config.TypeDetection" , + { "com.sun.star.document.TypeDetection" }, + FilterCache::E_TYPE ); +} + + +TypeDetection::~TypeDetection() +{ + css::frame::Desktop::create(m_xContext)->removeTerminateListener(m_xTerminateListener); +} + + +OUString SAL_CALL TypeDetection::queryTypeByURL(const OUString& sURL) +{ + OUString sType; + + // SAFE -> + osl::MutexGuard aLock(m_aMutex); + + css::util::URL aURL; + aURL.Complete = sURL; + css::uno::Reference< css::util::XURLTransformer > xParser( css::util::URLTransformer::create(m_xContext) ); + xParser->parseStrict(aURL); + + // set std types as minimum requirement first! + // Only in case no type was found for given URL, + // use optional types too ... + auto & cache = GetTheFilterCache(); + FlatDetection lFlatTypes; + cache.detectFlatForURL(aURL, lFlatTypes); + + if ( + (lFlatTypes.empty() ) && + (!cache.isFillState(FilterCache::E_CONTAINS_TYPES)) + ) + { + cache.load(FilterCache::E_CONTAINS_TYPES); + cache.detectFlatForURL(aURL, lFlatTypes); + } + + // first item is guaranteed as "preferred" one! + if (!lFlatTypes.empty()) + { + const FlatDetectionInfo& aMatch = *(lFlatTypes.begin()); + sType = aMatch.sType; + } + + return sType; + // <- SAFE +} + +namespace { + +/** + * Rank format types in order of complexity. More complex formats are + * ranked higher so that they get tested sooner over simpler formats. + * + * Guidelines to determine how complex a format is (subject to change): + * + * 1) compressed text (XML, HTML, etc) + * 2) binary + * 3) non-compressed text + * 3.1) structured text + * 3.1.1) dialect of a structured text (e.g. docbook XML) + * 3.1.2) generic structured text (e.g. generic XML) + * 3.2) non-structured text + * + * In each category, rank them from strictly-structured to + * loosely-structured. + */ +int getFlatTypeRank(std::u16string_view rType) +{ + // List formats from more complex to less complex. + // TODO: Add more. + static const char* ranks[] = { + + // Compressed XML (ODF XML zip formats) + "writer8_template", + "writer8", + "calc8_template", + "calc8", + "impress8_template", + "impress8", + "draw8_template", + "draw8", + "chart8", + "math8", + "writerglobal8_template", + "writerglobal8", + "writerweb8_writer_template", + "StarBase", + + // Compressed XML (OOXML) + "writer_OOXML_Text_Template", + "writer_OOXML", + "writer_MS_Word_2007_Template", + "writer_MS_Word_2007", + "Office Open XML Spreadsheet Template", + "Office Open XML Spreadsheet", + "MS Excel 2007 XML Template", + "MS Excel 2007 XML", + "MS PowerPoint 2007 XML Template", + "MS PowerPoint 2007 XML AutoPlay", + "MS PowerPoint 2007 XML", + + // Compressed XML (Uniform/Unified Office Format) + "Unified_Office_Format_text", + "Unified_Office_Format_spreadsheet", + "Unified_Office_Format_presentation", + + // Compressed XML (StarOffice XML zip formats) + "calc_StarOffice_XML_Calc", + "calc_StarOffice_XML_Calc_Template", + "chart_StarOffice_XML_Chart", + "draw_StarOffice_XML_Draw", + "draw_StarOffice_XML_Draw_Template", + "impress_StarOffice_XML_Impress", + "impress_StarOffice_XML_Impress_Template", + "math_StarOffice_XML_Math", + "writer_StarOffice_XML_Writer", + "writer_StarOffice_XML_Writer_Template", + "writer_globaldocument_StarOffice_XML_Writer_GlobalDocument", + "writer_web_StarOffice_XML_Writer_Web_Template", + + // Compressed text + "pdf_Portable_Document_Format", + + // Binary + "writer_T602_Document", + "writer_WordPerfect_Document", + "writer_MS_Works_Document", + "writer_MS_Word_97_Vorlage", + "writer_MS_Word_97", + "writer_MS_Word_95_Vorlage", + "writer_MS_Word_95", + "writer_MS_WinWord_60", + "writer_MS_WinWord_5", + "MS Excel 2007 Binary", + "calc_MS_Excel_97_VorlageTemplate", + "calc_MS_Excel_97", + "calc_MS_Excel_95_VorlageTemplate", + "calc_MS_Excel_95", + "calc_MS_Excel_5095_VorlageTemplate", + "calc_MS_Excel_5095", + "calc_MS_Excel_40_VorlageTemplate", + "calc_MS_Excel_40", + "calc_Pocket_Excel_File", + "impress_MS_PowerPoint_97_Vorlage", + "impress_MS_PowerPoint_97_AutoPlay", + "impress_MS_PowerPoint_97", + "calc_Lotus", + "calc_QPro", + "calc_SYLK", + "calc_DIF", + "calc_dBase", + + // Binary (raster and vector image files) + "emf_MS_Windows_Metafile", + "wmf_MS_Windows_Metafile", + "met_OS2_Metafile", + "svm_StarView_Metafile", + "sgv_StarDraw_20", + "tif_Tag_Image_File", + "tga_Truevision_TARGA", + "sgf_StarOffice_Writer_SGF", + "ras_Sun_Rasterfile", + "psd_Adobe_Photoshop", + "png_Portable_Network_Graphic", + "jpg_JPEG", + "mov_MOV", + "gif_Graphics_Interchange", + "bmp_MS_Windows", + "pcx_Zsoft_Paintbrush", + "pct_Mac_Pict", + "pcd_Photo_CD_Base", + "pcd_Photo_CD_Base4", + "pcd_Photo_CD_Base16", + "webp_WebP", + "impress_CGM_Computer_Graphics_Metafile", // There is binary and ascii variants ? + "draw_WordPerfect_Graphics", + "draw_Visio_Document", + "draw_Publisher_Document", + "draw_Corel_Presentation_Exchange", + "draw_CorelDraw_Document", + "writer_LotusWordPro_Document", + "writer_MIZI_Hwp_97", // Hanword (Hancom Office) + + // Non-compressed XML + "writer_ODT_FlatXML", + "calc_ODS_FlatXML", + "impress_ODP_FlatXML", + "draw_ODG_FlatXML", + "calc_ADO_rowset_XML", + "calc_MS_Excel_2003_XML", + "writer_MS_Word_2003_XML", + "writer_DocBook_File", + "XHTML_File", + "svg_Scalable_Vector_Graphics", + "math_MathML_XML_Math", + + // Non-compressed text + "dxf_AutoCAD_Interchange", + "eps_Encapsulated_PostScript", + "pbm_Portable_Bitmap", // There is 'raw' and 'ascii' variants. + "ppm_Portable_Pixelmap", // There is 'raw' and 'ascii' variants. + "pgm_Portable_Graymap", // There is 'raw' and 'ascii' variants. + "xpm_XPM", + "xbm_X_Consortium", + "writer_Rich_Text_Format", + "writer_web_HTML_help", + "generic_HTML", + + "generic_Text", // Plain text (catch all) + + // Anything ranked lower than generic_Text will never be used during + // type detection (since generic_Text catches all). + + // Export only + "writer_layout_dump_xml", + "writer_indexing_export", + "graphic_HTML", + + // Internal use only + "StarBaseReportChart", + "StarBaseReport", + "math_MathType_3x", // MathType equation embedded in Word doc. + }; + + size_t n = SAL_N_ELEMENTS(ranks); + + for (size_t i = 0; i < n; ++i) + { + if (o3tl::equalsAscii(rType, ranks[i])) + return n - i - 1; + } + + // Not ranked. Treat them equally. Unranked formats have higher priority + // than the ranked internal ones since they may be defined externally. + return n; +} + +/** + * Types with matching pattern first, then extension, then custom ranks by + * types, then types that are supported by the document service come next. + * Lastly, sort them alphabetically. + */ +struct SortByPriority +{ + bool operator() (const FlatDetectionInfo& r1, const FlatDetectionInfo& r2) const + { + if (r1.bMatchByPattern != r2.bMatchByPattern) + return r1.bMatchByPattern; + + if (r1.bMatchByExtension != r2.bMatchByExtension) + return r1.bMatchByExtension; + + int rank1 = getFlatTypeRank(r1.sType); + int rank2 = getFlatTypeRank(r2.sType); + + if (rank1 != rank2) + return rank1 > rank2; + + if (r1.bPreselectedByDocumentService != r2.bPreselectedByDocumentService) + return r1.bPreselectedByDocumentService; + + // All things being equal, sort them alphabetically. + return r1.sType > r2.sType; + } +}; + +struct SortByType +{ + bool operator() (const FlatDetectionInfo& r1, const FlatDetectionInfo& r2) const + { + return r1.sType > r2.sType; + } +}; + +struct EqualByType +{ + bool operator() (const FlatDetectionInfo& r1, const FlatDetectionInfo& r2) const + { + return r1.sType == r2.sType; + } +}; + +class FindByType +{ + OUString maType; +public: + explicit FindByType(OUString aType) : maType(std::move(aType)) {} + bool operator() (const FlatDetectionInfo& rInfo) const + { + return rInfo.sType == maType; + } +}; + +#if DEBUG_TYPE_DETECTION +void printFlatDetectionList(const char* caption, const FlatDetection& types) +{ + cout << "-- " << caption << " (size=" << types.size() << ")" << endl; + for (auto const& item : types) + { + cout << " type='" << item.sType << "'; match by extension (" << item.bMatchByExtension + << "); match by pattern (" << item.bMatchByPattern << "); pre-selected by doc service (" + << item.bPreselectedByDocumentService << ")" << endl; + } + cout << "--" << endl; +} +#endif + +} + +OUString SAL_CALL TypeDetection::queryTypeByDescriptor(css::uno::Sequence< css::beans::PropertyValue >& lDescriptor, + sal_Bool bAllowDeep ) +{ + // make the descriptor more usable :-) + utl::MediaDescriptor stlDescriptor(lDescriptor); + OUString sType, sURL; + + try + { + // SAFE -> ---------------------------------- + osl::ClearableMutexGuard aLock(m_aMutex); + + // parse given URL to split it into e.g. main and jump marks ... + sURL = stlDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_URL, OUString()); + +#if OSL_DEBUG_LEVEL > 0 + if (stlDescriptor.find( "FileName" ) != stlDescriptor.end()) + OSL_FAIL("Detect using of deprecated and already unsupported MediaDescriptor property \"FileName\"!"); +#endif + + css::util::URL aURL; + aURL.Complete = sURL; + css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext)); + xParser->parseStrict(aURL); + + OUString aSelectedFilter = stlDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_FILTERNAME, OUString()); + if (!aSelectedFilter.isEmpty()) + { + // Caller specified the filter type. Honor it. Just get the default + // type for that filter, and bail out. + if (impl_validateAndSetFilterOnDescriptor(stlDescriptor, aSelectedFilter)) + return stlDescriptor[utl::MediaDescriptor::PROP_TYPENAME].get<OUString>(); + } + + FlatDetection lFlatTypes; + impl_getAllFormatTypes(aURL, stlDescriptor, lFlatTypes); + + aLock.clear(); + // <- SAFE ---------------------------------- + + // Properly prioritize all candidate types. + std::stable_sort(lFlatTypes.begin(), lFlatTypes.end(), SortByPriority()); + auto last = std::unique(lFlatTypes.begin(), lFlatTypes.end(), EqualByType()); + lFlatTypes.erase(last, lFlatTypes.end()); + + OUString sLastChance; + + // verify every flat detected (or preselected!) type + // by calling its registered deep detection service. + // But break this loop if a type match to the given descriptor + // by a URL pattern(!) or if deep detection isn't allowed from + // outside (bAllowDeep=sal_False) or break the whole detection by + // throwing an exception if creation of the might needed input + // stream failed by e.g. an IO exception ... + if (!lFlatTypes.empty()) + sType = impl_detectTypeFlatAndDeep(stlDescriptor, lFlatTypes, bAllowDeep, sLastChance); + + // flat detection failed + // pure deep detection failed + // => ask might existing InteractionHandler + // means: ask user for its decision + if (sType.isEmpty() && !m_bCancel) + sType = impl_askUserForTypeAndFilterIfAllowed(stlDescriptor); + + + // no real detected type - but a might valid one. + // update descriptor and set last chance for return. + if (sType.isEmpty() && !sLastChance.isEmpty() && !m_bCancel) + { + OSL_FAIL("set first flat detected type without a registered deep detection service as \"last chance\" ... nevertheless some other deep detections said \"NO\". I TRY IT!"); + sType = sLastChance; + } + } + catch(const css::uno::RuntimeException&) + { + throw; + } + catch(const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("filter.config", "caught exception while querying type of " << sURL); + sType.clear(); + } + + // adapt media descriptor, so it contains the right values + // for type/filter name/document service/ etcpp. + impl_checkResultsAndAddBestFilter(stlDescriptor, sType); // Attention: sType is used as IN/OUT param here and will might be changed inside this method !!! + impl_validateAndSetTypeOnDescriptor(stlDescriptor, sType); + + stlDescriptor >> lDescriptor; + return sType; +} + + +void TypeDetection::impl_checkResultsAndAddBestFilter(utl::MediaDescriptor& rDescriptor, + OUString& sType ) +{ + // a) + // Don't overwrite a might preselected filter! + OUString sFilter = rDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_FILTERNAME, + OUString()); + if (!sFilter.isEmpty()) + return; + + auto & cache = GetTheFilterCache(); + + // b) + // check a preselected document service too. + // Then we have to search a suitable filter within this module. + OUString sDocumentService = rDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_DOCUMENTSERVICE, + OUString()); + if (!sDocumentService.isEmpty()) + { + try + { + OUString sRealType = sType; + + // SAFE -> + ::osl::ResettableMutexGuard aLock(m_aMutex); + + // Attention: For executing next lines of code, We must be sure that + // all filters already loaded :-( + // That can disturb our "load on demand feature". But we have no other chance! + cache.load(FilterCache::E_CONTAINS_FILTERS); + + css::beans::NamedValue lIProps[] { + { PROPNAME_DOCUMENTSERVICE, uno::Any(sDocumentService) }, + { PROPNAME_TYPE, uno::Any(sRealType) } }; + std::vector<OUString> lFilters = cache.getMatchingItemsByProps(FilterCache::E_FILTER, lIProps); + + aLock.clear(); + // <- SAFE + + for (auto const& filter : lFilters) + { + // SAFE -> + aLock.reset(); + try + { + CacheItem aFilter = cache.getItem(FilterCache::E_FILTER, filter); + sal_Int32 nFlags = 0; + aFilter[PROPNAME_FLAGS] >>= nFlags; + + if (static_cast<SfxFilterFlags>(nFlags) & SfxFilterFlags::IMPORT) + sFilter = filter; + if (static_cast<SfxFilterFlags>(nFlags) & SfxFilterFlags::PREFERED) + break; + } + catch(const css::uno::Exception&) {} + aLock.clear(); + // <- SAFE + } + + if (!sFilter.isEmpty()) + { + rDescriptor[utl::MediaDescriptor::PROP_TYPENAME ] <<= sRealType; + rDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter; + sType = sRealType; + return; + } + } + catch(const css::uno::Exception&) + {} + } + + // c) + // We can use the preferred filter for the specified type. + // Such preferred filter points: + // - to the default filter of the preferred application + // - or to any other filter if no preferred filter was set. + // Note: It's an optimization only! + // It's not guaranteed, that such preferred filter exists. + sFilter.clear(); + try + { + // SAFE -> + osl::ClearableMutexGuard aLock(m_aMutex); + + CacheItem aType = cache.getItem(FilterCache::E_TYPE, sType); + aType[PROPNAME_PREFERREDFILTER] >>= sFilter; + cache.getItem(FilterCache::E_FILTER, sFilter); + + aLock.clear(); + // <- SAFE + + // no exception => found valid type and filter => set it on the given descriptor + rDescriptor[utl::MediaDescriptor::PROP_TYPENAME ] <<= sType ; + rDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter; + return; + } + catch(const css::uno::Exception&) + {} + + // d) + // Search for any import(!) filter, which is registered for this type. + sFilter.clear(); + try + { + // SAFE -> + ::osl::ResettableMutexGuard aLock(m_aMutex); + + // Attention: For executing next lines of code, We must be sure that + // all filters already loaded :-( + // That can disturb our "load on demand feature". But we have no other chance! + cache.load(FilterCache::E_CONTAINS_FILTERS); + + css::beans::NamedValue lIProps[] { + { PROPNAME_TYPE, uno::Any(sType) } }; + std::vector<OUString> lFilters = cache.getMatchingItemsByProps(FilterCache::E_FILTER, lIProps); + + aLock.clear(); + // <- SAFE + + for (auto const& filter : lFilters) + { + sFilter = filter; + + // SAFE -> + aLock.reset(); + try + { + CacheItem aFilter = cache.getItem(FilterCache::E_FILTER, sFilter); + sal_Int32 nFlags = 0; + aFilter[PROPNAME_FLAGS] >>= nFlags; + + if (static_cast<SfxFilterFlags>(nFlags) & SfxFilterFlags::IMPORT) + break; + } + catch(const css::uno::Exception&) + { continue; } + aLock.clear(); + // <- SAFE + + sFilter.clear(); + } + + if (!sFilter.isEmpty()) + { + rDescriptor[utl::MediaDescriptor::PROP_TYPENAME ] <<= sType ; + rDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter; + return; + } + } + catch(const css::uno::Exception&) + {} +} + + +bool TypeDetection::impl_getPreselectionForType( + const OUString& sPreSelType, const util::URL& aParsedURL, FlatDetection& rFlatTypes, bool bDocService) +{ + // Can be used to suppress execution of some parts of this method + // if it's already clear that detected type is valid or not. + // It's necessary to use shared code at the end, which update + // all return parameters consistency! + bool bBreakDetection = false; + + // Further we must know if it matches by pattern + // Every flat detected type by pattern won't be detected deep! + bool bMatchByPattern = false; + + // And we must know if a preselection must be preferred, because + // it matches by its extension too. + bool bMatchByExtension = false; + + // validate type + OUString sType(sPreSelType); + CacheItem aType; + try + { + // SAFE -> -------------------------- + osl::MutexGuard aLock(m_aMutex); + aType = GetTheFilterCache().getItem(FilterCache::E_TYPE, sType); + // <- SAFE -------------------------- + } + catch(const css::container::NoSuchElementException&) + { + sType.clear(); + bBreakDetection = true; + } + + if (!bBreakDetection) + { + // We can't check a preselected type for a given stream! + // So we must believe, that it can work ... + if ( aParsedURL.Complete == "private:stream" ) + bBreakDetection = true; + } + + if (!bBreakDetection) + { + // extract extension from URL .. to check it case-insensitive ! + INetURLObject aParser (aParsedURL.Main); + OUString sExtension = aParser.getExtension(INetURLObject::LAST_SEGMENT , + true , + INetURLObject::DecodeMechanism::WithCharset); + sExtension = sExtension.toAsciiLowerCase(); + + // otherwise we must know, if it matches to the given URL really. + // especially if it matches by its extension or pattern registration. + const css::uno::Sequence<OUString> lExtensions = aType[PROPNAME_EXTENSIONS].get<css::uno::Sequence<OUString> >(); + const css::uno::Sequence<OUString> lURLPattern = aType[PROPNAME_URLPATTERN].get<css::uno::Sequence<OUString> >(); + + for (auto const& extension : lExtensions) + { + OUString sCheckExtension(extension.toAsciiLowerCase()); + if (sCheckExtension == sExtension) + { + bBreakDetection = true; + bMatchByExtension = true; + break; + } + } + + if (!bBreakDetection) + { + for (auto const& elem : lURLPattern) + { + WildCard aCheck(elem); + if (aCheck.Matches(aParsedURL.Main)) + { + bMatchByPattern = true; + break; + } + } + } + } + + // if it's a valid type - set it on all return values! + if (!sType.isEmpty()) + { + FlatDetection::iterator it = std::find_if(rFlatTypes.begin(), rFlatTypes.end(), FindByType(sType)); + if (it != rFlatTypes.end()) + { + if (bMatchByExtension) + it->bMatchByExtension = true; + if (bMatchByPattern) + it->bMatchByPattern = true; + if (bDocService) + it->bPreselectedByDocumentService = true; + } + + return true; + } + + // not valid! + return false; +} + +void TypeDetection::impl_getPreselectionForDocumentService( + const OUString& sPreSelDocumentService, const util::URL& aParsedURL, FlatDetection& rFlatTypes) +{ + // get all filters, which match to this doc service + std::vector<OUString> lFilters; + try + { + // SAFE -> -------------------------- + osl::MutexGuard aLock(m_aMutex); + + // Attention: For executing next lines of code, We must be sure that + // all filters already loaded :-( + // That can disturb our "load on demand feature". But we have no other chance! + auto & cache = GetTheFilterCache(); + cache.load(FilterCache::E_CONTAINS_FILTERS); + + css::beans::NamedValue lIProps[] { + { PROPNAME_DOCUMENTSERVICE, css::uno::Any(sPreSelDocumentService) } }; + lFilters = cache.getMatchingItemsByProps(FilterCache::E_FILTER, lIProps); + // <- SAFE -------------------------- + } + catch (const css::container::NoSuchElementException&) + { + lFilters.clear(); + } + + // step over all filters, and check if its registered type + // match the given URL. + // But use temp. list of "preselected types" instead of incoming rFlatTypes list! + // The reason behind: we must filter the obtained results. And copying stl entries + // is an easier job than removing them .-) + for (auto const& filter : lFilters) + { + OUString aType = impl_getTypeFromFilter(filter); + if (aType.isEmpty()) + continue; + + impl_getPreselectionForType(aType, aParsedURL, rFlatTypes, true); + } +} + +OUString TypeDetection::impl_getTypeFromFilter(const OUString& rFilterName) +{ + CacheItem aFilter; + try + { + osl::MutexGuard aLock(m_aMutex); + aFilter = GetTheFilterCache().getItem(FilterCache::E_FILTER, rFilterName); + } + catch (const container::NoSuchElementException&) + { + return OUString(); + } + + OUString aType; + aFilter[PROPNAME_TYPE] >>= aType; + return aType; +} + +void TypeDetection::impl_getAllFormatTypes( + const util::URL& aParsedURL, utl::MediaDescriptor const & rDescriptor, FlatDetection& rFlatTypes) +{ + rFlatTypes.clear(); + + // Get all filters that we have. + std::vector<OUString> aFilterNames; + try + { + osl::MutexGuard aLock(m_aMutex); + auto & cache = GetTheFilterCache(); + cache.load(FilterCache::E_CONTAINS_FILTERS); + aFilterNames = cache.getItemNames(FilterCache::E_FILTER); + } + catch (const container::NoSuchElementException&) + { + return; + } + + // Retrieve the default type for each of these filters, and store them. + for (auto const& filterName : aFilterNames) + { + OUString aType = impl_getTypeFromFilter(filterName); + + if (aType.isEmpty()) + continue; + + FlatDetectionInfo aInfo; // all flags set to false by default. + aInfo.sType = aType; + rFlatTypes.push_back(aInfo); + } + + { + // Get all types that match the URL alone. + FlatDetection aFlatByURL; + GetTheFilterCache().detectFlatForURL(aParsedURL, aFlatByURL); + for (auto const& elem : aFlatByURL) + { + FlatDetection::iterator itPos = std::find_if(rFlatTypes.begin(), rFlatTypes.end(), FindByType(elem.sType)); + if (itPos == rFlatTypes.end()) + // Not in the list yet. + rFlatTypes.push_back(elem); + else + { + // Already in the list. Update the flags. + FlatDetectionInfo& rInfo = *itPos; + const FlatDetectionInfo& rThisInfo = elem; + if (rThisInfo.bMatchByExtension) + rInfo.bMatchByExtension = true; + if (rThisInfo.bMatchByPattern) + rInfo.bMatchByPattern = true; + if (rThisInfo.bPreselectedByDocumentService) + rInfo.bPreselectedByDocumentService = true; + } + } + } + + // Remove duplicates. + std::stable_sort(rFlatTypes.begin(), rFlatTypes.end(), SortByType()); + auto last = std::unique(rFlatTypes.begin(), rFlatTypes.end(), EqualByType()); + rFlatTypes.erase(last, rFlatTypes.end()); + + // Mark pre-selected type (if any) to have it prioritized. + OUString sSelectedType = rDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TYPENAME, OUString()); + if (!sSelectedType.isEmpty()) + impl_getPreselectionForType(sSelectedType, aParsedURL, rFlatTypes, false); + + // Mark all types preferred by the current document service, to have it prioritized. + OUString sSelectedDoc = rDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_DOCUMENTSERVICE, OUString()); + if (!sSelectedDoc.isEmpty()) + impl_getPreselectionForDocumentService(sSelectedDoc, aParsedURL, rFlatTypes); +} + + +OUString TypeDetection::impl_detectTypeFlatAndDeep( utl::MediaDescriptor& rDescriptor , + const FlatDetection& lFlatTypes , + bool bAllowDeep , + OUString& rLastChance ) +{ + // reset it everytimes, so the outside code can distinguish between + // a set and a not set value. + rLastChance.clear(); + + // step over all possible types for this URL. + // solutions: + // a) no types => no detection + // b) deep detection not allowed => return first valid type of list (because it's the preferred or the first valid one) + // or(!) match by URLPattern => in such case a deep detection will be suppressed! + // c) type has no detect service => safe the first occurred type without a detect service + // as "last chance"(!). It will be used outside of this method + // if no further type could be detected. + // It must be the first one, because it can be a preferred type. + // Our types list was sorted by such criteria! + // d) detect service return a valid result => return its decision + // e) detect service return an invalid result + // or any needed information could not be + // obtained from the cache => ignore it, and continue with search + + for (auto const& flatTypeInfo : lFlatTypes) + { + if (m_bCancel) + break; + OUString sFlatType = flatTypeInfo.sType; + + if (!impl_validateAndSetTypeOnDescriptor(rDescriptor, sFlatType)) + continue; + + // b) + if ( + (!bAllowDeep ) || + (flatTypeInfo.bMatchByPattern) + ) + { + return sFlatType; + } + + try + { + // SAFE -> ---------------------------------- + osl::ClearableMutexGuard aLock(m_aMutex); + CacheItem aType = GetTheFilterCache().getItem(FilterCache::E_TYPE, sFlatType); + aLock.clear(); + + OUString sDetectService; + aType[PROPNAME_DETECTSERVICE] >>= sDetectService; + + // c) + if (sDetectService.isEmpty()) + { + // flat detected types without any registered deep detection service and not + // preselected by the user can be used as LAST CHANCE in case no other type could + // be detected. Of course only the first type without deep detector can be used. + // Further ones has to be ignored. + if (rLastChance.isEmpty()) + rLastChance = sFlatType; + + continue; + } + + OUString sDeepType = impl_askDetectService(sDetectService, rDescriptor); + + // d) + if (!sDeepType.isEmpty()) + return sDeepType; + } + catch(const css::container::NoSuchElementException&) + {} + // e) + } + + return OUString(); + // <- SAFE ---------------------------------- +} + +void TypeDetection::impl_seekStreamToZero(utl::MediaDescriptor const & rDescriptor) +{ + // try to seek to 0 ... + // But because XSeekable is an optional interface ... try it only .-) + css::uno::Reference< css::io::XInputStream > xStream = rDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_INPUTSTREAM, + css::uno::Reference< css::io::XInputStream >()); + css::uno::Reference< css::io::XSeekable > xSeek(xStream, css::uno::UNO_QUERY); + if (!xSeek.is()) + return; + + try + { + xSeek->seek(0); + } + catch(const css::uno::RuntimeException&) + { + throw; + } + catch(const css::uno::Exception&) + { + } +} + +OUString TypeDetection::impl_askDetectService(const OUString& sDetectService, + utl::MediaDescriptor& rDescriptor ) +{ + // Open the stream and add it to the media descriptor if this method is called for the first time. + // All following requests to this method will detect, that there already exists a stream .-) + // Attention: This method throws an exception if the stream could not be opened. + // It's important to break any further detection in such case. + // Catch it on the highest detection level only !!! + impl_openStream(rDescriptor); + + // seek to 0 is an optional feature to be more robust against + // "simple implemented detect services" .-) + impl_seekStreamToZero(rDescriptor); + + css::uno::Reference< css::document::XExtendedFilterDetection > xDetector; + css::uno::Reference< css::uno::XComponentContext > xContext; + + // SAFE -> + { + osl::MutexGuard aLock(m_aMutex); + xContext = m_xContext; + } + // <- SAFE + + try + { + // Attention! If e.g. an office module was not installed sometimes we + // find a registered detect service, which is referred inside the + // configuration ... but not really installed. On the other side we use + // third party components here, which can make trouble anyway. So we + // should handle errors during creation of such services more + // gracefully .-) + xDetector.set( + xContext->getServiceManager()->createInstanceWithContext(sDetectService, xContext), + css::uno::UNO_QUERY_THROW); + } + catch (...) + { + } + + if ( ! xDetector.is()) + return OUString(); + + OUString sDeepType; + try + { + // start deep detection + // Don't forget to convert stl descriptor to its uno representation. + + /* Attention! + You have to use an explicit instance of this uno sequence... + Because it's used as an in out parameter. And in case of a temp. used object + we will run into memory corruptions! + */ + css::uno::Sequence< css::beans::PropertyValue > lDescriptor; + rDescriptor >> lDescriptor; + sDeepType = xDetector->detect(lDescriptor); + rDescriptor << lDescriptor; + } + catch (...) + { + // We should ignore errors here. + // Thrown exceptions mostly will end in crash recovery... + // But might be we find another deep detection service which can detect the same + // document without a problem .-) + sDeepType.clear(); + } + + // seek to 0 is an optional feature to be more robust against + // "simple implemented detect services" .-) + impl_seekStreamToZero(rDescriptor); + + // analyze the results + // a) detect service returns "" => return "" too and remove TYPE/FILTER prop from descriptor + // b) returned type is unknown => return "" too and remove TYPE/FILTER prop from descriptor + // c) returned type is valid => check TYPE/FILTER props inside descriptor and return the type + + // this special helper checks for a valid type + // and set right values on the descriptor! + bool bValidType = impl_validateAndSetTypeOnDescriptor(rDescriptor, sDeepType); + if (bValidType) + return sDeepType; + + return OUString(); +} + + +OUString TypeDetection::impl_askUserForTypeAndFilterIfAllowed(utl::MediaDescriptor& rDescriptor) +{ + css::uno::Reference< css::task::XInteractionHandler > xInteraction = + rDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_INTERACTIONHANDLER, + css::uno::Reference< css::task::XInteractionHandler >()); + + if (!xInteraction.is()) + return OUString(); + + OUString sURL = + rDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_URL, + OUString()); + + css::uno::Reference< css::io::XInputStream > xStream = + rDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_INPUTSTREAM, + css::uno::Reference< css::io::XInputStream >()); + + // Don't disturb the user for "non existing files - means empty URLs" or + // if we were forced to detect a stream. + // Reason behind: we must be sure to ask user for "unknown contents" only... + // and not for "missing files". Especially if detection is done by a stream only + // we can't check if the stream points to an "existing content"! + if ( + (sURL.isEmpty() ) || // "non existing file" ? + (!xStream.is() ) || // non existing file ! + (sURL.equalsIgnoreAsciiCase("private:stream")) // not a good idea .-) + ) + return OUString(); + + try + { + // create a new request to ask user for its decision about the usable filter + ::framework::RequestFilterSelect aRequest(sURL); + xInteraction->handle(aRequest.GetRequest()); + + // "Cancel" pressed? => return with error + if (aRequest.isAbort()) + return OUString(); + + // "OK" pressed => verify the selected filter, get its corresponding + // type and return it. (BTW: We must update the media descriptor here ...) + // The user selected explicitly a filter ... but normally we are interested on + // a type here only. But we must be sure, that the selected filter is used + // too and no ambiguous filter registration disturb us .-) + + OUString sFilter = aRequest.getFilter(); + if (!impl_validateAndSetFilterOnDescriptor(rDescriptor, sFilter)) + return OUString(); + + OUString sType; + rDescriptor[utl::MediaDescriptor::PROP_TYPENAME] >>= sType; + return sType; + } + catch(const css::uno::Exception&) + {} + + return OUString(); +} + + +void TypeDetection::impl_openStream(utl::MediaDescriptor& rDescriptor) +{ + bool bSuccess = false; + OUString sURL = rDescriptor.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_URL, OUString() ); + bool bRequestedReadOnly = rDescriptor.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_READONLY, false ); + if ( comphelper::isFileUrl( sURL ) ) + { + // OOo uses own file locking mechanics in case of local file + bSuccess = rDescriptor.addInputStreamOwnLock(); + } + else + bSuccess = rDescriptor.addInputStream(); + + if ( !bSuccess ) + throw css::uno::Exception( + "Could not open stream for <" + sURL + ">", + static_cast<OWeakObject *>(this)); + + if ( !bRequestedReadOnly ) + { + // The MediaDescriptor implementation adds ReadOnly argument if the file can not be opened for writing + // this argument should be either removed or an additional argument should be added so that application + // can separate the case when the user explicitly requests readonly document. + // The current solution is to remove it here. + rDescriptor.erase( utl::MediaDescriptor::PROP_READONLY ); + } +} + + +void TypeDetection::impl_removeTypeFilterFromDescriptor(utl::MediaDescriptor& rDescriptor) +{ + utl::MediaDescriptor::iterator pItType = rDescriptor.find(utl::MediaDescriptor::PROP_TYPENAME ); + utl::MediaDescriptor::iterator pItFilter = rDescriptor.find(utl::MediaDescriptor::PROP_FILTERNAME); + if (pItType != rDescriptor.end()) + rDescriptor.erase(pItType); + if (pItFilter != rDescriptor.end()) + rDescriptor.erase(pItFilter); +} + + +bool TypeDetection::impl_validateAndSetTypeOnDescriptor( utl::MediaDescriptor& rDescriptor, + const OUString& sType ) +{ + // SAFE -> + { + osl::MutexGuard aLock(m_aMutex); + if (GetTheFilterCache().hasItem(FilterCache::E_TYPE, sType)) + { + rDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sType; + return true; + } + } + // <- SAFE + + // remove all related information from the descriptor + impl_removeTypeFilterFromDescriptor(rDescriptor); + return false; +} + + +bool TypeDetection::impl_validateAndSetFilterOnDescriptor( utl::MediaDescriptor& rDescriptor, + const OUString& sFilter ) +{ + try + { + // SAFE -> + osl::ClearableMutexGuard aLock(m_aMutex); + + auto & cache = GetTheFilterCache(); + CacheItem aFilter = cache.getItem(FilterCache::E_FILTER, sFilter); + OUString sType; + aFilter[PROPNAME_TYPE] >>= sType; + + aLock.clear(); + // <- SAFE + + // found valid type and filter => set it on the given descriptor + rDescriptor[utl::MediaDescriptor::PROP_TYPENAME ] <<= sType ; + rDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter; + return true; + } + catch(const css::container::NoSuchElementException&){} + + // remove all related information from the descriptor + impl_removeTypeFilterFromDescriptor(rDescriptor); + return false; +} + +} // namespace filter + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +filter_TypeDetection_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new filter::config::TypeDetection(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/config/cache/typedetection.hxx b/filter/source/config/cache/typedetection.hxx new file mode 100644 index 000000000..114410392 --- /dev/null +++ b/filter/source/config/cache/typedetection.hxx @@ -0,0 +1,350 @@ +/* -*- 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 . + */ +#pragma once + +#include "basecontainer.hxx" +#include <com/sun/star/document/XTypeDetection.hpp> +#include <com/sun/star/frame/XTerminateListener.hpp> +#include <unotools/mediadescriptor.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/implbase.hxx> +#include <comphelper/compbase.hxx> + +namespace filter::config { + +class TerminateDetection; + +/** @short implements the service <type scope="com.sun.star.document">TypeDetection</type>. + */ +class TypeDetection : public ::cppu::ImplInheritanceHelper< BaseContainer , + css::document::XTypeDetection > +{ + +// native interface + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + rtl::Reference<TerminateDetection> m_xTerminateListener; + bool m_bCancel; + +public: + + + // ctor/dtor + + /** @short standard ctor to connect this interface wrapper to + the global filter cache instance ... + + @param rxContext + reference to the uno service manager, which created this service instance. + */ + explicit TypeDetection(const css::uno::Reference< css::uno::XComponentContext >& rxContext); + + void cancel() + { + m_bCancel = true; + } + + + /** @short standard dtor. + */ + virtual ~TypeDetection() override; + + +// private helper + +private: + + bool impl_getPreselectionForType( + const OUString& sPreSelType, const css::util::URL& aParsedURL, FlatDetection& rFlatTypes, bool bDocService); + + void impl_getPreselectionForDocumentService( + const OUString& sPreSelDocumentService, const css::util::URL& aParsedURL, FlatDetection& rFlatTypes); + + OUString impl_getTypeFromFilter(const OUString& rFilterName); + + /** + * Get all format types that we handle. + */ + void impl_getAllFormatTypes( + const css::util::URL& aParsedURL, utl::MediaDescriptor const & rDescriptor, + FlatDetection& rFlatTypes); + + + /** @short make a combined flat/deep type detection + + @descr It steps over all flat detected types (given by the parameter lFlatTypes), + try it and search for most suitable one. + The specified MediaDescriptor will be patched, so it contain + the right values every time. Using of any deep detection service + can be enabled/disabled. And last but not least: If the results + won't be really clear (because a flat detected type has no deep + detection service), a "suggested" type name will be returned as "rLastChance". + It can be used after e.g. all well known deep detection services + was used without getting any result. Then this "last-chance-type" + should be returned. Of course using of it can fail too ... but it's a try :-) + + @param rDescriptor + provides any easy-to-use stl interface to the MediaDescriptor. + Note : Its content will be adapted to returned result of this method. + Means: The type/filter entries of it will be actualized or removed from it. + + @param lFlatTypes + a list of all flat detected types, which should be checked here. + No other types are allowed here! + + @param rLastChance + the internal name of a "suggested type" ... (see before) + Note: it will be reset to an empty string every time. So + a set value of "rLastChance" can be detected outside very easy. + + @param bAllowDeep + enable/disable using of a might existing deep detection service. + + @return The internal name of a detected type. + An empty value if detection failed. ... but see rLastChance + for additional returns! + */ + OUString impl_detectTypeFlatAndDeep( utl::MediaDescriptor& rDescriptor , + const FlatDetection& lFlatTypes , + bool bAllowDeep , + OUString& rLastChance ); + + + /** @short seek a might existing stream to position 0. + + @descr This is an optional action to be more robust + in case any detect service doesn't make this seek... + Normally it's part of any called detect service or filter... + but sometimes it's not done there. + + @param rDescriptor + a stl representation of the MediaDescriptor as in/out parameter. + */ + static void impl_seekStreamToZero(utl::MediaDescriptor const & rDescriptor); + + + /** @short make deep type detection for a specified + detect service (threadsafe!). + + @descr It creates the right uno service, prepare the + needed MediaDescriptor, call the right interfaces, + and return the results. + + @attention The results (means type and corresponding filter) + are already part of the in/out parameter pDescriptor. + (in case they were valid). + + @param sDetectService + uno service name of the detect service. + + @param rDescriptor + a stl representation of the MediaDescriptor as in/out parameter. + */ + OUString impl_askDetectService(const OUString& sDetectService, + utl::MediaDescriptor& rDescriptor ); + + + /** @short try to find an interaction handler and + ask him to select a possible filter for + this unknown format. + + @descr If the user select a filter, it will be used as return value + without further checking against the given file content! + + @param rDescriptor + a stl representation of the MediaDescriptor as in/out parameter. + + @return [string] + a valid type name or an empty string if user canceled interaction. + */ + OUString impl_askUserForTypeAndFilterIfAllowed(utl::MediaDescriptor& rDescriptor); + + + /** @short check if an input stream is already part of the + given MediaDescriptor and creates a new one if necessary. + + @attention This method does further something special! + <ul> + <li> + If the given URL seem to be a streamable content, but creation of the stream + failed (might by an IOException), this method throws an exception. + (May be an existing interaction handler must be called here too ...) + The whole detection must be interrupted then and the interface method queryTypeByDescriptor() + must return an empty type name value. + + That prevent us against multiple handling of the same error more than ones + (e.g. if we ask all detect services as fallback ...). + </li> + <li> + In case the stream already exists inside the descriptor this method does nothing. + </li> + <li> + In case the stream does not exists but can be created successfully, the stream will + be added to the descriptor. + </li> + </ul> + + @param rDescriptor + provides any easy-to-use stl interface to the MediaDescriptor. + Note : Its content will be adapted to returned result of this method. + Means: The stream will be added to it. + + @throw Any suitable exception if stream should be opened but operation was not successful. + Note: If an interactionHandler is part of the given descriptor too, it was already used. + Means: let the exception pass through the top most interface method! + */ + void impl_openStream(utl::MediaDescriptor& rDescriptor); + + + /** @short validate the specified type and its relationships + and set all needed information related to this type + in the specified descriptor. + + @descr Related information are: - corresponding filter + - media type + - ... + + @param rDescriptor + provides access to the outside MediaDescriptor. + + @param sType + the name of the type, which should be set on the descriptor. + Can be empty to remove any related value from the descriptor! + + @return TRUE the specified type and its registrations was valid(!) and + could be set on the descriptor. + */ + bool impl_validateAndSetTypeOnDescriptor( utl::MediaDescriptor& rDescriptor, + const OUString& sType ); + + + /** @short validate the specified filter and its relationships + and set all needed information related to this filter + in the specified descriptor. + + @descr Related information are: - corresponding type + - ... + + @param rDescriptor + provides access to the outside MediaDescriptor. + + @param sFilter + the name of the filter, which should be set on the descriptor. + Can be empty to remove any related value from the descriptor! + + @return TRUE the specified type and its registrations was valid(!) and + could be set on the descriptor. + */ + bool impl_validateAndSetFilterOnDescriptor( utl::MediaDescriptor& rDescriptor, + const OUString& sFilter ); + + + /** @short remove anything related to a TYPE/FILTER entry from the + specified MediaDescriptor. + + @descr This method works together with impl_validateAndSetTypeOnDescriptor()/ + impl_validateAndSetFilterOnDescriptor(). All information, which can be + set by these two operations must be "removable" by this method. + + @param rDescriptor + reference to the MediaDescriptor (represented by an easy-to-use + stl interface!), which should be patched. + */ + static void impl_removeTypeFilterFromDescriptor(utl::MediaDescriptor& rDescriptor); + + + /** @short search the best suitable filter for the given type + and add it into the media descriptor. + + @descr Normally this is a type detection only ... + but for some special features we must overwrite our detection + because a file must be loaded into a special (means preselected) + application. + + E.g. CSV/TXT format are sometimes ugly to handle .-) + + Note: If the descriptor already include a filter + (may be selected by a FilterSelect interaction or preselected + by the user itself) ... we don't change that here ! + + @param rDescriptor + reference to the MediaDescriptor (represented by an easy-to-use + stl interface!), which should be patched. + + @param sType + the internal type name, where we search a filter for. + Used as IN/OUT parameter so we can overrule the detection result for + types too ! + + @note #i60158# + sometimes our text ascii and our csv filter can't work together. + Then we overwrite our detection hardly. + sType param is used as out parameter then too ... and + rDescriptor will be changed by selecting another filter. + (see code) + */ + void impl_checkResultsAndAddBestFilter(utl::MediaDescriptor& rDescriptor, + OUString& sType ); + + +// uno interface + +public: + + + // XTypeDetection + + virtual OUString SAL_CALL queryTypeByURL(const OUString& sURL) override; + + virtual OUString SAL_CALL queryTypeByDescriptor(css::uno::Sequence< css::beans::PropertyValue >& lDescriptor, + sal_Bool bAllowDeep ) override; + +}; + +class TerminateDetection : public comphelper::WeakComponentImplHelper<css::frame::XTerminateListener> +{ +private: + TypeDetection* m_pTypeDetection; + +public: + + using comphelper::WeakComponentImplHelperBase::disposing; + virtual void SAL_CALL disposing(const css::lang::EventObject&) override + { + } + + // XTerminateListener + virtual void SAL_CALL queryTermination(const css::lang::EventObject&) override + { + m_pTypeDetection->cancel(); + } + + virtual void SAL_CALL notifyTermination(const css::lang::EventObject&) override + { + } + + TerminateDetection(TypeDetection* pTypeDetection) + : m_pTypeDetection(pTypeDetection) + { + } +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |