diff options
Diffstat (limited to 'filter/source/config/cache/typedetection.cxx')
-rw-r--r-- | filter/source/config/cache/typedetection.cxx | 1257 |
1 files changed, 1257 insertions, 0 deletions
diff --git a/filter/source/config/cache/typedetection.cxx b/filter/source/config/cache/typedetection.cxx new file mode 100644 index 000000000000..4fe9a1df64e6 --- /dev/null +++ b/filter/source/config/cache/typedetection.cxx @@ -0,0 +1,1257 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_filter.hxx" +#include "typedetection.hxx" +#include "constant.hxx" + +//_______________________________________________ +// includes +#include <com/sun/star/document/XExtendedFilterDetection.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#ifndef _COM_SUN_STAR_IO_XINPUSTREAM_HPP_ +#include <com/sun/star/io/XInputStream.hpp> +#endif +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <tools/wldcrd.hxx> +#include <rtl/ustrbuf.hxx> +#include <framework/interaction.hxx> +#include <tools/urlobj.hxx> +#include <unotools/localfilehelper.hxx> + +//_______________________________________________ +// namespace + +namespace filter{ + namespace config{ + +namespace css = ::com::sun::star; + +//_______________________________________________ +// definitions + +// Use this switch to change the behaviour of preselection DocumentService ... (see using for further informations) +#define IGNORE_NON_URLMATCHING_TYPES_FOR_PRESELECTION_DOCUMENTSERVICE + +// enable/disable special handling for CSV/TXT problem +#define WORKAROUND_CSV_TXT_BUG_i60158 + +/*----------------------------------------------- + 03.07.2003 11:25 +-----------------------------------------------*/ +TypeDetection::TypeDetection(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR) +{ + BaseContainer::init(xSMGR , + TypeDetection::impl_getImplementationName() , + TypeDetection::impl_getSupportedServiceNames(), + FilterCache::E_TYPE ); +} + +/*----------------------------------------------- + 03.07.2003 10:36 +-----------------------------------------------*/ +TypeDetection::~TypeDetection() +{ +} + +/*----------------------------------------------- + 03.11.2003 08:43 +-----------------------------------------------*/ +::rtl::OUString SAL_CALL TypeDetection::queryTypeByURL(const ::rtl::OUString& sURL) + throw (css::uno::RuntimeException) +{ + ::rtl::OUString sType; + + // SAFE -> + ::osl::ResettableMutexGuard aLock(m_aLock); + + css::util::URL aURL; + aURL.Complete = sURL; + css::uno::Reference< css::util::XURLTransformer > xParser(m_xSMGR->createInstance(SERVICE_URLTRANSFORMER), css::uno::UNO_QUERY); + xParser->parseStrict(aURL); + + // set std types as minimum requirement first! + // Only in case no type was found for given URL, + // use optional types too ... + FlatDetection lFlatTypes; + m_rCache->detectFlatForURL(aURL, lFlatTypes); + + if ( + (lFlatTypes.size() < 1 ) && + (!m_rCache->isFillState(FilterCache::E_CONTAINS_TYPES)) + ) + { + m_rCache->load(FilterCache::E_CONTAINS_TYPES); + m_rCache->detectFlatForURL(aURL, lFlatTypes); + } + + // first item is guaranteed as "preferred" one! + if (lFlatTypes.size() > 0) + { + const FlatDetectionInfo& aMatch = *(lFlatTypes.begin()); + sType = aMatch.sType; + } + + return sType; + // <- SAFE +} + +/*----------------------------------------------- + 31.10.2003 09:36 +-----------------------------------------------*/ +::rtl::OUString SAL_CALL TypeDetection::queryTypeByDescriptor(css::uno::Sequence< css::beans::PropertyValue >& lDescriptor, + sal_Bool bAllowDeep ) + throw (css::uno::RuntimeException) +{ + // make the descriptor more useable :-) + ::comphelper::MediaDescriptor stlDescriptor(lDescriptor); + + // SAFE -> ---------------------------------- + ::osl::ResettableMutexGuard aLock(m_aLock); + + //******************************************* + // parse given URL to split it into e.g. main and jump marks ... + ::rtl::OUString sURL = stlDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_URL(), ::rtl::OUString()); + +#if OSL_DEBUG_LEVEL > 0 + if (stlDescriptor.find(::rtl::OUString::createFromAscii("FileName")) != stlDescriptor.end()) + OSL_ENSURE(sal_False, "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(m_xSMGR->createInstance(SERVICE_URLTRANSFORMER), css::uno::UNO_QUERY); + xParser->parseStrict(aURL); + + //******************************************* + // preselected filter, type or document service? + // use it as first "flat" detected type later! + FlatDetection lFlatTypes; + impl_getPreselection(aURL, stlDescriptor, lFlatTypes); + + //******************************************* + // get all types, which match to the given descriptor + // That can be true by: extensions/url pattern/mime type etcpp. + m_rCache->detectFlatForURL(aURL, lFlatTypes); + + aLock.clear(); + // <- SAFE ---------------------------------- + + ::rtl::OUString sType ; + ::rtl::OUString sLastChance; + + try + { + //******************************************* + // 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 an URL pattern(!) or if deep detection isnt allowed from + // outside (bAllowDeep=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 ... + OUStringList lUsedDetectors; + if (lFlatTypes.size()>0) + sType = impl_detectTypeFlatAndDeep(stlDescriptor, lFlatTypes, bAllowDeep, lUsedDetectors, sLastChance); + + //******************************************* + // if no flat detected (nor preselected!) type could be + // verified and no error occured during creation of + // the might needed input stream, start detection + // which uses all registered deep detection services. + if ( + (!sType.getLength()) && + (bAllowDeep ) + ) + { + sType = impl_detectTypeDeepOnly(stlDescriptor, lUsedDetectors); + } + + //******************************************* + // flat detection failed + // pure deep detection failed + // => ask might existing InteractionHandler + // means: ask user for it's decision + if (!sType.getLength()) + sType = impl_askUserForTypeAndFilterIfAllowed(stlDescriptor); + + //******************************************* + // no real detected type - but a might valid one. + // update descriptor and set last chance for return. + if (!sType.getLength() && sLastChance.getLength()) + { + OSL_ENSURE(sal_False, "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&) + { sType = ::rtl::OUString(); } + + //******************************************* + // 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; +} + +/*----------------------------------------------- + 03.07.2003 10:36 +-----------------------------------------------*/ +void TypeDetection::impl_checkResultsAndAddBestFilter(::comphelper::MediaDescriptor& rDescriptor, + ::rtl::OUString& sType ) +{ + // a) + // Dont overwrite a might preselected filter! + ::rtl::OUString sFilter = rDescriptor.getUnpackedValueOrDefault( + ::comphelper::MediaDescriptor::PROP_FILTERNAME(), + ::rtl::OUString()); + if (sFilter.getLength()) + return; + + // b) + // check a preselected document service too. + // Then we have to search a suitable filter witin this module. + ::rtl::OUString sDocumentService = rDescriptor.getUnpackedValueOrDefault( + ::comphelper::MediaDescriptor::PROP_DOCUMENTSERVICE(), + ::rtl::OUString()); + if (sDocumentService.getLength()) + { + try + { + ::rtl::OUString sRealType = sType; + + #ifdef WORKAROUND_CSV_TXT_BUG_i60158 + // Workaround for #i60158# + // We do not have right filter for Text_Ascii in calc nor a suitable filter for CSV in writer. + // So we must overrule our detection and make the right things. Normaly we should have + // one type TextAscii and two filters registered for these one type. + // But then we loose automatic opening of CSV files in calc instead of opening these files + // inside writer. + if ( + (sDocumentService.equalsAscii("com.sun.star.sheet.SpreadsheetDocument")) && + ( + (sRealType.equalsAscii("writer_Text" )) || + (sRealType.equalsAscii("writer_Text_encoded")) + ) + ) + { + sRealType = ::rtl::OUString::createFromAscii("calc_Text_txt_csv_StarCalc"); + } + else + if ( + (sDocumentService.equalsAscii("com.sun.star.text.TextDocument")) && + (sRealType.equalsAscii("calc_Text_txt_csv_StarCalc" )) + ) + { + sRealType = ::rtl::OUString::createFromAscii("writer_Text"); + } + #endif // WORKAROUND_CSV_TXT_BUG_i60158 + + // SAFE -> + ::osl::ResettableMutexGuard aLock(m_aLock); + + // Attention: For executing next lines of code, We must be shure that + // all filters already loaded :-( + // That can disturb our "load on demand feature". But we have no other chance! + m_rCache->load(FilterCache::E_CONTAINS_FILTERS); + + CacheItem lIProps; + lIProps[PROPNAME_DOCUMENTSERVICE] <<= sDocumentService; + lIProps[PROPNAME_TYPE ] <<= sRealType; + OUStringList lFilters = m_rCache->getMatchingItemsByProps(FilterCache::E_FILTER, lIProps); + + aLock.clear(); + // <- SAFE + + for ( OUStringList::const_iterator pIt = lFilters.begin(); + pIt != lFilters.end() && sFilter.getLength() == 0 ; + ++pIt ) + { + // SAFE -> + aLock.reset(); + try + { + CacheItem aFilter = m_rCache->getItem(FilterCache::E_FILTER, *pIt); + sal_Int32 nFlags = 0; + aFilter[PROPNAME_FLAGS] >>= nFlags; + + if ((nFlags & FLAGVAL_IMPORT) == FLAGVAL_IMPORT) + sFilter = *pIt; + } + catch(const css::uno::Exception&) {} + aLock.clear(); + // <- SAFE + } + + if (sFilter.getLength() > 0) + { + rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME() ] <<= sRealType; + rDescriptor[::comphelper::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 = ::rtl::OUString(); + try + { + // SAFE -> + ::osl::ResettableMutexGuard aLock(m_aLock); + + CacheItem aType = m_rCache->getItem(FilterCache::E_TYPE, sType); + aType[PROPNAME_PREFERREDFILTER] >>= sFilter; + CacheItem aFilter = m_rCache->getItem(FilterCache::E_FILTER, sFilter); + + aLock.clear(); + // <- SAFE + + // no exception => found valid type and filter => set it on the given descriptor + rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME() ] <<= sType ; + rDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= sFilter; + return; + } + catch(const css::uno::Exception&) + {} + + // d) + // Search for any import(!) filter, which is registered for this type. + sFilter = ::rtl::OUString(); + try + { + // SAFE -> + ::osl::ResettableMutexGuard aLock(m_aLock); + + // Attention: For executing next lines of code, We must be shure that + // all filters already loaded :-( + // That can disturb our "load on demand feature". But we have no other chance! + m_rCache->load(FilterCache::E_CONTAINS_FILTERS); + + CacheItem lIProps; + lIProps[PROPNAME_TYPE] <<= sType; + OUStringList lFilters = m_rCache->getMatchingItemsByProps(FilterCache::E_FILTER, lIProps); + + aLock.clear(); + // <- SAFE + + OUStringList::const_iterator pIt; + for ( pIt = lFilters.begin(); + pIt != lFilters.end() ; + ++pIt ) + { + sFilter = *pIt; + + // SAFE -> + aLock.reset(); + try + { + CacheItem aFilter = m_rCache->getItem(FilterCache::E_FILTER, sFilter); + sal_Int32 nFlags = 0; + aFilter[PROPNAME_FLAGS] >>= nFlags; + + if ((nFlags & FLAGVAL_IMPORT) == FLAGVAL_IMPORT) + break; + } + catch(const css::uno::Exception&) + { continue; } + aLock.clear(); + // <- SAFE + + sFilter = ::rtl::OUString(); + } + + if (sFilter.getLength()) + { + rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME() ] <<= sType ; + rDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= sFilter; + return; + } + } + catch(const css::uno::Exception&) + {} +} + +/*----------------------------------------------- + 14.11.2003 12:06 +-----------------------------------------------*/ +sal_Bool TypeDetection::impl_getPreselectionForType(const ::rtl::OUString& sPreSelType, + const css::util::URL& aParsedURL , + FlatDetection& rFlatTypes ) +{ + // Can be used to supress execution of some parts of this method + // if its already clear that detected type is valid or not. + // Its neccessary to use shared code at the end, which update + // all return parameters constistency! + sal_Bool bBreakDetection = sal_False; + + // Further we must know if it matches by pattern + // Every flat detected type by pattern wont be detected deep! + sal_Bool bMatchByPattern = sal_False; + + // And we must know if a preselection must be preferred, because + // it matches by it's extension too. + sal_Bool bMatchByExtension = sal_False; + + // If we e.g. collect all filters of a factory (be a forced factory preselection) + // we should preferr all filters of this factory, where the type match the given URL. + // All other types (which sorrespond to filters of the same factory - but dont match + // the URL) should be "used later" for detection and sorted at the end of our return vector + // rFlatTypes! + // => bPreferredPreselection = (matchByExtension || matchByURLPattern) + sal_Bool bPreferredPreselection = sal_False; + + // validate type + ::rtl::OUString sType(sPreSelType); + CacheItem aType; + try + { + // SAFE -> -------------------------- + ::osl::ResettableMutexGuard aLock(m_aLock); + aType = m_rCache->getItem(FilterCache::E_TYPE, sType); + aLock.clear(); + // <- SAFE -------------------------- + } + catch(const css::container::NoSuchElementException&) + { + sType = ::rtl::OUString(); + bBreakDetection = sal_True; + } + + if (!bBreakDetection) + { + // We cant check a preselected type for a given stream! + // So we must believe, that it can work ... + if (aParsedURL.Complete.equalsAsciiL("private:stream", 14)) + bBreakDetection = sal_True; + } + + if (!bBreakDetection) + { + // extract extension from URL .. to check it case-insensitive ! + INetURLObject aParser (aParsedURL.Main); + ::rtl::OUString sExtension = aParser.getExtension(INetURLObject::LAST_SEGMENT , + sal_True , + INetURLObject::DECODE_WITH_CHARSET); + sExtension = sExtension.toAsciiLowerCase(); + + // otherwhise we must know, if it matches to the given URL realy. + // especialy if it matches by its extension or pattern registration. + OUStringList lExtensions(aType[PROPNAME_EXTENSIONS]); + OUStringList lURLPattern(aType[PROPNAME_URLPATTERN]); + + for (OUStringList::const_iterator pIt = lExtensions.begin(); + pIt != lExtensions.end() ; + ++pIt ) + { + ::rtl::OUString sCheckExtension(pIt->toAsciiLowerCase()); + if (sCheckExtension.equals(sExtension)) + { + bBreakDetection = sal_True; + bMatchByExtension = sal_True; + bPreferredPreselection = sal_True; + break; + } + } + + if (!bBreakDetection) + { + for (OUStringList::const_iterator pIt = lURLPattern.begin(); + pIt != lURLPattern.end() ; + ++pIt ) + { + WildCard aCheck(*pIt); + if (aCheck.Matches(aParsedURL.Main)) + { + bBreakDetection = sal_True; + bMatchByPattern = sal_True; + bPreferredPreselection = sal_True; + break; + } + } + } + + /* + Comment ... why the following line of code should be comened out .-) + + This type does not seem to fit the requirements + But its an existing and well known type. + At least - [because may be the extension was missing :-( ] + we should try to detect this type deep ... + So we accept it here :-) + + if (!bBreakDetection) + sType = ::rtl::OUString(); + */ + } + + // if its a valid type - set it on all return values! + if (sType.getLength()) + { + FlatDetectionInfo aInfo; + aInfo.sType = sType; + aInfo.bMatchByExtension = bMatchByExtension; + aInfo.bMatchByPattern = bMatchByPattern; + aInfo.bPreselectedAsType = sal_True; + + if (bPreferredPreselection) + rFlatTypes.push_front(aInfo); + else + rFlatTypes.push_back(aInfo); + + return sal_True; + } + + // not valid! + return sal_False; +} + +/*----------------------------------------------- + 14.11.2003 12:09 +-----------------------------------------------*/ +sal_Bool TypeDetection::impl_getPreselectionForFilter(const ::rtl::OUString& sPreSelFilter, + const css::util::URL& aParsedURL , + FlatDetection& rFlatTypes ) +{ + // Can be used to supress execution of some parts of this method + // if its already clear that detected filter is valid or not. + // Its neccessary to use shared code at the end, which update + // all return parameters constistency! + sal_Bool bBreakDetection = sal_False; + + // validate filter + ::rtl::OUString sFilter(sPreSelFilter); + CacheItem aFilter; + try + { + // SAFE -> -------------------------- + ::osl::ResettableMutexGuard aLock(m_aLock); + aFilter = m_rCache->getItem(FilterCache::E_FILTER, sFilter); + aLock.clear(); + // <- SAFE -------------------------- + } + catch(const css::container::NoSuchElementException&) + { + sFilter = ::rtl::OUString(); + bBreakDetection = sal_True; + } + + if (!bBreakDetection) + { + // get its type and check if it matches the given URL + ::rtl::OUString sType; + aFilter[PROPNAME_TYPE] >>= sType; + + bBreakDetection = impl_getPreselectionForType(sType, aParsedURL, rFlatTypes); + + // not a valid type? -> not a valid filter! + if (!bBreakDetection) + sFilter = ::rtl::OUString(); + } + + // We have to mark all retrieved preselection items as "preselected by filter"! + FlatDetection::iterator pIt; + for ( pIt = rFlatTypes.begin(); + pIt != rFlatTypes.end() ; + ++pIt ) + { + FlatDetectionInfo& rInfo = *pIt; + rInfo.bPreselectedAsType = sal_False; + rInfo.bPreselectedByFilter = sal_True; + } + + if (sFilter.getLength()) + return sal_True; + else + return sal_False; +} + +/*----------------------------------------------- + 14.11.2003 12:11 +-----------------------------------------------*/ +sal_Bool TypeDetection::impl_getPreselectionForDocumentService(const ::rtl::OUString& sPreSelDocumentService, + const css::util::URL& aParsedURL , + FlatDetection& rFlatTypes ) +{ + // get all filters, which match to this doc service + OUStringList lFilters; + try + { + // SAFE -> -------------------------- + ::osl::ResettableMutexGuard aLock(m_aLock); + + // Attention: For executing next lines of code, We must be shure that + // all filters already loaded :-( + // That can disturb our "load on demand feature". But we have no other chance! + m_rCache->load(FilterCache::E_CONTAINS_FILTERS); + + CacheItem lIProps; + lIProps[PROPNAME_DOCUMENTSERVICE] <<= sPreSelDocumentService; + lFilters = m_rCache->getMatchingItemsByProps(FilterCache::E_FILTER, lIProps); + + aLock.clear(); + // <- 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 getted results. And copying of stl entries + // is an easier job then removing it .-) + FlatDetection lPreselections; + for (OUStringList::const_iterator pFilter = lFilters.begin(); + pFilter != lFilters.end() ; + ++pFilter ) + { + const ::rtl::OUString sFilter = *pFilter; + impl_getPreselectionForFilter(sFilter, aParsedURL, lPreselections); + } + + // We have to mark all retrieved preselection items as "preselected by document service". + // Further we must ignore all preselected items, which does not match the URL! + FlatDetection::iterator pIt; + for ( pIt = lPreselections.begin(); + pIt != lPreselections.end() ; + ++pIt ) + { + FlatDetectionInfo& rInfo = *pIt; + + /* + #i60158# + Preselection by DocumentService ... + How many filters (and corresponding types) must be checked ? + All or only the list of filters/types, which match to the given URL too ? + There is no final decision about this currently. So we make it "configurable" .-) + */ + #ifdef IGNORE_NON_URLMATCHING_TYPES_FOR_PRESELECTION_DOCUMENTSERVICE + if ( + (!rInfo.bMatchByExtension) && + (!rInfo.bMatchByPattern ) + ) + continue; + #endif + + rInfo.bPreselectedAsType = sal_False; + rInfo.bPreselectedByFilter = sal_False; + rInfo.bPreselectedByDocumentService = sal_True ; + rFlatTypes.push_back(rInfo); + } + + return sal_True; +} + +/*----------------------------------------------- + 14.11.2003 12:21 +-----------------------------------------------*/ +void TypeDetection::impl_getPreselection(const css::util::URL& aParsedURL , + ::comphelper::MediaDescriptor& rDescriptor, + FlatDetection& rFlatTypes ) +{ + // done to be shure, that only valid results leave this function! + rFlatTypes.clear(); + + /* #i55122# + Sometimes we must detect files without or with real unknown extensions. + If it does not work /which can happen of course .-)/, the user tried to preselect + the right format. But some special dialogs (e.g. "Insert->Sheet From File") + add it's own preselection too. + So we have a combination of preselected values ... + + The we should preferr the most important one - set by the user. + And the user normaly preselects a filter or type. The preslected + document service cames from the dialog. + + Further it doesnt matter if the user preselected a filter or a document service. + A filter selection is always more explicit then a document service selection. + So it must be pereferred. An order between type and filter selection cant be discussed .-) + */ + + ::rtl::OUString sSelectedType = rDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_TYPENAME(), ::rtl::OUString()); + if (sSelectedType.getLength()) + impl_getPreselectionForType(sSelectedType, aParsedURL, rFlatTypes); + + ::rtl::OUString sSelectedFilter = rDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_FILTERNAME(), ::rtl::OUString()); + if (sSelectedFilter.getLength()) + impl_getPreselectionForFilter(sSelectedFilter, aParsedURL, rFlatTypes); + + ::rtl::OUString sSelectedDoc = rDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_DOCUMENTSERVICE(), ::rtl::OUString()); + if (sSelectedDoc.getLength()) + impl_getPreselectionForDocumentService(sSelectedDoc, aParsedURL, rFlatTypes); +} + +/*----------------------------------------------- + 03.11.2003 09:17 +-----------------------------------------------*/ +::rtl::OUString TypeDetection::impl_detectTypeFlatAndDeep( ::comphelper::MediaDescriptor& rDescriptor , + const FlatDetection& lFlatTypes , + sal_Bool bAllowDeep , + OUStringList& rUsedDetectors, + ::rtl::OUString& rLastChance ) +{ + // reset it everytimes, so the outside code can distinguish between + // a set and a not set value. + rLastChance = ::rtl::OUString(); + rUsedDetectors.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 its the preferred or the first valid one) + // or(!) match by URLPattern => in such case a deep detection will be supressed! + // c) type has no detect service => safe the first occured 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 + // getted from the cache => ignore it, and continue with search + + for (FlatDetection::const_iterator pFlatIt = lFlatTypes.begin(); + pFlatIt != lFlatTypes.end() ; + ++pFlatIt ) + { + const FlatDetectionInfo& aFlatTypeInfo = *pFlatIt; + ::rtl::OUString sFlatType = aFlatTypeInfo.sType; + + if (!impl_validateAndSetTypeOnDescriptor(rDescriptor, sFlatType)) + continue; + + // b) + if ( + (!bAllowDeep ) || + (aFlatTypeInfo.bMatchByPattern) + ) + { + return sFlatType; + } + + try + { + // SAFE -> ---------------------------------- + ::osl::ResettableMutexGuard aLock(m_aLock); + CacheItem aType = m_rCache->getItem(FilterCache::E_TYPE, sFlatType); + aLock.clear(); + + ::rtl::OUString sDetectService; + aType[PROPNAME_DETECTSERVICE] >>= sDetectService; + + // c) + if (!sDetectService.getLength()) + { + // accept or not accept flat types without deep detection: that's the question :-) + // May be there exists some states, where we have to use our LastChance feature instead + // of using the flat type directly. + // Here the list of task ID's, which wasrelated to these lines of code: + // #i47159#, #i43404#, #i46494# + + // a flat detected type without the chance for a deep detection ... but preselected by the user + // explicitly (means preselected as type or filter ... not as documentservice!) + // should be accepted. So the user can overrule our detection. + if ( + (aFlatTypeInfo.bPreselectedAsType ) || + (aFlatTypeInfo.bPreselectedByFilter) + ) + return sFlatType; + + // 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.getLength() < 1) + rLastChance = sFlatType; + + continue; + } + + // dont forget to add every real asked deep detection service here. + // Such detectors will be ignored if may be "impl_detectTypeDeepOnly()" + // must be called later! + rUsedDetectors.push_back(sDetectService); + ::rtl::OUString sDeepType = impl_askDetectService(sDetectService, rDescriptor); + + // d) + if (sDeepType.getLength()) + return sDeepType; + } + catch(const css::container::NoSuchElementException&) + {} + // e) + } + + return ::rtl::OUString(); + // <- SAFE ---------------------------------- +} + +/*----------------------------------------------- + 03.11.2003 09:19 +-----------------------------------------------*/ +::rtl::OUString TypeDetection::impl_detectTypeDeepOnly( ::comphelper::MediaDescriptor& rDescriptor , + const OUStringList& lOutsideUsedDetectors) +{ + // We must know if a detect service was already used: + // i) in a combined flat/deep detection scenario outside or + // ii) in this method for a deep detection only. + // Reason: Such deep detection services work differently in these two modes! + OUStringList lInsideUsedDetectors; + OUStringList::const_iterator pIt; + + // a) + // The list of "already used detect services" correspond to the list + // of preselected or flat detected types. But these detect services was called + // to check these types explicitly and return black/white ... yes/no only. + // Now they are called to return any possible result. But we should preferr + // these already used detect services against all other ones! + for ( pIt = lOutsideUsedDetectors.begin(); + pIt != lOutsideUsedDetectors.end() ; + ++pIt ) + { + const ::rtl::OUString& sDetectService = *pIt; + ::rtl::OUString sDeepType = impl_askDetectService(sDetectService, rDescriptor); + if (sDeepType.getLength()) + return sDeepType; + lInsideUsedDetectors.push_back(sDetectService); + } + + // SAFE -> ---------------------------------- + ::osl::ResettableMutexGuard aLock(m_aLock); + OUStringList lDetectors = m_rCache->getItemNames(FilterCache::E_DETECTSERVICE); + aLock.clear(); + // <- SAFE ---------------------------------- + + // b) + // Sometimes it would be nice to ask a special set of detect services before + // any other detect service is asked. E.g. by using a preselection of a DocumentService. + // That's needed to prevent us from asking the "wrong application module" and + // opening the files into the "wrong application". + ::rtl::OUString sPreselDocumentService = rDescriptor.getUnpackedValueOrDefault( + ::comphelper::MediaDescriptor::PROP_DOCUMENTSERVICE(), + ::rtl::OUString()); + if (sPreselDocumentService.getLength()) + { + for ( pIt = lDetectors.begin(); + pIt != lDetectors.end() ; + ++pIt ) + { + const ::rtl::OUString& sDetectService = *pIt; + + OUStringList::const_iterator pAlreadyUsed = ::std::find(lInsideUsedDetectors.begin(), lInsideUsedDetectors.end(), sDetectService); + if (pAlreadyUsed != lInsideUsedDetectors.end()) + continue; + + // SAFE -> -------------------------------------------------------- + aLock.reset(); + + CacheItem lIProps; + lIProps[PROPNAME_DETECTSERVICE] <<= sDetectService; + OUStringList lTypes = m_rCache->getMatchingItemsByProps(FilterCache::E_TYPE, lIProps); + + aLock.clear(); + // <- SAFE -------------------------------------------------------- + + sal_Bool bMatchDetectorToDocumentService = sal_False; + OUStringList::const_iterator pIt2; + for ( pIt2 = lTypes.begin(); + pIt2 != lTypes.end() ; + ++pIt2 ) + { + const ::rtl::OUString& sType = *pIt2; + + try + { + // SAFE -> ---------------------------------------------------- + aLock.reset(); + + CacheItem aType = m_rCache->getItem(FilterCache::E_TYPE, sType); + ::rtl::OUString sFilter; + aType[PROPNAME_PREFERREDFILTER] >>= sFilter; + CacheItem aFilter = m_rCache->getItem(FilterCache::E_FILTER, sFilter); + ::rtl::OUString sCheckDocumentService; + aFilter[PROPNAME_DOCUMENTSERVICE] >>= sCheckDocumentService; + + aLock.clear(); + // <- SAFE + + if (sCheckDocumentService.equals(sPreselDocumentService)) + { + bMatchDetectorToDocumentService = sal_True; + break; + } + } + catch(const css::uno::Exception&) + { continue; } + } + + if (bMatchDetectorToDocumentService) + { + ::rtl::OUString sDeepType = impl_askDetectService(sDetectService, rDescriptor); + if (sDeepType.getLength()) + return sDeepType; + lInsideUsedDetectors.push_back(sDetectService); + } + } + } + + // c) + // Last chance. No "used detectors", no "preselected detectors" ... ask any existing detect services + // for this till know unknown format. + for ( pIt = lDetectors.begin(); + pIt != lDetectors.end() ; + ++pIt ) + { + const ::rtl::OUString& sDetectService = *pIt; + + OUStringList::const_iterator pAlreadyUsed = ::std::find(lInsideUsedDetectors.begin(), lInsideUsedDetectors.end(), sDetectService); + if (pAlreadyUsed != lInsideUsedDetectors.end()) + continue; + + ::rtl::OUString sDeepType = impl_askDetectService(sDetectService, rDescriptor); + if (sDeepType.getLength()) + return sDeepType; + } + + return ::rtl::OUString(); +} + +/*----------------------------------------------- + 07.03.2005 11:13 +-----------------------------------------------*/ +void TypeDetection::impl_seekStreamToZero(comphelper::MediaDescriptor& 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( + ::comphelper::MediaDescriptor::PROP_INPUTSTREAM(), + css::uno::Reference< css::io::XInputStream >()); + css::uno::Reference< css::io::XSeekable > xSeek(xStream, css::uno::UNO_QUERY); + if (xSeek.is()) + { + try + { + xSeek->seek(0); + } + catch(const css::uno::RuntimeException& exRun) + { throw exRun; } + catch(const css::uno::Exception&) + {} + } +} + +/*----------------------------------------------- + 30.10.2003 15:12 +-----------------------------------------------*/ +::rtl::OUString TypeDetection::impl_askDetectService(const ::rtl::OUString& sDetectService, + ::comphelper::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::lang::XMultiServiceFactory > xSMGR; + + // SAFE -> + ::osl::ResettableMutexGuard aLock(m_aLock); + xSMGR = m_xSMGR; + aLock.clear(); + // <- SAFE + + // 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 realy + // 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 = css::uno::Reference< css::document::XExtendedFilterDetection >( + xSMGR->createInstance(sDetectService), + css::uno::UNO_QUERY); + + if ( ! xDetector.is()) + return ::rtl::OUString(); + + ::rtl::OUString sDeepType; + try + { + // start deep detection + // Dont forget to convert stl descriptor to its uno representation. + + /* Attention! + You have to use an explicit instance of this uno sequence ... + Because its 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(const css::uno::Exception&) + { + // 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 = ::rtl::OUString(); + } + + // 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! + sal_Bool bValidType = impl_validateAndSetTypeOnDescriptor(rDescriptor, sDeepType); + if (bValidType) + return sDeepType; + + return ::rtl::OUString(); +} + +/*----------------------------------------------- + 17.12.2004 13:47 +-----------------------------------------------*/ +::rtl::OUString TypeDetection::impl_askUserForTypeAndFilterIfAllowed(::comphelper::MediaDescriptor& rDescriptor) +{ + // SAFE -> + ::osl::ResettableMutexGuard aLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aLock.clear(); + // <- SAFE + + css::uno::Reference< css::task::XInteractionHandler > xInteraction = + rDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_INTERACTIONHANDLER(), + css::uno::Reference< css::task::XInteractionHandler >()); + + if (!xInteraction.is()) + return ::rtl::OUString(); + + ::rtl::OUString sURL = + rDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_URL(), + ::rtl::OUString()); + + css::uno::Reference< css::io::XInputStream > xStream = + rDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_INPUTSTREAM(), + css::uno::Reference< css::io::XInputStream >()); + + // Dont distrub the user for "non existing files - means empty URLs" or + // if we was forced to detect a stream. + // Reason behind: We must be shure to ask user for "unknown contents" only ... + // and not for "missing files". Especialy if detection is done by a stream only + // we cant check if the stream points to an "existing content"! + if ( + (!sURL.getLength() ) || // "non existing file" ? + (!xStream.is() ) || // non existing file ! + (sURL.equalsIgnoreAsciiCaseAsciiL("private:stream", 14)) // not a good idea .-) + ) + return ::rtl::OUString(); + + try + { + // create a new request to ask user for it's decision about the usable filter + ::framework::RequestFilterSelect* pRequest = new ::framework::RequestFilterSelect(sURL); + css::uno::Reference< css::task::XInteractionRequest > xRequest(static_cast< css::task::XInteractionRequest* >(pRequest), css::uno::UNO_QUERY_THROW); + xInteraction->handle(xRequest); + + // "Cancel" pressed? => return with error + if (pRequest->isAbort()) + return ::rtl::OUString(); + + // "OK" pressed => verify the selected filter, get it's coressponding + // type and return it. (BTW: We must update the media descriptor here ...) + // The user selected explicitly a filter ... but normaly we are interested on + // a type here only. But we must be shure, that the selected filter is used + // too and no ambigous filter registration disturb us .-) + + ::rtl::OUString sFilter = pRequest->getFilter(); + if (!impl_validateAndSetFilterOnDescriptor(rDescriptor, sFilter)) + return ::rtl::OUString(); + + ::rtl::OUString sType; + rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME()] >>= sType; + return sType; + } + catch(const css::uno::Exception&) + {} + + return ::rtl::OUString(); +} + +/*----------------------------------------------- + 10.03.2004 10:30 +-----------------------------------------------*/ +void TypeDetection::impl_openStream(::comphelper::MediaDescriptor& rDescriptor) + throw (css::uno::Exception) +{ + sal_Bool bSuccess = sal_False; + ::rtl::OUString sURL = rDescriptor.getUnpackedValueOrDefault( ::comphelper::MediaDescriptor::PROP_URL(), ::rtl::OUString() ); + sal_Bool bRequestedReadOnly = rDescriptor.getUnpackedValueOrDefault( ::comphelper::MediaDescriptor::PROP_READONLY(), sal_False ); + if ( sURL.getLength() && ::utl::LocalFileHelper::IsLocalFile( INetURLObject( sURL ).GetMainURL( INetURLObject::NO_DECODE ) ) ) + { + // OOo uses own file locking mechanics in case of local file + bSuccess = rDescriptor.addInputStreamOwnLock(); + } + else + bSuccess = rDescriptor.addInputStream(); + + if ( !bSuccess ) + throw css::uno::Exception(_FILTER_CONFIG_FROM_ASCII_("Could not open stream."), static_cast< css::document::XTypeDetection* >(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( ::comphelper::MediaDescriptor::PROP_READONLY() ); + } +} + +/*----------------------------------------------- + 04.07.2003 13:47 +-----------------------------------------------*/ +void TypeDetection::impl_removeTypeFilterFromDescriptor(::comphelper::MediaDescriptor& rDescriptor) +{ + ::comphelper::MediaDescriptor::iterator pItType = rDescriptor.find(::comphelper::MediaDescriptor::PROP_TYPENAME() ); + ::comphelper::MediaDescriptor::iterator pItFilter = rDescriptor.find(::comphelper::MediaDescriptor::PROP_FILTERNAME()); + if (pItType != rDescriptor.end()) + rDescriptor.erase(pItType); + if (pItFilter != rDescriptor.end()) + rDescriptor.erase(pItFilter); +} + +/*----------------------------------------------- + 14.10.2003 11:15 +-----------------------------------------------*/ +sal_Bool TypeDetection::impl_validateAndSetTypeOnDescriptor( ::comphelper::MediaDescriptor& rDescriptor, + const ::rtl::OUString& sType ) +{ + // SAFE -> + ::osl::ResettableMutexGuard aLock(m_aLock); + if (m_rCache->hasItem(FilterCache::E_TYPE, sType)) + { + rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME()] <<= sType; + return sal_True; + } + aLock.clear(); + // <- SAFE + + // remove all related informations from the descriptor + impl_removeTypeFilterFromDescriptor(rDescriptor); + return sal_False; +} + +/*----------------------------------------------- + 04.07.2003 14:01 +-----------------------------------------------*/ +sal_Bool TypeDetection::impl_validateAndSetFilterOnDescriptor( ::comphelper::MediaDescriptor& rDescriptor, + const ::rtl::OUString& sFilter ) +{ + try + { + // SAFE -> + ::osl::ResettableMutexGuard aLock(m_aLock); + + CacheItem aFilter = m_rCache->getItem(FilterCache::E_FILTER, sFilter); + ::rtl::OUString sType; + aFilter[PROPNAME_TYPE] >>= sType; + CacheItem aType = m_rCache->getItem(FilterCache::E_TYPE, sType); + + aLock.clear(); + // <- SAFE + + // found valid type and filter => set it on the given descriptor + rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME() ] <<= sType ; + rDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= sFilter; + return sal_True; + } + catch(const css::container::NoSuchElementException&){} + + // remove all related informations from the descriptor + impl_removeTypeFilterFromDescriptor(rDescriptor); + return sal_False; +} + +/*----------------------------------------------- + 03.07.2003 10:36 +-----------------------------------------------*/ +::rtl::OUString TypeDetection::impl_getImplementationName() +{ + return ::rtl::OUString::createFromAscii("com.sun.star.comp.filter.config.TypeDetection"); +} + +/*----------------------------------------------- + 03.07.2003 11:27 +-----------------------------------------------*/ +css::uno::Sequence< ::rtl::OUString > TypeDetection::impl_getSupportedServiceNames() +{ + css::uno::Sequence< ::rtl::OUString > lServiceNames(1); + lServiceNames[0] = ::rtl::OUString::createFromAscii("com.sun.star.document.TypeDetection"); + return lServiceNames; +} + +/*----------------------------------------------- + 09.07.2003 08:02 +-----------------------------------------------*/ +css::uno::Reference< css::uno::XInterface > SAL_CALL TypeDetection::impl_createInstance(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR) +{ + TypeDetection* pNew = new TypeDetection(xSMGR); + return css::uno::Reference< css::uno::XInterface >(static_cast< css::document::XTypeDetection* >(pNew), css::uno::UNO_QUERY); +} + + } // namespace config +} // namespace filter |