1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/beans/XPropertySet.hpp>
21 #include <com/sun/star/embed/ElementModes.hpp>
22 #include <com/sun/star/embed/InvalidStorageException.hpp>
23 #include <com/sun/star/embed/StorageFactory.hpp>
24 #include <com/sun/star/embed/StorageWrappedTargetException.hpp>
25 #include <com/sun/star/io/IOException.hpp>
26 #include <com/sun/star/packages/NoEncryptionException.hpp>
27 #include <cppuhelper/exc_hlp.hxx>
29 #include <osl/diagnose.h>
31 #include "tdoc_uri.hxx"
32 #include "tdoc_docmgr.hxx"
33 #include "tdoc_stgelems.hxx"
35 #include "tdoc_storage.hxx"
37 using namespace com::sun::star
;
38 using namespace tdoc_ucp
;
41 // StorageElementFactory Implementation.
44 StorageElementFactory::StorageElementFactory(
45 uno::Reference
< uno::XComponentContext
> xContext
,
46 rtl::Reference
< OfficeDocumentsManager
> xDocsMgr
)
47 : m_xDocsMgr(std::move( xDocsMgr
)),
48 m_xContext(std::move( xContext
))
53 StorageElementFactory::~StorageElementFactory()
55 OSL_ENSURE( m_aMap
.empty(),
56 "StorageElementFactory::~StorageElementFactory - Dangling storages!" );
60 uno::Reference
< embed::XStorage
>
61 StorageElementFactory::createTemporaryStorage()
63 uno::Reference
< embed::XStorage
> xStorage
;
64 uno::Reference
< lang::XSingleServiceFactory
> xStorageFac
;
65 if ( m_xContext
.is() )
67 xStorageFac
= embed::StorageFactory::create( m_xContext
);
70 OSL_ENSURE( xStorageFac
.is(), "Can't create storage factory!" );
71 if ( xStorageFac
.is() )
72 xStorage
.set( xStorageFac
->createInstance(), uno::UNO_QUERY
);
75 throw uno::RuntimeException();
81 uno::Reference
< embed::XStorage
>
82 StorageElementFactory::createStorage( const OUString
& rUri
,
83 StorageAccessMode eMode
)
85 osl::MutexGuard
aGuard( m_aMutex
);
87 if ( ( eMode
!= READ
) &&
88 ( eMode
!= READ_WRITE_NOCREATE
) &&
89 ( eMode
!= READ_WRITE_CREATE
) )
90 throw lang::IllegalArgumentException(
92 uno::Reference
< uno::XInterface
>(),
98 throw lang::IllegalArgumentException(
99 "Root never has a storage!",
100 uno::Reference
< uno::XInterface
>(),
106 ? rUri
.copy( 0, rUri
.getLength() - 1 )
109 StorageMap::iterator
aIt ( m_aMap
.begin() );
110 StorageMap::iterator
aEnd( m_aMap
.end() );
112 while ( aIt
!= aEnd
)
114 if ( (*aIt
).first
.first
== aUriKey
)
116 // URI matches. Now, check open mode.
121 // No need to check; storage is at least readable.
125 case READ_WRITE_NOCREATE
:
126 case READ_WRITE_CREATE
:
127 // If found storage is writable, it can be used.
128 // If not, a new one must be created.
129 bMatch
= (*aIt
).first
.second
;
141 uno::Reference
< embed::XStorage
> xParentStorage
;
143 // documents never have a parent storage.
144 if ( !aUri
.isDocument() )
146 xParentStorage
= queryParentStorage( aUriKey
, eMode
);
148 if ( !xParentStorage
.is() )
150 // requested to create new storage, but failed?
151 OSL_ENSURE( eMode
!= READ_WRITE_CREATE
,
152 "Unable to create parent storage!" );
153 return xParentStorage
;
157 uno::Reference
< embed::XStorage
> xStorage
158 = queryStorage( xParentStorage
, aUriKey
, eMode
);
160 if ( !xStorage
.is() )
162 // requested to create new storage, but failed?
163 OSL_ENSURE( eMode
!= READ_WRITE_CREATE
,
164 "Unable to create storage!" );
168 bool bWritable
= ( ( eMode
== READ_WRITE_NOCREATE
)
169 || ( eMode
== READ_WRITE_CREATE
) );
171 rtl::Reference
< Storage
> xElement(
172 new Storage( m_xContext
, this, aUriKey
, xParentStorage
, xStorage
) );
174 aIt
= m_aMap
.emplace(
175 std::pair
< OUString
, bool >( aUriKey
, bWritable
),
176 xElement
.get() ).first
;
178 aIt
->second
->m_aContainerIt
= aIt
;
181 else if ( osl_atomic_increment( &aIt
->second
->m_refCount
) > 1 )
183 uno::Reference
< embed::XStorage
> xElement( aIt
->second
);
184 osl_atomic_decrement( &aIt
->second
->m_refCount
);
189 osl_atomic_decrement( &aIt
->second
->m_refCount
);
190 aIt
->second
->m_aContainerIt
= m_aMap
.end();
192 uno::Reference
< embed::XStorage
> xParentStorage
;
194 // documents never have a parent storage.
195 if ( !aUri
.isDocument() )
197 xParentStorage
= queryParentStorage( aUriKey
, eMode
);
199 if ( !xParentStorage
.is() )
201 // requested to create new storage, but failed?
202 OSL_ENSURE( eMode
!= READ_WRITE_CREATE
,
203 "Unable to create parent storage!" );
204 return xParentStorage
;
208 uno::Reference
< embed::XStorage
> xStorage
209 = queryStorage( xParentStorage
, aUriKey
, eMode
);
211 if ( !xStorage
.is() )
213 // requested to create new storage, but failed?
214 OSL_ENSURE( eMode
!= READ_WRITE_CREATE
,
215 "Unable to create storage!" );
219 rtl::Reference
<Storage
> pNewStorage
= new Storage( m_xContext
, this, aUriKey
, xParentStorage
, xStorage
);
220 aIt
->second
= pNewStorage
.get();
221 aIt
->second
->m_aContainerIt
= aIt
;
227 uno::Reference
< io::XInputStream
>
228 StorageElementFactory::createInputStream( const OUString
& rUri
,
229 const OUString
& rPassword
)
231 osl::MutexGuard
aGuard( m_aMutex
);
233 uno::Reference
< embed::XStorage
> xParentStorage
234 = queryParentStorage( rUri
, READ
);
236 // Each stream must have a parent storage.
237 if ( !xParentStorage
.is() )
238 return uno::Reference
< io::XInputStream
>();
240 uno::Reference
< io::XStream
> xStream
241 = queryStream( xParentStorage
, rUri
, rPassword
, READ
, false );
244 return uno::Reference
< io::XInputStream
>();
246 return xStream
->getInputStream();
250 uno::Reference
< io::XOutputStream
>
251 StorageElementFactory::createOutputStream( const OUString
& rUri
,
252 const OUString
& rPassword
,
255 osl::MutexGuard
aGuard( m_aMutex
);
257 uno::Reference
< embed::XStorage
> xParentStorage
258 = queryParentStorage( rUri
, READ_WRITE_CREATE
);
260 // Each stream must have a parent storage.
261 if ( !xParentStorage
.is() )
263 OSL_FAIL( "StorageElementFactory::createOutputStream - "
264 "Unable to create parent storage!" );
265 return uno::Reference
< io::XOutputStream
>();
268 uno::Reference
< io::XStream
> xStream
270 xParentStorage
, rUri
, rPassword
, READ_WRITE_CREATE
, bTruncate
);
274 OSL_FAIL( "StorageElementFactory::createOutputStream - "
275 "Unable to create stream!" );
276 return uno::Reference
< io::XOutputStream
>();
279 // Note: We need a wrapper to hold a reference to the parent storage to
280 // ensure that nobody else owns it at the moment we want to commit
281 // our changes. (There can be only one writable instance at a time
282 // and even no writable instance if there is already another
283 // read-only instance!)
284 return uno::Reference
< io::XOutputStream
>(
285 new OutputStream( m_xContext
, rUri
, xParentStorage
, xStream
->getOutputStream() ) );
289 uno::Reference
< io::XStream
>
290 StorageElementFactory::createStream( const OUString
& rUri
,
291 const OUString
& rPassword
,
294 osl::MutexGuard
aGuard( m_aMutex
);
296 uno::Reference
< embed::XStorage
> xParentStorage
297 = queryParentStorage( rUri
, READ_WRITE_CREATE
);
299 // Each stream must have a parent storage.
300 if ( !xParentStorage
.is() )
302 OSL_FAIL( "StorageElementFactory::createStream - "
303 "Unable to create parent storage!" );
304 return uno::Reference
< io::XStream
>();
307 uno::Reference
< io::XStream
> xStream
309 xParentStorage
, rUri
, rPassword
, READ_WRITE_NOCREATE
, bTruncate
);
313 OSL_FAIL( "StorageElementFactory::createStream - "
314 "Unable to create stream!" );
315 return uno::Reference
< io::XStream
>();
318 return uno::Reference
< io::XStream
>(
319 new Stream( m_xContext
, m_xDocsMgr
, rUri
, xParentStorage
, xStream
) );
323 void StorageElementFactory::releaseElement( Storage
const * pElement
)
325 OSL_ASSERT( pElement
);
326 osl::MutexGuard
aGuard( m_aMutex
);
327 if ( pElement
->m_aContainerIt
!= m_aMap
.end() )
328 m_aMap
.erase( pElement
->m_aContainerIt
);
335 uno::Reference
< embed::XStorage
> StorageElementFactory::queryParentStorage(
336 const OUString
& rUri
, StorageAccessMode eMode
)
338 uno::Reference
< embed::XStorage
> xParentStorage
;
341 Uri
aParentUri( aUri
.getParentUri() );
342 if ( !aParentUri
.isRoot() )
344 xParentStorage
= createStorage( aUri
.getParentUri(), eMode
);
345 OSL_ENSURE( xParentStorage
.is()
346 // requested to create new storage, but failed?
347 || ( eMode
!= READ_WRITE_CREATE
),
348 "StorageElementFactory::queryParentStorage - No storage!" );
350 return xParentStorage
;
354 uno::Reference
< embed::XStorage
> StorageElementFactory::queryStorage(
355 const uno::Reference
< embed::XStorage
> & xParentStorage
,
356 const OUString
& rUri
,
357 StorageAccessMode eMode
)
359 uno::Reference
< embed::XStorage
> xStorage
;
363 if ( !xParentStorage
.is() )
367 xStorage
= m_xDocsMgr
->queryStorage( aUri
.getDocumentId() );
369 if ( !xStorage
.is() )
371 if ( eMode
== READ_WRITE_CREATE
)
372 throw lang::IllegalArgumentException(
373 "Invalid open mode: document storages cannot be created!",
374 uno::Reference
< uno::XInterface
>(),
377 throw embed::InvalidStorageException(
378 "Invalid document id!",
379 uno::Reference
< uno::XInterface
>() );
382 // match xStorage's open mode against requested open mode
384 uno::Reference
< beans::XPropertySet
> xPropSet(
385 xStorage
, uno::UNO_QUERY
);
386 OSL_ENSURE( xPropSet
.is(),
387 "StorageElementFactory::queryStorage - "
388 "No XPropertySet interface!" );
391 uno::Any aPropValue
= xPropSet
->getPropertyValue("OpenMode");
393 sal_Int32 nOpenMode
= 0;
394 if ( aPropValue
>>= nOpenMode
)
399 if ( !( nOpenMode
& embed::ElementModes::READ
) )
401 // document opened, but not readable.
402 throw embed::InvalidStorageException(
403 "Storage is open, but not readable!" );
408 case READ_WRITE_NOCREATE
:
409 case READ_WRITE_CREATE
:
410 if ( !( nOpenMode
& embed::ElementModes::WRITE
) )
412 // document opened, but not writable.
413 throw embed::InvalidStorageException(
414 "Storage is open, but not writable!" );
423 "Bug! Value of property OpenMode has wrong type!" );
425 throw uno::RuntimeException(
426 "Bug! Value of property OpenMode has wrong type!" );
429 catch ( beans::UnknownPropertyException
const & )
431 css::uno::Any anyEx
= cppu::getCaughtException();
432 OSL_FAIL( "Property OpenMode not supported!" );
434 throw embed::StorageWrappedTargetException(
435 "Bug! Value of property OpenMode has wrong type!",
436 uno::Reference
< uno::XInterface
>(),
439 catch ( lang::WrappedTargetException
const & )
441 css::uno::Any anyEx
= cppu::getCaughtException();
442 OSL_FAIL( "Caught WrappedTargetException!" );
444 throw embed::StorageWrappedTargetException(
445 "WrappedTargetException during getPropertyValue!",
446 uno::Reference
< uno::XInterface
>(),
454 const OUString
& rName
= aUri
.getDecodedName();
460 sal_Int32
const nOpenMode
= embed::ElementModes::READ
461 | embed::ElementModes::NOCREATE
;
463 = xParentStorage
->openStorageElement( rName
, nOpenMode
);
465 catch ( io::IOException
const & )
467 // Another chance: Try to clone storage.
468 xStorage
= createTemporaryStorage();
469 xParentStorage
->copyStorageElementLastCommitTo( rName
,
475 sal_Int32 nOpenMode
= embed::ElementModes::READWRITE
;
476 if ( eMode
== READ_WRITE_NOCREATE
)
477 nOpenMode
|= embed::ElementModes::NOCREATE
;
479 xStorage
= xParentStorage
->openStorageElement( rName
, nOpenMode
);
483 OSL_ENSURE( xStorage
.is() || ( eMode
!= READ_WRITE_CREATE
),
484 "StorageElementFactory::queryStorage - No storage!" );
489 uno::Reference
< io::XStream
>
490 StorageElementFactory::queryStream(
491 const uno::Reference
< embed::XStorage
> & xParentStorage
,
492 const OUString
& rUri
,
493 const OUString
& rPassword
,
494 StorageAccessMode eMode
,
497 osl::MutexGuard
aGuard( m_aMutex
);
499 if ( !xParentStorage
.is() )
501 throw lang::IllegalArgumentException(
502 "No parent storage!",
503 uno::Reference
< uno::XInterface
>(),
510 throw lang::IllegalArgumentException(
511 "Root never is a stream!",
512 uno::Reference
< uno::XInterface
>(),
515 else if ( aUri
.isDocument() )
517 throw lang::IllegalArgumentException(
518 "A document never is a stream!",
519 uno::Reference
< uno::XInterface
>(),
527 nOpenMode
= embed::ElementModes::READ
528 | embed::ElementModes::NOCREATE
529 | embed::ElementModes::SEEKABLE
;
532 case READ_WRITE_NOCREATE
:
533 nOpenMode
= embed::ElementModes::READWRITE
534 | embed::ElementModes::NOCREATE
535 | embed::ElementModes::SEEKABLE
;
538 nOpenMode
|= embed::ElementModes::TRUNCATE
;
542 case READ_WRITE_CREATE
:
543 nOpenMode
= embed::ElementModes::READWRITE
544 | embed::ElementModes::SEEKABLE
;
547 nOpenMode
|= embed::ElementModes::TRUNCATE
;
552 OSL_FAIL( "StorageElementFactory::queryStream : Unknown open mode!" );
554 throw embed::InvalidStorageException(
555 "Unknown open mode!",
556 uno::Reference
< uno::XInterface
>() );
559 // No object re-usage mechanism; streams are seekable => not stateless.
561 uno::Reference
< io::XStream
> xStream
;
562 if ( !rPassword
.isEmpty() )
568 xStream
= xParentStorage
->cloneEncryptedStreamElement(
569 aUri
.getDecodedName(),
572 catch ( packages::NoEncryptionException
const & )
575 = xParentStorage
->cloneStreamElement( aUri
.getDecodedName() );
582 xStream
= xParentStorage
->openEncryptedStreamElement(
583 aUri
.getDecodedName(),
587 catch ( packages::NoEncryptionException
const & )
590 = xParentStorage
->openStreamElement( aUri
.getDecodedName(),
599 xStream
= xParentStorage
->cloneStreamElement( aUri
.getDecodedName() );
603 xStream
= xParentStorage
->openStreamElement( aUri
.getDecodedName(),
610 throw embed::InvalidStorageException(
612 uno::Reference
< uno::XInterface
>() );
618 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */