0031353: TDocStd_Application does not have api to set 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_ProgressSentry.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 Handle(Message_ProgressIndicator)& theProgress)
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, theProgress);
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 Handle(Message_ProgressIndicator)& theProgress)
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_ProgressSentry aPS(theProgress, "Writing document", 0, 3, 1);
144
145 //  Write Doc structure
146     WriteSubTree (aData->Root(), theOStream, theProgress); // Doc is written
147     if (!aPS.More())
148     {
149       SetIsError(Standard_True);
150       SetStoreStatus(PCDM_SS_UserBreak);
151       return;
152     }
153     aPS.Next();
154 //  4. Write Shapes section
155     WriteShapeSection (aShapesSection, theOStream, theProgress);
156     if (!aPS.More())
157     {
158        SetIsError(Standard_True);
159        SetStoreStatus(PCDM_SS_UserBreak);
160        return;
161     }
162     aPS.Next();
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 Handle(Message_ProgressIndicator)& theProgress)
234 {
235   // Skip empty labels
236   if (!myEmptyLabels.IsEmpty() && myEmptyLabels.First() == theLabel) {
237     myEmptyLabels.RemoveFirst();
238     return;
239   }
240   Message_ProgressSentry aPS(theProgress, "Writing sub tree", 0, 2, 1, 1);
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     aPS.Next();
302     WriteSubTree (aChildLab, theOS, theProgress);
303   }
304
305   // Write the end label marker
306   BinLDrivers_Marker anEndLabel = BinLDrivers_ENDLABEL;
307 #if DO_INVERSE
308   anEndLabel = (BinLDrivers_Marker) InverseInt (anEndLabel);
309 #endif
310   theOS.write ((char*)&anEndLabel, sizeof(anEndLabel));
311
312 }
313
314 //=======================================================================
315 //function : AttributeDrivers
316 //purpose  :
317 //=======================================================================
318
319 Handle(BinMDF_ADriverTable) BinLDrivers_DocumentStorageDriver::AttributeDrivers
320        (const Handle(Message_Messenger)& theMessageDriver)
321 {
322   return BinLDrivers::AttributeDrivers (theMessageDriver);
323 }
324
325 //=======================================================================
326 //function : FirstPassSubTree
327 //purpose  :
328 //=======================================================================
329
330 Standard_Boolean BinLDrivers_DocumentStorageDriver::FirstPassSubTree
331                          (const TDF_Label&                   L,
332                           TDF_LabelList&                     ListOfEmptyL)
333 {
334   // are there writable attributes on L ?
335   Standard_Boolean hasAttr = Standard_False;
336   TDF_AttributeIterator itAtt (L);
337   for ( ; itAtt.More(); itAtt.Next()) {
338     const Handle(Standard_Type)& aType = itAtt.Value()->DynamicType();
339     Handle(BinMDF_ADriver) aDriver;
340     // do not rely on a value returned by GetDriver here, because
341     // the IDs have not yet been assigned to the types
342     myDrivers->GetDriver (aType, aDriver);
343     if (!aDriver.IsNull()) {
344       hasAttr = Standard_True;
345       myTypesMap.Add (aType);
346     }
347 #ifdef OCCT_DEBUG
348     else
349       UnsupportedAttrMsg (aType);
350 #endif
351   }
352
353   // are there writable attributes on sub-labels ?
354   Standard_Boolean hasChildAttr = Standard_False;
355   TDF_LabelList emptyChildrenList;
356   TDF_ChildIterator itChld (L);
357   for ( ; itChld.More(); itChld.Next())
358   {
359     if (FirstPassSubTree (itChld.Value(), emptyChildrenList))
360       emptyChildrenList.Append( itChld.Value() );
361     else
362       hasChildAttr = Standard_True;
363   }
364
365   Standard_Boolean isEmpty = !(hasAttr || hasChildAttr);
366
367   if (!isEmpty)
368     ListOfEmptyL.Append( emptyChildrenList );
369
370   return isEmpty;
371 }
372
373 //=======================================================================
374 //function : FirstPass
375 //purpose  :
376 //=======================================================================
377
378 void BinLDrivers_DocumentStorageDriver::FirstPass
379                          (const TDF_Label& theRoot)
380 {
381   myTypesMap.Clear();
382   myEmptyLabels.Clear();
383
384   if (FirstPassSubTree( theRoot, myEmptyLabels))
385     myEmptyLabels.Append( theRoot );
386
387   myDrivers->AssignIds (myTypesMap);
388 }
389
390 //=======================================================================
391 //function : WriteInfoSection
392 //purpose  : Write info secton using FSD_BinaryFile driver
393 //=======================================================================
394
395 #define START_TYPES "START_TYPES"
396 #define END_TYPES "END_TYPES"
397
398 void BinLDrivers_DocumentStorageDriver::WriteInfoSection 
399                          (const Handle(CDM_Document)&    theDoc,
400                           Standard_OStream&              theOStream)
401 {
402   // Magic number
403   theOStream.write (FSD_BinaryFile::MagicNumber(), strlen(FSD_BinaryFile::MagicNumber()));
404
405   FSD_FileHeader aHeader;
406
407   {
408     aHeader.testindian  = -1;
409     aHeader.binfo       = -1;
410     aHeader.einfo       = -1;
411     aHeader.bcomment    = -1;
412     aHeader.ecomment    = -1;
413     aHeader.btype       = -1;
414     aHeader.etype       = -1;
415     aHeader.broot       = -1;
416     aHeader.eroot       = -1;
417     aHeader.bref        = -1;
418     aHeader.eref        = -1;
419     aHeader.bdata       = -1;
420     aHeader.edata       = -1;
421   }
422
423   // aHeader.testindian
424   {
425     union {
426       char ti2[4];
427       Standard_Integer aResult;
428     } aWrapUnion;
429
430     aWrapUnion.ti2[0] = 1;
431     aWrapUnion.ti2[1] = 2;
432     aWrapUnion.ti2[2] = 3;
433     aWrapUnion.ti2[3] = 4;
434
435     aHeader.testindian = aWrapUnion.aResult;
436   }
437
438   // info section
439   aHeader.binfo = (Standard_Integer)theOStream.tellp();
440
441   // header section
442   aHeader.einfo = aHeader.binfo + FSD_BinaryFile::WriteHeader (theOStream, aHeader, Standard_True);
443   
444   // add format
445   Handle(Storage_Data) theData = new Storage_Data;
446   PCDM_ReadWriter::WriteFileFormat (theData, theDoc);
447   PCDM_ReadWriter::Writer()->WriteReferenceCounter (theData, theDoc);
448   PCDM_ReadWriter::Writer()->WriteReferences       (theData, theDoc, myFileName);
449   PCDM_ReadWriter::Writer()->WriteExtensions       (theData, theDoc);
450   PCDM_ReadWriter::Writer()->WriteVersion          (theData, theDoc);
451
452   // add the types table
453   theData->AddToUserInfo(START_TYPES);
454   for (Standard_Integer i = 1; i <= myTypesMap.Extent(); i++)
455   {
456     Handle(BinMDF_ADriver) aDriver = myDrivers->GetDriver(i);
457     if (!aDriver.IsNull())
458     {
459       const TCollection_AsciiString& aTypeName = aDriver->TypeName();
460       theData->AddToUserInfo (aTypeName);
461     }
462   }
463   theData->AddToUserInfo(END_TYPES);
464
465   Standard_Integer aObjNb = 1;
466   Standard_Integer aShemaVer = 1;
467
468   // Store the name and version of the application that has created the
469   // document.
470   theData->SetApplicationVersion(theDoc->Application()->Version());
471   theData->SetApplicationName(theDoc->Application()->Name());
472
473   aHeader.einfo += FSD_BinaryFile::WriteInfo (theOStream,
474                                               aObjNb,
475                                               BinLDrivers::StorageVersion(),
476                                               Storage_Schema::ICreationDate(),
477                                               "", // schema name
478                                               aShemaVer,
479                                               theData->ApplicationName(),
480                                               theData->ApplicationVersion(),
481                                               theData->DataType(),
482                                               theData->UserInfo(),
483                                               Standard_True); // only count the size of the section
484
485   // calculate comment section
486   TColStd_SequenceOfExtendedString aComments;
487   theDoc->Comments(aComments);
488   for (Standard_Integer i = 1; i <= aComments.Length(); i++)
489   {
490     theData->AddToComments (aComments(i));
491   }
492
493   aHeader.bcomment = aHeader.einfo;
494   aHeader.ecomment = aHeader.bcomment + FSD_BinaryFile::WriteComment(theOStream, theData->Comments(), Standard_True);
495
496   aHeader.edata = aHeader.ecomment;
497
498   // write header information
499   FSD_BinaryFile::WriteHeader (theOStream, aHeader);
500
501   // write info section
502   FSD_BinaryFile::WriteInfo (theOStream,
503                              aObjNb,
504                              BinLDrivers::StorageVersion(),
505                              Storage_Schema::ICreationDate(),
506                              "", // schema name
507                              aShemaVer,
508                              theData->ApplicationName(),
509                              theData->ApplicationVersion(),
510                              theData->DataType(),
511                              theData->UserInfo());
512
513   // write the comments
514   FSD_BinaryFile::WriteComment(theOStream, theData->Comments());
515   
516 }
517
518 //=======================================================================
519 //function : AddSection
520 //purpose  :
521 //=======================================================================
522
523 void BinLDrivers_DocumentStorageDriver::AddSection
524                                 (const TCollection_AsciiString& theName,
525                                  const Standard_Boolean         isPostRead)
526 {
527   mySections.Append (BinLDrivers_DocumentSection (theName, isPostRead));
528 }
529
530 //=======================================================================
531 //function : WriteSection
532 //purpose  :
533 //=======================================================================
534
535 void BinLDrivers_DocumentStorageDriver::WriteSection
536                                 (const TCollection_AsciiString& /*theName*/,
537                                  const Handle(CDM_Document)&     /*theDocument*/,
538                                  Standard_OStream&              /*theOS*/)
539 {
540   // empty; should be redefined in subclasses
541 }
542
543 //=======================================================================
544 //function : WriteShapeSection
545 //purpose  : defines WriteShapeSection
546 //=======================================================================
547 void BinLDrivers_DocumentStorageDriver::WriteShapeSection
548                                 (BinLDrivers_DocumentSection&   theSection,
549                                  Standard_OStream&              theOS,
550                                  const Handle(Message_ProgressIndicator)& /*theProgress*/)
551 {
552   const Standard_Size aShapesSectionOffset = (Standard_Size) theOS.tellp();
553   theSection.Write (theOS, aShapesSectionOffset);
554 }