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