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