tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / dbaccess / source / core / recovery / dbdocrecovery.cxx
blobd367517a031589bc2b2a35c7a3983e3faaa36e9a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <recovery/dbdocrecovery.hxx>
21 #include <sdbcoretools.hxx>
22 #include "storagetextstream.hxx"
23 #include "subcomponentrecovery.hxx"
24 #include "subcomponents.hxx"
26 #include <com/sun/star/sdb/application/XDatabaseDocumentUI.hpp>
27 #include <com/sun/star/embed/ElementModes.hpp>
28 #include <com/sun/star/io/TextInputStream.hpp>
29 #include <com/sun/star/util/XModifiable.hpp>
31 #include <o3tl/string_view.hxx>
32 #include <rtl/ustrbuf.hxx>
33 #include <sal/log.hxx>
34 #include <comphelper/diagnose_ex.hxx>
36 namespace dbaccess
39 using css::uno::Reference;
40 using css::uno::UNO_QUERY;
41 using css::uno::UNO_QUERY_THROW;
42 using css::uno::UNO_SET_THROW;
43 using css::uno::Exception;
44 using css::uno::Sequence;
45 using css::uno::XComponentContext;
46 using css::embed::XStorage;
47 using css::frame::XController;
48 using css::sdb::application::XDatabaseDocumentUI;
49 using css::lang::XComponent;
50 using css::io::XStream;
51 using css::io::TextInputStream;
52 using css::io::XTextInputStream2;
53 using css::util::XModifiable;
55 namespace ElementModes = css::embed::ElementModes;
57 // helpers
58 namespace
60 void lcl_getPersistentRepresentation( const MapStringToCompDesc::value_type& i_rComponentDesc, OUStringBuffer& o_rBuffer )
62 o_rBuffer.append( i_rComponentDesc.first );
63 o_rBuffer.append( '=' );
64 o_rBuffer.append( i_rComponentDesc.second.sName );
65 o_rBuffer.append( ',' );
66 o_rBuffer.append( sal_Unicode( i_rComponentDesc.second.bForEditing ? '1' : '0' ) );
69 bool lcl_extractCompDesc( std::u16string_view i_rIniLine, OUString& o_rStorName, SubComponentDescriptor& o_rCompDesc )
71 const size_t nEqualSignPos = i_rIniLine.find( '=' );
72 if ( nEqualSignPos == 0 || nEqualSignPos == std::u16string_view::npos )
74 OSL_FAIL( "lcl_extractCompDesc: invalid map file entry - unexpected pos of '='" );
75 return false;
77 o_rStorName = i_rIniLine.substr( 0, nEqualSignPos );
79 const size_t nCommaPos = i_rIniLine.rfind( ',' );
80 if ( nCommaPos != i_rIniLine.size() - 2 )
82 OSL_FAIL( "lcl_extractCompDesc: invalid map file entry - unexpected pos of ','" );
83 return false;
85 o_rCompDesc.sName = i_rIniLine.substr( nEqualSignPos + 1, nCommaPos - nEqualSignPos - 1 );
86 o_rCompDesc.bForEditing = ( i_rIniLine[ nCommaPos + 1 ] == '1' );
87 return true;
90 constexpr OUString sRecoveryDataSubStorageName = u"recovery"_ustr;
92 constexpr OUString sObjectMapStreamName = u"storage-component-map.ini"_ustr;
94 void lcl_writeObjectMap_throw( const Reference<XComponentContext> & i_rContext, const Reference< XStorage >& i_rStorage,
95 const MapStringToCompDesc& i_mapStorageToCompDesc )
97 if ( i_mapStorageToCompDesc.empty() )
98 // nothing to do
99 return;
101 StorageTextOutputStream aTextOutput( i_rContext, i_rStorage, sObjectMapStreamName );
103 aTextOutput.writeLine( u"[storages]"_ustr );
105 for (auto const& elem : i_mapStorageToCompDesc)
107 OUStringBuffer aLine;
108 lcl_getPersistentRepresentation(elem, aLine);
110 aTextOutput.writeLine( aLine.makeStringAndClear() );
113 aTextOutput.writeLine();
116 bool lcl_isSectionStart( std::u16string_view i_rIniLine, OUString& o_rSectionName )
118 const size_t nLen = i_rIniLine.size();
119 if ( o3tl::starts_with(i_rIniLine, u"[") && o3tl::ends_with(i_rIniLine, u"]") )
121 o_rSectionName = i_rIniLine.substr( 1, nLen -2 );
122 return true;
124 return false;
127 void lcl_stripTrailingLineFeed( OUString& io_rLine )
129 const sal_Int32 nLen = io_rLine.getLength();
130 if ( io_rLine.endsWith("\n") )
131 io_rLine = io_rLine.copy( 0, nLen - 1 );
134 void lcl_readObjectMap_throw( const Reference<XComponentContext> & i_rxContext, const Reference< XStorage >& i_rStorage,
135 MapStringToCompDesc& o_mapStorageToObjectName )
137 ENSURE_OR_THROW( i_rStorage.is(), "invalid storage" );
138 if ( !i_rStorage->hasByName( sObjectMapStreamName ) )
139 { // nothing to do, though suspicious
140 OSL_FAIL( "lcl_readObjectMap_throw: if there's no map file, then there's expected to be no storage, too!" );
141 return;
144 Reference< XStream > xIniStream( i_rStorage->openStreamElement(
145 sObjectMapStreamName, ElementModes::READ ), UNO_SET_THROW );
147 Reference< XTextInputStream2 > xTextInput = TextInputStream::create( i_rxContext );
148 xTextInput->setEncoding( u"UTF-8"_ustr );
149 xTextInput->setInputStream( xIniStream->getInputStream() );
151 OUString sCurrentSection;
152 bool bCurrentSectionIsKnownToBeUnsupported = true;
153 while ( !xTextInput->isEOF() )
155 OUString sLine = xTextInput->readLine();
156 lcl_stripTrailingLineFeed( sLine );
158 if ( sLine.isEmpty() )
159 continue;
161 if ( lcl_isSectionStart( sLine, sCurrentSection ) )
163 bCurrentSectionIsKnownToBeUnsupported = false;
164 continue;
167 if ( bCurrentSectionIsKnownToBeUnsupported )
168 continue;
170 // the only section we support so far is "storages"
171 if ( sCurrentSection != "storages" )
173 bCurrentSectionIsKnownToBeUnsupported = true;
174 continue;
177 OUString sStorageName;
178 SubComponentDescriptor aCompDesc;
179 if ( !lcl_extractCompDesc( sLine, sStorageName, aCompDesc ) )
180 continue;
181 o_mapStorageToObjectName[ sStorageName ] = std::move(aCompDesc);
185 void lcl_markModified( const Reference< XComponent >& i_rSubComponent )
187 const Reference< XModifiable > xModify( i_rSubComponent, UNO_QUERY );
188 if ( !xModify.is() )
190 OSL_FAIL( "lcl_markModified: unhandled case!" );
191 return;
194 xModify->setModified( true );
198 // DatabaseDocumentRecovery
199 DatabaseDocumentRecovery::DatabaseDocumentRecovery( const Reference<XComponentContext> & i_rContext )
200 : mxContext( i_rContext )
204 DatabaseDocumentRecovery::~DatabaseDocumentRecovery()
208 void DatabaseDocumentRecovery::saveModifiedSubComponents( const Reference< XStorage >& i_rTargetStorage,
209 const std::vector< Reference< XController > >& i_rControllers )
211 ENSURE_OR_THROW( i_rTargetStorage.is(), "invalid document storage" );
213 // create a sub storage for recovery data
214 if ( i_rTargetStorage->hasByName( sRecoveryDataSubStorageName ) )
215 i_rTargetStorage->removeElement( sRecoveryDataSubStorageName );
216 Reference< XStorage > xRecoveryStorage = i_rTargetStorage->openStorageElement( sRecoveryDataSubStorageName, ElementModes::READWRITE );
218 // store recovery data for open sub components of the given controller(s)
219 if ( !i_rControllers.empty() )
221 ENSURE_OR_THROW( i_rControllers.size() == 1, "can't handle more than one controller" );
222 // At the moment, there can be only one view to a database document. If we ever allow for more than this,
223 // then we need a concept for sub documents opened from different controllers (i.e. two document views,
224 // and the user opens the very same form in both views). And depending on this, we need a concept for
225 // how those are saved to the recovery file.
227 MapCompTypeToCompDescs aMapCompDescs;
229 for (auto const& controller : i_rControllers)
231 Reference< XDatabaseDocumentUI > xDatabaseUI(controller, UNO_QUERY_THROW);
232 const Sequence< Reference< XComponent > > aComponents( xDatabaseUI->getSubComponents() );
234 for ( auto const & component : aComponents )
236 SubComponentRecovery aComponentRecovery( mxContext, xDatabaseUI, component );
237 aComponentRecovery.saveToRecoveryStorage( xRecoveryStorage, aMapCompDescs );
241 for (auto const& elem : aMapCompDescs)
243 Reference< XStorage > xComponentsStor( xRecoveryStorage->openStorageElement(
244 SubComponentRecovery::getComponentsStorageName( elem.first ), ElementModes::WRITE | ElementModes::NOCREATE ) );
245 lcl_writeObjectMap_throw( mxContext, xComponentsStor, elem.second );
246 tools::stor::commitStorageIfWriteable( xComponentsStor );
250 // commit the recovery storage
251 tools::stor::commitStorageIfWriteable( xRecoveryStorage );
254 void DatabaseDocumentRecovery::recoverSubDocuments( const Reference< XStorage >& i_rDocumentStorage,
255 const Reference< XController >& i_rTargetController )
257 ENSURE_OR_THROW( i_rDocumentStorage.is(), "illegal document storage" );
258 Reference< XDatabaseDocumentUI > xDocumentUI( i_rTargetController, UNO_QUERY_THROW );
260 if ( !i_rDocumentStorage->hasByName( sRecoveryDataSubStorageName ) )
261 // that's allowed
262 return;
264 // the "recovery" sub storage
265 Reference< XStorage > xRecoveryStorage = i_rDocumentStorage->openStorageElement( sRecoveryDataSubStorageName, ElementModes::READ );
267 // read the map from sub storages to object names
268 MapCompTypeToCompDescs aMapCompDescs;
269 const SubComponentType aKnownTypes[] = { TABLE, QUERY, FORM, REPORT, RELATION_DESIGN };
270 for (SubComponentType aKnownType : aKnownTypes)
272 if ( !xRecoveryStorage->hasByName( SubComponentRecovery::getComponentsStorageName( aKnownType ) ) )
273 continue;
275 Reference< XStorage > xComponentsStor( xRecoveryStorage->openStorageElement(
276 SubComponentRecovery::getComponentsStorageName( aKnownType ), ElementModes::READ ) );
277 lcl_readObjectMap_throw( mxContext, xComponentsStor, aMapCompDescs[ aKnownType ] );
278 xComponentsStor->dispose();
281 // recover all sub components as indicated by the map
282 for (auto const& elemMapCompDescs : aMapCompDescs)
284 const SubComponentType eComponentType = elemMapCompDescs.first;
286 // the storage for all components of the current type
287 Reference< XStorage > xComponentsStor( xRecoveryStorage->openStorageElement(
288 SubComponentRecovery::getComponentsStorageName( eComponentType ), ElementModes::READ ), UNO_SET_THROW );
290 // loop through all components of this type
291 for (auto const& elem : elemMapCompDescs.second)
293 const OUString sComponentName(elem.second.sName);
294 if ( !xComponentsStor->hasByName(elem.first) )
296 SAL_WARN( "dbaccess",
297 "DatabaseDocumentRecovery::recoverSubDocuments: inconsistent recovery storage: storage '" <<
298 elem.first <<
299 "' not found in '" <<
300 SubComponentRecovery::getComponentsStorageName( eComponentType ) <<
301 "', but required per map file!" );
302 continue;
305 // the controller needs to have a connection to be able to open sub components
306 if ( !xDocumentUI->isConnected() )
307 xDocumentUI->connect();
309 // recover the single component
310 Reference< XStorage > xCompStor( xComponentsStor->openStorageElement( elem.first, ElementModes::READ ) );
311 SubComponentRecovery aComponentRecovery( mxContext, xDocumentUI, eComponentType );
312 Reference< XComponent > xSubComponent( aComponentRecovery.recoverFromStorage( xCompStor, sComponentName, elem.second.bForEditing ) );
314 // at the moment, we only store, during session save, sub components which are modified. So, set this
315 // recovered sub component to "modified", too.
316 lcl_markModified( xSubComponent );
319 xComponentsStor->dispose();
322 xRecoveryStorage->dispose();
324 // now that we successfully recovered, removed the "recovery" sub storage
327 i_rDocumentStorage->removeElement( sRecoveryDataSubStorageName );
329 catch( const Exception& )
331 DBG_UNHANDLED_EXCEPTION("dbaccess");
335 } // namespace dbaccess
337 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */