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