0031918: Application Framework - New binary format for fast reading part of OCAF...
[occt.git] / src / BinLDrivers / BinLDrivers_DocumentStorageDriver.cxx
1 // Created on: 2002-10-29
2 // Created by: Michael SAZONOV
3 // Copyright (c) 2002-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16
17 #include <BinLDrivers.hxx>
18 #include <BinLDrivers_DocumentSection.hxx>
19 #include <BinLDrivers_DocumentStorageDriver.hxx>
20 #include <BinLDrivers_Marker.hxx>
21 #include <BinMDF_ADriver.hxx>
22 #include <BinMDF_ADriverTable.hxx>
23 #include <BinObjMgt_Persistent.hxx>
24 #include <BinObjMgt_Position.hxx>
25 #include <CDM_Application.hxx>
26 #include <CDM_Document.hxx>
27 #include <Message_Messenger.hxx>
28 #include <FSD_BinaryFile.hxx>
29 #include <FSD_FileHeader.hxx>
30 #include <OSD_OpenFile.hxx>
31 #include <PCDM_ReadWriter.hxx>
32 #include <Standard_ErrorHandler.hxx>
33 #include <Standard_Type.hxx>
34 #include <Storage_Schema.hxx>
35 #include <TCollection_AsciiString.hxx>
36 #include <TCollection_ExtendedString.hxx>
37 #include <TColStd_Array1OfInteger.hxx>
38 #include <TColStd_ListIteratorOfListOfInteger.hxx>
39 #include <TColStd_ListOfInteger.hxx>
40 #include <TDF_AttributeIterator.hxx>
41 #include <TDF_ChildIterator.hxx>
42 #include <TDF_Data.hxx>
43 #include <TDF_Label.hxx>
44 #include <TDF_Tool.hxx>
45 #include <TDocStd_Document.hxx>
46 #include <Message_ProgressScope.hxx>
47
48 IMPLEMENT_STANDARD_RTTIEXT(BinLDrivers_DocumentStorageDriver,PCDM_StorageDriver)
49
50 #define SHAPESECTION_POS (Standard_CString)"SHAPE_SECTION_POS:"
51 #define ENDSECTION_POS (Standard_CString)":"
52
53 //=======================================================================
54 //function : BinLDrivers_DocumentStorageDriver
55 //purpose  : Constructor
56 //=======================================================================
57
58 BinLDrivers_DocumentStorageDriver::BinLDrivers_DocumentStorageDriver()
59 {
60 }
61
62 //=======================================================================
63 //function : Write
64 //purpose  :
65 //=======================================================================
66
67 void BinLDrivers_DocumentStorageDriver::Write
68                           (const Handle(CDM_Document)&       theDocument,
69                            const TCollection_ExtendedString& theFileName,
70                            const Message_ProgressRange&      theRange)
71 {
72   SetIsError(Standard_False);
73   SetStoreStatus(PCDM_SS_OK);
74
75   myFileName = theFileName;
76
77   std::ofstream aFileStream;
78   OSD_OpenStream (aFileStream, theFileName, std::ios::out | std::ios::binary);
79
80   if (aFileStream.is_open() && aFileStream.good())
81   {
82     Write(theDocument, aFileStream, theRange);
83   }
84   else
85   {
86     SetIsError (Standard_True);
87     SetStoreStatus(PCDM_SS_WriteFailure);
88   }
89 }
90
91 //=======================================================================
92 //function : Write
93 //purpose  :
94 //=======================================================================
95
96 void BinLDrivers_DocumentStorageDriver::Write (const Handle(CDM_Document)&  theDoc, 
97                                                Standard_OStream&            theOStream,
98                                                const Message_ProgressRange& theRange)
99 {
100   myMsgDriver = theDoc->Application()->MessageDriver();
101   myMapUnsupported.Clear();
102   mySizesToWrite.Clear();
103
104   Handle(TDocStd_Document) aDoc = Handle(TDocStd_Document)::DownCast (theDoc);
105   if (aDoc.IsNull()) {
106     SetIsError(Standard_True);
107     SetStoreStatus(PCDM_SS_Doc_IsNull);
108   }
109   else {
110     // First pass: collect empty labels, assign IDs to the types
111     if (myDrivers.IsNull())
112       myDrivers = AttributeDrivers (myMsgDriver);
113     Handle(TDF_Data) aData = aDoc->GetData();
114     FirstPass (aData->Root());
115     if(aDoc->EmptyLabelsSavingMode()) 
116       myEmptyLabels.Clear(); // 
117
118 //  1. Write info section (including types table)
119     WriteInfoSection (aDoc, theOStream);
120
121     myTypesMap.Clear();
122     if (IsError())
123     {
124         SetStoreStatus(PCDM_SS_Info_Section_Error);
125         return;
126     }
127
128 //  2. Write the Table of Contents of Sections
129     const TDocStd_FormatVersion aDocVer = aDoc->StorageFormatVersion();
130     BinLDrivers_VectorOfDocumentSection::Iterator anIterS (mySections);
131     for (; anIterS.More(); anIterS.Next())
132       anIterS.ChangeValue().WriteTOC (theOStream, aDocVer);
133
134     EnableQuickPartWriting (myMsgDriver, IsQuickPart (aDocVer));
135     BinLDrivers_DocumentSection* aShapesSection = 0;
136     Standard_Boolean aQuickPart = IsQuickPart (aDocVer);
137     if (!aQuickPart)
138     {
139       // Shapes Section is the last one, it indicates the end of the table.
140       aShapesSection = new BinLDrivers_DocumentSection (SHAPESECTION_POS, Standard_False);
141       aShapesSection->WriteTOC (theOStream, aDocVer);
142     }
143     else
144     {
145       // End Section is the last one, it indicates the end of the table.
146       BinLDrivers_DocumentSection anEndSection (ENDSECTION_POS, Standard_False);
147       anEndSection.WriteTOC (theOStream, aDocVer);
148     }
149
150 //  3. Write document contents
151     // (Storage data to the stream)
152     myRelocTable.Clear();
153     myPAtt.Init();
154     if (aQuickPart)
155       myPAtt.SetOStream (theOStream); // for writing shapes data into the stream directly
156
157     Message_ProgressScope aPS(theRange, "Writing document", 3);
158
159 //  Write Doc structure
160     WriteSubTree (aData->Root(), theOStream, aQuickPart, aPS.Next()); // Doc is written
161     if (!aPS.More())
162     {
163       SetIsError(Standard_True);
164       SetStoreStatus(PCDM_SS_UserBreak);
165       return;
166     }
167
168 //  4. Write Shapes section
169     if (!aQuickPart)
170     {
171       WriteShapeSection (*aShapesSection, theOStream, aDocVer, aPS.Next());
172       delete aShapesSection;
173     }
174     else
175       Clear();
176
177     if (!aPS.More())
178     {
179       SetIsError (Standard_True);
180       SetStoreStatus (PCDM_SS_UserBreak);
181       return;
182     }
183
184     // Write application-defined sections
185     for (anIterS.Init (mySections); anIterS.More(); anIterS.Next()) {
186       BinLDrivers_DocumentSection& aSection = anIterS.ChangeValue();
187       const Standard_Size aSectionOffset = (Standard_Size) theOStream.tellp();
188       WriteSection (aSection.Name(), aDoc, theOStream);
189       aSection.Write (theOStream, aSectionOffset, aDocVer);
190     }
191
192 //  5. Write sizes along the file where it is needed for quick part mode
193     if (aQuickPart)
194       WriteSizes (theOStream);
195
196     // End of processing: close structures and check the status
197     myPAtt.Destroy();   // free buffer
198     myEmptyLabels.Clear();
199     myMapUnsupported.Clear();
200
201     if (!myRelocTable.Extent()) {
202       // No objects written
203 #ifdef OCCT_DEBUG
204       myMsgDriver->Send ("BinLDrivers_DocumentStorageDriver, no objects written", Message_Info);
205 #endif
206       SetIsError(Standard_True);
207       SetStoreStatus(PCDM_SS_No_Obj);
208     }
209     myRelocTable.Clear();
210     if (!aPS.More())
211     {
212       SetIsError(Standard_True);
213       SetStoreStatus(PCDM_SS_UserBreak);
214       return;
215     }
216     aPS.Next();
217     if (!theOStream) {
218       // A problem with the stream
219 #ifdef OCCT_DEBUG
220       TCollection_ExtendedString anErrorStr ("BinLDrivers_DocumentStorageDriver, Problem with the file stream, rdstate = ");
221       myMsgDriver->Send (anErrorStr + (Standard_Integer )theOStream.rdstate(), Message_Info);
222 #endif
223       SetIsError(Standard_True);
224       SetStoreStatus(PCDM_SS_WriteFailure);
225     }
226
227   }
228 }
229
230 //=======================================================================
231 //function : UnsupportedAttrMsg
232 //purpose  :
233 //=======================================================================
234
235 void BinLDrivers_DocumentStorageDriver::UnsupportedAttrMsg
236                         (const Handle(Standard_Type)& theType)
237 {
238 #ifdef OCCT_DEBUG
239   TCollection_ExtendedString aMsg
240     ("BinLDrivers_DocumentStorageDriver: warning: attribute driver for type ");
241 #endif
242   if (!myMapUnsupported.Contains(theType)) {
243     myMapUnsupported.Add(theType);
244 #ifdef OCCT_DEBUG
245     myMsgDriver->Send (aMsg + theType->Name() + " not found", Message_Info);
246 #endif
247   }
248 }
249
250 //=======================================================================
251 //function : WriteSubTree
252 //purpose  :
253 //=======================================================================
254
255 void BinLDrivers_DocumentStorageDriver::WriteSubTree
256                         (const TDF_Label&             theLabel,
257                          Standard_OStream&            theOS,
258                          const Standard_Boolean&      theQuickPart,
259                          const Message_ProgressRange& theRange)
260 {
261   // Skip empty labels
262   if (!myEmptyLabels.IsEmpty() && myEmptyLabels.First() == theLabel) {
263     myEmptyLabels.RemoveFirst();
264     return;
265   }
266   Message_ProgressScope aPS(theRange, "Writing sub tree", 2, true);
267   // Write label header: tag
268   Standard_Integer aTag = theLabel.Tag();
269 #ifdef DO_INVERSE
270   aTag = InverseInt (aTag);
271 #endif
272   theOS.write ((char*)&aTag, sizeof(Standard_Integer));
273
274   Handle(BinObjMgt_Position) aPosition;
275   if (theQuickPart)
276   {
277     aPosition = mySizesToWrite.Append (new BinObjMgt_Position (theOS));
278     aPosition->WriteSize (theOS, Standard_True);
279   }
280
281   // Write attributes
282   TDF_AttributeIterator itAtt (theLabel);
283   for ( ; itAtt.More() && theOS && aPS.More(); itAtt.Next()) {
284     const Handle(TDF_Attribute) tAtt = itAtt.Value();
285     const Handle(Standard_Type)& aType = tAtt->DynamicType();
286     // Get type ID and driver
287     Handle(BinMDF_ADriver) aDriver;
288     const Standard_Integer aTypeId = myDrivers->GetDriver (aType, aDriver);
289     if (aTypeId > 0) {
290       // Add source to relocation table
291       const Standard_Integer anId = myRelocTable.Add (tAtt);
292
293       // Create and fill data item
294       myPAtt.SetTypeId (aTypeId);
295       myPAtt.SetId (anId);
296       aDriver->Paste (tAtt, myPAtt, myRelocTable);
297       if (!myPAtt.StreamStart().IsNull())
298       {
299         Handle(BinObjMgt_Position) anAttrPosition = myPAtt.StreamStart();
300         anAttrPosition->StoreSize (theOS);
301         mySizesToWrite.Append (anAttrPosition);
302       }
303
304       // Write data to the stream -->!!!
305       theOS << myPAtt;
306     }
307 #ifdef OCCT_DEBUG
308     else
309       UnsupportedAttrMsg (aType);
310 #endif
311   }
312   if (!theOS) {
313     // Problem with the stream
314     return;
315   }
316   if (!aPS.More())
317   {
318     SetIsError(Standard_True);
319     SetStoreStatus(PCDM_SS_UserBreak);
320     return;
321   }
322   // Write the end attributes list marker
323   BinLDrivers_Marker anEndAttr = BinLDrivers_ENDATTRLIST;
324 #ifdef DO_INVERSE
325   anEndAttr = (BinLDrivers_Marker) InverseInt (anEndAttr);
326 #endif
327   theOS.write ((char*)&anEndAttr, sizeof(anEndAttr));
328
329   // Process sub-labels
330   TDF_ChildIterator itChld (theLabel);
331   for ( ; itChld.More(); itChld.Next())
332   {
333     const TDF_Label& aChildLab = itChld.Value();
334     if (!aPS.More())
335     {
336       SetIsError(Standard_True);
337       SetStoreStatus(PCDM_SS_UserBreak);
338       return;
339     }
340     WriteSubTree (aChildLab, theOS, theQuickPart, aPS.Next());
341   }
342   // Write the end label marker
343   BinLDrivers_Marker anEndLabel = BinLDrivers_ENDLABEL;
344 #ifdef DO_INVERSE
345   anEndLabel = (BinLDrivers_Marker)InverseInt (anEndLabel);
346 #endif
347   theOS.write ((char*)&anEndLabel, sizeof (anEndLabel));
348   if (theQuickPart)
349     aPosition->StoreSize (theOS);
350 }
351
352 //=======================================================================
353 //function : AttributeDrivers
354 //purpose  :
355 //=======================================================================
356
357 Handle(BinMDF_ADriverTable) BinLDrivers_DocumentStorageDriver::AttributeDrivers
358        (const Handle(Message_Messenger)& theMessageDriver)
359 {
360   return BinLDrivers::AttributeDrivers (theMessageDriver);
361 }
362
363 //=======================================================================
364 //function : FirstPassSubTree
365 //purpose  :
366 //=======================================================================
367
368 Standard_Boolean BinLDrivers_DocumentStorageDriver::FirstPassSubTree
369                          (const TDF_Label&                   L,
370                           TDF_LabelList&                     ListOfEmptyL)
371 {
372   // are there writable attributes on L ?
373   Standard_Boolean hasAttr = Standard_False;
374   TDF_AttributeIterator itAtt (L);
375   for ( ; itAtt.More(); itAtt.Next()) {
376     const Handle(Standard_Type)& aType = itAtt.Value()->DynamicType();
377     Handle(BinMDF_ADriver) aDriver;
378     // do not rely on a value returned by GetDriver here, because
379     // the IDs have not yet been assigned to the types
380     myDrivers->GetDriver (aType, aDriver);
381     if (!aDriver.IsNull()) {
382       hasAttr = Standard_True;
383       myTypesMap.Add (aType);
384     }
385 #ifdef OCCT_DEBUG
386     else
387       UnsupportedAttrMsg (aType);
388 #endif
389   }
390
391   // are there writable attributes on sub-labels ?
392   Standard_Boolean hasChildAttr = Standard_False;
393   TDF_LabelList emptyChildrenList;
394   TDF_ChildIterator itChld (L);
395   for ( ; itChld.More(); itChld.Next())
396   {
397     if (FirstPassSubTree (itChld.Value(), emptyChildrenList))
398       emptyChildrenList.Append( itChld.Value() );
399     else
400       hasChildAttr = Standard_True;
401   }
402
403   Standard_Boolean isEmpty = !(hasAttr || hasChildAttr);
404
405   if (!isEmpty)
406     ListOfEmptyL.Append( emptyChildrenList );
407
408   return isEmpty;
409 }
410
411 //=======================================================================
412 //function : FirstPass
413 //purpose  :
414 //=======================================================================
415
416 void BinLDrivers_DocumentStorageDriver::FirstPass
417                          (const TDF_Label& theRoot)
418 {
419   myTypesMap.Clear();
420   myEmptyLabels.Clear();
421
422   if (FirstPassSubTree( theRoot, myEmptyLabels))
423     myEmptyLabels.Append( theRoot );
424
425   myDrivers->AssignIds (myTypesMap);
426 }
427
428 //=======================================================================
429 //function : WriteInfoSection
430 //purpose  : Write info section using FSD_BinaryFile driver
431 //=======================================================================
432
433 #define START_TYPES "START_TYPES"
434 #define END_TYPES "END_TYPES"
435
436 void BinLDrivers_DocumentStorageDriver::WriteInfoSection 
437                          (const Handle(CDM_Document)&    theDoc,
438                           Standard_OStream&              theOStream)
439 {
440   // Magic number
441   theOStream.write (FSD_BinaryFile::MagicNumber(), strlen(FSD_BinaryFile::MagicNumber()));
442
443   FSD_FileHeader aHeader;
444
445   {
446     aHeader.testindian  = -1;
447     aHeader.binfo       = -1;
448     aHeader.einfo       = -1;
449     aHeader.bcomment    = -1;
450     aHeader.ecomment    = -1;
451     aHeader.btype       = -1;
452     aHeader.etype       = -1;
453     aHeader.broot       = -1;
454     aHeader.eroot       = -1;
455     aHeader.bref        = -1;
456     aHeader.eref        = -1;
457     aHeader.bdata       = -1;
458     aHeader.edata       = -1;
459   }
460
461   // aHeader.testindian
462   {
463     union {
464       char ti2[4];
465       Standard_Integer aResult;
466     } aWrapUnion;
467
468     aWrapUnion.ti2[0] = 1;
469     aWrapUnion.ti2[1] = 2;
470     aWrapUnion.ti2[2] = 3;
471     aWrapUnion.ti2[3] = 4;
472
473     aHeader.testindian = aWrapUnion.aResult;
474   }
475
476   // info section
477   aHeader.binfo = (Standard_Integer)theOStream.tellp();
478
479   // header section
480   aHeader.einfo = aHeader.binfo + FSD_BinaryFile::WriteHeader (theOStream, aHeader, Standard_True);
481   
482   // add format
483   Handle(Storage_Data) theData = new Storage_Data;
484   PCDM_ReadWriter::WriteFileFormat (theData, theDoc);
485   PCDM_ReadWriter::Writer()->WriteReferenceCounter (theData, theDoc);
486   PCDM_ReadWriter::Writer()->WriteReferences       (theData, theDoc, myFileName);
487   PCDM_ReadWriter::Writer()->WriteExtensions       (theData, theDoc);
488   PCDM_ReadWriter::Writer()->WriteVersion          (theData, theDoc);
489
490   // add the types table
491   theData->AddToUserInfo(START_TYPES);
492   for (Standard_Integer i = 1; i <= myTypesMap.Extent(); i++)
493   {
494     Handle(BinMDF_ADriver) aDriver = myDrivers->GetDriver(i);
495     if (!aDriver.IsNull())
496     {
497       const TCollection_AsciiString& aTypeName = aDriver->TypeName();
498       theData->AddToUserInfo (aTypeName);
499     }
500   }
501   theData->AddToUserInfo(END_TYPES);
502
503   Standard_Integer aObjNb = 1;
504   Standard_Integer aShemaVer = 1;
505
506   // Store the name and version of the application that has created the
507   // document.
508   theData->SetApplicationVersion(theDoc->Application()->Version());
509   theData->SetApplicationName(theDoc->Application()->Name());
510
511   Handle(TDocStd_Document) aDoc = Handle(TDocStd_Document)::DownCast(theDoc);
512   const Standard_Integer aDocVer = aDoc->StorageFormatVersion();
513   aHeader.einfo += FSD_BinaryFile::WriteInfo (theOStream,
514                                               aObjNb,
515                                               aDocVer,
516                                               Storage_Schema::ICreationDate(),
517                                               "", // schema name
518                                               aShemaVer,
519                                               theData->ApplicationName(),
520                                               theData->ApplicationVersion(),
521                                               theData->DataType(),
522                                               theData->UserInfo(),
523                                               Standard_True); // only count the size of the section
524
525   // calculate comment section
526   TColStd_SequenceOfExtendedString aComments;
527   theDoc->Comments(aComments);
528   for (Standard_Integer i = 1; i <= aComments.Length(); i++)
529   {
530     theData->AddToComments (aComments(i));
531   }
532
533   aHeader.bcomment = aHeader.einfo;
534   aHeader.ecomment = aHeader.bcomment + FSD_BinaryFile::WriteComment(theOStream, theData->Comments(), Standard_True);
535
536   aHeader.edata = aHeader.ecomment;
537
538   // write header information
539   FSD_BinaryFile::WriteHeader (theOStream, aHeader);
540
541   // write info section
542   FSD_BinaryFile::WriteInfo (theOStream,
543                              aObjNb,
544                              aDocVer,
545                              Storage_Schema::ICreationDate(),
546                              "", // schema name
547                              aShemaVer,
548                              theData->ApplicationName(),
549                              theData->ApplicationVersion(),
550                              theData->DataType(),
551                              theData->UserInfo());
552
553   // write the comments
554   FSD_BinaryFile::WriteComment(theOStream, theData->Comments());
555   
556 }
557
558 //=======================================================================
559 //function : AddSection
560 //purpose  :
561 //=======================================================================
562
563 void BinLDrivers_DocumentStorageDriver::AddSection
564                                 (const TCollection_AsciiString& theName,
565                                  const Standard_Boolean         isPostRead)
566 {
567   mySections.Append (BinLDrivers_DocumentSection (theName, isPostRead));
568 }
569
570 //=======================================================================
571 //function : WriteSection
572 //purpose  :
573 //=======================================================================
574
575 void BinLDrivers_DocumentStorageDriver::WriteSection
576                                 (const TCollection_AsciiString& /*theName*/,
577                                  const Handle(CDM_Document)&     /*theDocument*/,
578                                  Standard_OStream&              /*theOS*/)
579 {
580   // empty; should be redefined in subclasses
581 }
582
583 //=======================================================================
584 //function : WriteShapeSection
585 //purpose  : defines WriteShapeSection
586 //=======================================================================
587 void BinLDrivers_DocumentStorageDriver::WriteShapeSection
588                                 (BinLDrivers_DocumentSection&   theSection,
589                                  Standard_OStream&              theOS,
590                                  const TDocStd_FormatVersion    theDocVer,
591                                  const Message_ProgressRange& /*theRange*/)
592 {
593   const Standard_Size aShapesSectionOffset = (Standard_Size) theOS.tellp();
594   theSection.Write (theOS, aShapesSectionOffset, theDocVer);
595 }
596
597 //=======================================================================
598 //function : IsQuickPart
599 //purpose  : Return true if document should be stored in quick mode for partial reading
600 //=======================================================================
601 Standard_Boolean BinLDrivers_DocumentStorageDriver::IsQuickPart (const Standard_Integer theVersion) const
602 {
603   return theVersion >= TDocStd_FormatVersion_VERSION_12;
604 }
605
606 //=======================================================================
607 //function : Clear
608 //purpose  : 
609 //=======================================================================
610 void BinLDrivers_DocumentStorageDriver::Clear()
611 {
612   // empty; should be redefined in subclasses
613 }
614
615
616 //=======================================================================
617 //function : WriteSizes
618 //purpose  : 
619 //=======================================================================
620 void BinLDrivers_DocumentStorageDriver::WriteSizes (Standard_OStream& theOS)
621 {
622   NCollection_List<Handle(BinObjMgt_Position)>::Iterator anIter (mySizesToWrite);
623   for (; anIter.More() && theOS; anIter.Next())
624     anIter.Value()->WriteSize (theOS);
625   mySizesToWrite.Clear();
626 }