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 .
23 #include <sal/log.hxx>
24 #include <tools/solar.h>
25 #include <svl/itempool.hxx>
26 #include "whassert.hxx"
27 #include <svl/SfxBroadcaster.hxx>
28 #include <svl/filerec.hxx>
30 #include <boost/scoped_ptr.hpp>
31 #include <boost/scoped_array.hpp>
34 * Returns the <SfxItemPool> that is being saved.
35 * This should only be used in very exceptional cases e.g.
36 * when guaranteeing file format compatibility:
37 * When overriding a <SfxPoolItem::Store()> getting additional data from the Pool
39 const SfxItemPool
* SfxItemPool::GetStoringPool()
44 static sal_uInt16
convertSfxItemKindToUInt16(SfxItemKind x
)
46 if ( x
== SFX_ITEMS_NONE
)
48 if ( x
== SFX_ITEMS_DELETEONIDLE
)
50 if ( x
== SFX_ITEMS_STATICDEFAULT
)
52 if ( x
== SFX_ITEMS_POOLDEFAULT
)
58 static SfxItemKind
convertUInt16ToSfxItemKind(sal_uInt16 x
)
61 return SFX_ITEMS_NONE
;
63 return SFX_ITEMS_DELETEONIDLE
;
65 return SFX_ITEMS_STATICDEFAULT
;
67 return SFX_ITEMS_POOLDEFAULT
;
75 * The SfxItemPool is saved to the specified Stream (together with all its
76 * secondary Pools) using its Pool Defaults and pooled Items.
77 * The static defaults are not saved.
80 * ;First, a compatibility header section
81 * Start: 0x1111 SFX_ITEMPOOL_TAG_STARTPOOLS(_4/_5)
82 * sal_uInt8 MAJOR_VER ;SfxItemPool version
83 * sal_uInt8 MINOR_VER ;"
84 * 0xFFFF SFX_ITEMPOOL_TAG_TRICK4OLD ;ex. GetVersion()
85 * sal_uInt16 0x0000 ;Pseudo StyleSheetPool
86 * sal_uInt16 0x0000 ;Pseudo StyleSheetPool
88 * ;The whole Pool into a record
89 * record SfxMiniRecod(SFX_ITEMPOOL_REC)
91 * ;Start with a Header for each
92 * Header: record SfxMiniRecord(SFX_ITEMPOOL_REC_HEADER)
93 * sal_uInt16 GetVersion() ;Which-Ranges etc.
94 * String GetName() ;Pool name
96 * ;The version map: in order to be able to map WhichIds of new file version
97 * Versions: record SfxMultiRecord(SFX_ITEMPOOL_REC_VERSIONS, 0)
98 * sal_uInt16 OldVersion
99 * sal_uInt16 OldStartWhich
100 * sal_uInt16 OldEndWhich
101 * sal_uInt16[] NewWhich (OldEndWhich-OldStartWhich+1)
103 * ;Now the pooled Items (first the non-SfxSetItems)
104 * Items: record SfxMultiRecord(SFX_ITEMPOOL_REC_WHICHIDS, 0)
107 * sal_uInt16 pItem->GetVersion()
108 * sal_uInt16 Array-Size
109 * record SfxMultiRecord(SFX_, 0)
111 * sal_uInt16 RefCount
112 * unknown pItem->Store()
114 * ;Now the set Pool defaults
115 * Defaults: record SfxMultiRecord(SFX_ITEMPOOL_REC_DEFAULTS, 0)
118 * sal_uInt16 pPoolDef->GetVersion()
119 * unknown pPoolDef->Store();
121 * ;Hereafter the secondary follows (if present) without compatibility header section
123 SvStream
&SfxItemPool::Store(SvStream
&rStream
) const
126 SfxItemPool
*pStoreMaster
= pImp
->mpMaster
!= this ? pImp
->mpMaster
: 0;
127 while ( pStoreMaster
&& !pStoreMaster
->pImp
->bStreaming
)
128 pStoreMaster
= pStoreMaster
->pImp
->mpSecondary
;
130 // Old header (version of the Pool and content version is 0xffff by default)
131 pImp
->bStreaming
= true;
134 rStream
.WriteUInt16( rStream
.GetVersion() >= SOFFICE_FILEFORMAT_50
135 ? SFX_ITEMPOOL_TAG_STARTPOOL_5
136 : SFX_ITEMPOOL_TAG_STARTPOOL_4
);
137 rStream
.WriteUInt8( SFX_ITEMPOOL_VER_MAJOR
).WriteUInt8( SFX_ITEMPOOL_VER_MINOR
);
138 rStream
.WriteUInt16( SFX_ITEMPOOL_TAG_TRICK4OLD
);
140 // Work around SfxStyleSheet bug
141 rStream
.WriteUInt16( 0 ); // Version
142 rStream
.WriteUInt16( 0 ); // Count (or else 2nd loop breaks)
145 // Every Pool as a whole is a record
146 SfxMiniRecordWriter
aPoolRec( &rStream
, SFX_ITEMPOOL_REC
);
147 pStoringPool_
= this;
149 // Single header (content version and name)
151 SfxMiniRecordWriter
aPoolHeaderRec( &rStream
, SFX_ITEMPOOL_REC_HEADER
);
152 rStream
.WriteUInt16( pImp
->nVersion
);
153 writeByteString(rStream
, pImp
->aName
);
158 SfxMultiVarRecordWriter
aVerRec( &rStream
, SFX_ITEMPOOL_REC_VERSIONMAP
, 0 );
159 for ( size_t nVerNo
= 0; nVerNo
< pImp
->aVersions
.size(); ++nVerNo
)
161 aVerRec
.NewContent();
162 SfxPoolVersion_ImplPtr pVer
= pImp
->aVersions
[nVerNo
];
163 rStream
.WriteUInt16( pVer
->_nVer
).WriteUInt16( pVer
->_nStart
).WriteUInt16( pVer
->_nEnd
);
164 sal_uInt16 nCount
= pVer
->_nEnd
- pVer
->_nStart
+ 1;
165 sal_uInt16 nNewWhich
= 0;
166 for ( sal_uInt16 n
= 0; n
< nCount
; ++n
)
168 nNewWhich
= pVer
->_pMap
[n
];
169 rStream
.WriteUInt16( nNewWhich
);
172 // Workaround for bug in SetVersionMap 312
173 if ( SOFFICE_FILEFORMAT_31
== pImp
->mnFileFormatVersion
)
174 rStream
.WriteUInt16( nNewWhich
+1 );
180 SfxMultiMixRecordWriter
aWhichIdsRec( &rStream
, SFX_ITEMPOOL_REC_WHICHIDS
, 0 );
182 // First write the atomic Items and then write the Sets (important when loading)
183 for (int ft
= 0 ; ft
< 2 && !rStream
.GetError(); ft
++)
185 pImp
->bInSetItem
= ft
!= 0;
187 std::vector
<SfxPoolItemArray_Impl
*>::iterator itrArr
= pImp
->maPoolItems
.begin();
188 SfxPoolItem
**ppDefItem
= pImp
->ppStaticDefaults
;
189 const sal_uInt16 nSize
= GetSize_Impl();
190 for ( size_t i
= 0; i
< nSize
&& !rStream
.GetError(); ++i
, ++itrArr
, ++ppDefItem
)
192 // Get version of the Item
193 sal_uInt16 nItemVersion
= (*ppDefItem
)->GetVersion( pImp
->mnFileFormatVersion
);
194 if ( USHRT_MAX
== nItemVersion
)
195 // => Was not present in the version that was supposed to be exported
198 // ! Poolable is not even saved in the Pool
199 // And itemsets/plain-items depending on the round
200 if ( *itrArr
&& IsItemFlag(**ppDefItem
, SfxItemPoolFlags::POOLABLE
) &&
201 pImp
->bInSetItem
== (bool) (*ppDefItem
)->ISA(SfxSetItem
) )
203 // Own signature, global WhichId and ItemVersion
204 sal_uInt16 nSlotId
= GetSlotId( (*ppDefItem
)->Which(), false );
205 aWhichIdsRec
.NewContent(nSlotId
, 0);
206 rStream
.WriteUInt16( (*ppDefItem
)->Which() );
207 rStream
.WriteUInt16( nItemVersion
);
208 const sal_uInt32 nCount
= ::std::min
<size_t>( (*itrArr
)->size(), SAL_MAX_UINT32
);
209 DBG_ASSERT(nCount
, "ItemArr is empty");
210 rStream
.WriteUInt32( nCount
);
213 SfxMultiMixRecordWriter
aItemsRec( &rStream
, SFX_ITEMPOOL_REC_ITEMS
, 0 );
214 for ( size_t j
= 0; j
< nCount
; ++j
)
217 const SfxPoolItem
*pItem
= (*itrArr
)->operator[](j
);
218 if ( pItem
&& pItem
->GetRefCount() ) //! See other MI-REF
220 aItemsRec
.NewContent((sal_uInt16
)j
, 'X' );
222 if ( pItem
->GetRefCount() == SFX_ITEMS_SPECIAL
)
223 rStream
.WriteUInt16( convertSfxItemKindToUInt16(pItem
->GetKind()) );
226 rStream
.WriteUInt16( pItem
->GetRefCount() );
227 if( pItem
->GetRefCount() > SFX_ITEMS_OLD_MAXREF
)
228 rStream
.SetError( ERRCODE_IO_NOTSTORABLEINBINARYFORMAT
);
231 if ( !rStream
.GetError() )
232 pItem
->Store(rStream
, nItemVersion
);
236 if ( !pItem
->ISA(SfxSetItem
) )
238 sal_uLong nMark
= rStream
.Tell();
239 rStream
.Seek( nItemStartPos
+ sizeof(sal_uInt16
) );
240 boost::scoped_ptr
<SfxPoolItem
> pClone(pItem
->Create(rStream
, nItemVersion
));
241 sal_uInt16 nWh
= pItem
->Which();
242 SFX_ASSERT( rStream
.Tell() == nMark
, nWh
,"asymmetric store/create" );
243 SFX_ASSERT( *pClone
== *pItem
, nWh
, "unequal after store/create" );
252 pImp
->bInSetItem
= false;
255 // Save the set Defaults (PoolDefaults)
256 if ( !rStream
.GetError() )
258 SfxMultiMixRecordWriter
aDefsRec( &rStream
, SFX_ITEMPOOL_REC_DEFAULTS
, 0 );
259 sal_uInt16 nCount
= GetSize_Impl();
260 for ( sal_uInt16 n
= 0; n
< nCount
; ++n
)
262 const SfxPoolItem
* pDefaultItem
= pImp
->ppPoolDefaults
[n
];
266 sal_uInt16 nItemVersion
= pDefaultItem
->GetVersion( pImp
->mnFileFormatVersion
);
267 if ( USHRT_MAX
== nItemVersion
)
268 // => Was not present in the version yet
271 // Own signature, global signature, version
272 sal_uInt16 nSlotId
= GetSlotId( pDefaultItem
->Which(), false );
273 aDefsRec
.NewContent( nSlotId
, 0 );
274 rStream
.WriteUInt16( pDefaultItem
->Which() );
275 rStream
.WriteUInt16( nItemVersion
);
278 pDefaultItem
->Store( rStream
, nItemVersion
);
283 // Write out additional Pools
286 if ( !rStream
.GetError() && pImp
->mpSecondary
)
287 pImp
->mpSecondary
->Store( rStream
);
289 pImp
->bStreaming
= false;
293 bool SfxItemPool::HasPersistentRefCounts() const
295 return pImp
->mbPersistentRefCounts
;
299 * If the SfxItemPool was loaded with 'bRefCounts' == sal_False, we need
300 * to finish the loading of the document contents with a call of this method.
301 * In any other case calling this function has no meaning.
303 * When loading without RefCounts, they are actually set to 1 so that
304 * SfxPoolItems that are needed during and after loading are not deleted.
305 * This method resets the RefCount and also removes all items that are not
308 * @see SfxItemPool::Load()
310 void SfxItemPool::LoadCompleted()
312 // Did we load without RefCounts?
313 if ( pImp
->nInitRefCount
> 1 )
315 // Iterate over all Which values
316 std::vector
<SfxPoolItemArray_Impl
*>::iterator itrItemArr
= pImp
->maPoolItems
.begin();
317 for( sal_uInt16 nArrCnt
= GetSize_Impl(); nArrCnt
; --nArrCnt
, ++itrItemArr
)
319 // Is there an item with the Which value present at all?
322 // Iterate over all items with this WhichId
323 SfxPoolItemArrayBase_Impl::iterator ppHtArr
= (*itrItemArr
)->begin();
324 for( size_t n
= (*itrItemArr
)->size(); n
; --n
, ++ppHtArr
)
328 if ( !ReleaseRef( **ppHtArr
, 1 ) )
332 (*itrItemArr
)->ReHash();
336 // from now on normal initial ref count
337 pImp
->nInitRefCount
= 1;
340 // notify secondary pool
341 if ( pImp
->mpSecondary
)
342 pImp
->mpSecondary
->LoadCompleted();
345 sal_uInt16
SfxItemPool::GetFirstWhich() const
347 return pImp
->mnStart
;
350 sal_uInt16
SfxItemPool::GetLastWhich() const
355 bool SfxItemPool::IsInRange( sal_uInt16 nWhich
) const
357 return nWhich
>= pImp
->mnStart
&& nWhich
<= pImp
->mnEnd
;
360 // This had to be moved to a method of its own to keep Solaris GCC happy:
361 void SfxItemPool_Impl::readTheItems (
362 SvStream
& rStream
, sal_uInt32 nItemCount
, sal_uInt16 nVer
,
363 SfxPoolItem
* pDefItem
, SfxPoolItemArray_Impl
** ppArr
)
365 SfxMultiRecordReader
aItemsRec( &rStream
, SFX_ITEMPOOL_REC_ITEMS
);
367 SfxPoolItemArray_Impl
*pNewArr
= new SfxPoolItemArray_Impl();
368 SfxPoolItem
*pItem
= 0;
370 sal_uLong n
, nLastSurrogate
= sal_uLong(-1);
371 while (aItemsRec
.GetContent())
373 // Get next surrogate
374 sal_uInt16 nSurrogate
= aItemsRec
.GetContentTag();
375 DBG_ASSERT( aItemsRec
.GetContentVersion() == 'X',
376 "not an item content" );
378 // Fill up missing ones
379 // coverity[tainted_data] - ignore this, though we should finally kill off this format
380 for ( pItem
= 0, n
= nLastSurrogate
+1; n
< nSurrogate
; ++n
)
381 pNewArr
->push_back( pItem
);
382 nLastSurrogate
= nSurrogate
;
384 // Load RefCount and Item
386 rStream
.ReadUInt16( nRef
);
388 pItem
= pDefItem
->Create(rStream
, nVer
);
389 pNewArr
->push_back( pItem
);
391 if ( !mbPersistentRefCounts
)
392 // Hold onto it until SfxItemPool::LoadCompleted()
393 SfxItemPool::AddRef(*pItem
, 1);
396 if ( nRef
> SFX_ITEMS_OLD_MAXREF
)
397 SfxItemPool::SetKind(*pItem
, convertUInt16ToSfxItemKind(nRef
));
399 SfxItemPool::AddRef(*pItem
, nRef
);
403 // Fill up missing ones
404 for ( pItem
= 0, n
= nLastSurrogate
+1; n
< nItemCount
; ++n
)
405 pNewArr
->push_back( pItem
);
407 SfxPoolItemArray_Impl
*pOldArr
= *ppArr
;
410 // Remember items that are already in the pool
413 for ( n
= 0; bEmpty
&& n
< pOldArr
->size(); ++n
)
414 bEmpty
= pOldArr
->operator[](n
) == 0;
415 DBG_ASSERTWARNING( bEmpty
, "loading non-empty pool" );
418 // See if there's a new one for all old ones
419 for ( size_t nOld
= 0; nOld
< pOldArr
->size(); ++nOld
)
421 SfxPoolItem
*pOldItem
= (*pOldArr
)[nOld
];
424 sal_uInt32 nFree
= SAL_MAX_UINT32
;
426 for ( size_t nNew
= (*ppArr
)->size(); nNew
--; )
429 SfxPoolItem
*&rpNewItem
=
430 (SfxPoolItem
*&)(*ppArr
)->operator[](nNew
);
437 else if ( *rpNewItem
== *pOldItem
)
440 SfxItemPool::AddRef( *pOldItem
, rpNewItem
->GetRefCount() );
441 SfxItemPool::SetRefCount( *rpNewItem
, 0 );
443 rpNewItem
= pOldItem
;
449 // Take over the ones that were previously present, but had not been loaded
452 if ( nFree
!= SAL_MAX_UINT32
)
453 (SfxPoolItem
*&)(*ppArr
)->operator[](nFree
) = pOldItem
;
455 (*ppArr
)->push_back( pOldItem
);
462 (*ppArr
)->ReHash(); // paranoid
465 SvStream
&SfxItemPool::Load(SvStream
&rStream
)
467 DBG_ASSERT(pImp
->ppStaticDefaults
, "No DefaultArray");
469 // Protect items by increasing ref count
470 if ( !pImp
->mbPersistentRefCounts
)
473 // Iterate over all Which values
474 std::vector
<SfxPoolItemArray_Impl
*>::iterator itrItemArr
= pImp
->maPoolItems
.begin();
475 for( size_t nArrCnt
= GetSize_Impl(); nArrCnt
; --nArrCnt
, ++itrItemArr
)
477 // Is there an Item with that Which value present at all?
480 SfxPoolItemArrayBase_Impl::iterator ppHtArr
= (*itrItemArr
)->begin();
481 for( size_t n
= (*itrItemArr
)->size(); n
; --n
, ++ppHtArr
)
484 DBG_WARNING( "loading non-empty ItemPool" );
486 AddRef( **ppHtArr
, 1 );
491 // During loading (until LoadCompleted()) protect all items
492 pImp
->nInitRefCount
= 2;
496 SfxItemPool
*pLoadMaster
= pImp
->mpMaster
!= this ? pImp
->mpMaster
: 0;
497 while ( pLoadMaster
&& !pLoadMaster
->pImp
->bStreaming
)
498 pLoadMaster
= pLoadMaster
->pImp
->mpSecondary
;
501 pImp
->bStreaming
= true;
504 // Load format version
505 CHECK_FILEFORMAT2( rStream
,
506 SFX_ITEMPOOL_TAG_STARTPOOL_5
, SFX_ITEMPOOL_TAG_STARTPOOL_4
);
507 rStream
.ReadUChar( pImp
->nMajorVer
).ReadUChar( pImp
->nMinorVer
);
509 // Take over format version to MasterPool
510 pImp
->mpMaster
->pImp
->nMajorVer
= pImp
->nMajorVer
;
511 pImp
->mpMaster
->pImp
->nMinorVer
= pImp
->nMinorVer
;
514 if (pImp
->nMajorVer
< 2 || pImp
->nMajorVer
> SFX_ITEMPOOL_VER_MAJOR
)
516 rStream
.SetError(SVSTREAM_FILEFORMAT_ERROR
);
517 pImp
->bStreaming
= false;
521 // Trick for version 1.2: skip data
522 CHECK_FILEFORMAT( rStream
, SFX_ITEMPOOL_TAG_TRICK4OLD
);
523 rStream
.SeekRel( 4 ); // Hack: Skip data due to SfxStyleSheetPool bug
526 // New record-oriented format
527 SfxMiniRecordReader
aPoolRec( &rStream
, SFX_ITEMPOOL_REC
);
528 if ( rStream
.GetError() )
530 pImp
->bStreaming
= false;
535 OUString aExternName
;
538 SfxMiniRecordReader
aPoolHeaderRec( &rStream
, SFX_ITEMPOOL_REC_HEADER
);
539 if ( rStream
.GetError() )
541 pImp
->bStreaming
= false;
546 rStream
.ReadUInt16( pImp
->nLoadingVersion
);
547 aExternName
= readByteString(rStream
);
548 bool bOwnPool
= aExternName
== pImp
->aName
;
550 //! As long as we cannot read foreign Pools
553 rStream
.SetError(SVSTREAM_FILEFORMAT_ERROR
);
555 pImp
->bStreaming
= false;
562 SfxMultiRecordReader
aVerRec( &rStream
, SFX_ITEMPOOL_REC_VERSIONMAP
);
563 if ( rStream
.GetError() )
565 pImp
->bStreaming
= false;
569 // Version maps einlesen
570 sal_uInt16 nOwnVersion
= pImp
->nVersion
;
571 for ( sal_uInt16 nVerNo
= 0; aVerRec
.GetContent(); ++nVerNo
)
573 // Read header for single versions
574 sal_uInt16
nVersion(0), nHStart(0), nHEnd(0);
575 rStream
.ReadUInt16( nVersion
).ReadUInt16( nHStart
).ReadUInt16( nHEnd
);
576 sal_uInt16 nCount
= nHEnd
- nHStart
+ 1;
578 // Is new version is known?
579 if ( nVerNo
>= pImp
->aVersions
.size() )
582 const size_t nMaxRecords
= rStream
.remainingSize() / sizeof(sal_uInt16
);
583 if (nCount
> nMaxRecords
)
585 SAL_WARN("svl", "Parsing error: " << nMaxRecords
<<
586 " max possible entries, but " << nCount
<< " claimed, truncating");
587 nCount
= nMaxRecords
;
589 sal_uInt16
*pMap
= new sal_uInt16
[nCount
];
590 memset(pMap
, 0, nCount
* sizeof(sal_uInt16
));
591 for ( sal_uInt16 n
= 0; n
< nCount
; ++n
)
592 rStream
.ReadUInt16( pMap
[n
] );
593 SetVersionMap( nVersion
, nHStart
, nHEnd
, pMap
);
596 pImp
->nVersion
= nOwnVersion
;
600 bool bSecondaryLoaded
= false;
601 long nSecondaryEnd
= 0;
603 SfxMultiRecordReader
aWhichIdsRec( &rStream
, SFX_ITEMPOOL_REC_WHICHIDS
);
604 while ( aWhichIdsRec
.GetContent() )
606 // Get SlotId, WhichId and Item version
607 sal_uInt32
nCount(0);
608 sal_uInt16
nVersion(0), nWhich(0);
609 //!sal_uInt16 nSlotId = aWhichIdsRec.GetContentTag();
610 rStream
.ReadUInt16( nWhich
);
611 if ( pImp
->nLoadingVersion
!= pImp
->nVersion
)
612 // Move WhichId from file version to Pool version
613 nWhich
= GetNewWhich( nWhich
);
615 // Unknown Item from newer version
616 if ( !IsInRange(nWhich
) )
619 rStream
.ReadUInt16( nVersion
);
620 rStream
.ReadUInt32( nCount
);
621 //!SFX_ASSERTWARNING( !nSlotId || !HasMap() ||
622 //! ( nSlotId == GetSlotId( nWhich, sal_False ) ) ||
623 //! !GetSlotId( nWhich, sal_False ),
624 //! nWhich, "Slot/Which mismatch" );
626 sal_uInt16 nIndex
= GetIndex_Impl(nWhich
);
627 SfxPoolItemArray_Impl
**ppArr
= &pImp
->maPoolItems
[0] + nIndex
;
629 // SfxSetItems could contain Items from secondary Pools
630 SfxPoolItem
*pDefItem
= *(pImp
->ppStaticDefaults
+ nIndex
);
631 pImp
->bInSetItem
= pDefItem
->ISA(SfxSetItem
);
632 if ( !bSecondaryLoaded
&& pImp
->mpSecondary
&& pImp
->bInSetItem
)
634 // Seek to end of own Pool
635 sal_uLong nLastPos
= rStream
.Tell();
638 // Read secondary Pool
639 pImp
->mpSecondary
->Load( rStream
);
640 bSecondaryLoaded
= true;
641 nSecondaryEnd
= rStream
.Tell();
643 // Back to our own Items
644 rStream
.Seek(nLastPos
);
648 pImp
->readTheItems(rStream
, nCount
, nVersion
, pDefItem
, ppArr
);
650 pImp
->bInSetItem
= false;
654 // Read Pool defaults
656 SfxMultiRecordReader
aDefsRec( &rStream
, SFX_ITEMPOOL_REC_DEFAULTS
);
658 while ( aDefsRec
.GetContent() )
660 // Get SlotId, WhichId and Item versions
661 sal_uInt16
nVersion(0), nWhich(0);
662 //!sal_uInt16 nSlotId = aDefsRec.GetContentTag();
663 rStream
.ReadUInt16( nWhich
);
664 if ( pImp
->nLoadingVersion
!= pImp
->nVersion
)
665 // Move WhichId from file version to Pool version
666 nWhich
= GetNewWhich( nWhich
);
668 // Unknown Item from newer version
669 if ( !IsInRange(nWhich
) )
672 rStream
.ReadUInt16( nVersion
);
673 //!SFX_ASSERTWARNING( !HasMap() || ( nSlotId == GetSlotId( nWhich, sal_False ) ),
674 //! nWhich, "Slot/Which mismatch" );
676 // Load PoolDefaultItem
678 ( *( pImp
->ppStaticDefaults
+ GetIndex_Impl(nWhich
) ) )
679 ->Create( rStream
, nVersion
);
680 pItem
->SetKind( SFX_ITEMS_POOLDEFAULT
);
681 *( pImp
->ppPoolDefaults
+ GetIndex_Impl(nWhich
) ) = pItem
;
685 // Load secondary Pool if needed
687 if ( pImp
->mpSecondary
)
689 if ( !bSecondaryLoaded
)
690 pImp
->mpSecondary
->Load( rStream
);
692 rStream
.Seek( nSecondaryEnd
);
695 // If not own Pool, then no name
696 if ( aExternName
!= pImp
->aName
)
697 (pImp
->aName
).clear();
699 pImp
->bStreaming
= false;
703 sal_uInt16
SfxItemPool::GetIndex_Impl(sal_uInt16 nWhich
) const
705 if (nWhich
< pImp
->mnStart
|| nWhich
> pImp
->mnEnd
)
707 assert(false && "missing bounds check before use");
710 return nWhich
- pImp
->mnStart
;
713 sal_uInt16
SfxItemPool::GetSize_Impl() const
715 return pImp
->mnEnd
- pImp
->mnStart
+ 1;
719 * Loads surrogate from 'rStream' and returns the corresponding SfxPoolItem
721 * If the surrogate contained within the stream == SFX_ITEMS_DIRECT
722 * (!SfxItemPoolFlags::POOLABLE), we return 0 and the Item is to be loaded directly
724 * We also return 0 for 0xfffffff0 (SFX_ITEMS_NULL) and rWhich is set to 0,
725 * making the Items unavailable.
727 * Apart from that we also take into account whether the Pool is loaded without
728 * RefCounts, if we reload from a new Pool (&rRefPool != this) or if we're
729 * building from a differently constructed Pool.
731 * If we load from a differently constructed Pool and the 'nSlotId' cannot be
732 * mapped to a WhichId of this Pool, we also return 0.
734 * Preconditions: - Pool must be loaded
735 * - LoadCompleted must not have been called yet
736 * - 'rStream' is at the position at which a surrogate
737 * for an Item with the SlotId 'nSlotId', the WhichId
738 * 'rWhichId' was saved with StoreSurrogate
740 * Postconditions: - 'rStream' is at the same position as after StoreSurrogate
741 * had finished saving
742 * - If we were able to load an Item, it's now in this
744 * - 'rWhichId' now contains the mapped WhichId
746 * Runtime: Depth of the traget secondary Pool * 10 + 10
748 * @see SfxItemPool::StoreSurrogate(SvStream&,const SfxPoolItem &)const
750 const SfxPoolItem
* SfxItemPool::LoadSurrogate
752 SvStream
& rStream
, // Stream before a surrogate
753 sal_uInt16
& rWhich
, // WhichId of the SfxPoolItem that is to be loaded
754 sal_uInt16 nSlotId
, // SlotId of the SfxPoolItem that is to be loaded
755 const SfxItemPool
* pRefPool
// SfxItemPool in which the surrogate is valid
758 // Read the first surrogate
759 sal_uInt32
nSurrogat(0);
760 rStream
.ReadUInt32( nSurrogat
);
762 // Is item stored directly?
763 if ( SFX_ITEMS_DIRECT
== nSurrogat
)
766 // Item does not exist?
767 if ( SFX_ITEMS_NULL
== nSurrogat
)
773 // If the Pool in the stream has the same structure, the surrogate
774 // can be resolved in any case
778 bool bResolvable
= !pRefPool
->GetName().isEmpty();
781 // If the pool in the stream has a different structure, the SlotId
782 // from the stream must be mapable to a WhichId
783 sal_uInt16 nMappedWhich
= nSlotId
? GetWhich(nSlotId
, true) : 0;
784 if ( IsWhich(nMappedWhich
) )
786 // Mapped SlotId can be taken over
787 rWhich
= nMappedWhich
;
792 // Can the surrogate be resolved?
795 const SfxPoolItem
*pItem
= 0;
796 for ( SfxItemPool
*pTarget
= this; pTarget
; pTarget
= pTarget
->pImp
->mpSecondary
)
798 // Found the right (Range-)Pool?
799 if ( pTarget
->IsInRange(rWhich
) )
801 // Default attribute?
802 if ( SFX_ITEMS_DEFAULT
== nSurrogat
)
803 return *(pTarget
->pImp
->ppStaticDefaults
+
804 pTarget
->GetIndex_Impl(rWhich
));
806 SfxPoolItemArray_Impl
* pItemArr
=
807 pTarget
->pImp
->maPoolItems
[pTarget
->GetIndex_Impl(rWhich
)];
808 pItem
= pItemArr
&& nSurrogat
< pItemArr
->size()
809 ? (*pItemArr
)[nSurrogat
]
813 OSL_FAIL( "can't resolve surrogate" );
814 rWhich
= 0; // Just to be sure; for the right StreamPos
818 // Reload from RefPool?
819 if ( pRefPool
!= pImp
->mpMaster
)
820 return &pTarget
->Put( *pItem
);
822 // References have NOT been loaded together with the pool?
823 if ( !pTarget
->HasPersistentRefCounts() )
832 SFX_ASSERT( false, rWhich
, "can't resolve Which-Id in LoadSurrogate" );
840 * Saves a surrogate for '*pItem' in 'rStream'
842 * @returns sal_True: a real surrogates has been saved
843 * SFX_ITEMS_NULL for 'pItem==0', SFX_ITEMS_STATICDEFAULT
844 * and SFX_ITEMS_POOLDEFAULT are 'real' surrogates
846 * @returns sal_False: a dummy surrogate (SFX_ITEMS_DIRECT) has been saved;
847 * the actual Item needs to be saved right after it on
850 bool SfxItemPool::StoreSurrogate ( SvStream
& rStream
, const SfxPoolItem
* pItem
) const
854 bool bRealSurrogate
= IsItemFlag(*pItem
, SfxItemPoolFlags::POOLABLE
);
855 rStream
.WriteUInt32( bRealSurrogate
856 ? GetSurrogate( pItem
)
857 : SFX_ITEMS_DIRECT
);
858 return bRealSurrogate
;
861 rStream
.WriteUInt32( SFX_ITEMS_NULL
);
867 sal_uInt32
SfxItemPool::GetSurrogate(const SfxPoolItem
*pItem
) const
869 DBG_ASSERT( pItem
, "no 0-Pointer Surrogate" );
870 DBG_ASSERT( !IsInvalidItem(pItem
), "no Invalid-Item Surrogate" );
871 DBG_ASSERT( !IsPoolDefaultItem(pItem
), "no Pool-Default-Item Surrogate" );
873 if ( !IsInRange(pItem
->Which()) )
875 if ( pImp
->mpSecondary
)
876 return pImp
->mpSecondary
->GetSurrogate( pItem
);
877 SFX_ASSERT( false, pItem
->Which(), "unknown Which-Id - dont ask me for surrogates" );
880 // Pointer on static or pool-default attribute?
881 if( IsStaticDefaultItem(pItem
) || IsPoolDefaultItem(pItem
) )
882 return SFX_ITEMS_DEFAULT
;
884 SfxPoolItemArray_Impl
* pItemArr
= pImp
->maPoolItems
[GetIndex_Impl(pItem
->Which())];
885 DBG_ASSERT(pItemArr
, "ItemArr is not available");
887 for ( size_t i
= 0; i
< pItemArr
->size(); ++i
)
889 const SfxPoolItem
*p
= (*pItemArr
)[i
];
893 SFX_ASSERT( false, pItem
->Which(), "Item not in the pool");
894 return SFX_ITEMS_NULL
;
898 bool SfxItemPool::IsInStoringRange( sal_uInt16 nWhich
) const
900 return nWhich
>= pImp
->nStoringStart
&&
901 nWhich
<= pImp
->nStoringEnd
;
905 * This method allows for restricting the WhichRange, which is saved
906 * by ItemSets of this Pool (and the Pool itself).
907 * The method must be called before SfxItemPool::Store() and the values
908 * must also be still set when the actual document (the ItemSets) is
911 * Resetting it is not necessary, if this range is set correctly before
912 * _every_ save, because its only accounted for when saving.
914 * We need to do this for the 3.1 format, because there's a bug in the
915 * Pool loading method.
917 void SfxItemPool::SetStoringRange( sal_uInt16 nFrom
, sal_uInt16 nTo
)
919 pImp
->nStoringStart
= nFrom
;
920 pImp
->nStoringEnd
= nTo
;
925 * This method allows for the creation of new and incompatible WhichId
926 * Ranges or distributions. Pools that were saved with old versions
927 * are mapped using the provided conversion table until the current
928 * version has been reached. Newer pools can be loaded, but will lose
929 * newer attributes, because the map is saved in conjunction with the pool.
931 * Precondition: Pool must not be loaded yet
932 * Postcondition: WhichIds from older versions can be mapped to version 'nVer'
933 * Runtime: 1.5 * new + 10
935 * For newer WhichRanges (nStart,nEnd) it must hold that older WhichRanges
936 * (nOldStart,nOldEnd) are completely contained in the newer WhichRange.
937 * It is valid to extend the WhichRange to both sides; also by inserting
938 * WhichIds. Moving WhichIds is not permitted.
939 * This method should only be called in or right after the ctor.
941 * The array must be static, because its not copied and resued in the
942 * copy-ctor of the SfxItemPool
945 * Originally (version 0) the pool had the following WhichIds:
949 * A newer version (version 1) is now supposed to contain two new Ids
950 * X and Y between B and C, looking like this:
952 * 1:A, 2:B, 3:X, 4:Y, 5:C, 6:D
954 * We see that the Ids 3 and 4 have changed. For the new version, we
955 * would need to set the following in the new Pool:
957 * static sal_uInt16 nVersion1Map = { 1, 2, 5, 6 };
958 * pPool->SetVersionMap( 1, 1, 4, &nVersion1Map );
960 * @see SfxItemPool::IsLoadingVersionCurrent() const
961 * @see SfxItemPool::GetNewWhich(sal_uInt16)
962 * @see SfxItemPool::GetVersion() const
964 void SfxItemPool::SetVersionMap
966 sal_uInt16 nVer
, // New version number
967 sal_uInt16 nOldStart
, // Old first WhichId
968 sal_uInt16 nOldEnd
, // Old last WhichId
969 const sal_uInt16
* pOldWhichIdTab
/* Array containing the structure of the WhichIds
970 of the previous version, in which the new
971 corresponding new WhichId is located */
974 // Create new map entry to insert
975 const SfxPoolVersion_ImplPtr pVerMap
= SfxPoolVersion_ImplPtr( new SfxPoolVersion_Impl(
976 nVer
, nOldStart
, nOldEnd
, pOldWhichIdTab
) );
977 pImp
->aVersions
.push_back( pVerMap
);
979 DBG_ASSERT( nVer
> pImp
->nVersion
, "Versions not sorted" );
980 pImp
->nVersion
= nVer
;
982 // Adapt version range
983 for ( sal_uInt16 n
= 0; n
< nOldEnd
-nOldStart
+1; ++n
)
985 sal_uInt16 nWhich
= pOldWhichIdTab
[n
];
986 if ( nWhich
< pImp
->nVerStart
)
990 pImp
->nVerStart
= nWhich
;
992 else if ( nWhich
> pImp
->nVerEnd
)
993 pImp
->nVerEnd
= nWhich
;
999 * This method converts WhichIds from a file format to the version of the
1001 * If the file format is older, the conversion tables (set by the pool developer
1002 * using SetVersion()) are used. If the file format is newer the conversion tables
1003 * loaded from the file format are used. In this case, not every WhichId can be
1004 * mapped in which case we return 0.
1006 * The calculation is only defined for WhichIds supported by the corresponding
1007 * file version, which is guarded by an assertion.
1009 * Precondition: Pool must be loaded
1010 * Postcondition: Unchanged
1011 * Runtime: linear(Count of the secondary pools) +
1012 * linear(Difference of the old and newer version)
1014 * @see SfxItemPool::IsLoadingVersionCurrent() const
1015 * @see SfxItemPool::SetVersionMap(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16*)
1016 * @see SfxItemPool::GetVersion() const
1018 sal_uInt16
SfxItemPool::GetNewWhich
1020 sal_uInt16 nFileWhich
// The WhichId loaded from the stream
1023 // Determine (secondary) Pool
1024 if ( !IsInVersionsRange(nFileWhich
) )
1026 if ( pImp
->mpSecondary
)
1027 return pImp
->mpSecondary
->GetNewWhich( nFileWhich
);
1028 SFX_ASSERT( false, nFileWhich
, "unknown which in GetNewWhich()" );
1031 // Newer/the same/older version?
1032 short nDiff
= (short)pImp
->nLoadingVersion
- (short)pImp
->nVersion
;
1034 // WhichId of a newer version?
1037 // Map step by step from the top version down to the file version
1038 for ( size_t nMap
= pImp
->aVersions
.size(); nMap
> 0; --nMap
)
1040 SfxPoolVersion_ImplPtr pVerInfo
= pImp
->aVersions
[nMap
-1];
1041 if ( pVerInfo
->_nVer
> pImp
->nVersion
)
1043 sal_uInt16 nCount
= pVerInfo
->_nEnd
- pVerInfo
->_nStart
+ 1;
1046 pVerInfo
->_pMap
[nOfs
] != nFileWhich
;
1050 if ( pVerInfo
->_pMap
[nOfs
] == nFileWhich
)
1051 nFileWhich
= pVerInfo
->_nStart
+ nOfs
;
1060 // WhichId of a newer version?
1061 else if ( nDiff
< 0 )
1063 // Map step by step from the top version down to the file version
1064 for ( size_t nMap
= 0; nMap
< pImp
->aVersions
.size(); ++nMap
)
1066 SfxPoolVersion_ImplPtr pVerInfo
= pImp
->aVersions
[nMap
];
1067 if ( pVerInfo
->_nVer
> pImp
->nLoadingVersion
)
1069 if (nFileWhich
>= pVerInfo
->_nStart
&&
1070 nFileWhich
<= pVerInfo
->_nEnd
)
1072 nFileWhich
= pVerInfo
->_pMap
[nFileWhich
- pVerInfo
->_nStart
];
1076 SAL_WARN("svl.items", "which-id unknown in version");
1082 // Return original (nDiff==0) or mapped (nDiff!=0) Id
1089 bool SfxItemPool::IsInVersionsRange( sal_uInt16 nWhich
) const
1091 return nWhich
>= pImp
->nVerStart
&& nWhich
<= pImp
->nVerEnd
;
1096 * This method determines whether the loaded Pool version corresponds to the
1097 * currently loaded Pool structure.
1099 * Precondition: Pool is loaded
1100 * Postcondition: Unchanged
1101 * Runtime: linear(Count of secondary pools)
1103 * @see SfxItemPool::SetVersionMap(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16*)
1104 * @see SfxItemPool::GetNewWhich(sal_uInt16) const
1105 * @see SfxItemPool::GetVersion() const
1107 bool SfxItemPool::IsCurrentVersionLoading() const
1109 return ( pImp
->nVersion
== pImp
->nLoadingVersion
) &&
1110 ( !pImp
->mpSecondary
|| pImp
->mpSecondary
->IsCurrentVersionLoading() );
1115 * Saves the SfxPoolItem 'rItem' to the SvStream 'rStream':
1116 * either as a surrogate ('bDirect == sal_False') or directly with
1118 * Non-poolable Items are always saved directly. Items without WhichId and
1119 * SID-Items as well as Items that were not yet present in the file format
1120 * version (return sal_False) are not saved.
1122 * The Item is saved to the Stream in the following manner:
1123 * sal_uInt16 rItem.Which()
1124 * sal_uInt16 GetSlotId( rItem.Which() ) or 0 if not available
1125 * sal_uInt16 GetSurrogate( &rItem ) or SFX_ITEM_DIRECT fo '!SFX_ITEM_POOLBLE'
1127 * Optionally (if 'bDirect == sal_True' or '!rItem.IsPoolable()':
1128 * sal_uInt16 rItem.GetVersion()
1130 * Size rItem.Store()
1132 * @see SfxItemPool::LoadItem(SvStream&,bool) const
1134 bool SfxItemPool::StoreItem( SvStream
&rStream
, const SfxPoolItem
&rItem
,
1135 bool bDirect
) const
1137 DBG_ASSERT( !IsInvalidItem(&rItem
), "cannot store invalid items" );
1139 if ( IsSlot( rItem
.Which() ) )
1142 const SfxItemPool
*pPool
= this;
1143 while ( !pPool
->IsInStoringRange(rItem
.Which()) )
1144 if ( 0 == ( pPool
= pPool
->pImp
->mpSecondary
) )
1147 DBG_ASSERT( !pImp
->bInSetItem
|| !rItem
.ISA(SfxSetItem
),
1148 "SetItem contains ItemSet with SetItem" );
1150 sal_uInt16 nSlotId
= pPool
->GetSlotId( rItem
.Which(), true );
1151 sal_uInt16 nItemVersion
= rItem
.GetVersion(pImp
->mnFileFormatVersion
);
1152 if ( USHRT_MAX
== nItemVersion
)
1155 rStream
.WriteUInt16( rItem
.Which() ).WriteUInt16( nSlotId
);
1156 if ( bDirect
|| !pPool
->StoreSurrogate( rStream
, &rItem
) )
1158 rStream
.WriteUInt16( nItemVersion
);
1159 rStream
.WriteUInt32( 0L ); // Room for length in bytes
1160 sal_uLong nIStart
= rStream
.Tell();
1161 rItem
.Store(rStream
, nItemVersion
);
1162 sal_uLong nIEnd
= rStream
.Tell();
1163 rStream
.Seek( nIStart
-4 );
1164 rStream
.WriteInt32( nIEnd
-nIStart
);
1165 rStream
.Seek( nIEnd
);
1173 * If pRefPool==-1 => do not put!
1175 const SfxPoolItem
* SfxItemPool::LoadItem( SvStream
&rStream
, bool bDirect
,
1176 const SfxItemPool
*pRefPool
)
1178 sal_uInt16
nWhich(0), nSlot(0); // nSurrogate;
1179 rStream
.ReadUInt16( nWhich
).ReadUInt16( nSlot
);
1181 bool bDontPut
= reinterpret_cast<SfxItemPool
*>(-1) == pRefPool
;
1182 if ( bDontPut
|| !pRefPool
)
1185 // Find right secondary Pool
1186 while ( !pRefPool
->IsInVersionsRange(nWhich
) )
1188 if ( pRefPool
->pImp
->mpSecondary
)
1189 pRefPool
= pRefPool
->pImp
->mpSecondary
;
1192 // WID not present in this version => skip
1193 sal_uInt32
nSurro(0);
1194 sal_uInt16
nVersion(0), nLen(0);
1195 rStream
.ReadUInt32( nSurro
);
1196 if ( SFX_ITEMS_DIRECT
== nSurro
)
1198 rStream
.ReadUInt16( nVersion
).ReadUInt16( nLen
);
1199 rStream
.SeekRel( nLen
);
1205 // Are we loading a different version?
1206 bool bCurVersion
= pRefPool
->IsCurrentVersionLoading();
1208 nWhich
= pRefPool
->GetNewWhich( nWhich
); // Map WhichId to new version
1210 DBG_ASSERT( !nWhich
|| !pImp
->bInSetItem
||
1211 !pRefPool
->pImp
->ppStaticDefaults
[pRefPool
->GetIndex_Impl(nWhich
)]->ISA(SfxSetItem
),
1212 "loading SetItem in ItemSet of SetItem" );
1214 // Are we loading via surrogate?
1215 const SfxPoolItem
*pItem
= 0;
1218 // WhichId known in this version?
1220 // Load surrogate and react if none present
1221 pItem
= LoadSurrogate( rStream
, nWhich
, nSlot
, pRefPool
);
1224 rStream
.SeekRel( sizeof(sal_uInt16
) );
1227 // Is loaded directly (not via surrogate)?
1228 if ( bDirect
|| ( nWhich
&& !pItem
) )
1230 // bDirekt or not IsPoolable() => Load Item directly
1231 sal_uInt16
nVersion(0);
1233 rStream
.ReadUInt16( nVersion
).ReadUInt32( nLen
);
1234 sal_uLong nIStart
= rStream
.Tell();
1236 // WhichId known in this version?
1239 // Load Item directly
1240 SfxPoolItem
*pNewItem
=
1241 pRefPool
->GetDefaultItem(nWhich
).Create(rStream
, nVersion
);
1247 pItem
= &Put(*pNewItem
);
1252 sal_uLong nIEnd
= rStream
.Tell();
1253 DBG_ASSERT( nIEnd
<= (nIStart
+nLen
), "read past end of item" );
1254 if ( (nIStart
+nLen
) != nIEnd
)
1255 rStream
.Seek( nIStart
+nLen
);
1259 rStream
.Seek( nIStart
+nLen
);
1266 OUString
readByteString(SvStream
& rStream
)
1268 return rStream
.ReadUniOrByteString(rStream
.GetStreamCharSet());
1271 void writeByteString(SvStream
& rStream
, const OUString
& rString
)
1273 rStream
.WriteUniOrByteString(rString
, rStream
.GetStreamCharSet());
1276 OUString
readUnicodeString(SvStream
& rStream
, bool bUnicode
)
1278 return rStream
.ReadUniOrByteString(bUnicode
? RTL_TEXTENCODING_UCS2
:
1279 rStream
.GetStreamCharSet());
1282 void writeUnicodeString(SvStream
& rStream
, const OUString
& rString
)
1284 rStream
.WriteUniOrByteString(rString
, RTL_TEXTENCODING_UCS2
);
1288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */