LibreOffice Module ucb (master)  1
tdoc_provider.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
21 /**************************************************************************
22  TODO
23  **************************************************************************
24 
25  *************************************************************************/
26 
27 #include <tools/diagnose_ex.h>
28 
29 #include <com/sun/star/embed/InvalidStorageException.hpp>
30 #include <com/sun/star/embed/StorageWrappedTargetException.hpp>
31 #include <com/sun/star/io/IOException.hpp>
32 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
35 #include <ucbhelper/macros.hxx>
36 
37 #include "tdoc_provider.hxx"
38 #include "tdoc_content.hxx"
39 #include "tdoc_uri.hxx"
40 #include "tdoc_docmgr.hxx"
41 #include "tdoc_storage.hxx"
42 
43 using namespace com::sun::star;
44 using namespace tdoc_ucp;
45 
46 
47 // ContentProvider Implementation.
48 
49 
51  const uno::Reference< uno::XComponentContext >& rxContext )
52 : ::ucbhelper::ContentProviderImplHelper( rxContext ),
53  m_xDocsMgr( new OfficeDocumentsManager( rxContext, this ) ),
54  m_xStgElemFac( new StorageElementFactory( rxContext, m_xDocsMgr ) )
55 {
56 }
57 
58 
59 // virtual
61 {
62  if ( m_xDocsMgr.is() )
63  m_xDocsMgr->destroy();
64 }
65 
66 
67 // XInterface methods.
68 void SAL_CALL ContentProvider::acquire()
69  noexcept
70 {
71  OWeakObject::acquire();
72 }
73 
74 void SAL_CALL ContentProvider::release()
75  noexcept
76 {
77  OWeakObject::release();
78 }
79 
80 css::uno::Any SAL_CALL ContentProvider::queryInterface( const css::uno::Type & rType )
81 {
82  css::uno::Any aRet = cppu::queryInterface( rType,
83  static_cast< lang::XTypeProvider* >(this),
84  static_cast< lang::XServiceInfo* >(this),
85  static_cast< ucb::XContentProvider* >(this),
86  static_cast< frame::XTransientDocumentsDocumentContentIdentifierFactory* >(this),
87  static_cast< frame::XTransientDocumentsDocumentContentFactory* >(this)
88  );
89  return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
90 }
91 
92 // XTypeProvider methods.
93 
94 
96  lang::XTypeProvider,
97  lang::XServiceInfo,
98  ucb::XContentProvider,
99  frame::XTransientDocumentsDocumentContentIdentifierFactory,
100  frame::XTransientDocumentsDocumentContentFactory );
101 
102 
103 // XServiceInfo methods.
105 {
106  return "com.sun.star.comp.ucb.TransientDocumentsContentProvider";
107 }
108 
109 sal_Bool SAL_CALL ContentProvider::supportsService( const OUString& ServiceName )
110 {
111  return cppu::supportsService( this, ServiceName );
112 }
113 
114 css::uno::Sequence< OUString > SAL_CALL ContentProvider::getSupportedServiceNames()
115 {
116  return { "com.sun.star.ucb.TransientDocumentsContentProvider" };
117 }
118 
119 
120 // Service factory implementation.
121 
122 
123 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
125  css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
126 {
127  return cppu::acquire(new ContentProvider(context));
128 }
129 
130 // XContentProvider methods.
131 
132 
133 // virtual
134 uno::Reference< ucb::XContent > SAL_CALL
136  const uno::Reference< ucb::XContentIdentifier >& Identifier )
137 {
138  Uri aUri( Identifier->getContentIdentifier() );
139  if ( !aUri.isValid() )
140  throw ucb::IllegalIdentifierException(
141  "Invalid URL!",
142  Identifier );
143 
144  // Normalize URI.
145  uno::Reference< ucb::XContentIdentifier > xCanonicId
146  = new ::ucbhelper::ContentIdentifier( aUri.getUri() );
147 
148  osl::MutexGuard aGuard( m_aMutex );
149 
150  // Check, if a content with given id already exists...
151  uno::Reference< ucb::XContent > xContent
152  = queryExistingContent( xCanonicId );
153 
154  if ( !xContent.is() )
155  {
156  // Create a new content.
157  xContent = Content::create( m_xContext, this, xCanonicId );
158  registerNewContent( xContent );
159  }
160 
161  return xContent;
162 }
163 
164 
165 // XTransientDocumentsDocumentContentIdentifierFactory methods.
166 
167 uno::Reference<ucb::XContentIdentifier> SAL_CALL
169  uno::Reference<frame::XModel> const& xModel)
170 {
171  // model -> id -> content identifier -> queryContent
172  if ( !m_xDocsMgr.is() )
173  {
174  throw lang::IllegalArgumentException(
175  "No Document Manager!",
176  static_cast< cppu::OWeakObject * >( this ),
177  1 );
178  }
179 
180  OUString aDocId = tdoc_ucp::OfficeDocumentsManager::queryDocumentId(xModel);
181  if ( aDocId.isEmpty() )
182  {
183  throw lang::IllegalArgumentException(
184  "Unable to obtain document id from model!",
185  static_cast< cppu::OWeakObject * >( this ),
186  1 );
187  }
188 
189  OUString aBuffer = TDOC_URL_SCHEME ":/" + aDocId;
190 
191  uno::Reference< ucb::XContentIdentifier > xId
192  = new ::ucbhelper::ContentIdentifier( aBuffer );
193  return xId;
194 }
195 
196 // XTransientDocumentsDocumentContentFactory methods.
197 
198 uno::Reference< ucb::XContent > SAL_CALL
200  uno::Reference<frame::XModel> const& xModel)
201 {
202  uno::Reference<ucb::XContentIdentifier> const xId(
204 
205  osl::MutexGuard aGuard( m_aMutex );
206 
207  // Check, if a content with given id already exists...
208  uno::Reference< ucb::XContent > xContent
209  = queryExistingContent( xId );
210 
211  if ( !xContent.is() )
212  {
213  // Create a new content.
214  xContent = Content::create( m_xContext, this, xId );
215  }
216 
217  if ( xContent.is() )
218  return xContent;
219 
220  // no content.
221  throw lang::IllegalArgumentException(
222  "Illegal Content Identifier!",
223  static_cast< cppu::OWeakObject * >( this ),
224  1 );
225 }
226 
227 
228 // interface OfficeDocumentsEventListener
229 
230 
231 // virtual
232 void ContentProvider::notifyDocumentClosed( const OUString & rDocId )
233 {
234  osl::MutexGuard aGuard( getContentListMutex() );
235 
236  ::ucbhelper::ContentRefList aAllContents;
237  queryExistingContents( aAllContents );
238 
239  // Notify all content objects related to the closed doc.
240 
241  bool bFoundDocumentContent = false;
243 
244  for ( const auto& rContent : aAllContents )
245  {
246  Uri aUri( rContent->getIdentifier()->getContentIdentifier() );
247  OSL_ENSURE( aUri.isValid(),
248  "ContentProvider::notifyDocumentClosed - Invalid URI!" );
249 
250  if ( !bFoundDocumentContent )
251  {
252  if ( aUri.isRoot() )
253  {
254  xRoot = static_cast< Content * >( rContent.get() );
255  }
256  else if ( aUri.isDocument() )
257  {
258  if ( aUri.getDocumentId() == rDocId )
259  {
260  bFoundDocumentContent = true;
261 
262  // document content will notify removal of child itself;
263  // no need for the root to propagate this.
264  xRoot.clear();
265  }
266  }
267  }
268 
269  if ( aUri.getDocumentId() == rDocId )
270  {
271  // Inform content.
273  = static_cast< Content * >( rContent.get() );
274 
275  xContent->notifyDocumentClosed();
276  }
277  }
278 
279  if ( xRoot.is() )
280  {
281  // No document content found for rDocId but root content
282  // instantiated. Root content must announce document removal
283  // to content event listeners.
284  xRoot->notifyChildRemoved( rDocId );
285  }
286 }
287 
288 
289 // virtual
290 void ContentProvider::notifyDocumentOpened( const OUString & rDocId )
291 {
292  osl::MutexGuard aGuard( getContentListMutex() );
293 
294  ::ucbhelper::ContentRefList aAllContents;
295  queryExistingContents( aAllContents );
296 
297  // Find root content. If instantiated let it propagate document insertion.
298 
299  for ( const auto& rContent : aAllContents )
300  {
301  Uri aUri( rContent->getIdentifier()->getContentIdentifier() );
302  OSL_ENSURE( aUri.isValid(),
303  "ContentProvider::notifyDocumentOpened - Invalid URI!" );
304 
305  if ( aUri.isRoot() )
306  {
308  = static_cast< Content * >( rContent.get() );
309  xRoot->notifyChildInserted( rDocId );
310 
311  // Done.
312  break;
313  }
314  }
315 }
316 
317 
318 // Non-UNO
319 
320 
321 uno::Reference< embed::XStorage >
322 ContentProvider::queryStorage( const OUString & rUri,
323  StorageAccessMode eMode ) const
324 {
325  if ( m_xStgElemFac.is() )
326  {
327  try
328  {
329  return m_xStgElemFac->createStorage( rUri, eMode );
330  }
331  catch ( embed::InvalidStorageException const & )
332  {
333  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
334  }
335  catch ( lang::IllegalArgumentException const & )
336  {
337  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
338  }
339  catch ( io::IOException const & )
340  {
341  // Okay to happen, for instance when the storage does not exist.
342  //OSL_ENSURE( false, "Caught IOException!" );
343  }
344  catch ( embed::StorageWrappedTargetException const & )
345  {
346  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
347  }
348  }
349  return uno::Reference< embed::XStorage >();
350 }
351 
352 
353 uno::Reference< embed::XStorage >
354 ContentProvider::queryStorageClone( const OUString & rUri ) const
355 {
356  if ( m_xStgElemFac.is() )
357  {
358  try
359  {
360  Uri aUri( rUri );
361  uno::Reference< embed::XStorage > xParentStorage
362  = m_xStgElemFac->createStorage( aUri.getParentUri(), READ );
363  uno::Reference< embed::XStorage > xStorage
364  = m_xStgElemFac->createTemporaryStorage();
365 
366  xParentStorage->copyStorageElementLastCommitTo(
367  aUri.getDecodedName(), xStorage );
368  return xStorage;
369  }
370  catch ( embed::InvalidStorageException const & )
371  {
372  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
373  }
374  catch ( lang::IllegalArgumentException const & )
375  {
376  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
377  }
378  catch ( io::IOException const & )
379  {
380  // Okay to happen, for instance when the storage does not exist.
381  //OSL_ENSURE( false, "Caught IOException!" );
382  }
383  catch ( embed::StorageWrappedTargetException const & )
384  {
385  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
386  }
387  }
388 
389  return uno::Reference< embed::XStorage >();
390 }
391 
392 
393 uno::Reference< io::XInputStream >
394 ContentProvider::queryInputStream( const OUString & rUri,
395  const OUString & rPassword ) const
396 {
397  if ( m_xStgElemFac.is() )
398  {
399  try
400  {
401  return m_xStgElemFac->createInputStream( rUri, rPassword );
402  }
403  catch ( embed::InvalidStorageException const & )
404  {
405  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
406  }
407  catch ( lang::IllegalArgumentException const & )
408  {
409  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
410  }
411  catch ( io::IOException const & )
412  {
413  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
414  }
415  catch ( embed::StorageWrappedTargetException const & )
416  {
417  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
418  }
419 // catch ( packages::WrongPasswordException const & )
420 // {
421 // // the key provided is wrong; rethrow; to be handled by caller.
422 // throw;
423 // }
424  }
425  return uno::Reference< io::XInputStream >();
426 }
427 
428 
429 uno::Reference< io::XOutputStream >
430 ContentProvider::queryOutputStream( const OUString & rUri,
431  const OUString & rPassword,
432  bool bTruncate ) const
433 {
434  if ( m_xStgElemFac.is() )
435  {
436  try
437  {
438  return
439  m_xStgElemFac->createOutputStream( rUri, rPassword, bTruncate );
440  }
441  catch ( embed::InvalidStorageException const & )
442  {
443  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
444  }
445  catch ( lang::IllegalArgumentException const & )
446  {
447  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
448  }
449  catch ( io::IOException const & )
450  {
451  // Okay to happen, for instance when the storage does not exist.
452  //OSL_ENSURE( false, "Caught IOException!" );
453  }
454  catch ( embed::StorageWrappedTargetException const & )
455  {
456  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
457  }
458 // catch ( packages::WrongPasswordException const & )
459 // {
460 // // the key provided is wrong; rethrow; to be handled by caller.
461 // throw;
462 // }
463  }
464  return uno::Reference< io::XOutputStream >();
465 }
466 
467 
468 uno::Reference< io::XStream >
469 ContentProvider::queryStream( const OUString & rUri,
470  const OUString & rPassword,
471  bool bTruncate ) const
472 {
473  if ( m_xStgElemFac.is() )
474  {
475  try
476  {
477  return m_xStgElemFac->createStream( rUri, rPassword, bTruncate );
478  }
479  catch ( embed::InvalidStorageException const & )
480  {
481  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
482  }
483  catch ( lang::IllegalArgumentException const & )
484  {
485  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
486  }
487  catch ( io::IOException const & )
488  {
489  // Okay to happen, for instance when the storage does not exist.
490  //OSL_ENSURE( false, "Caught IOException!" );
491  }
492  catch ( embed::StorageWrappedTargetException const & )
493  {
494  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
495  }
496 // catch ( packages::WrongPasswordException const & )
497 // {
498 // // the key provided is wrong; rethrow; to be handled by caller.
499 // throw;
500 // }
501  }
502  return uno::Reference< io::XStream >();
503 }
504 
505 
507  const OUString & rUri, uno::Sequence< OUString > & rNames ) const
508 {
509  Uri aUri( rUri );
510  if ( aUri.isRoot() )
511  {
512  // special handling for root, which has no storage, but children.
513  if ( m_xDocsMgr.is() )
514  {
515  rNames = m_xDocsMgr->queryDocuments();
516  return true;
517  }
518  }
519  else
520  {
521  if ( m_xStgElemFac.is() )
522  {
523  try
524  {
525  uno::Reference< embed::XStorage > xStorage
526  = m_xStgElemFac->createStorage( rUri, READ );
527 
528  OSL_ENSURE( xStorage.is(), "Got no Storage!" );
529 
530  if ( xStorage.is() )
531  {
532  rNames = xStorage->getElementNames();
533  return true;
534  }
535  }
536  catch ( embed::InvalidStorageException const & )
537  {
538  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
539  }
540  catch ( lang::IllegalArgumentException const & )
541  {
542  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
543  }
544  catch ( io::IOException const & )
545  {
546  // Okay to happen, for instance if the storage does not exist.
547  //OSL_ENSURE( false, "Caught IOException!" );
548  }
549  catch ( embed::StorageWrappedTargetException const & )
550  {
551  TOOLS_WARN_EXCEPTION("ucb.ucp", "");
552  }
553  }
554  }
555  return false;
556 }
557 
558 
559 OUString
560 ContentProvider::queryStorageTitle( const OUString & rUri ) const
561 {
562  OUString aTitle;
563 
564  Uri aUri( rUri );
565  if ( aUri.isRoot() )
566  {
567  // always empty.
568  aTitle.clear();
569  }
570  else if ( aUri.isDocument() )
571  {
572  // for documents, title shall not be derived from URL. It shall
573  // be something more 'speaking' than just the document UID.
574  if ( m_xDocsMgr.is() )
575  aTitle = m_xDocsMgr->queryStorageTitle( aUri.getDocumentId() );
576  }
577  else
578  {
579  // derive title from URL
580  aTitle = aUri.getDecodedName();
581  }
582 
583  OSL_ENSURE( !aTitle.isEmpty() || aUri.isRoot(),
584  "ContentProvider::queryStorageTitle - empty title!" );
585  return aTitle;
586 }
587 
588 
589 uno::Reference< frame::XModel >
590 ContentProvider::queryDocumentModel( const OUString & rUri ) const
591 {
592  uno::Reference< frame::XModel > xModel;
593 
594  if ( m_xDocsMgr.is() )
595  {
596  Uri aUri( rUri );
597  xModel = m_xDocsMgr->queryDocumentModel( aUri.getDocumentId() );
598  }
599 
600  OSL_ENSURE( xModel.is(),
601  "ContentProvider::queryDocumentModel - no model!" );
602  return xModel;
603 }
604 
605 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::vector< ContentImplHelperRef > ContentRefList
virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType) override
rtl::Reference< StorageElementFactory > m_xStgElemFac
css::uno::Reference< css::frame::XModel > queryDocumentModel(const OUString &rUri) const
void queryExistingContents(ContentRefList &rContents)
css::uno::Reference< css::embed::XStorage > queryStorageClone(const OUString &rUri) const
virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL createDocumentContentIdentifier(css::uno::Reference< css::frame::XModel > const &xModel) override
#define TDOC_URL_SCHEME
Definition: tdoc_uri.hxx:27
css::uno::Reference< css::io::XInputStream > queryInputStream(const OUString &rUri, const OUString &rPassword) const
css::uno::Reference< css::io::XStream > queryStream(const OUString &rUri, const OUString &rPassword, bool bTruncate) const
bool isDocument() const
Definition: tdoc_uri.hxx:98
bool isRoot() const
Definition: tdoc_uri.hxx:92
virtual css::uno::Reference< css::ucb::XContent > SAL_CALL createDocumentContent(const css::uno::Reference< css::frame::XModel > &Model) override
OUString queryStorageTitle(const OUString &rUri) const
virtual void SAL_CALL acquire() noexcept override
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
virtual OUString SAL_CALL getImplementationName() override
virtual css::uno::Reference< css::ucb::XContent > SAL_CALL queryContent(const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier) override
XTYPEPROVIDER_IMPL_5(ContentProvider, lang::XTypeProvider, lang::XServiceInfo, ucb::XContentProvider, frame::XTransientDocumentsDocumentContentIdentifierFactory, frame::XTransientDocumentsDocumentContentFactory)
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * ucb_tdoc_ContentProvider_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
#define TOOLS_WARN_EXCEPTION(area, stream)
const OUString & getDecodedName() const
Definition: tdoc_uri.hxx:73
css::uno::Reference< css::io::XOutputStream > queryOutputStream(const OUString &rUri, const OUString &rPassword, bool bTruncate) const
bool queryNamesOfChildren(const OUString &rUri, css::uno::Sequence< OUString > &rNames) const
unsigned char sal_Bool
rtl::Reference< ContentImplHelper > queryExistingContent(const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier)
static OUString queryDocumentId(const css::uno::Reference< css::frame::XModel > &xModel)
css::uno::Reference< css::uno::XComponentContext > m_xContext
void registerNewContent(const css::uno::Reference< css::ucb::XContent > &xContent)
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
css::uno::Reference< css::embed::XStorage > queryStorage(const OUString &rUri, StorageAccessMode eMode) const
virtual ~ContentProvider() override
std::unique_ptr< char[]> aBuffer
rtl::Reference< OfficeDocumentsManager > m_xDocsMgr
virtual void SAL_CALL release() noexcept override
const OUString & getDocumentId() const
Definition: tdoc_uri.hxx:67
static rtl::Reference< Content > create(const css::uno::Reference< css::uno::XComponentContext > &rxContext, ContentProvider *pProvider, const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier)
void notifyDocumentClosed(const OUString &rDocId)
const OUString & getParentUri() const
Definition: tdoc_uri.hxx:64
friend ContentProvider
Definition: pkgprovider.cxx:53
Reference< XContentIdentifier > xId
uno::Reference< ucb::XContent > xContent
void notifyDocumentOpened(const OUString &rDocId)
Reference< XModel > xModel
css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType, Interface1 *p1)