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 "subcomponentrecovery.hxx"
22 #include "sdbcoretools.hxx"
23 #include "storagexmlstream.hxx"
24 #include "subcomponentloader.hxx"
25 #include "settingsimport.hxx"
27 #include <com/sun/star/embed/ElementModes.hpp>
28 #include <com/sun/star/frame/ModuleManager.hpp>
29 #include <com/sun/star/document/XStorageBasedDocument.hpp>
30 #include <com/sun/star/ucb/XCommandProcessor.hpp>
31 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
32 #include <com/sun/star/sdb/XFormDocumentsSupplier.hpp>
33 #include <com/sun/star/sdb/XReportDocumentsSupplier.hpp>
34 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
36 #include <comphelper/namedvaluecollection.hxx>
37 #include <connectivity/dbtools.hxx>
38 #include <tools/diagnose_ex.h>
39 #include <xmloff/XMLSettingsExportContext.hxx>
40 #include <xmloff/SettingsExportHelper.hxx>
45 using css::uno::Reference
;
46 using css::uno::XInterface
;
47 using css::uno::UNO_QUERY
;
48 using css::uno::UNO_QUERY_THROW
;
49 using css::uno::UNO_SET_THROW
;
50 using css::uno::Exception
;
51 using css::uno::RuntimeException
;
53 using css::uno::makeAny
;
54 using css::uno::Sequence
;
56 using css::uno::XComponentContext
;
57 using css::lang::XMultiServiceFactory
;
58 using css::embed::XStorage
;
59 using css::sdb::application::XDatabaseDocumentUI
;
60 using css::beans::Pair
;
61 using css::frame::ModuleManager
;
62 using css::frame::XModuleManager2
;
63 using css::lang::XComponent
;
64 using css::frame::XModel
;
65 using css::frame::XController
;
66 using css::beans::XPropertySet
;
67 using css::beans::PropertyValue
;
68 using css::document::XStorageBasedDocument
;
69 using css::ucb::XCommandProcessor
;
70 using css::container::XHierarchicalNameAccess
;
71 using css::sdb::XFormDocumentsSupplier
;
72 using css::sdb::XReportDocumentsSupplier
;
73 using css::xml::sax::SAXException
;
74 using css::xml::sax::XLocator
;
75 using css::xml::sax::XDocumentHandler
;
76 using css::xml::sax::XAttributeList
;
78 namespace ElementModes
= css::embed::ElementModes
;
79 namespace DatabaseObject
= css::sdb::application::DatabaseObject
;
84 static OUString
lcl_getComponentStorageBaseName( const SubComponentType i_eType
)
89 return OUString("form");
91 return OUString("report");
93 return OUString("table");
95 return OUString("query");
100 OSL_FAIL( "lcl_getComponentStorageBaseName: unimplemented case!" );
104 static SubComponentType
lcl_databaseObjectToSubComponentType( const sal_Int32 i_nObjectType
)
106 switch ( i_nObjectType
)
108 case DatabaseObject::TABLE
: return TABLE
;
109 case DatabaseObject::QUERY
: return QUERY
;
110 case DatabaseObject::FORM
: return FORM
;
111 case DatabaseObject::REPORT
:return REPORT
;
118 static bool lcl_determineReadOnly( const Reference
< XComponent
>& i_rComponent
)
120 Reference
< XModel
> xDocument( i_rComponent
, UNO_QUERY
);
121 if ( !xDocument
.is() )
123 Reference
< XController
> xController( i_rComponent
, UNO_QUERY_THROW
);
124 xDocument
= xController
->getModel();
127 if ( !xDocument
.is() )
130 ::comphelper::NamedValueCollection
aDocArgs( xDocument
->getArgs() );
131 return aDocArgs
.getOrDefault( "ReadOnly", false );
134 static Reference
< XCommandProcessor
> lcl_getSubComponentDef_nothrow( const Reference
< XDatabaseDocumentUI
>& i_rAppUI
,
135 const SubComponentType i_eType
, const OUString
& i_rName
)
137 Reference
< XController
> xController( i_rAppUI
, UNO_QUERY_THROW
);
138 ENSURE_OR_RETURN( ( i_eType
== FORM
) || ( i_eType
== REPORT
), "lcl_getSubComponentDef_nothrow: illegal controller", NULL
);
140 Reference
< XCommandProcessor
> xCommandProcessor
;
143 Reference
< XHierarchicalNameAccess
> xDefinitionContainer
;
144 if ( i_eType
== FORM
)
146 Reference
< XFormDocumentsSupplier
> xSuppForms( xController
->getModel(), UNO_QUERY_THROW
);
147 xDefinitionContainer
.set( xSuppForms
->getFormDocuments(), UNO_QUERY_THROW
);
151 Reference
< XReportDocumentsSupplier
> xSuppReports( xController
->getModel(), UNO_QUERY_THROW
);
152 xDefinitionContainer
.set( xSuppReports
->getReportDocuments(), UNO_QUERY_THROW
);
154 xCommandProcessor
.set( xDefinitionContainer
->getByHierarchicalName( i_rName
), UNO_QUERY_THROW
);
156 catch( const Exception
& )
158 DBG_UNHANDLED_EXCEPTION();
160 return xCommandProcessor
;
163 static const char sSettingsStreamName
[] = "settings.xml";
164 static const char sCurrentQueryDesignName
[] = "ooo:current-query-design";
167 // SettingsExportContext
168 class DBACCESS_DLLPRIVATE SettingsExportContext
: public ::xmloff::XMLSettingsExportContext
171 SettingsExportContext( const Reference
<XComponentContext
>& i_rContext
, const StorageXMLOutputStream
& i_rDelegator
)
172 :m_rContext( i_rContext
)
173 ,m_rDelegator( i_rDelegator
)
174 ,m_aNamespace( ::xmloff::token::GetXMLToken( ::xmloff::token::XML_NP_CONFIG
) )
178 virtual ~SettingsExportContext()
183 virtual void AddAttribute( enum ::xmloff::token::XMLTokenEnum i_eName
, const OUString
& i_rValue
) SAL_OVERRIDE
;
184 virtual void AddAttribute( enum ::xmloff::token::XMLTokenEnum i_eName
, enum ::xmloff::token::XMLTokenEnum i_eValue
) SAL_OVERRIDE
;
185 virtual void StartElement( enum ::xmloff::token::XMLTokenEnum i_eName
, const bool i_bIgnoreWhitespace
) SAL_OVERRIDE
;
186 virtual void EndElement ( const bool i_bIgnoreWhitespace
) SAL_OVERRIDE
;
187 virtual void Characters( const OUString
& i_rCharacters
) SAL_OVERRIDE
;
189 virtual css::uno::Reference
< css::uno::XComponentContext
>
190 GetComponentContext() const SAL_OVERRIDE
;
193 OUString
impl_prefix( const ::xmloff::token::XMLTokenEnum i_eToken
)
195 return m_aNamespace
+ ":" + ::xmloff::token::GetXMLToken( i_eToken
);
199 const Reference
<XComponentContext
>& m_rContext
;
200 const StorageXMLOutputStream
& m_rDelegator
;
201 const OUString m_aNamespace
;
204 void SettingsExportContext::AddAttribute( enum ::xmloff::token::XMLTokenEnum i_eName
, const OUString
& i_rValue
)
206 m_rDelegator
.addAttribute( impl_prefix( i_eName
), i_rValue
);
209 void SettingsExportContext::AddAttribute( enum ::xmloff::token::XMLTokenEnum i_eName
, enum ::xmloff::token::XMLTokenEnum i_eValue
)
211 m_rDelegator
.addAttribute( impl_prefix( i_eName
), ::xmloff::token::GetXMLToken( i_eValue
) );
214 void SettingsExportContext::StartElement( enum ::xmloff::token::XMLTokenEnum i_eName
, const bool i_bIgnoreWhitespace
)
216 if ( i_bIgnoreWhitespace
)
217 m_rDelegator
.ignorableWhitespace( " " );
219 m_rDelegator
.startElement( impl_prefix( i_eName
) );
222 void SettingsExportContext::EndElement( const bool i_bIgnoreWhitespace
)
224 if ( i_bIgnoreWhitespace
)
225 m_rDelegator
.ignorableWhitespace( " " );
226 m_rDelegator
.endElement();
229 void SettingsExportContext::Characters( const OUString
& i_rCharacters
)
231 m_rDelegator
.characters( i_rCharacters
);
234 Reference
< com::sun::star::uno::XComponentContext
> SettingsExportContext::GetComponentContext() const
239 // SettingsDocumentHandler
240 typedef ::cppu::WeakImplHelper1
< XDocumentHandler
241 > SettingsDocumentHandler_Base
;
242 class DBACCESS_DLLPRIVATE SettingsDocumentHandler
: public SettingsDocumentHandler_Base
245 SettingsDocumentHandler()
250 virtual ~SettingsDocumentHandler()
256 virtual void SAL_CALL
startDocument( ) throw (SAXException
, RuntimeException
, std::exception
) SAL_OVERRIDE
;
257 virtual void SAL_CALL
endDocument( ) throw (SAXException
, RuntimeException
, std::exception
) SAL_OVERRIDE
;
258 virtual void SAL_CALL
startElement( const OUString
& aName
, const Reference
< XAttributeList
>& xAttribs
) throw (SAXException
, RuntimeException
, std::exception
) SAL_OVERRIDE
;
259 virtual void SAL_CALL
endElement( const OUString
& aName
) throw (SAXException
, RuntimeException
, std::exception
) SAL_OVERRIDE
;
260 virtual void SAL_CALL
characters( const OUString
& aChars
) throw (SAXException
, RuntimeException
, std::exception
) SAL_OVERRIDE
;
261 virtual void SAL_CALL
ignorableWhitespace( const OUString
& aWhitespaces
) throw (SAXException
, RuntimeException
, std::exception
) SAL_OVERRIDE
;
262 virtual void SAL_CALL
processingInstruction( const OUString
& aTarget
, const OUString
& aData
) throw (SAXException
, RuntimeException
, std::exception
) SAL_OVERRIDE
;
263 virtual void SAL_CALL
setDocumentLocator( const Reference
< XLocator
>& xLocator
) throw (SAXException
, RuntimeException
, std::exception
) SAL_OVERRIDE
;
265 const ::comphelper::NamedValueCollection
& getSettings() const { return m_aSettings
; }
268 ::std::stack
< ::rtl::Reference
< SettingsImport
> > m_aStates
;
269 ::comphelper::NamedValueCollection m_aSettings
;
272 void SAL_CALL
SettingsDocumentHandler::startDocument( ) throw (SAXException
, RuntimeException
, std::exception
)
276 void SAL_CALL
SettingsDocumentHandler::endDocument( ) throw (SAXException
, RuntimeException
, std::exception
)
280 void SAL_CALL
SettingsDocumentHandler::startElement( const OUString
& i_Name
, const Reference
< XAttributeList
>& i_Attribs
) throw (SAXException
, RuntimeException
, std::exception
)
282 ::rtl::Reference
< SettingsImport
> pNewState
;
284 if ( m_aStates
.empty() )
286 if ( i_Name
== "office:settings" )
288 pNewState
= new OfficeSettingsImport( m_aSettings
);
292 OSL_FAIL( "SettingsDocumentHandler::startElement: invalid settings file!" );
293 // Yes, that's not correct. Somebody could, in theory, give us a document which starts with "foo:settings",
294 // where "foo" is mapped to the proper namespace URL.
295 // However, there's no need to bother with this. The "recovery" sub storage we're recovering from is
296 // not part of ODF, so we can impose any format restrictions on it ...
301 ::rtl::Reference
< SettingsImport
> pCurrentState( m_aStates
.top() );
302 pNewState
= pCurrentState
->nextState( i_Name
);
305 ENSURE_OR_THROW( pNewState
.is(), "no new state - aborting import" );
306 pNewState
->startElement( i_Attribs
);
308 m_aStates
.push( pNewState
);
311 void SAL_CALL
SettingsDocumentHandler::endElement( const OUString
& i_Name
) throw (SAXException
, RuntimeException
, std::exception
)
313 ENSURE_OR_THROW( !m_aStates
.empty(), "no active element" );
316 ::rtl::Reference
< SettingsImport
> pCurrentState( m_aStates
.top() );
317 pCurrentState
->endElement();
321 void SAL_CALL
SettingsDocumentHandler::characters( const OUString
& i_Chars
) throw (SAXException
, RuntimeException
, std::exception
)
323 ENSURE_OR_THROW( !m_aStates
.empty(), "no active element" );
325 ::rtl::Reference
< SettingsImport
> pCurrentState( m_aStates
.top() );
326 pCurrentState
->characters( i_Chars
);
329 void SAL_CALL
SettingsDocumentHandler::ignorableWhitespace( const OUString
& aWhitespaces
) throw (SAXException
, RuntimeException
, std::exception
)
331 // ignore them - that's why they're called "ignorable"
335 void SAL_CALL
SettingsDocumentHandler::processingInstruction( const OUString
& i_Target
, const OUString
& i_Data
) throw (SAXException
, RuntimeException
, std::exception
)
337 OSL_FAIL( "SettingsDocumentHandler::processingInstruction: unexpected ..." );
342 void SAL_CALL
SettingsDocumentHandler::setDocumentLocator( const Reference
< XLocator
>& i_Locator
) throw (SAXException
, RuntimeException
, std::exception
)
347 // SubComponentRecovery
348 const OUString
SubComponentRecovery::getComponentsStorageName( const SubComponentType i_eType
)
353 return OUString("forms");
355 return OUString("reports");
357 return OUString("tables");
359 return OUString("queries");
360 case RELATION_DESIGN
:
361 return OUString("relations");
366 OSL_FAIL( "SubComponentRecovery::getComponentsStorageName: unimplemented case!" );
370 void SubComponentRecovery::saveToRecoveryStorage( const Reference
< XStorage
>& i_rRecoveryStorage
,
371 MapCompTypeToCompDescs
& io_mapCompDescs
)
373 if ( m_eType
== UNKNOWN
)
374 // quite fatal, but has already been reported (as assertion) before
377 // open the sub storage for the given kind of components
378 const OUString
& rStorageName( getComponentsStorageName( m_eType
) );
379 const Reference
< XStorage
> xComponentsStorage( i_rRecoveryStorage
->openStorageElement(
380 rStorageName
, ElementModes::READWRITE
), UNO_QUERY_THROW
);
382 // find a free sub storage name, and create Yet Another Sub Storage
383 const OUString
& rBaseName( lcl_getComponentStorageBaseName( m_eType
) );
384 const OUString sStorName
= ::dbtools::createUniqueName( xComponentsStorage
.get(), rBaseName
, true );
385 const Reference
< XStorage
> xObjectStor( xComponentsStorage
->openStorageElement(
386 sStorName
, ElementModes::READWRITE
), UNO_QUERY_THROW
);
392 impl_saveSubDocument_throw( xObjectStor
);
396 impl_saveQueryDesign_throw( xObjectStor
);
401 OSL_FAIL( "SubComponentRecoverys::saveToRecoveryStorage: unimplemented case!" );
405 // commit the storage(s)
406 tools::stor::commitStorageIfWriteable( xObjectStor
);
407 tools::stor::commitStorageIfWriteable( xComponentsStorage
);
409 // remember the relationship from the component name to the storage name
410 MapStringToCompDesc
& rMapCompDescs
= io_mapCompDescs
[ m_eType
];
411 OSL_ENSURE( rMapCompDescs
.find( sStorName
) == rMapCompDescs
.end(),
412 "SubComponentRecoverys::saveToRecoveryStorage: object name already used!" );
413 rMapCompDescs
[ sStorName
] = m_aCompDesc
;
416 void SubComponentRecovery::impl_identifyComponent_throw()
418 // ask the controller
419 Pair
< sal_Int32
, OUString
> aComponentIdentity
= m_xDocumentUI
->identifySubComponent( m_xComponent
);
420 m_eType
= lcl_databaseObjectToSubComponentType( aComponentIdentity
.First
);
421 m_aCompDesc
.sName
= aComponentIdentity
.Second
;
423 // what the controller didn't give us is the information whether this is in edit mode or not ...
424 Reference
< XModuleManager2
> xModuleManager( ModuleManager::create(m_rContext
) );
425 const OUString sModuleIdentifier
= xModuleManager
->identify( m_xComponent
);
430 m_aCompDesc
.bForEditing
= sModuleIdentifier
== "com.sun.star.sdb.TableDesign";
434 m_aCompDesc
.bForEditing
= sModuleIdentifier
== "com.sun.star.sdb.QueryDesign";
438 if ( sModuleIdentifier
== "com.sun.star.report.ReportDefinition" )
440 // it's an SRB report designer
441 m_aCompDesc
.bForEditing
= true;
447 m_aCompDesc
.bForEditing
= !lcl_determineReadOnly( m_xComponent
);
451 if ( sModuleIdentifier
== "com.sun.star.sdb.RelationDesign" )
453 m_eType
= RELATION_DESIGN
;
454 m_aCompDesc
.bForEditing
= true;
458 OSL_FAIL( "SubComponentRecovery::impl_identifyComponent_throw: couldn't classify the given sub component!" );
463 SAL_WARN_IF( m_eType
== UNKNOWN
, "dbaccess",
464 "SubComponentRecovery::impl_identifyComponent_throw: couldn't classify the component!" );
467 void SubComponentRecovery::impl_saveQueryDesign_throw( const Reference
< XStorage
>& i_rObjectStorage
)
469 ENSURE_OR_THROW( m_eType
== QUERY
, "illegal sub component type" );
470 ENSURE_OR_THROW( i_rObjectStorage
.is(), "illegal storage" );
472 // retrieve the current query design (which might differ from what we can retrieve as ActiveCommand property, since
473 // the latter is updated only upon successful save of the design)
474 Reference
< XPropertySet
> xDesignerProps( m_xComponent
, UNO_QUERY_THROW
);
475 Sequence
< PropertyValue
> aCurrentQueryDesign
;
476 OSL_VERIFY( xDesignerProps
->getPropertyValue( "CurrentQueryDesign" ) >>= aCurrentQueryDesign
);
478 // write the query design
479 StorageXMLOutputStream
aDesignOutput( m_rContext
, i_rObjectStorage
, sSettingsStreamName
);
480 SettingsExportContext
aSettingsExportContext( m_rContext
, aDesignOutput
);
482 const OUString
sWhitespace( " " );
484 aDesignOutput
.startElement( "office:settings" );
485 aDesignOutput
.ignorableWhitespace( sWhitespace
);
487 XMLSettingsExportHelper
aSettingsExporter( aSettingsExportContext
);
488 aSettingsExporter
.exportAllSettings( aCurrentQueryDesign
, sCurrentQueryDesignName
);
490 aDesignOutput
.ignorableWhitespace( sWhitespace
);
491 aDesignOutput
.endElement();
492 aDesignOutput
.close();
495 void SubComponentRecovery::impl_saveSubDocument_throw( const Reference
< XStorage
>& i_rObjectStorage
)
497 ENSURE_OR_THROW( ( m_eType
== FORM
) || ( m_eType
== REPORT
), "illegal sub component type" );
498 ENSURE_OR_THROW( i_rObjectStorage
.is(), "illegal storage" );
500 // store the document into the storage
501 Reference
< XStorageBasedDocument
> xStorageDocument( m_xComponent
, UNO_QUERY_THROW
);
502 xStorageDocument
->storeToStorage( i_rObjectStorage
, Sequence
< PropertyValue
>() );
505 Reference
< XComponent
> SubComponentRecovery::impl_recoverSubDocument_throw( const Reference
< XStorage
>& i_rRecoveryStorage
,
506 const OUString
& i_rComponentName
, const bool i_bForEditing
)
508 Reference
< XComponent
> xSubComponent
;
509 Reference
< XCommandProcessor
> xDocDefinition
;
511 ::comphelper::NamedValueCollection aLoadArgs
;
512 aLoadArgs
.put( "RecoveryStorage", i_rRecoveryStorage
);
514 // load/create the sub component hidden. We'll show it when the main app window is shown.
515 aLoadArgs
.put( "Hidden", true );
517 if ( !i_rComponentName
.isEmpty() )
519 xDocDefinition
= lcl_getSubComponentDef_nothrow( m_xDocumentUI
, m_eType
, i_rComponentName
);
520 xSubComponent
.set( m_xDocumentUI
->loadComponentWithArguments(
524 aLoadArgs
.getPropertyValues()
531 Reference
< XComponent
> xDocDefComponent
;
532 xSubComponent
.set( m_xDocumentUI
->createComponentWithArguments(
534 aLoadArgs
.getPropertyValues(),
540 xDocDefinition
.set( xDocDefComponent
, UNO_QUERY
);
541 OSL_ENSURE( xDocDefinition
.is(), "DatabaseDocumentRecovery::recoverSubDocuments: loaded a form/report, but don't have a document definition?!" );
544 if ( xDocDefinition
.is() )
546 Reference
< XController
> xController( m_xDocumentUI
, UNO_QUERY_THROW
);
547 Reference
< XInterface
> xLoader( *new SubComponentLoader( xController
, xDocDefinition
) );
551 return xSubComponent
;
554 Reference
< XComponent
> SubComponentRecovery::impl_recoverQueryDesign_throw( const Reference
< XStorage
>& i_rRecoveryStorage
,
555 const OUString
& i_rComponentName
, const bool i_bForEditing
)
557 Reference
< XComponent
> xSubComponent
;
559 // first read the settings query design settings from the storage
560 StorageXMLInputStream
aDesignInput( m_rContext
, i_rRecoveryStorage
, sSettingsStreamName
);
562 ::rtl::Reference
< SettingsDocumentHandler
> pDocHandler( new SettingsDocumentHandler
);
563 aDesignInput
.import( pDocHandler
.get() );
565 const ::comphelper::NamedValueCollection
& rSettings( pDocHandler
->getSettings() );
566 const Any aCurrentQueryDesign
= rSettings
.get( sCurrentQueryDesignName
);
567 #if OSL_DEBUG_LEVEL > 0
568 Sequence
< PropertyValue
> aQueryDesignLayout
;
569 OSL_VERIFY( aCurrentQueryDesign
>>= aQueryDesignLayout
);
572 // then load the query designer
573 ::comphelper::NamedValueCollection aLoadArgs
;
574 aLoadArgs
.put( "CurrentQueryDesign", aCurrentQueryDesign
);
575 aLoadArgs
.put( "Hidden", true );
577 if ( !i_rComponentName
.isEmpty() )
579 xSubComponent
.set( m_xDocumentUI
->loadComponentWithArguments(
583 aLoadArgs
.getPropertyValues()
590 Reference
< XComponent
> xDummy
;
591 xSubComponent
.set( m_xDocumentUI
->createComponentWithArguments(
593 aLoadArgs
.getPropertyValues(),
600 Reference
< XController
> xController( m_xDocumentUI
, UNO_QUERY_THROW
);
601 Reference
< XInterface
> xLoader( *new SubComponentLoader( xController
, xSubComponent
) );
604 return xSubComponent
;
607 Reference
< XComponent
> SubComponentRecovery::recoverFromStorage( const Reference
< XStorage
>& i_rRecoveryStorage
,
608 const OUString
& i_rComponentName
, const bool i_bForEditing
)
610 Reference
< XComponent
> xSubComponent
;
615 xSubComponent
= impl_recoverSubDocument_throw( i_rRecoveryStorage
, i_rComponentName
, i_bForEditing
);
618 xSubComponent
= impl_recoverQueryDesign_throw( i_rRecoveryStorage
, i_rComponentName
, i_bForEditing
);
621 OSL_FAIL( "SubComponentRecovery::recoverFromStorage: unimplemented case!" );
624 return xSubComponent
;
627 } // namespace dbaccess
629 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */