bump product version to 5.0.4.1
[LibreOffice.git] / svl / source / items / poolio.cxx
blob6b0fee8785f1b175f93e98909168d75539839cc9
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 .
21 #include <string.h>
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>
29 #include "poolio.hxx"
30 #include <boost/scoped_ptr.hpp>
31 #include <boost/scoped_array.hpp>
33 /**
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()
41 return pStoringPool_;
44 static sal_uInt16 convertSfxItemKindToUInt16(SfxItemKind x)
46 if ( x == SFX_ITEMS_NONE )
47 return 0;
48 if ( x == SFX_ITEMS_DELETEONIDLE )
49 return 0xfffd;
50 if ( x == SFX_ITEMS_STATICDEFAULT )
51 return 0xfffe;
52 if ( x == SFX_ITEMS_POOLDEFAULT )
53 return 0xffff;
54 assert(false);
55 abort();
58 static SfxItemKind convertUInt16ToSfxItemKind(sal_uInt16 x)
60 if ( x == 0 )
61 return SFX_ITEMS_NONE;
62 if ( x == 0xfffd )
63 return SFX_ITEMS_DELETEONIDLE;
64 if ( x == 0xfffe )
65 return SFX_ITEMS_STATICDEFAULT;
66 if ( x == 0xffff )
67 return SFX_ITEMS_POOLDEFAULT;
68 assert(false);
69 abort();
74 /**
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.
78 * [Fileformat]
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)
105 * content SlotId, 0
106 * sal_uInt16 WhichId
107 * sal_uInt16 pItem->GetVersion()
108 * sal_uInt16 Array-Size
109 * record SfxMultiRecord(SFX_, 0)
110 * content Surrogate
111 * sal_uInt16 RefCount
112 * unknown pItem->Store()
114 * ;Now the set Pool defaults
115 * Defaults: record SfxMultiRecord(SFX_ITEMPOOL_REC_DEFAULTS, 0)
116 * content SlotId, 0
117 * sal_uInt16 WhichId
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
125 // Find StoreMaster
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;
132 if ( !pStoreMaster )
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);
156 // VersionMaps
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 );
178 // Pooled Items
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
196 continue;
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 );
212 // Write Items
213 SfxMultiMixRecordWriter aItemsRec( &rStream, SFX_ITEMPOOL_REC_ITEMS, 0 );
214 for ( size_t j = 0; j < nCount; ++j )
216 // Get Item
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()) );
224 else
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);
233 else
234 break;
235 #ifdef DBG_UTIL_MI
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" );
245 #endif
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];
263 if ( pDefaultItem )
265 // Get version
266 sal_uInt16 nItemVersion = pDefaultItem->GetVersion( pImp->mnFileFormatVersion );
267 if ( USHRT_MAX == nItemVersion )
268 // => Was not present in the version yet
269 continue;
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 );
277 // Item
278 pDefaultItem->Store( rStream, nItemVersion );
283 // Write out additional Pools
284 pStoringPool_ = 0;
285 aPoolRec.Close();
286 if ( !rStream.GetError() && pImp->mpSecondary )
287 pImp->mpSecondary->Store( rStream );
289 pImp->bStreaming = false;
290 return rStream;
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
306 * needed anymore.
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?
320 if ( *itrItemArr )
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 )
326 if (*ppHtArr)
328 if ( !ReleaseRef( **ppHtArr, 1 ) )
329 DELETEZ( *ppHtArr );
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
352 return pImp->mnEnd;
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
385 sal_uInt16 nRef(0);
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);
394 else
396 if ( nRef > SFX_ITEMS_OLD_MAXREF )
397 SfxItemPool::SetKind(*pItem, convertUInt16ToSfxItemKind(nRef));
398 else
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;
408 *ppArr = pNewArr;
410 // Remember items that are already in the pool
411 bool bEmpty = true;
412 if ( 0 != pOldArr )
413 for ( n = 0; bEmpty && n < pOldArr->size(); ++n )
414 bEmpty = pOldArr->operator[](n) == 0;
415 DBG_ASSERTWARNING( bEmpty, "loading non-empty pool" );
416 if ( !bEmpty )
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];
422 if ( pOldItem )
424 sal_uInt32 nFree = SAL_MAX_UINT32;
425 bool bFound = false;
426 for ( size_t nNew = (*ppArr)->size(); nNew--; )
428 // Loaded Item
429 SfxPoolItem *&rpNewItem =
430 (SfxPoolItem*&)(*ppArr)->operator[](nNew);
432 // Unused surrogate?
433 if ( !rpNewItem )
434 nFree = nNew;
436 // Found it?
437 else if ( *rpNewItem == *pOldItem )
439 // Reuse
440 SfxItemPool::AddRef( *pOldItem, rpNewItem->GetRefCount() );
441 SfxItemPool::SetRefCount( *rpNewItem, 0 );
442 delete rpNewItem;
443 rpNewItem = pOldItem;
444 bFound = true;
445 break;
449 // Take over the ones that were previously present, but had not been loaded
450 if ( !bFound )
452 if ( nFree != SAL_MAX_UINT32 )
453 (SfxPoolItem*&)(*ppArr)->operator[](nFree) = pOldItem;
454 else
455 (*ppArr)->push_back( pOldItem );
460 delete pOldArr;
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?
478 if ( *itrItemArr )
480 SfxPoolItemArrayBase_Impl::iterator ppHtArr = (*itrItemArr)->begin();
481 for( size_t n = (*itrItemArr)->size(); n; --n, ++ppHtArr )
482 if (*ppHtArr)
484 DBG_WARNING( "loading non-empty ItemPool" );
486 AddRef( **ppHtArr, 1 );
491 // During loading (until LoadCompleted()) protect all items
492 pImp->nInitRefCount = 2;
495 // Find LoadMaster
496 SfxItemPool *pLoadMaster = pImp->mpMaster != this ? pImp->mpMaster : 0;
497 while ( pLoadMaster && !pLoadMaster->pImp->bStreaming )
498 pLoadMaster = pLoadMaster->pImp->mpSecondary;
500 // Read whole Header
501 pImp->bStreaming = true;
502 if ( !pLoadMaster )
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;
513 // Unknown Format
514 if (pImp->nMajorVer < 2 || pImp->nMajorVer > SFX_ITEMPOOL_VER_MAJOR)
516 rStream.SetError(SVSTREAM_FILEFORMAT_ERROR);
517 pImp->bStreaming = false;
518 return rStream;
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;
531 return rStream;
534 // Single header
535 OUString aExternName;
537 // Find HeaderRecord
538 SfxMiniRecordReader aPoolHeaderRec( &rStream, SFX_ITEMPOOL_REC_HEADER );
539 if ( rStream.GetError() )
541 pImp->bStreaming = false;
542 return rStream;
545 // Read Header
546 rStream.ReadUInt16( pImp->nLoadingVersion );
547 aExternName = readByteString(rStream);
548 bool bOwnPool = aExternName == pImp->aName;
550 //! As long as we cannot read foreign Pools
551 if ( !bOwnPool )
553 rStream.SetError(SVSTREAM_FILEFORMAT_ERROR);
554 aPoolRec.Skip();
555 pImp->bStreaming = false;
556 return rStream;
560 // Version maps
562 SfxMultiRecordReader aVerRec( &rStream, SFX_ITEMPOOL_REC_VERSIONMAP );
563 if ( rStream.GetError() )
565 pImp->bStreaming = false;
566 return rStream;
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() )
581 // Add new Version
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;
599 // Load Items
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) )
617 continue;
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();
636 aPoolRec.Skip();
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);
647 // Read Items
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) )
670 continue;
672 rStream.ReadUInt16( nVersion );
673 //!SFX_ASSERTWARNING( !HasMap() || ( nSlotId == GetSlotId( nWhich, sal_False ) ),
674 //! nWhich, "Slot/Which mismatch" );
676 // Load PoolDefaultItem
677 SfxPoolItem *pItem =
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
686 aPoolRec.Skip();
687 if ( pImp->mpSecondary )
689 if ( !bSecondaryLoaded )
690 pImp->mpSecondary->Load( rStream );
691 else
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;
700 return rStream;
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");
708 return 0;
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
720 * from the rRefPool.
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
723 * from the stream.
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
743 * SfxItemPool
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 )
764 return 0;
766 // Item does not exist?
767 if ( SFX_ITEMS_NULL == nSurrogat )
769 rWhich = 0;
770 return 0;
773 // If the Pool in the stream has the same structure, the surrogate
774 // can be resolved in any case
775 if ( !pRefPool )
776 pRefPool = this;
778 bool bResolvable = !pRefPool->GetName().isEmpty();
779 if ( !bResolvable )
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;
788 bResolvable = true;
792 // Can the surrogate be resolved?
793 if ( bResolvable )
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]
810 : 0;
811 if ( !pItem )
813 OSL_FAIL( "can't resolve surrogate" );
814 rWhich = 0; // Just to be sure; for the right StreamPos
815 return 0;
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() )
824 AddRef( *pItem, 1 );
825 else
826 return pItem;
828 return pItem;
832 SFX_ASSERT( false, rWhich, "can't resolve Which-Id in LoadSurrogate" );
835 return 0;
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
848 * its own
850 bool SfxItemPool::StoreSurrogate ( SvStream& rStream, const SfxPoolItem* pItem) const
852 if ( pItem )
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 );
862 return true;
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];
890 if ( p == pItem )
891 return 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
909 * being saved.
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
944 * Example usage:
945 * Originally (version 0) the pool had the following WhichIds:
947 * 1:A, 2:B, 3:C, 4:D
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 )
988 if ( !nWhich )
989 nWhich = 0;
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
1000 * current pool.
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
1021 ) const
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?
1035 if ( nDiff > 0 )
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 )
1042 { sal_uInt16 nOfs;
1043 sal_uInt16 nCount = pVerInfo->_nEnd - pVerInfo->_nStart + 1;
1044 for ( nOfs = 0;
1045 nOfs <= nCount &&
1046 pVerInfo->_pMap[nOfs] != nFileWhich;
1047 ++nOfs )
1048 continue;
1050 if ( pVerInfo->_pMap[nOfs] == nFileWhich )
1051 nFileWhich = pVerInfo->_nStart + nOfs;
1052 else
1053 return 0;
1055 else
1056 break;
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];
1074 else
1076 SAL_WARN("svl.items", "which-id unknown in version");
1082 // Return original (nDiff==0) or mapped (nDiff!=0) Id
1083 return nFileWhich;
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
1117 * 'rItem.Store()'.
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()
1129 * sal_uLong Size
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() ) )
1140 return false;
1142 const SfxItemPool *pPool = this;
1143 while ( !pPool->IsInStoringRange(rItem.Which()) )
1144 if ( 0 == ( pPool = pPool->pImp->mpSecondary ) )
1145 return false;
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 )
1153 return false;
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 );
1168 return true;
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 )
1183 pRefPool = this;
1185 // Find right secondary Pool
1186 while ( !pRefPool->IsInVersionsRange(nWhich) )
1188 if ( pRefPool->pImp->mpSecondary )
1189 pRefPool = pRefPool->pImp->mpSecondary;
1190 else
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 );
1201 return 0;
1205 // Are we loading a different version?
1206 bool bCurVersion = pRefPool->IsCurrentVersionLoading();
1207 if ( !bCurVersion )
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;
1216 if ( !bDirect )
1218 // WhichId known in this version?
1219 if ( nWhich )
1220 // Load surrogate and react if none present
1221 pItem = LoadSurrogate( rStream, nWhich, nSlot, pRefPool );
1222 else
1223 // Else skip it
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);
1232 sal_uInt32 nLen(0);
1233 rStream.ReadUInt16( nVersion ).ReadUInt32( nLen );
1234 sal_uLong nIStart = rStream.Tell();
1236 // WhichId known in this version?
1237 if ( nWhich )
1239 // Load Item directly
1240 SfxPoolItem *pNewItem =
1241 pRefPool->GetDefaultItem(nWhich).Create(rStream, nVersion);
1242 if ( bDontPut )
1243 pItem = pNewItem;
1244 else
1245 if ( pNewItem )
1247 pItem = &Put(*pNewItem);
1248 delete pNewItem;
1250 else
1251 pItem = 0;
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 );
1257 else
1258 // SKip Item
1259 rStream.Seek( nIStart+nLen );
1262 return pItem;
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: */