Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #ifndef INCLUDED_SVL_FILEREC_HXX
21 : #define INCLUDED_SVL_FILEREC_HXX
22 :
23 : #include <svl/svldllapi.h>
24 : #include <tools/debug.hxx>
25 : #include <tools/stream.hxx>
26 : #include <vector>
27 :
28 : #define SFX_REC_PRETAG_EXT sal_uInt8(0x00) // Pre-Tag for Extended-Records
29 : #define SFX_REC_PRETAG_EOR sal_uInt8(0xFF) // Pre-Tag for End-Of-Records
30 :
31 : #define SFX_REC_TYPE_NONE sal_uInt8(0x00) // unknown Record-Typ
32 : #define SFX_REC_TYPE_FIRST sal_uInt8(0x01)
33 : #define SFX_REC_TYPE_SINGLE sal_uInt8(0x01) // Single-Content-Record
34 : #define SFX_REC_TYPE_FIXSIZE sal_uInt8(0x02) // Fix-Size-Multi-Content-Record
35 : #define SFX_REC_TYPE_VARSIZE_RELOC sal_uInt8(0x03) // variable Rec-Size
36 : #define SFX_REC_TYPE_VARSIZE sal_uInt8(0x04) // old (not movable)
37 : #define SFX_REC_TYPE_MIXTAGS_RELOC sal_uInt8(0x07) // Mixed Tag Content-Record
38 : #define SFX_REC_TYPE_MIXTAGS sal_uInt8(0x08) // old (not movable)
39 : #define SFX_REC_TYPE_LAST sal_uInt8(0x08)
40 : #define SFX_REC_TYPE_MINI 0x100 // Mini-Record
41 : #define SFX_REC_TYPE_DRAWENG 0x400 // Drawing-Engine-Record
42 : #define SFX_REC_TYPE_EOR 0xF00 // End-Of-Records
43 :
44 : #define SFX_REC_HEADERSIZE_MINI 4 // size of the Mini-Record-Header
45 : #define SFX_REC_HEADERSIZE_SINGLE 4 // additional HEADERSIZE_MINI => 8
46 : #define SFX_REC_HEADERSIZE_MULTI 6 // additional HEADERSIZE_SINGLE => 14
47 :
48 : #ifndef DBG
49 : #ifdef DBG_UTIL
50 : #define DBG(x) x
51 : #else
52 : #define DBG(x)
53 : #endif
54 : #endif
55 :
56 : /* [Fileformat]
57 :
58 : Jeder Record beginnt mit einem Byte, dem sogenannten 'Pre-Tag'.
59 :
60 : Ist dieses 'Pre-Tag' == 0x00, dann handelt es sich um einen Extended-
61 : Record, dessen Typ durch ein weiteres Byte an Position 5 n?her
62 : beschrieben wird:
63 :
64 : 0x01: SfxSingleRecord
65 : 0x02: SfxMultiFixRecord
66 : 0x03+0x04: SfxMultiVarRecord
67 : 0x07+0x08: SfxMultiMixRecord
68 : (Alle weiteren Record-Typ-Kennungen sind reserviert.)
69 :
70 : I.d.R. werden File-Formate schon aus Performance-Gr"unden so aufgebaut,
71 : da\s beim Lesen jeweils vorher schon feststeht, welcher Record-Typ
72 : vorliegt. Diese Kennung dient daher hautps"achlich der "Uberpr"ufung
73 : und File-Viewern, die das genaue File-Format (unterhalb der Records)
74 : nicht kennen.
75 :
76 : Der 'SfxMiniRecordReader' verf"ugt dazu auch "uber eine statische
77 : Methode 'ScanRecordType()', mit der festgestellt werden kann, welcher
78 : Record-Typ in dem "ubergebenen Stream zu finden ist.
79 :
80 : Ein 'Pre-Tag' mit dem Wert 0xFF ist als Terminator reserviert.
81 : Terminatoren werden verwendet, um das Suchen nach einem speziellen
82 : Record zu terminieren, d.h. ist er bis dorthin nicht gefunden, wird
83 : auch nicht weitergesucht.
84 :
85 : Bei allen anderen Werten des 'Pre-Tags' (also von 0x01 bis 0xFE)
86 : handelt es sich um einen zum SW3 kompatbilen Record, der hier
87 : 'SfxMiniRecord' genannt wird, er kann daher mit einem <SfxMiniRecordReader>
88 : gelesen werden.
89 :
90 : Beginnt ein Record mit 0x44 k"onnte es sich um einen Drawing-Engine-
91 : Record handeln. Dies ist dann der Fall, wenn die folgenden drei Bytes
92 : die Zeichenkette 'RMD' bzw. 'RVW' ergeben (zusammen mit 'D'==0x44
93 : ergibt dies die K"urzel f"ur 'DRaw-MoDel' bzw. 'DRaw-VieW'). Records
94 : dieser Art k"onnen von den hier dargestellten Klassen weder gelesen,
95 : noch in irgendeiner Weise interpretiert werden. Einzig die Methode
96 : 'ScanRecordType()' kann sie erkennen - weitere Behandlung obliegt
97 : jedoch der Anwendungsprogrammierung.
98 :
99 : Diese drei Bytes an den Positionen 2 bis 4 enthalten normalerweise
100 : die Gr"o\se des Records ohne Pre-Tag und Gr"o\sen-Bytes selbst,
101 : also die Restgr"o\se nach diesem 4-Byte-Header.
102 :
103 : Struktur des Mini-Records:
104 :
105 : 1 sal_uInt8 Pre-Tag
106 : 3 sal_uInt8 OffsetToEndOfRec
107 : OffsetToEndOfRec* 1 sal_uInt8 Content
108 :
109 : Bei den Extended-Reords folgt auf diesen 4-Byte-Header ein erweiterter
110 : Header, der zun"achst den o.g. Record-Typ, dann eine Versions-Kennung
111 : sowie ein Tag enth"alt, welches den Inhalt kennzeichnet.
112 :
113 : Struktur des Extended-Records:
114 :
115 : 1 sal_uInt8 Pre-Tag (==0x00)
116 : 3 sal_uInt8 OffsetToEndOfRec
117 : OffsetToEndOfRec* 1 sal_uInt8 Content
118 : 1 sal_uInt8 Record-Type
119 : 1 sal_uInt8 Version
120 : 2 sal_uInt8 Tag
121 : ContentSize* 1 sal_uInt8 Content
122 :
123 : (ContentSize = OffsetToEndOfRec - 8)
124 :
125 : [Anmerkung]
126 :
127 : Der Aufbau der Records wird wie folgt begr"undet:
128 :
129 : Der SW-Record-Typ war zuerst vorhanden, mu\ste also 1:1 "ubernommen
130 : werden. Zum Gl"uck wurden einige Record-Tags nicht verwendet, (Z.B.
131 : 0x00 und 0xFF).
132 : => 1. Byte 0x00 kann als Kennung f"ur erweiterten Record verwendet werden
133 : => 1. Byte 0xFF kann f"ur besondere Zwecke verwendet werden
134 :
135 : Egal welcher Record-Typ vorliegt, sollte eine Erkennung des Typs, ein
136 : Auslesen des Headers und ein "uberpspringen des Records m"oglich sein,
137 : ohne zu"uck-seeken zu m"ussen und ohne "uberfl"ussige Daten lesen zu
138 : m"ussen.
139 : => die Bytes 2-4 werden bei allen Records als Offset zum Ende des
140 : Records interpretiert, so da\s die Gesamt-Recors-Size sich wie
141 : folgt berechnet: sizeof(sal_uInt32) + OffsetToEndOfRec
142 :
143 : Die Records sollten einfach zu parsen un einheitlich aufgebaut sein.
144 : => Sie bauen aufeinander auf, so ist z.B. der SfxMiniRecord in jedem
145 : anderen enthalten.
146 :
147 : Die Records sollten auch von denen der Drawing Enginge unterscheidbar
148 : sein. Diese beginnen mit 'DRMD' und 'DRVW'.
149 : => Mini-Records mit dem Pre-Tag 'D' d"urfen maximal 4MB gro\s sein,
150 : um nicht in diesen Kennungs-Bereich zu reichen.
151 :
152 : [Erweiterungen]
153 :
154 : Es ist geplant das File-Format so zu erweitern, da\s das High-Nibble
155 : des Record-Typs der erweiterten Records besondere Aufgaben "ubernehmen
156 : soll. Zum Beispiel ist geplant, Record-Contents als 'nur aus Records
157 : bestehend' zu kennzeichnen. Ein File-Viewer k"onnte sich dann automatisch
158 : durch solche Strukturen 'hangeln', ohne Gefahr zu laufen, auf Daten
159 : zu sto\sen, die sich zwar als Records interpretieren lassen, aber
160 : tats"achlis als 'flache' Daten geschrieben wurden. Die m"ogliche
161 : Erweiterung wird schon jetzt insofern vorbereitet, als da\s das
162 : High-Nibble des Typs bei Vergleichen nicht ber"ucksichtigt wird.
163 : */
164 :
165 : /** Writes simple records in a stream
166 : *
167 : * An instance of this class can write a simple record into a stream. It identifies itself
168 : * with a sal_uInt8 and stores its own size. This allows it to be skipped with old versions or
169 : * readers if they do not know the record type (= tag). No version number will be stored.
170 : *
171 : * One can either provide the size or the latter will be automatically calculated based on the
172 : * difference of Tell() before and after streaming the content.
173 : *
174 : * [File Format]
175 : * 1* sal_uInt8 Content-Tag (!= 0)
176 : * 1* 3-sal_uInt8 OffsetToEndOfRec in Bytes
177 : * SizeOfContent* sal_uInt8 Content
178 : *
179 : * @example
180 : * {
181 : * SfxMiniRecordWriter aRecord( pStream, MY_TAG_X );
182 : * *aRecord << aMember1;
183 : * *aRecord << aMember2;
184 : * }
185 : * @note To ensure up- and downwards compatibility, new versions need to include
186 : * the data of the older ones and are only allowed to add data afterwards.
187 : * @see SfxMiniRecordReader
188 : */
189 : class SVL_DLLPUBLIC SfxMiniRecordWriter
190 : {
191 : protected:
192 : SvStream* _pStream; // <SvStream> with the record
193 : sal_uInt32 _nStartPos; // starting position of the total record in the stream
194 : bool _bHeaderOk; /* TRUE, if header already written */
195 : sal_uInt8 _nPreTag; // 'pre-Tag' to write to header
196 :
197 : public:
198 : inline SfxMiniRecordWriter( SvStream *pStream, sal_uInt8 nTag );
199 : inline ~SfxMiniRecordWriter();
200 :
201 : inline SvStream& operator*() const;
202 :
203 : inline void Reset();
204 : sal_uInt32 Close( bool bSeekToEndOfRec = true );
205 :
206 : private:
207 : /// not implementend, not allowed
208 : SfxMiniRecordWriter( const SfxMiniRecordWriter& );
209 : SfxMiniRecordWriter& operator=(const SfxMiniRecordWriter&);
210 : };
211 :
212 : /** Reads simple record from a stream
213 : *
214 : * An instance of this class allows to read a simple record from a stream that was written by
215 : * SfxMiniRecordWriter. It is also possible to skip a record, even without knowing its internal
216 : * format.
217 : *
218 : * @example
219 : * {
220 : * SfxMiniRecordReader aRecord( pStream );
221 : * switch ( aRecord.GetTag() )
222 : * {
223 : * case MY_TAG_X:
224 : * *aRecord >> aMember1;
225 : * *aRecord >> aMember2;
226 : * break;
227 : *
228 : * ...
229 : * }
230 : * }
231 : * @see SfxMiniRecordWriter
232 : */
233 : class SVL_DLLPUBLIC SfxMiniRecordReader
234 : {
235 : protected:
236 : SvStream* _pStream; // <SvStream> to read from
237 : sal_uInt32 _nEofRec; // Position direkt hinter dem Record
238 : bool _bSkipped; // TRUE: der Record wurde explizit geskippt
239 : sal_uInt8 _nPreTag; // aus dem Header gelesenes Pre-Tag
240 :
241 : // Drei-Phasen-Ctor f"ur Subklassen
242 0 : SfxMiniRecordReader()
243 : : _pStream(NULL)
244 : , _nEofRec(0)
245 : , _bSkipped(false)
246 0 : , _nPreTag(0)
247 : {
248 0 : }
249 0 : void Construct_Impl( SvStream *pStream, sal_uInt8 nTag )
250 : {
251 0 : _pStream = pStream;
252 0 : _bSkipped = false;
253 0 : _nPreTag = nTag;
254 0 : }
255 : inline bool SetHeader_Impl( sal_uInt32 nHeader );
256 :
257 : // als ung"ultig markieren und zur"uck-seeken
258 0 : void SetInvalid_Impl( sal_uInt32 nRecordStartPos )
259 : {
260 0 : _nPreTag = SFX_REC_PRETAG_EOR;
261 0 : _pStream->Seek( nRecordStartPos );
262 0 : }
263 :
264 : public:
265 : SfxMiniRecordReader( SvStream *pStream, sal_uInt8 nTag );
266 : inline ~SfxMiniRecordReader();
267 :
268 : inline sal_uInt8 GetTag() const;
269 : inline bool IsValid() const;
270 :
271 : inline SvStream& operator*() const;
272 :
273 : inline void Skip();
274 :
275 : private:
276 : /// not implementend, not allowed
277 : SfxMiniRecordReader( const SfxMiniRecordReader& );
278 : SfxMiniRecordReader& operator=(const SfxMiniRecordReader&);
279 : };
280 :
281 : /* [Beschreibung]
282 :
283 : Mit Instanzen dieser Klasse kann ein Record in einen Stream geschrieben
284 : werden, dessen einziger Inhalt sich durch ein sal_uInt16-Tag und eine
285 : sal_uInt8-Versions-Nummer identifiziert, sowie seine eigene L"ange speichert
286 : und somit auch von "alteren Versionen bzw. Readern, die diesen
287 : Record-Type (Tag) nicht kennen, "ubersprungen werden kann.
288 :
289 : Alternativ kann die Gr"o\se fest angegeben werden oder sie wird
290 : automatisch aus der Differenz der Tell()-Angaben vor und nach dem
291 : Streamen des Inhalts ermittelt.
292 :
293 : Um Auf- und Abw"artskompatiblit"at gew"ahrleisten zu k"onnen, m"ussen
294 : neue Versionen die Daten der "alteren immer komplett enthalten,
295 : es d"urfen allenfalls neue Daten hintenan geh"angt werden!
296 :
297 : [Fileformat]
298 :
299 : 1* sal_uInt8 Pre-Tag (!= 0)
300 : 1* 3-sal_uInt8 OffsetToEndOfRec in Bytes
301 : 1* sal_uInt8 Record-Type (==SFX_REC_TYPE_SINGLE)
302 : 1* sal_uInt8 Content-Version
303 : 1* sal_uInt16 Content-Tag
304 : SizeOfContent* sal_uInt8 Content
305 : */
306 0 : class SVL_DLLPUBLIC SfxSingleRecordWriter: public SfxMiniRecordWriter
307 : {
308 : protected:
309 : SfxSingleRecordWriter( sal_uInt8 nRecordType,
310 : SvStream *pStream,
311 : sal_uInt16 nTag, sal_uInt8 nCurVer );
312 :
313 : public:
314 : inline void Reset();
315 :
316 : sal_uInt32 Close( bool bSeekToEndOfRec = true );
317 : };
318 :
319 : /* [Beschreibung]
320 :
321 : Mit Instanzen dieser Klasse kann ein einfacher Record aus einem Stream
322 : gelesen werden, der mit der Klasse <SfxSingleRecordWriter> geschrieben
323 : wurde.
324 :
325 : Es ist auch m"oglich, den Record zu "uberspringen, ohne sein internes
326 : Format zu kennen.
327 : */
328 0 : class SVL_DLLPUBLIC SfxSingleRecordReader: public SfxMiniRecordReader
329 : {
330 : protected:
331 : sal_uInt16 _nRecordTag; // Art des Gesamt-Inhalts
332 : sal_uInt8 _nRecordVer; // Version des Gesamt-Inhalts
333 : sal_uInt8 _nRecordType; // Record Type aus dem Header
334 :
335 : // Drei-Phasen-Ctor f"ur Subklassen
336 0 : SfxSingleRecordReader()
337 : : _nRecordTag(0)
338 : , _nRecordVer(0)
339 0 : , _nRecordType(0)
340 : {
341 0 : }
342 0 : void Construct_Impl( SvStream *pStream )
343 : {
344 : SfxMiniRecordReader::Construct_Impl(
345 0 : pStream, SFX_REC_PRETAG_EXT );
346 0 : }
347 : bool FindHeader_Impl( sal_uInt16 nTypes, sal_uInt16 nTag );
348 : bool ReadHeader_Impl( sal_uInt16 nTypes );
349 :
350 : public:
351 :
352 : inline sal_uInt16 GetTag() const;
353 :
354 : inline sal_uInt8 GetVersion() const;
355 : inline bool HasVersion( sal_uInt16 nVersion ) const;
356 : };
357 :
358 : /* [Beschreibung]
359 :
360 : Mit Instanzen dieser Klasse kann ein Record in einen Stream geschrieben
361 : werden, der seine eigene L"ange speichert und somit auch von "alteren
362 : Versionen bzw. Readern, die diesen Record-Type (Tag) nicht kennen,
363 : "ubersprungen werden kann.
364 :
365 : Er enth"alt mehrere Inhalte von demselben Typ (Tag) und derselben
366 : Version, die einmalig (stellvertretend f"ur alle) im Header des Records
367 : identifiziert werden. Alle Inhalte haben eine vorher bekannte und
368 : identische L"ange.
369 :
370 : Um Auf- und Abw"artskompatiblit"at gew"ahrleisten zu k"onnen, m"ussen
371 : neue Versionen die Daten der "alteren immer komplett enthalten,
372 : es d"urfen allenfalls neue Daten hinten angeh"angt werden! Hier sind
373 : damit selbstverst"andlich nur die Daten der einzelnen Inhalte gemeint,
374 : die Anzahl der Inhalte ist selbstverst"andlich variabel und sollte
375 : von lesenden Applikationen auch so behandelt werden.
376 :
377 : [Fileformat]
378 :
379 : 1* sal_uInt8 Pre-Tag (==0)
380 : 1* 3-sal_uInt8 OffsetToEndOfRec in Bytes
381 : 1* sal_uInt8 Record-Type (==SFX_REC_TYPE_FIXSIZE)
382 : 1* sal_uInt8 Content-Version
383 : 1* sal_uInt16 Content-Tag
384 : 1* sal_uInt16 NumberOfContents
385 : 1* sal_uInt32 SizeOfEachContent
386 : NumberOfContents* (
387 : SizeOfEachContent sal_uInt8 Content
388 : )
389 :
390 : [Beispiel]
391 :
392 : {
393 : SfxMultiFixRecordWriter aRecord( pStream, MY_TAG_X, MY_VERSION );
394 : for ( sal_uInt16 n = 0; n < Count(); ++n )
395 : {
396 : aRecord.NewContent();
397 : *aRecord << aMember1[n];
398 : *aRecord << aMember2[n];
399 : }
400 : }
401 : */
402 : class SVL_DLLPUBLIC SfxMultiFixRecordWriter: public SfxSingleRecordWriter
403 : {
404 : protected:
405 : sal_uInt32 _nContentStartPos; /* Startposition des jeweiligen
406 : Contents - nur bei DBG_UTIL
407 : und f"ur Subklassen */
408 : sal_uInt32 _nContentSize; // Gr"o\se jedes Contents
409 : sal_uInt16 _nContentCount; // jeweilige Anzahl der Contents
410 :
411 : SfxMultiFixRecordWriter( sal_uInt8 nRecordType,
412 : SvStream *pStream,
413 : sal_uInt16 nTag,
414 : sal_uInt8 nCurVer );
415 :
416 : public:
417 : inline ~SfxMultiFixRecordWriter();
418 :
419 : inline void NewContent();
420 :
421 : inline void Reset();
422 :
423 : sal_uInt32 Close( bool bSeekToEndOfRec = true );
424 : };
425 :
426 : /** write record with multiple content items
427 : *
428 : * Write a record into a stream that stores its own size. This allows it to be
429 : * skipped with old versions or readers if they do not know the record type (= tag).
430 : *
431 : * It contains multiple content items of the same tag and version, that are both
432 : * stored in the header of the record. The size of each content will be calculated
433 : * automatically and stored so that single content items can be skipped without
434 : * having to read them.
435 : *
436 : * [Fileformat]
437 : * 1* sal_uInt8 Pre-Tag (==0)
438 : * 1* 3-sal_uInt8 OffsetToEndOfRec in Bytes
439 : * 1* sal_uInt8 Record-Type (==SFX_FILETYPE_TYPE_VARSIZE)
440 : * 1* sal_uInt8 Content-Version
441 : * 1* sal_uInt16 Content-Tag
442 : * 1* sal_uInt16 NumberOfContents
443 : * 1* sal_uInt32 OffsetToOfsTable
444 : * NumberOfContents* (
445 : * ContentSize* sal_uInt8 Content
446 : * )
447 : * NumberOfContents* sal_uInt32 ContentOfs (je per <<8 verschoben)
448 : * @example
449 : * {
450 : * SfxMultiVarRecordWriter aRecord( pStream, MY_TAG_X, MY_VERSION );
451 : * for ( sal_uInt16 n = 0; n < Count(); ++n )
452 : * {
453 : * aRecord.NewContent();
454 : * *aRecord << aMember1[n];
455 : * *aRecord << aMember2[n];
456 : * }
457 : * }
458 : * @note To ensure up- and downwards compatibility, new versions need to include
459 : * the data of the older ones and are only allowed to add data afterwards.
460 : */
461 : class SVL_DLLPUBLIC SfxMultiVarRecordWriter: public SfxMultiFixRecordWriter
462 : {
463 : protected:
464 : std::vector<sal_uInt32> _aContentOfs;
465 : sal_uInt16 _nContentVer; // only for SfxMultiMixRecordWriter
466 :
467 : SfxMultiVarRecordWriter( sal_uInt8 nRecordType,
468 : SvStream *pStream,
469 : sal_uInt16 nRecordTag,
470 : sal_uInt8 nRecordVer );
471 :
472 : void FlushContent_Impl();
473 :
474 : public:
475 : SfxMultiVarRecordWriter( SvStream *pStream,
476 : sal_uInt16 nRecordTag,
477 : sal_uInt8 nRecordVer );
478 : virtual ~SfxMultiVarRecordWriter();
479 :
480 : void NewContent();
481 :
482 : virtual sal_uInt32 Close( bool bSeekToEndOfRec = true );
483 : };
484 :
485 : /** write record with multiple content items with identical size
486 : *
487 : * Write a record into a stream that stores its own size. This allows it to be
488 : * skipped with old versions or readers if they do not know the record type (= tag).
489 : *
490 : * It contains multiple content items of the same tag and version, that are both
491 : * stored in the header of the record. All content items have a known identical
492 : * size.
493 : *
494 : * [Fileformat]
495 : * 1* sal_uInt8 Pre-Tag (==0)
496 : * 1* 3-sal_uInt8 OffsetToEndOfRec in Bytes
497 : * 1* sal_uInt8 record type (==SFX_REC_TYPE_MIXTAGS)
498 : * 1* sal_uInt8 content version
499 : * 1* sal_uInt16 record tag
500 : * 1* sal_uInt16 NumberOfContents
501 : * 1* sal_uInt32 OffsetToOfsTable
502 : * NumberOfContents* (
503 : * 1* sal_uInt16 content tag
504 : * ContentSize* sal_uInt8 content
505 : * )
506 : * NumberOfContents* sal_uInt32 ( ContentOfs << 8 + Version )
507 : * @note To ensure up- and downwards compatibility, new versions need to include
508 : * the data of the older ones and are only allowed to add data afterwards.
509 : */
510 0 : class SVL_DLLPUBLIC SfxMultiMixRecordWriter: public SfxMultiVarRecordWriter
511 : {
512 : public:
513 : inline SfxMultiMixRecordWriter( SvStream *pStream,
514 : sal_uInt16 nRecordTag,
515 : sal_uInt8 nRecordVer );
516 :
517 : void NewContent( sal_uInt16 nTag, sal_uInt8 nVersion );
518 : // private: not possible, since some compilers then make the previous also private
519 : void NewContent()
520 : { OSL_FAIL( "NewContent() only allowed with args" ); }
521 : };
522 :
523 : /** Read multiple content items of an existing record
524 : *
525 : * Instances of this class allow to read multiple content items of a record
526 : * that was written with
527 : * - SfxMultiFixRecordWriter
528 : * - SfxMultiVarRecordWriter
529 : * - SfxMultiMixRecordWriter
530 : *
531 : * It is possible to skip single content or the whole record without knowing
532 : * its internal format.
533 : *
534 : * @example
535 : * {
536 : * SfxMultiRecordReader aRecord( pStream );
537 : * for ( sal_uInt16 nRecNo = 0; aRecord.GetContent(); ++nRecNo )
538 : * {
539 : * switch ( aRecord.GetTag() )
540 : * {
541 : * case MY_TAG_X:
542 : * X *pObj = new X;
543 : * *aRecord >> pObj.>aMember1;
544 : * if ( aRecord.HasVersion(2) )
545 : * *aRecord >> pObj->aMember2;
546 : * Append( pObj );
547 : * break;
548 : *
549 : * ...
550 : * }
551 : * }
552 : * }
553 : */
554 : class SVL_DLLPUBLIC SfxMultiRecordReader: public SfxSingleRecordReader
555 : {
556 : sal_uInt32 _nStartPos; // start position of this record
557 : sal_uInt32* _pContentOfs; // offsets of the start positions
558 : sal_uInt32 _nContentSize; // size of each record or table position
559 : sal_uInt16 _nContentCount; // number of content items
560 : sal_uInt16 _nContentNo; /* the index of the current content
561 : contains the next content's index
562 : for GetContent() */
563 : sal_uInt16 _nContentTag; // tag of the current content
564 : sal_uInt8 _nContentVer; // version of the current content
565 :
566 : bool ReadHeader_Impl();
567 :
568 : public:
569 : SfxMultiRecordReader( SvStream *pStream, sal_uInt16 nTag );
570 : ~SfxMultiRecordReader();
571 :
572 : bool GetContent();
573 : inline sal_uInt16 GetContentTag();
574 : inline sal_uInt8 GetContentVersion() const;
575 : inline bool HasContentVersion( sal_uInt16 nVersion ) const;
576 :
577 : inline sal_uInt32 ContentCount() const;
578 : };
579 :
580 : /** create a mini record
581 : *
582 : * The content size is calculated automatically after streaming.
583 : *
584 : * @param pStream the stream that will contain the record
585 : * @param nTag a record tag between 0x01 and 0xFE
586 : */
587 0 : inline SfxMiniRecordWriter::SfxMiniRecordWriter( SvStream* pStream, sal_uInt8 nTag )
588 : : _pStream( pStream ),
589 0 : _nStartPos( pStream->Tell() ),
590 : _bHeaderOk(false),
591 0 : _nPreTag( nTag )
592 : {
593 : DBG_ASSERT( _nPreTag != 0xFF, "invalid Tag" );
594 : SAL_INFO("svl", "SfxFileRec: writing record to " << pStream->Tell());
595 :
596 0 : pStream->SeekRel( + SFX_REC_HEADERSIZE_MINI );
597 0 : }
598 :
599 : /** The destructor closes the record automatically if not done earlier */
600 0 : inline SfxMiniRecordWriter::~SfxMiniRecordWriter()
601 : {
602 : // the header was not written, yet, or needs to be checked
603 0 : if ( !_bHeaderOk )
604 0 : Close();
605 0 : }
606 :
607 : /** Get the record's stream
608 : * @return The stream containing the record
609 : * @note The record must not be already closed!
610 : */
611 : inline SvStream& SfxMiniRecordWriter::operator*() const
612 : {
613 : DBG_ASSERT( !_bHeaderOk, "getting Stream of closed record" );
614 : return *_pStream;
615 : }
616 :
617 : inline void SfxMiniRecordWriter::Reset()
618 : {
619 : _pStream->Seek( _nStartPos + SFX_REC_HEADERSIZE_MINI );
620 : _bHeaderOk = false;
621 : }
622 :
623 : /** The dtor moves the stream automatically to the position directly behind the record */
624 0 : inline SfxMiniRecordReader::~SfxMiniRecordReader()
625 : {
626 0 : if ( !_bSkipped )
627 0 : Skip();
628 0 : }
629 :
630 : /** position the stream directly behind the record's end */
631 0 : inline void SfxMiniRecordReader::Skip()
632 : {
633 0 : _pStream->Seek(_nEofRec);
634 0 : _bSkipped = true;
635 0 : }
636 :
637 : /** Get the pre-tag of this record
638 : *
639 : * The pre-tag might also be SFX_REC_PRETAG_EXT or SFX_REC_PRETAG_EOR.
640 : * The latter means that in the stream the error code ERRCODE_IO_WRONGFORMAT
641 : * is set. The former is valid, since extended records are just building on
642 : * top of SfxMiniRecord.
643 : *
644 : * @return The pre-tag
645 : */
646 : inline sal_uInt8 SfxMiniRecordReader::GetTag() const
647 : {
648 : return _nPreTag;
649 : }
650 :
651 : /** This method allows to check if the record could be recreated successfully
652 : * from the stream and, hence, was correct for this record type.
653 : */
654 : inline bool SfxMiniRecordReader::IsValid() const
655 : {
656 : return _nPreTag != SFX_REC_PRETAG_EOR;
657 : }
658 :
659 : /** get the owning stream
660 : *
661 : * This method returns the stream in which the record is contained.
662 : * The current position of the stream must be inside the record.
663 : */
664 : inline SvStream& SfxMiniRecordReader::operator*() const
665 : {
666 : DBG_ASSERT( _pStream->Tell() < _nEofRec, "read behind record" );
667 : return *_pStream;
668 : }
669 :
670 : /// @see SfxMiniRecordWriter::Close()
671 0 : inline sal_uInt32 SfxSingleRecordWriter::Close( bool bSeekToEndOfRec )
672 : {
673 0 : sal_uInt32 nRet = 0;
674 :
675 : // was the header already written?
676 0 : if ( !_bHeaderOk )
677 : {
678 : // write base class header
679 0 : sal_uInt32 nEndPos = SfxMiniRecordWriter::Close( bSeekToEndOfRec );
680 :
681 : // seek the end of the own header if needed or stay behind the record
682 0 : if ( !bSeekToEndOfRec )
683 0 : _pStream->SeekRel( SFX_REC_HEADERSIZE_SINGLE );
684 0 : nRet = nEndPos;
685 : }
686 : #ifdef DBG_UTIL
687 : else
688 : // check base class header
689 : SfxMiniRecordWriter::Close( bSeekToEndOfRec );
690 : #endif
691 :
692 0 : return nRet;
693 : }
694 :
695 : inline void SfxSingleRecordWriter::Reset()
696 : {
697 : _pStream->Seek( _nStartPos + SFX_REC_HEADERSIZE_MINI +
698 : SFX_REC_HEADERSIZE_SINGLE );
699 : _bHeaderOk = false;
700 : }
701 :
702 : /** @returns the tag for the overall record (stored in the record's head) */
703 : inline sal_uInt16 SfxSingleRecordReader::GetTag() const
704 : {
705 : return _nRecordTag;
706 : }
707 :
708 : /** @returns version of the record */
709 : inline sal_uInt8 SfxSingleRecordReader::GetVersion() const
710 : {
711 : return _nRecordVer;
712 : }
713 :
714 : /** determine if the read record has at least the given version */
715 : inline bool SfxSingleRecordReader::HasVersion( sal_uInt16 nVersion ) const
716 : {
717 : return _nRecordVer >= nVersion;
718 : }
719 :
720 : /** The destructor closes the record automatically if not done earlier */
721 0 : inline SfxMultiFixRecordWriter::~SfxMultiFixRecordWriter()
722 : {
723 : // the header was not written, yet, or needs to be checked
724 0 : if ( !_bHeaderOk )
725 0 : Close();
726 0 : }
727 :
728 : /** add a new content into a record
729 : *
730 : * @note each, also the first record, must be initialized by this method
731 : */
732 : inline void SfxMultiFixRecordWriter::NewContent()
733 : {
734 : #ifdef DBG_UTIL
735 : sal_uLong nOldStartPos;
736 : // store starting position of the current content - CAUTION: sub classes!
737 : nOldStartPos = _nContentStartPos;
738 : #endif
739 : _nContentStartPos = _pStream->Tell();
740 :
741 : #ifdef DBG_UTIL
742 : // is there a previous content?
743 : if ( _nContentCount )
744 : {
745 : // check if the previous content stays in specified max. size
746 : DBG_ASSERT( _nContentStartPos - nOldStartPos == _nContentSize,
747 : "wrong content size detected" );
748 : }
749 : #endif
750 :
751 : // count how many
752 : ++_nContentCount;
753 : }
754 :
755 : /**
756 : * Creates a SfxMultiMixRecord in the given stream with a seperate tags and
757 : * versions of its content parts. The sizes of each part are calculated
758 : * automatically.
759 : *
760 : * @param pStream target stream in which the record will be created
761 : * @param nRecordTag tag for the total record
762 : * @param nRecordVer version for the total record
763 : */
764 0 : inline SfxMultiMixRecordWriter::SfxMultiMixRecordWriter( SvStream* pStream,
765 : sal_uInt16 nRecordTag,
766 : sal_uInt8 nRecordVer )
767 0 : : SfxMultiVarRecordWriter( SFX_REC_TYPE_MIXTAGS, pStream, nRecordTag, nRecordVer )
768 : {
769 0 : }
770 :
771 : inline void SfxMultiFixRecordWriter::Reset()
772 : {
773 : _pStream->Seek( _nStartPos + SFX_REC_HEADERSIZE_MINI +
774 : SFX_REC_HEADERSIZE_SINGLE +
775 : SFX_REC_HEADERSIZE_MULTI );
776 : _bHeaderOk = false;
777 : }
778 :
779 : /** @returns the tag of the last opened content
780 : * @see SfxMultiRecordReder::GetContent()
781 : */
782 0 : inline sal_uInt16 SfxMultiRecordReader::GetContentTag()
783 : {
784 0 : return _nContentTag;
785 : }
786 :
787 : /** @returns the version of the last opened content
788 : * @see SfxMultiRecordReder::GetContent()
789 : */
790 : inline sal_uInt8 SfxMultiRecordReader::GetContentVersion() const
791 : {
792 : return _nContentVer;
793 : }
794 :
795 : /** Determines if the given version is in the last opened content
796 : *
797 : * This method checks if the version is contained in the last version of the
798 : * content that was opened with SfxMultiRecordReder::GetContent().
799 : *
800 : * @param nVersion The version to find
801 : * @return true, if found
802 : * @see SfxMultiRecordReder::GetContent()
803 : */
804 : inline bool SfxMultiRecordReader::HasContentVersion( sal_uInt16 nVersion ) const
805 : {
806 : return _nContentVer >= nVersion;
807 : }
808 :
809 : /** @returns number of this record's contents */
810 : inline sal_uInt32 SfxMultiRecordReader::ContentCount() const
811 : {
812 : return _nContentCount;
813 : }
814 :
815 : #endif
816 :
817 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|