Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : : #include "typedetection.hxx"
30 : : #include "constant.hxx"
31 : :
32 : : #include <com/sun/star/document/XExtendedFilterDetection.hpp>
33 : : #include <com/sun/star/util/URLTransformer.hpp>
34 : : #include <com/sun/star/util/XURLTransformer.hpp>
35 : :
36 : : #include <com/sun/star/io/XInputStream.hpp>
37 : : #include <com/sun/star/io/XSeekable.hpp>
38 : : #include <com/sun/star/task/XInteractionHandler.hpp>
39 : : #include <tools/wldcrd.hxx>
40 : : #include <rtl/ustrbuf.hxx>
41 : : #include <framework/interaction.hxx>
42 : : #include <tools/urlobj.hxx>
43 : : #include <unotools/localfilehelper.hxx>
44 : : #include <comphelper/componentcontext.hxx>
45 : :
46 : :
47 : : namespace filter{
48 : : namespace config{
49 : :
50 : : namespace css = ::com::sun::star;
51 : :
52 : 11828 : TypeDetection::TypeDetection(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR)
53 : : {
54 : : BaseContainer::init(xSMGR ,
55 : : TypeDetection::impl_getImplementationName() ,
56 : : TypeDetection::impl_getSupportedServiceNames(),
57 [ + - ][ + - ]: 11828 : FilterCache::E_TYPE );
[ + - ][ + - ]
58 : 11828 : }
59 : :
60 : :
61 : :
62 : 11828 : TypeDetection::~TypeDetection()
63 : : {
64 [ - + ]: 23656 : }
65 : :
66 : :
67 : :
68 : 644 : ::rtl::OUString SAL_CALL TypeDetection::queryTypeByURL(const ::rtl::OUString& sURL)
69 : : throw (css::uno::RuntimeException)
70 : : {
71 : 644 : ::rtl::OUString sType;
72 : :
73 : : // SAFE ->
74 [ + - ]: 644 : ::osl::ResettableMutexGuard aLock(m_aLock);
75 : :
76 : 644 : css::util::URL aURL;
77 : 644 : aURL.Complete = sURL;
78 [ + - ][ + - ]: 644 : css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(comphelper::ComponentContext(m_xSMGR).getUNOContext()));
[ + - ][ + - ]
79 [ + - ][ + - ]: 644 : xParser->parseStrict(aURL);
80 : :
81 : : // set std types as minimum requirement first!
82 : : // Only in case no type was found for given URL,
83 : : // use optional types too ...
84 [ + - ]: 644 : FlatDetection lFlatTypes;
85 [ + - ][ + - ]: 644 : m_rCache->detectFlatForURL(aURL, lFlatTypes);
86 : :
87 [ - + ][ - + ]: 793 : if (
[ + + ]
88 : 644 : (lFlatTypes.size() < 1 ) &&
89 [ + - ][ + - ]: 149 : (!m_rCache->isFillState(FilterCache::E_CONTAINS_TYPES))
90 : : )
91 : : {
92 [ # # ][ # # ]: 0 : m_rCache->load(FilterCache::E_CONTAINS_TYPES);
93 [ # # ][ # # ]: 0 : m_rCache->detectFlatForURL(aURL, lFlatTypes);
94 : : }
95 : :
96 : : // first item is guaranteed as "preferred" one!
97 [ + + ]: 644 : if (lFlatTypes.size() > 0)
98 : : {
99 : 495 : const FlatDetectionInfo& aMatch = *(lFlatTypes.begin());
100 : 495 : sType = aMatch.sType;
101 : : }
102 : :
103 [ + - ]: 644 : return sType;
104 : : // <- SAFE
105 : : }
106 : :
107 : : namespace {
108 : :
109 : : /**
110 : : * Rank format types in order of complexity. More complex formats are
111 : : * ranked higher so that they get tested sooner over simpler formats.
112 : : *
113 : : * Guidelines to determine how complex a format is (subject to change):
114 : : *
115 : : * 1) compressed text (XML, HTML, etc)
116 : : * 2) binary
117 : : * 3) non-compressed text
118 : : * 3.1) structured text
119 : : * 3.1.1) dialect of a structured text (e.g. docbook XML)
120 : : * 3.1.2) generic structured text (e.g. generic XML)
121 : : * 3.2) non-structured text
122 : : *
123 : : * In each category, rank them from strictly-structured to
124 : : * loosely-structured.
125 : : */
126 : 2080 : int getFlatTypeRank(const rtl::OUString& rType)
127 : : {
128 : : // List formats from more complex to less complex.
129 : : // TODO: Add more.
130 : : static const char* ranks[] = {
131 : : // Compressed XML
132 : : "writer8_template",
133 : : "writer8",
134 : : "calc8_template",
135 : : "calc8",
136 : : "writer_OOXML_Text_Template",
137 : : "writer_OOXML",
138 : : "writer_MS_Word_2007_Template",
139 : : "writer_MS_Word_2007",
140 : : "Office Open XML Spreadsheet Template",
141 : : "Office Open XML Spreadsheet",
142 : : "MS Excel 2007 XML Template",
143 : : "MS Excel 2007 XML",
144 : :
145 : : // Compressed text
146 : : "pdf_Portable_Document_Format",
147 : :
148 : : // Binary
149 : : "writer_T602_Document",
150 : : "writer_WordPerfect_Document",
151 : : "writer_MS_Works_Document",
152 : : "writer_MS_Word_97_Vorlage",
153 : : "writer_MS_Word_97",
154 : : "writer_MS_Word_95_Vorlage",
155 : : "writer_MS_Word_95",
156 : : "writer_MS_WinWord_60",
157 : : "writer_MS_WinWord_5",
158 : : "MS Excel 2007 Binary",
159 : : "calc_MS_Excel_97_VorlageTemplate",
160 : : "calc_MS_Excel_97",
161 : : "calc_MS_Excel_95_VorlageTemplate",
162 : : "calc_MS_Excel_95",
163 : : "calc_MS_Excel_5095_VorlageTemplate",
164 : : "calc_MS_Excel_5095",
165 : : "calc_MS_Excel_40_VorlageTemplate",
166 : : "calc_MS_Excel_40",
167 : : "calc_Pocket_Excel_File",
168 : : "calc_Lotus",
169 : : "calc_QPro",
170 : : "calc_SYLK",
171 : : "calc_DIF",
172 : : "calc_dBase",
173 : :
174 : :
175 : : // Non-compressed XML
176 : : "writer_ODT_FlatXML",
177 : : "calc_ODS_FlatXML",
178 : : "calc_MS_Excel_2003_XML",
179 : : "writer_MS_Word_2003_XML",
180 : : "writer_DocBook_File",
181 : : "XHTML_File",
182 : :
183 : : // Non-compressed text
184 : : "writer_Rich_Text_Format",
185 : : "generic_HTML",
186 : : "generic_Text"
187 : : };
188 : :
189 : 2080 : size_t n = SAL_N_ELEMENTS(ranks);
190 : :
191 [ + + ]: 50803 : for (size_t i = 0; i < n; ++i)
192 : : {
193 [ + + ]: 50716 : if (rType.equalsAscii(ranks[i]))
194 : 1993 : return n - i - 1;
195 : : }
196 : :
197 : : // Not ranked. Treat them equally. Unranked formats have higher priority
198 : : // than the ranked internal ones since they may be defined externally.
199 : 2080 : return n;
200 : : }
201 : :
202 : : /**
203 : : * Types with matching pattern first, then extension, then custom ranks by
204 : : * types, then types that are supported by the document service come next.
205 : : * Lastly, sort them alphabetically.
206 : : */
207 : : struct SortByPriority : public std::binary_function<FlatDetectionInfo, FlatDetectionInfo, bool>
208 : : {
209 : 1075 : bool operator() (const FlatDetectionInfo& r1, const FlatDetectionInfo& r2) const
210 : : {
211 [ - + ]: 1075 : if (r1.bMatchByPattern != r2.bMatchByPattern)
212 : 0 : return r1.bMatchByPattern;
213 : :
214 [ + + ]: 1075 : if (r1.bMatchByExtension != r2.bMatchByExtension)
215 : 35 : return r1.bMatchByExtension;
216 : :
217 : 1040 : int rank1 = getFlatTypeRank(r1.sType);
218 : 1040 : int rank2 = getFlatTypeRank(r2.sType);
219 : :
220 [ + + ]: 1040 : if (rank1 != rank2)
221 : 1016 : return rank1 > rank2;
222 : :
223 [ + + ]: 24 : if (r1.bPreselectedByDocumentService != r2.bPreselectedByDocumentService)
224 : 5 : return r1.bPreselectedByDocumentService;
225 : :
226 : : // All things being equal, sort them alphabetically.
227 : 1075 : return r1.sType > r2.sType;
228 : : }
229 : : };
230 : :
231 : : struct EqualByName : public std::binary_function<FlatDetectionInfo, FlatDetectionInfo, bool>
232 : : {
233 : 506 : bool operator() (const FlatDetectionInfo& r1, const FlatDetectionInfo& r2) const
234 : : {
235 : 506 : return r1.sType == r2.sType;
236 : : }
237 : : };
238 : :
239 : : }
240 : :
241 : 1757 : ::rtl::OUString SAL_CALL TypeDetection::queryTypeByDescriptor(css::uno::Sequence< css::beans::PropertyValue >& lDescriptor,
242 : : sal_Bool bAllowDeep )
243 : : throw (css::uno::RuntimeException)
244 : : {
245 : : // make the descriptor more useable :-)
246 [ + - ]: 1757 : ::comphelper::MediaDescriptor stlDescriptor(lDescriptor);
247 : :
248 : : // SAFE -> ----------------------------------
249 [ + - ]: 1757 : ::osl::ResettableMutexGuard aLock(m_aLock);
250 : :
251 : : //*******************************************
252 : : // parse given URL to split it into e.g. main and jump marks ...
253 [ + - ][ + - ]: 1757 : ::rtl::OUString sURL = stlDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_URL(), ::rtl::OUString());
254 : :
255 : : #if OSL_DEBUG_LEVEL > 0
256 : : if (stlDescriptor.find(::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FileName" ))) != stlDescriptor.end())
257 : : OSL_FAIL("Detect using of deprecated and already unsupported MediaDescriptor property \"FileName\"!");
258 : : #endif
259 : :
260 : 1757 : css::util::URL aURL;
261 : 1757 : aURL.Complete = sURL;
262 [ + - ][ + - ]: 1757 : css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(comphelper::ComponentContext(m_xSMGR).getUNOContext()));
[ + - ][ + - ]
263 [ + - ][ + - ]: 1757 : xParser->parseStrict(aURL);
264 : :
265 : : rtl::OUString aSelectedFilter = stlDescriptor.getUnpackedValueOrDefault(
266 [ + - ][ + - ]: 1757 : comphelper::MediaDescriptor::PROP_FILTERNAME(), rtl::OUString());
267 [ - + ]: 1757 : if (!aSelectedFilter.isEmpty())
268 : : {
269 : : // Caller specified the filter type. Honor it. Just get the default
270 : : // type for that filter, and bail out.
271 [ # # ][ # # ]: 0 : if (impl_validateAndSetFilterOnDescriptor(stlDescriptor, aSelectedFilter))
272 [ # # ][ # # ]: 0 : return stlDescriptor[comphelper::MediaDescriptor::PROP_TYPENAME()].get<rtl::OUString>();
[ # # ]
273 : : }
274 : :
275 : : // preselected type or document service? use it as first "flat" detected
276 : : // type later!
277 [ + - ]: 1757 : FlatDetection lFlatTypes;
278 [ + - ]: 1757 : impl_getPreselection(aURL, stlDescriptor, lFlatTypes);
279 : :
280 : : //*******************************************
281 : : // get all types, which match to the given descriptor
282 : : // That can be true by: extensions/url pattern/mime type etcpp.
283 [ + - ][ + - ]: 1757 : m_rCache->detectFlatForURL(aURL, lFlatTypes);
284 : :
285 [ + - ]: 1757 : aLock.clear();
286 : : // <- SAFE ----------------------------------
287 : :
288 : : // Properly prioritize all candidate types.
289 [ + - ]: 1757 : lFlatTypes.sort(SortByPriority());
290 [ + - ]: 1757 : lFlatTypes.unique(EqualByName());
291 : :
292 : 1757 : ::rtl::OUString sType ;
293 : 1757 : ::rtl::OUString sLastChance;
294 : :
295 : : try
296 : : {
297 : : //*******************************************
298 : : // verify every flat detected (or preselected!) type
299 : : // by calling its registered deep detection service.
300 : : // But break this loop if a type match to the given descriptor
301 : : // by an URL pattern(!) or if deep detection isnt allowed from
302 : : // outside (bAllowDeep=sal_False) or break the whole detection by
303 : : // throwing an exception if creation of the might needed input
304 : : // stream failed by e.g. an IO exception ...
305 [ + - ]: 1757 : OUStringList lUsedDetectors;
306 [ + + ]: 1757 : if (lFlatTypes.size()>0)
307 [ + - ]: 1599 : sType = impl_detectTypeFlatAndDeep(stlDescriptor, lFlatTypes, bAllowDeep, lUsedDetectors, sLastChance);
308 : :
309 : : //*******************************************
310 : : // if no flat detected (nor preselected!) type could be
311 : : // verified and no error occurred during creation of
312 : : // the might needed input stream, start detection
313 : : // which uses all registered deep detection services.
314 [ + - ][ + + ]: 1757 : if (
[ + + ]
315 : 1757 : (sType.isEmpty()) &&
316 : : (bAllowDeep )
317 : : )
318 : : {
319 [ + - ]: 166 : sType = impl_detectTypeDeepOnly(stlDescriptor, lUsedDetectors);
320 : : }
321 : :
322 : : //*******************************************
323 : : // flat detection failed
324 : : // pure deep detection failed
325 : : // => ask might existing InteractionHandler
326 : : // means: ask user for it's decision
327 [ + + ]: 1757 : if (sType.isEmpty())
328 [ + - ]: 9 : sType = impl_askUserForTypeAndFilterIfAllowed(stlDescriptor);
329 : :
330 : : //*******************************************
331 : : // no real detected type - but a might valid one.
332 : : // update descriptor and set last chance for return.
333 [ + + ][ - + ]: 1757 : if (sType.isEmpty() && !sLastChance.isEmpty())
[ - + ]
334 : : {
335 : : 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!");
336 : 0 : sType = sLastChance;
337 : 1757 : }
338 : : }
339 [ # # # ]: 0 : catch(const css::uno::RuntimeException&)
340 : 0 : { throw; }
341 [ # # ]: 0 : catch(const css::uno::Exception&)
342 : 0 : { sType = ::rtl::OUString(); }
343 : :
344 : : //*******************************************
345 : : // adapt media descriptor, so it contains the right values
346 : : // for type/filter name/document service/ etcpp.
347 [ + - ]: 1757 : impl_checkResultsAndAddBestFilter(stlDescriptor, sType); // Attention: sType is used as IN/OUT param here and will might be changed inside this method !!!
348 [ + - ]: 1757 : impl_validateAndSetTypeOnDescriptor(stlDescriptor, sType);
349 : :
350 [ + - ]: 1757 : stlDescriptor >> lDescriptor;
351 [ + - ][ + - ]: 1757 : return sType;
352 : : }
353 : :
354 : :
355 : :
356 : 1757 : void TypeDetection::impl_checkResultsAndAddBestFilter(::comphelper::MediaDescriptor& rDescriptor,
357 : : ::rtl::OUString& sType )
358 : : {
359 : : // a)
360 : : // Dont overwrite a might preselected filter!
361 : : ::rtl::OUString sFilter = rDescriptor.getUnpackedValueOrDefault(
362 [ + - ]: 1757 : ::comphelper::MediaDescriptor::PROP_FILTERNAME(),
363 [ + - ]: 3514 : ::rtl::OUString());
364 [ - + ]: 1757 : if (!sFilter.isEmpty())
365 : : return;
366 : :
367 : : // b)
368 : : // check a preselected document service too.
369 : : // Then we have to search a suitable filter witin this module.
370 : : ::rtl::OUString sDocumentService = rDescriptor.getUnpackedValueOrDefault(
371 [ + - ]: 1757 : ::comphelper::MediaDescriptor::PROP_DOCUMENTSERVICE(),
372 [ + - ]: 3514 : ::rtl::OUString());
373 [ + + ]: 1757 : if (!sDocumentService.isEmpty())
374 : : {
375 : : try
376 : : {
377 : 5 : ::rtl::OUString sRealType = sType;
378 : :
379 : : // SAFE ->
380 [ + - ]: 5 : ::osl::ResettableMutexGuard aLock(m_aLock);
381 : :
382 : : // Attention: For executing next lines of code, We must be shure that
383 : : // all filters already loaded :-(
384 : : // That can disturb our "load on demand feature". But we have no other chance!
385 [ + - ][ + - ]: 5 : m_rCache->load(FilterCache::E_CONTAINS_FILTERS);
386 : :
387 [ + - ]: 5 : CacheItem lIProps;
388 [ + - ][ + - ]: 5 : lIProps[PROPNAME_DOCUMENTSERVICE] <<= sDocumentService;
389 [ + - ][ + - ]: 5 : lIProps[PROPNAME_TYPE ] <<= sRealType;
390 [ + - ][ + - ]: 5 : OUStringList lFilters = m_rCache->getMatchingItemsByProps(FilterCache::E_FILTER, lIProps);
[ + - ][ + - ]
391 : :
392 [ + - ]: 5 : aLock.clear();
393 : : // <- SAFE
394 : :
395 [ + - ][ + - ]: 20 : for (OUStringList::const_iterator pIt = lFilters.begin();
[ + + ]
396 : 10 : pIt != lFilters.end(); ++pIt)
397 : : {
398 : : // SAFE ->
399 [ + - ]: 5 : aLock.reset();
400 : : try
401 : : {
402 [ + - ][ + - ]: 5 : CacheItem aFilter = m_rCache->getItem(FilterCache::E_FILTER, *pIt);
403 : 5 : sal_Int32 nFlags = 0;
404 [ + - ]: 5 : aFilter[PROPNAME_FLAGS] >>= nFlags;
405 : :
406 [ + - ]: 5 : if ((nFlags & FLAGVAL_IMPORT) == FLAGVAL_IMPORT)
407 : 5 : sFilter = *pIt;
408 [ - + ]: 5 : if ((nFlags & FLAGVAL_PREFERRED) == FLAGVAL_PREFERRED)
409 [ + - ][ + - ]: 5 : break;
[ # # ]
410 : : }
411 [ # # ]: 0 : catch(const css::uno::Exception&) {}
412 [ + - ]: 5 : aLock.clear();
413 : : // <- SAFE
414 : : }
415 : :
416 [ + - ]: 5 : if (!sFilter.isEmpty())
417 : : {
418 [ + - ][ + - ]: 5 : rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME() ] <<= sRealType;
[ + - ]
419 [ + - ][ + - ]: 5 : rDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= sFilter;
[ + - ]
420 : 5 : sType = sRealType;
421 : : return;
422 [ + - ][ + - ]: 5 : }
[ + - ][ + - ]
[ + - ][ - + ]
[ # # ]
423 : : }
424 [ # # ]: 0 : catch(const css::uno::Exception&)
425 : : {}
426 : : }
427 : :
428 : : // c)
429 : : // We can use the preferred filter for the specified type.
430 : : // Such preferred filter points:
431 : : // - to the default filter of the preferred application
432 : : // - or to any other filter if no preferred filter was set.
433 : : // Note: It's an optimization only!
434 : : // It's not guaranteed, that such preferred filter exists.
435 : 1752 : sFilter = ::rtl::OUString();
436 : : try
437 : : {
438 : : // SAFE ->
439 [ + - ]: 1752 : ::osl::ResettableMutexGuard aLock(m_aLock);
440 : :
441 [ + - ][ + + ]: 1752 : CacheItem aType = m_rCache->getItem(FilterCache::E_TYPE, sType);
442 [ + - ]: 1743 : aType[PROPNAME_PREFERREDFILTER] >>= sFilter;
443 [ + + ][ + - ]: 1743 : CacheItem aFilter = m_rCache->getItem(FilterCache::E_FILTER, sFilter);
444 : :
445 [ + - ]: 1741 : aLock.clear();
446 : : // <- SAFE
447 : :
448 : : // no exception => found valid type and filter => set it on the given descriptor
449 [ + - ][ + - ]: 1741 : rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME() ] <<= sType ;
[ + - ]
450 [ + - ][ + - ]: 1741 : rDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= sFilter;
[ + - ]
451 [ + - ][ + - ]: 1752 : return;
[ + - ][ - + ]
452 : : }
453 [ + - ]: 11 : catch(const css::uno::Exception&)
454 : : {}
455 : :
456 : : // d)
457 : : // Search for any import(!) filter, which is registered for this type.
458 : 11 : sFilter = ::rtl::OUString();
459 : : try
460 : : {
461 : : // SAFE ->
462 [ + - ]: 11 : ::osl::ResettableMutexGuard aLock(m_aLock);
463 : :
464 : : // Attention: For executing next lines of code, We must be shure that
465 : : // all filters already loaded :-(
466 : : // That can disturb our "load on demand feature". But we have no other chance!
467 [ + - ][ + - ]: 11 : m_rCache->load(FilterCache::E_CONTAINS_FILTERS);
468 : :
469 [ + - ]: 11 : CacheItem lIProps;
470 [ + - ][ + - ]: 11 : lIProps[PROPNAME_TYPE] <<= sType;
471 [ + - ][ + - ]: 11 : OUStringList lFilters = m_rCache->getMatchingItemsByProps(FilterCache::E_FILTER, lIProps);
[ + - ][ + - ]
472 : :
473 [ + - ]: 11 : aLock.clear();
474 : : // <- SAFE
475 : :
476 : 11 : OUStringList::const_iterator pIt;
477 [ + - ][ + - ]: 22 : for ( pIt = lFilters.begin();
[ - + ]
478 : 11 : pIt != lFilters.end() ;
479 : : ++pIt )
480 : : {
481 : 0 : sFilter = *pIt;
482 : :
483 : : // SAFE ->
484 [ # # ]: 0 : aLock.reset();
485 : : try
486 : : {
487 [ # # # # ]: 0 : CacheItem aFilter = m_rCache->getItem(FilterCache::E_FILTER, sFilter);
488 : 0 : sal_Int32 nFlags = 0;
489 [ # # ]: 0 : aFilter[PROPNAME_FLAGS] >>= nFlags;
490 : :
491 [ # # ]: 0 : if ((nFlags & FLAGVAL_IMPORT) == FLAGVAL_IMPORT)
492 [ # # # # : 0 : break;
# # ]
493 : : }
494 [ # # ]: 0 : catch(const css::uno::Exception&)
495 : 0 : { continue; }
496 [ # # ]: 0 : aLock.clear();
497 : : // <- SAFE
498 : :
499 : 0 : sFilter = ::rtl::OUString();
500 : : }
501 : :
502 [ - + ]: 11 : if (!sFilter.isEmpty())
503 : : {
504 [ # # # # : 0 : rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME() ] <<= sType ;
# # ]
505 [ # # # # : 11 : rDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= sFilter;
# # ]
506 : : return;
507 [ - + ][ + - ]: 11 : }
[ - + ][ + - ]
[ + - ][ # # ]
508 : : }
509 [ # # ]: 0 : catch(const css::uno::Exception&)
510 [ + + ][ + + ]: 1757 : {}
511 : : }
512 : :
513 : :
514 : :
515 : 165 : sal_Bool TypeDetection::impl_getPreselectionForType(const ::rtl::OUString& sPreSelType,
516 : : const css::util::URL& aParsedURL ,
517 : : FlatDetection& rFlatTypes )
518 : : {
519 : : // Can be used to supress execution of some parts of this method
520 : : // if its already clear that detected type is valid or not.
521 : : // Its neccessary to use shared code at the end, which update
522 : : // all return parameters constistency!
523 : 165 : sal_Bool bBreakDetection = sal_False;
524 : :
525 : : // Further we must know if it matches by pattern
526 : : // Every flat detected type by pattern wont be detected deep!
527 : 165 : sal_Bool bMatchByPattern = sal_False;
528 : :
529 : : // And we must know if a preselection must be preferred, because
530 : : // it matches by it's extension too.
531 : 165 : sal_Bool bMatchByExtension = sal_False;
532 : :
533 : : // If we e.g. collect all filters of a factory (be a forced factory preselection)
534 : : // we should preferr all filters of this factory, where the type match the given URL.
535 : : // All other types (which sorrespond to filters of the same factory - but dont match
536 : : // the URL) should be "used later" for detection and sorted at the end of our return vector
537 : : // rFlatTypes!
538 : : // => bPreferredPreselection = (matchByExtension || matchByURLPattern)
539 : 165 : sal_Bool bPreferredPreselection = sal_False;
540 : :
541 : : // validate type
542 : 165 : ::rtl::OUString sType(sPreSelType);
543 [ + - ]: 165 : CacheItem aType;
544 : : try
545 : : {
546 : : // SAFE -> --------------------------
547 [ + - ]: 165 : ::osl::ResettableMutexGuard aLock(m_aLock);
548 [ + - ][ + - ]: 165 : aType = m_rCache->getItem(FilterCache::E_TYPE, sType);
[ + - ][ + - ]
549 [ + - ][ + - ]: 165 : aLock.clear();
[ # # ]
550 : : // <- SAFE --------------------------
551 : : }
552 [ # # ]: 0 : catch(const css::container::NoSuchElementException&)
553 : : {
554 : 0 : sType = ::rtl::OUString();
555 : 0 : bBreakDetection = sal_True;
556 : : }
557 : :
558 [ + - ]: 165 : if (!bBreakDetection)
559 : : {
560 : : // We cant check a preselected type for a given stream!
561 : : // So we must believe, that it can work ...
562 [ - + ]: 165 : if ( aParsedURL.Complete == "private:stream" )
563 : 0 : bBreakDetection = sal_True;
564 : : }
565 : :
566 [ + - ]: 165 : if (!bBreakDetection)
567 : : {
568 : : // extract extension from URL .. to check it case-insensitive !
569 [ + - ]: 165 : INetURLObject aParser (aParsedURL.Main);
570 : : ::rtl::OUString sExtension = aParser.getExtension(INetURLObject::LAST_SEGMENT ,
571 : : sal_True ,
572 [ + - ]: 165 : INetURLObject::DECODE_WITH_CHARSET);
573 : 165 : sExtension = sExtension.toAsciiLowerCase();
574 : :
575 : : // otherwhise we must know, if it matches to the given URL realy.
576 : : // especialy if it matches by its extension or pattern registration.
577 [ + - ][ + - ]: 165 : OUStringList lExtensions(aType[PROPNAME_EXTENSIONS]);
578 [ + - ][ + - ]: 165 : OUStringList lURLPattern(aType[PROPNAME_URLPATTERN]);
579 : :
580 [ + - ][ + + ]: 935 : for (OUStringList::const_iterator pIt = lExtensions.begin();
[ + - ]
581 : 465 : pIt != lExtensions.end() ;
582 : : ++pIt )
583 : : {
584 : 305 : ::rtl::OUString sCheckExtension(pIt->toAsciiLowerCase());
585 [ + + ]: 305 : if (sCheckExtension.equals(sExtension))
586 : : {
587 : 5 : bBreakDetection = sal_True;
588 : 5 : bMatchByExtension = sal_True;
589 : 305 : bPreferredPreselection = sal_True;
590 : : break;
591 : : }
592 [ + + ]: 305 : }
593 : :
594 [ + + ]: 165 : if (!bBreakDetection)
595 : : {
596 [ + - ][ + - ]: 350 : for (OUStringList::const_iterator pIt = lURLPattern.begin();
[ + + ]
597 : 175 : pIt != lURLPattern.end() ;
598 : : ++pIt )
599 : : {
600 [ + - ]: 15 : WildCard aCheck(*pIt);
601 [ + - ][ + - ]: 15 : if (aCheck.Matches(aParsedURL.Main))
[ + - ][ - + ]
602 : : {
603 : 0 : bBreakDetection = sal_True;
604 : 0 : bMatchByPattern = sal_True;
605 : 15 : bPreferredPreselection = sal_True;
606 : : break;
607 : : }
608 [ + - ][ + - ]: 15 : }
609 [ + - ]: 165 : }
610 : : }
611 : :
612 : : // if its a valid type - set it on all return values!
613 [ + - ]: 165 : if (!sType.isEmpty())
614 : : {
615 : 165 : FlatDetectionInfo aInfo;
616 : 165 : aInfo.sType = sType;
617 : 165 : aInfo.bMatchByExtension = bMatchByExtension;
618 : 165 : aInfo.bMatchByPattern = bMatchByPattern;
619 : :
620 [ + + ]: 165 : if (bPreferredPreselection)
621 [ + - ]: 5 : rFlatTypes.push_front(aInfo);
622 : : else
623 [ + - ]: 160 : rFlatTypes.push_back(aInfo);
624 : :
625 : 165 : return sal_True;
626 : : }
627 : :
628 : : // not valid!
629 [ + - ]: 165 : return sal_False;
630 : : }
631 : :
632 : :
633 : :
634 : 165 : sal_Bool TypeDetection::impl_getPreselectionForFilter(const ::rtl::OUString& sPreSelFilter,
635 : : const css::util::URL& aParsedURL ,
636 : : FlatDetection& rFlatTypes )
637 : : {
638 : : // Can be used to supress execution of some parts of this method
639 : : // if its already clear that detected filter is valid or not.
640 : : // Its neccessary to use shared code at the end, which update
641 : : // all return parameters constistency!
642 : 165 : sal_Bool bBreakDetection = sal_False;
643 : :
644 : : // validate filter
645 : 165 : ::rtl::OUString sFilter(sPreSelFilter);
646 [ + - ]: 165 : CacheItem aFilter;
647 : : try
648 : : {
649 : : // SAFE -> --------------------------
650 [ + - ]: 165 : ::osl::ResettableMutexGuard aLock(m_aLock);
651 [ + - ][ + - ]: 165 : aFilter = m_rCache->getItem(FilterCache::E_FILTER, sFilter);
[ + - ][ + - ]
652 [ + - ][ + - ]: 165 : aLock.clear();
[ # # ]
653 : : // <- SAFE --------------------------
654 : : }
655 [ # # ]: 0 : catch(const css::container::NoSuchElementException&)
656 : : {
657 : 0 : sFilter = ::rtl::OUString();
658 : 0 : bBreakDetection = sal_True;
659 : : }
660 : :
661 [ + - ]: 165 : if (!bBreakDetection)
662 : : {
663 : : // get its type and check if it matches the given URL
664 : 165 : ::rtl::OUString sType;
665 [ + - ]: 165 : aFilter[PROPNAME_TYPE] >>= sType;
666 : :
667 [ + - ]: 165 : bBreakDetection = impl_getPreselectionForType(sType, aParsedURL, rFlatTypes);
668 : :
669 : : // not a valid type? -> not a valid filter!
670 [ - + ]: 165 : if (!bBreakDetection)
671 : 165 : sFilter = ::rtl::OUString();
672 : : }
673 : :
674 [ + - ]: 165 : if (!sFilter.isEmpty())
675 : 165 : return sal_True;
676 : : else
677 [ + - ]: 165 : return sal_False;
678 : : }
679 : :
680 : :
681 : :
682 : 5 : sal_Bool TypeDetection::impl_getPreselectionForDocumentService(const ::rtl::OUString& sPreSelDocumentService,
683 : : const css::util::URL& aParsedURL ,
684 : : FlatDetection& rFlatTypes )
685 : : {
686 : : // get all filters, which match to this doc service
687 [ + - ]: 5 : OUStringList lFilters;
688 : : try
689 : : {
690 : : // SAFE -> --------------------------
691 [ + - ]: 5 : ::osl::ResettableMutexGuard aLock(m_aLock);
692 : :
693 : : // Attention: For executing next lines of code, We must be shure that
694 : : // all filters already loaded :-(
695 : : // That can disturb our "load on demand feature". But we have no other chance!
696 [ + - ][ + - ]: 5 : m_rCache->load(FilterCache::E_CONTAINS_FILTERS);
697 : :
698 [ + - ]: 5 : CacheItem lIProps;
699 [ + - ][ + - ]: 5 : lIProps[PROPNAME_DOCUMENTSERVICE] <<= sPreSelDocumentService;
700 [ + - ][ + - ]: 5 : lFilters = m_rCache->getMatchingItemsByProps(FilterCache::E_FILTER, lIProps);
[ + - ][ + - ]
[ + - ]
701 : :
702 [ + - ][ + - ]: 5 : aLock.clear();
[ + - ][ # # ]
703 : : // <- SAFE --------------------------
704 : : }
705 [ # # ]: 0 : catch(const css::container::NoSuchElementException&)
706 : : {
707 : 0 : lFilters.clear();
708 : : }
709 : :
710 : : // step over all filters, and check if its registered type
711 : : // match the given URL.
712 : : // But use temp. list of "preselected types" instead of incoming rFlatTypes list!
713 : : // The reason behind: we must filter the getted results. And copying of stl entries
714 : : // is an easier job then removing it .-)
715 [ + - ]: 5 : FlatDetection lPreselections;
716 [ + - ][ + - ]: 340 : for (OUStringList::const_iterator pFilter = lFilters.begin();
[ + + ]
717 : 170 : pFilter != lFilters.end() ;
718 : : ++pFilter )
719 : : {
720 : 165 : const ::rtl::OUString sFilter = *pFilter;
721 [ + - ]: 165 : impl_getPreselectionForFilter(sFilter, aParsedURL, lPreselections);
722 : 165 : }
723 : :
724 : : // We have to mark all retrieved preselection items as "preselected by document service".
725 : : // Further we must ignore all preselected items, which does not match the URL!
726 : 5 : FlatDetection::iterator pIt;
727 [ + + ]: 340 : for ( pIt = lPreselections.begin();
728 : 170 : pIt != lPreselections.end() ;
729 : : ++pIt )
730 : : {
731 : 165 : FlatDetectionInfo& rInfo = *pIt;
732 : 165 : rInfo.bPreselectedByDocumentService = sal_True ;
733 [ + - ]: 165 : rFlatTypes.push_back(rInfo);
734 : : }
735 : :
736 : 5 : return sal_True;
737 : : }
738 : :
739 : :
740 : :
741 : 1757 : void TypeDetection::impl_getPreselection(const css::util::URL& aParsedURL ,
742 : : ::comphelper::MediaDescriptor& rDescriptor,
743 : : FlatDetection& rFlatTypes )
744 : : {
745 : : // done to be shure, that only valid results leave this function!
746 : 1757 : rFlatTypes.clear();
747 : :
748 : : /* #i55122#
749 : : Sometimes we must detect files without or with real unknown extensions.
750 : : If it does not work /which can happen of course .-)/, the user tried to preselect
751 : : the right format. But some special dialogs (e.g. "Insert->Sheet From File")
752 : : add it's own preselection too.
753 : : So we have a combination of preselected values ...
754 : :
755 : : The we should preferr the most important one - set by the user.
756 : : And the user normaly preselects a filter or type. The preslected
757 : : document service cames from the dialog.
758 : :
759 : : Further it doesnt matter if the user preselected a filter or a document service.
760 : : A filter selection is always more explicit then a document service selection.
761 : : So it must be pereferred. An order between type and filter selection cant be discussed .-)
762 : : */
763 : :
764 [ + - ][ + - ]: 1757 : ::rtl::OUString sSelectedType = rDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_TYPENAME(), ::rtl::OUString());
765 [ - + ]: 1757 : if (!sSelectedType.isEmpty())
766 [ # # ]: 0 : impl_getPreselectionForType(sSelectedType, aParsedURL, rFlatTypes);
767 : :
768 [ + - ][ + - ]: 1757 : ::rtl::OUString sSelectedDoc = rDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_DOCUMENTSERVICE(), ::rtl::OUString());
769 [ + + ]: 1757 : if (!sSelectedDoc.isEmpty())
770 [ + - ]: 1757 : impl_getPreselectionForDocumentService(sSelectedDoc, aParsedURL, rFlatTypes);
771 : 1757 : }
772 : :
773 : :
774 : :
775 : 1599 : ::rtl::OUString TypeDetection::impl_detectTypeFlatAndDeep( ::comphelper::MediaDescriptor& rDescriptor ,
776 : : const FlatDetection& lFlatTypes ,
777 : : sal_Bool bAllowDeep ,
778 : : OUStringList& rUsedDetectors,
779 : : ::rtl::OUString& rLastChance )
780 : : {
781 : : // reset it everytimes, so the outside code can distinguish between
782 : : // a set and a not set value.
783 : 1599 : rLastChance = ::rtl::OUString();
784 : 1599 : rUsedDetectors.clear();
785 : :
786 : : // step over all possible types for this URL.
787 : : // solutions:
788 : : // a) no types => no detection
789 : : // b) deep detection not allowed => return first valid type of list (because its the preferred or the first valid one)
790 : : // or(!) match by URLPattern => in such case a deep detection will be supressed!
791 : : // c) type has no detect service => safe the first occurred type without a detect service
792 : : // as "last chance"(!). It will be used outside of this method
793 : : // if no further type could be detected.
794 : : // It must be the first one, because it can be a preferred type.
795 : : // Our types list was sorted by such criteria!
796 : : // d) detect service return a valid result => return its decision
797 : : // e) detect service return an invalid result
798 : : // or any needed information could not be
799 : : // getted from the cache => ignore it, and continue with search
800 : :
801 [ + + ]: 3230 : for (FlatDetection::const_iterator pFlatIt = lFlatTypes.begin();
802 : 1615 : pFlatIt != lFlatTypes.end() ;
803 : : ++pFlatIt )
804 : : {
805 : 1607 : const FlatDetectionInfo& aFlatTypeInfo = *pFlatIt;
806 : 1607 : ::rtl::OUString sFlatType = aFlatTypeInfo.sType;
807 : :
808 [ - + ][ + - ]: 1607 : if (!impl_validateAndSetTypeOnDescriptor(rDescriptor, sFlatType))
809 : 0 : continue;
810 : :
811 : : // b)
812 [ + - ][ + + ]: 1607 : if (
813 : : (!bAllowDeep ) ||
814 : : (aFlatTypeInfo.bMatchByPattern)
815 : : )
816 : : {
817 : 1123 : return sFlatType;
818 : : }
819 : :
820 : : try
821 : : {
822 : : // SAFE -> ----------------------------------
823 [ + - ]: 484 : ::osl::ResettableMutexGuard aLock(m_aLock);
824 [ + - ][ + - ]: 484 : CacheItem aType = m_rCache->getItem(FilterCache::E_TYPE, sFlatType);
825 [ + - ]: 484 : aLock.clear();
826 : :
827 : 484 : ::rtl::OUString sDetectService;
828 [ + - ]: 484 : aType[PROPNAME_DETECTSERVICE] >>= sDetectService;
829 : :
830 : : // c)
831 [ + + ]: 484 : if (sDetectService.isEmpty())
832 : : {
833 : : // flat detected types without any registered deep detection service and not
834 : : // preselected by the user can be used as LAST CHANCE in case no other type could
835 : : // be detected. Of course only the first type without deep detector can be used.
836 : : // Further ones has to be ignored.
837 [ + - ]: 8 : if (rLastChance.isEmpty())
838 : 8 : rLastChance = sFlatType;
839 : :
840 : 8 : continue;
841 : : }
842 : :
843 : : // dont forget to add every real asked deep detection service here.
844 : : // Such detectors will be ignored if may be "impl_detectTypeDeepOnly()"
845 : : // must be called later!
846 [ + - ]: 476 : rUsedDetectors.push_back(sDetectService);
847 [ + - ]: 476 : ::rtl::OUString sDeepType = impl_askDetectService(sDetectService, rDescriptor);
848 : :
849 : : // d)
850 [ + + ]: 476 : if (!sDeepType.isEmpty())
851 [ + + ]: 484 : return sDeepType;
[ + + + ]
[ + - ]
[ + + + ]
[ + - ]
[ + + + ]
[ # # ]
852 : : }
853 [ # # ]: 0 : catch(const css::container::NoSuchElementException&)
854 : : {}
855 : : // e)
856 [ + + + ]: 1607 : }
857 : :
858 : 1599 : return ::rtl::OUString();
859 : : // <- SAFE ----------------------------------
860 : : }
861 : :
862 : : //TO-DO: add a priority entry to filter config, e.g. defaulting to 50 and
863 : : //flag externally that some filters are lower e.g. 25 and are catch-alls
864 : : //to be tried last. Split up writer/calc/etc. filter detection to standalone
865 : : //those problematic formats
866 : : namespace
867 : : {
868 : 21534 : bool sort_catchalls_to_end(const rtl::OUString& rA, const rtl::OUString& rB)
869 : : {
870 [ - + ]: 21534 : if (rA == rB)
871 : 0 : return false;
872 [ + + ]: 21534 : if ( rA == "com.sun.star.text.FormatDetector" )
873 : 332 : return false;
874 [ + + ]: 21202 : if ( rB == "com.sun.star.text.FormatDetector" )
875 : 156 : return true;
876 : 21534 : return rA < rB;
877 : : }
878 : : }
879 : :
880 : 166 : ::rtl::OUString TypeDetection::impl_detectTypeDeepOnly( ::comphelper::MediaDescriptor& rDescriptor ,
881 : : const OUStringList& lOutsideUsedDetectors)
882 : : {
883 : : // We must know if a detect service was already used:
884 : : // i) in a combined flat/deep detection scenario outside or
885 : : // ii) in this method for a deep detection only.
886 : : // Reason: Such deep detection services work differently in these two modes!
887 [ + - ]: 166 : OUStringList lInsideUsedDetectors;
888 : 166 : OUStringList::const_iterator pIt;
889 : :
890 : : // a)
891 : : // The list of "already used detect services" correspond to the list
892 : : // of preselected or flat detected types. But these detect services was called
893 : : // to check these types explicitly and return black/white ... yes/no only.
894 : : // Now they are called to return any possible result. But we should preferr
895 : : // these already used detect services against all other ones!
896 [ + - ][ + + ]: 336 : for ( pIt = lOutsideUsedDetectors.begin();
897 : 168 : pIt != lOutsideUsedDetectors.end() ;
898 : : ++pIt )
899 : : {
900 : 2 : const ::rtl::OUString& sDetectService = *pIt;
901 [ + - ]: 2 : ::rtl::OUString sDeepType = impl_askDetectService(sDetectService, rDescriptor);
902 [ - + ]: 2 : if (!sDeepType.isEmpty())
903 : 0 : return sDeepType;
904 [ + - ][ + - ]: 4 : lInsideUsedDetectors.push_back(sDetectService);
905 : 2 : }
906 : :
907 : : // SAFE -> ----------------------------------
908 [ + - ]: 166 : ::osl::ResettableMutexGuard aLock(m_aLock);
909 [ + - ][ + - ]: 166 : OUStringList lDetectors = m_rCache->getItemNames(FilterCache::E_DETECTSERVICE);
910 [ + - ]: 166 : std::sort(lDetectors.begin(), lDetectors.end(), sort_catchalls_to_end);
911 [ + - ]: 166 : aLock.clear();
912 : : // <- SAFE ----------------------------------
913 : :
914 : : // b)
915 : : // Sometimes it would be nice to ask a special set of detect services before
916 : : // any other detect service is asked. E.g. by using a preselection of a DocumentService.
917 : : // That's needed to prevent us from asking the "wrong application module" and
918 : : // opening the files into the "wrong application".
919 : : ::rtl::OUString sPreselDocumentService = rDescriptor.getUnpackedValueOrDefault(
920 [ + - ]: 166 : ::comphelper::MediaDescriptor::PROP_DOCUMENTSERVICE(),
921 [ + - ]: 332 : ::rtl::OUString());
922 [ - + ]: 166 : if (!sPreselDocumentService.isEmpty())
923 : : {
924 [ # # ][ # # ]: 0 : for ( pIt = lDetectors.begin();
[ # # ]
925 : 0 : pIt != lDetectors.end() ;
926 : : ++pIt )
927 : : {
928 : 0 : const ::rtl::OUString& sDetectService = *pIt;
929 : :
930 [ # # ][ # # ]: 0 : OUStringList::const_iterator pAlreadyUsed = ::std::find(lInsideUsedDetectors.begin(), lInsideUsedDetectors.end(), sDetectService);
931 [ # # ][ # # ]: 0 : if (pAlreadyUsed != lInsideUsedDetectors.end())
932 : 0 : continue;
933 : :
934 : : // SAFE -> --------------------------------------------------------
935 [ # # ]: 0 : aLock.reset();
936 : :
937 [ # # ]: 0 : CacheItem lIProps;
938 [ # # ][ # # ]: 0 : lIProps[PROPNAME_DETECTSERVICE] <<= sDetectService;
939 [ # # ][ # # ]: 0 : OUStringList lTypes = m_rCache->getMatchingItemsByProps(FilterCache::E_TYPE, lIProps);
[ # # ][ # # ]
940 : :
941 [ # # ]: 0 : aLock.clear();
942 : : // <- SAFE --------------------------------------------------------
943 : :
944 : 0 : sal_Bool bMatchDetectorToDocumentService = sal_False;
945 : 0 : OUStringList::const_iterator pIt2;
946 [ # # ][ # # ]: 0 : for ( pIt2 = lTypes.begin();
[ # # ]
947 : 0 : pIt2 != lTypes.end() ;
948 : : ++pIt2 )
949 : : {
950 : 0 : const ::rtl::OUString& sType = *pIt2;
951 : :
952 : : try
953 : : {
954 : : // SAFE -> ----------------------------------------------------
955 [ # # ]: 0 : aLock.reset();
956 : :
957 [ # # ][ # # ]: 0 : CacheItem aType = m_rCache->getItem(FilterCache::E_TYPE, sType);
958 : 0 : ::rtl::OUString sFilter;
959 [ # # ]: 0 : aType[PROPNAME_PREFERREDFILTER] >>= sFilter;
960 [ # # ][ # # ]: 0 : CacheItem aFilter = m_rCache->getItem(FilterCache::E_FILTER, sFilter);
961 : 0 : ::rtl::OUString sCheckDocumentService;
962 [ # # ]: 0 : aFilter[PROPNAME_DOCUMENTSERVICE] >>= sCheckDocumentService;
963 : :
964 [ # # ]: 0 : aLock.clear();
965 : : // <- SAFE
966 : :
967 [ # # ]: 0 : if (sCheckDocumentService.equals(sPreselDocumentService))
968 : : {
969 : 0 : bMatchDetectorToDocumentService = sal_True;
970 : : break;
971 [ # # ][ # # ]: 0 : }
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
972 : : }
973 [ # # ]: 0 : catch(const css::uno::Exception&)
974 : 0 : { continue; }
975 : : }
976 : :
977 [ # # ]: 0 : if (bMatchDetectorToDocumentService)
978 : : {
979 [ # # ]: 0 : ::rtl::OUString sDeepType = impl_askDetectService(sDetectService, rDescriptor);
980 [ # # ]: 0 : if (!sDeepType.isEmpty())
981 : 0 : return sDeepType;
982 [ # # ][ # # ]: 0 : lInsideUsedDetectors.push_back(sDetectService);
983 : : }
984 [ # # ][ # # ]: 0 : }
[ # # ]
985 : : }
986 : :
987 : : // c)
988 : : // Last chance. No "used detectors", no "preselected detectors" ... ask any existing detect services
989 : : // for this till know unknown format.
990 [ + - ][ + - ]: 6802 : for ( pIt = lDetectors.begin();
[ + + ]
991 : 3401 : pIt != lDetectors.end() ;
992 : : ++pIt )
993 : : {
994 : 3392 : const ::rtl::OUString& sDetectService = *pIt;
995 : :
996 [ + - ][ + - ]: 3392 : OUStringList::const_iterator pAlreadyUsed = ::std::find(lInsideUsedDetectors.begin(), lInsideUsedDetectors.end(), sDetectService);
997 [ + - ][ + + ]: 3392 : if (pAlreadyUsed != lInsideUsedDetectors.end())
998 : 2 : continue;
999 : :
1000 [ + - ]: 3390 : ::rtl::OUString sDeepType = impl_askDetectService(sDetectService, rDescriptor);
1001 [ + + ]: 3390 : if (!sDeepType.isEmpty())
1002 : 3390 : return sDeepType;
1003 [ + + ]: 3390 : }
1004 : :
1005 [ + - ]: 166 : return ::rtl::OUString();
1006 : : }
1007 : :
1008 : 4730 : void TypeDetection::impl_seekStreamToZero(comphelper::MediaDescriptor& rDescriptor)
1009 : : {
1010 : : // try to seek to 0 ...
1011 : : // But because XSeekable is an optional interface ... try it only .-)
1012 : : css::uno::Reference< css::io::XInputStream > xStream = rDescriptor.getUnpackedValueOrDefault(
1013 [ + - ]: 4730 : ::comphelper::MediaDescriptor::PROP_INPUTSTREAM(),
1014 [ + - ]: 9460 : css::uno::Reference< css::io::XInputStream >());
1015 [ + - ]: 4730 : css::uno::Reference< css::io::XSeekable > xSeek(xStream, css::uno::UNO_QUERY);
1016 [ + - ]: 4730 : if (xSeek.is())
1017 : : {
1018 : : try
1019 : : {
1020 [ + - ][ + - ]: 4730 : xSeek->seek(0);
1021 : : }
1022 [ # # # ]: 0 : catch(const css::uno::RuntimeException&)
1023 : : {
1024 : 0 : throw;
1025 : : }
1026 [ # # ]: 0 : catch(const css::uno::Exception&)
1027 : : {
1028 : : }
1029 : 4730 : }
1030 : 4730 : }
1031 : :
1032 : 3868 : ::rtl::OUString TypeDetection::impl_askDetectService(const ::rtl::OUString& sDetectService,
1033 : : ::comphelper::MediaDescriptor& rDescriptor )
1034 : : {
1035 : : // Open the stream and add it to the media descriptor if this method is called for the first time.
1036 : : // All following requests to this method will detect, that there already exists a stream .-)
1037 : : // Attention: This method throws an exception if the stream could not be opened.
1038 : : // It's important to break any further detection in such case.
1039 : : // Catch it on the highest detection level only !!!
1040 [ + - ]: 3868 : impl_openStream(rDescriptor);
1041 : :
1042 : : // seek to 0 is an optional feature to be more robust against
1043 : : // "simple implemented detect services" .-)
1044 [ + - ]: 3868 : impl_seekStreamToZero(rDescriptor);
1045 : :
1046 : 3868 : css::uno::Reference< css::document::XExtendedFilterDetection > xDetector;
1047 : 3868 : css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR;
1048 : :
1049 : : // SAFE ->
1050 [ + - ]: 3868 : ::osl::ResettableMutexGuard aLock(m_aLock);
1051 [ + - ]: 3868 : xSMGR = m_xSMGR;
1052 [ + - ]: 3868 : aLock.clear();
1053 : : // <- SAFE
1054 : :
1055 : : try
1056 : : {
1057 : : // Attention! If e.g. an office module was not installed sometimes we
1058 : : // find a registered detect service, which is referred inside the
1059 : : // configuration ... but not realy installed. On the other side we use
1060 : : // third party components here, which can make trouble anyway. So we
1061 : : // should handle errors during creation of such services more
1062 : : // gracefully .-)
1063 : : xDetector = css::uno::Reference< css::document::XExtendedFilterDetection >(
1064 [ + - ]: 3868 : xSMGR->createInstance(sDetectService),
1065 [ + - ][ + + ]: 3868 : css::uno::UNO_QUERY_THROW);
[ + - ]
1066 : : }
1067 [ + - ]: 3006 : catch (...)
1068 : : {
1069 : : }
1070 : :
1071 [ + + ]: 3868 : if ( ! xDetector.is())
1072 : 3006 : return ::rtl::OUString();
1073 : :
1074 : 862 : ::rtl::OUString sDeepType;
1075 : : try
1076 : : {
1077 : : // start deep detection
1078 : : // Dont forget to convert stl descriptor to its uno representation.
1079 : :
1080 : : /* Attention!
1081 : : You have to use an explicit instance of this uno sequence ...
1082 : : Because its used as an in out parameter. And in case of a temp. used object
1083 : : we will run into memory corruptions!
1084 : : */
1085 [ + - ]: 862 : css::uno::Sequence< css::beans::PropertyValue > lDescriptor;
1086 [ + - ]: 862 : rDescriptor >> lDescriptor;
1087 [ + - ][ + - ]: 862 : sDeepType = xDetector->detect(lDescriptor);
1088 [ + - ][ # # ]: 862 : rDescriptor << lDescriptor;
[ + - ]
1089 : : }
1090 [ # # ]: 0 : catch(const css::uno::Exception&)
1091 : : {
1092 : : // We should ignore errors here.
1093 : : // Thrown exceptions mostly will end in crash recovery ...
1094 : : // But might be we find another deep detection service which can detect the same
1095 : : // document without a problem .-)
1096 : 0 : sDeepType = ::rtl::OUString();
1097 : : }
1098 : :
1099 : : // seek to 0 is an optional feature to be more robust against
1100 : : // "simple implemented detect services" .-)
1101 [ + - ]: 862 : impl_seekStreamToZero(rDescriptor);
1102 : :
1103 : : // analyze the results
1104 : : // a) detect service returns "" => return "" too and remove TYPE/FILTER prop from descriptor
1105 : : // b) returned type is unknown => return "" too and remove TYPE/FILTER prop from descriptor
1106 : : // c) returned type is valid => check TYPE/FILTER props inside descriptor and return the type
1107 : :
1108 : : // this special helper checks for a valid type
1109 : : // and set right values on the descriptor!
1110 [ + - ]: 862 : sal_Bool bValidType = impl_validateAndSetTypeOnDescriptor(rDescriptor, sDeepType);
1111 [ + + ]: 862 : if (bValidType)
1112 : 625 : return sDeepType;
1113 : :
1114 [ + - ]: 3868 : return ::rtl::OUString();
1115 : : }
1116 : :
1117 : :
1118 : :
1119 : 9 : ::rtl::OUString TypeDetection::impl_askUserForTypeAndFilterIfAllowed(::comphelper::MediaDescriptor& rDescriptor)
1120 : : {
1121 : : // SAFE ->
1122 [ + - ]: 9 : ::osl::ResettableMutexGuard aLock(m_aLock);
1123 : 9 : css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1124 [ + - ]: 9 : aLock.clear();
1125 : : // <- SAFE
1126 : :
1127 : : css::uno::Reference< css::task::XInteractionHandler > xInteraction =
1128 [ + - ]: 9 : rDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_INTERACTIONHANDLER(),
1129 [ + - ]: 18 : css::uno::Reference< css::task::XInteractionHandler >());
1130 : :
1131 [ + - ]: 9 : if (!xInteraction.is())
1132 : 9 : return ::rtl::OUString();
1133 : :
1134 : : ::rtl::OUString sURL =
1135 [ # # ]: 0 : rDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_URL(),
1136 [ # # ]: 0 : ::rtl::OUString());
1137 : :
1138 : : css::uno::Reference< css::io::XInputStream > xStream =
1139 [ # # ]: 0 : rDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_INPUTSTREAM(),
1140 [ # # ]: 0 : css::uno::Reference< css::io::XInputStream >());
1141 : :
1142 : : // Dont distrub the user for "non existing files - means empty URLs" or
1143 : : // if we was forced to detect a stream.
1144 : : // Reason behind: We must be shure to ask user for "unknown contents" only ...
1145 : : // and not for "missing files". Especialy if detection is done by a stream only
1146 : : // we cant check if the stream points to an "existing content"!
1147 [ # # ][ # # : 0 : if (
# # # # ]
1148 : 0 : (sURL.isEmpty() ) || // "non existing file" ?
1149 : 0 : (!xStream.is() ) || // non existing file !
1150 : 0 : (sURL.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("private:stream"))) // not a good idea .-)
1151 : : )
1152 : 0 : return ::rtl::OUString();
1153 : :
1154 : : try
1155 : : {
1156 : : // create a new request to ask user for it's decision about the usable filter
1157 [ # # ]: 0 : ::framework::RequestFilterSelect aRequest(sURL);
1158 [ # # ][ # # ]: 0 : xInteraction->handle(aRequest.GetRequest());
[ # # ]
1159 : :
1160 : : // "Cancel" pressed? => return with error
1161 [ # # ][ # # ]: 0 : if (aRequest.isAbort())
1162 : 0 : return ::rtl::OUString();
1163 : :
1164 : : // "OK" pressed => verify the selected filter, get it's coressponding
1165 : : // type and return it. (BTW: We must update the media descriptor here ...)
1166 : : // The user selected explicitly a filter ... but normaly we are interested on
1167 : : // a type here only. But we must be shure, that the selected filter is used
1168 : : // too and no ambigous filter registration disturb us .-)
1169 : :
1170 [ # # ]: 0 : ::rtl::OUString sFilter = aRequest.getFilter();
1171 [ # # ][ # # ]: 0 : if (!impl_validateAndSetFilterOnDescriptor(rDescriptor, sFilter))
1172 : 0 : return ::rtl::OUString();
1173 : :
1174 : 0 : ::rtl::OUString sType;
1175 [ # # ][ # # ]: 0 : rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME()] >>= sType;
1176 [ # # ][ # # ]: 0 : return sType;
1177 : : }
1178 [ # # ]: 0 : catch(const css::uno::Exception&)
1179 : : {}
1180 : :
1181 [ + - ]: 9 : return ::rtl::OUString();
1182 : : }
1183 : :
1184 : :
1185 : :
1186 : 3868 : void TypeDetection::impl_openStream(::comphelper::MediaDescriptor& rDescriptor)
1187 : : throw (css::uno::Exception)
1188 : : {
1189 : 3868 : sal_Bool bSuccess = sal_False;
1190 [ + - ][ + - ]: 3868 : ::rtl::OUString sURL = rDescriptor.getUnpackedValueOrDefault( ::comphelper::MediaDescriptor::PROP_URL(), ::rtl::OUString() );
1191 [ + - ][ + - ]: 3868 : sal_Bool bRequestedReadOnly = rDescriptor.getUnpackedValueOrDefault( ::comphelper::MediaDescriptor::PROP_READONLY(), sal_False );
1192 [ + - ][ + - ]: 3868 : if ( !sURL.isEmpty() && ::utl::LocalFileHelper::IsLocalFile( INetURLObject( sURL ).GetMainURL( INetURLObject::NO_DECODE ) ) )
[ + - ][ + - ]
[ + + ][ + - ]
[ + - ][ + - ]
[ + + # #
# # ]
1193 : : {
1194 : : // OOo uses own file locking mechanics in case of local file
1195 [ + - ]: 3679 : bSuccess = rDescriptor.addInputStreamOwnLock();
1196 : : }
1197 : : else
1198 [ + - ]: 189 : bSuccess = rDescriptor.addInputStream();
1199 : :
1200 [ - + ]: 3868 : if ( !bSuccess )
1201 [ # # ][ # # ]: 0 : throw css::uno::Exception(_FILTER_CONFIG_FROM_ASCII_("Could not open stream."), static_cast< css::document::XTypeDetection* >(this));
[ # # ]
1202 : :
1203 [ + + ]: 3868 : if ( !bRequestedReadOnly )
1204 : : {
1205 : : // The MediaDescriptor implementation adds ReadOnly argument if the file can not be opened for writing
1206 : : // this argument should be either removed or an additional argument should be added so that application
1207 : : // can separate the case when the user explicitly requests readonly document.
1208 : : // The current solution is to remove it here.
1209 [ + - ][ + - ]: 3866 : rDescriptor.erase( ::comphelper::MediaDescriptor::PROP_READONLY() );
1210 : 3868 : }
1211 : 3868 : }
1212 : :
1213 : :
1214 : :
1215 : 246 : void TypeDetection::impl_removeTypeFilterFromDescriptor(::comphelper::MediaDescriptor& rDescriptor)
1216 : : {
1217 [ + - ][ + - ]: 246 : ::comphelper::MediaDescriptor::iterator pItType = rDescriptor.find(::comphelper::MediaDescriptor::PROP_TYPENAME() );
1218 [ + - ][ + - ]: 246 : ::comphelper::MediaDescriptor::iterator pItFilter = rDescriptor.find(::comphelper::MediaDescriptor::PROP_FILTERNAME());
1219 [ + - ][ + + ]: 246 : if (pItType != rDescriptor.end())
1220 [ + - ]: 14 : rDescriptor.erase(pItType);
1221 [ + - ][ - + ]: 246 : if (pItFilter != rDescriptor.end())
1222 [ # # ]: 0 : rDescriptor.erase(pItFilter);
1223 : 246 : }
1224 : :
1225 : :
1226 : :
1227 : 4226 : sal_Bool TypeDetection::impl_validateAndSetTypeOnDescriptor( ::comphelper::MediaDescriptor& rDescriptor,
1228 : : const ::rtl::OUString& sType )
1229 : : {
1230 : : // SAFE ->
1231 [ + - ]: 4226 : ::osl::ResettableMutexGuard aLock(m_aLock);
1232 [ + - ][ + - ]: 4226 : if (m_rCache->hasItem(FilterCache::E_TYPE, sType))
[ + + ]
1233 : : {
1234 [ + - ][ + - ]: 3980 : rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME()] <<= sType;
[ + - ]
1235 : 3980 : return sal_True;
1236 : : }
1237 [ + - ]: 246 : aLock.clear();
1238 : : // <- SAFE
1239 : :
1240 : : // remove all related informations from the descriptor
1241 [ + - ]: 246 : impl_removeTypeFilterFromDescriptor(rDescriptor);
1242 [ + - ]: 4226 : return sal_False;
1243 : : }
1244 : :
1245 : :
1246 : :
1247 : 0 : sal_Bool TypeDetection::impl_validateAndSetFilterOnDescriptor( ::comphelper::MediaDescriptor& rDescriptor,
1248 : : const ::rtl::OUString& sFilter )
1249 : : {
1250 : : try
1251 : : {
1252 : : // SAFE ->
1253 [ # # ]: 0 : ::osl::ResettableMutexGuard aLock(m_aLock);
1254 : :
1255 [ # # ][ # # ]: 0 : CacheItem aFilter = m_rCache->getItem(FilterCache::E_FILTER, sFilter);
1256 : 0 : ::rtl::OUString sType;
1257 [ # # ]: 0 : aFilter[PROPNAME_TYPE] >>= sType;
1258 [ # # ][ # # ]: 0 : CacheItem aType = m_rCache->getItem(FilterCache::E_TYPE, sType);
1259 : :
1260 [ # # ]: 0 : aLock.clear();
1261 : : // <- SAFE
1262 : :
1263 : : // found valid type and filter => set it on the given descriptor
1264 [ # # ][ # # ]: 0 : rDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME() ] <<= sType ;
[ # # ]
1265 [ # # ][ # # ]: 0 : rDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= sFilter;
[ # # ]
1266 [ # # ][ # # ]: 0 : return sal_True;
[ # # ][ # # ]
1267 : : }
1268 : 0 : catch(const css::container::NoSuchElementException&){}
1269 : :
1270 : : // remove all related informations from the descriptor
1271 : 0 : impl_removeTypeFilterFromDescriptor(rDescriptor);
1272 : 0 : return sal_False;
1273 : : }
1274 : :
1275 : :
1276 : :
1277 : 12467 : ::rtl::OUString TypeDetection::impl_getImplementationName()
1278 : : {
1279 : 12467 : return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.filter.config.TypeDetection" ));
1280 : : }
1281 : :
1282 : :
1283 : :
1284 : 11955 : css::uno::Sequence< ::rtl::OUString > TypeDetection::impl_getSupportedServiceNames()
1285 : : {
1286 : 11955 : css::uno::Sequence< ::rtl::OUString > lServiceNames(1);
1287 [ + - ][ + - ]: 11955 : lServiceNames[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.document.TypeDetection" ));
1288 : 11955 : return lServiceNames;
1289 : : }
1290 : :
1291 : :
1292 : :
1293 : 11828 : css::uno::Reference< css::uno::XInterface > SAL_CALL TypeDetection::impl_createInstance(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR)
1294 : : {
1295 [ + - ]: 11828 : TypeDetection* pNew = new TypeDetection(xSMGR);
1296 [ + - ]: 11828 : return css::uno::Reference< css::uno::XInterface >(static_cast< css::document::XTypeDetection* >(pNew), css::uno::UNO_QUERY);
1297 : : }
1298 : :
1299 : : } // namespace config
1300 : : } // namespace filter
1301 : :
1302 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|