a36a41a59f010822c8ab0378f226bad5ecc0062b
[occt.git] / src / BinLDrivers / BinLDrivers_DocumentRetrievalDriver.cxx
1 // Created on: 2002-10-31
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_DocumentRetrievalDriver.hxx>
19 #include <BinLDrivers_DocumentSection.hxx>
20 #include <BinLDrivers_Marker.hxx>
21 #include <BinMDataStd.hxx>
22 #include <BinMDF_ADriver.hxx>
23 #include <BinMDF_ADriverTable.hxx>
24 #include <BinObjMgt_Persistent.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_Document.hxx>
32 #include <PCDM_ReadWriter.hxx>
33 #include <Standard_ErrorHandler.hxx>
34 #include <Standard_Stream.hxx>
35 #include <Standard_Type.hxx>
36 #include <Storage_HeaderData.hxx>
37 #include <Storage_Schema.hxx>
38 #include <TCollection_AsciiString.hxx>
39 #include <TCollection_ExtendedString.hxx>
40 #include <TDF_Attribute.hxx>
41 #include <TDF_Data.hxx>
42 #include <TDF_Label.hxx>
43 #include <TDocStd_Document.hxx>
44 #include <TDocStd_FormatVersion.hxx>
45 #include <TDocStd_Owner.hxx>
46 #include <Message_ProgressScope.hxx>
47
48
49 IMPLEMENT_STANDARD_RTTIEXT(BinLDrivers_DocumentRetrievalDriver,PCDM_RetrievalDriver)
50
51 #define SHAPESECTION_POS "SHAPE_SECTION_POS:"
52 #define SIZEOFSHAPELABEL  18
53
54 #define DATATYPE_MIGRATION
55 //#define DATATYPE_MIGRATION_DEB
56 //=======================================================================
57 //function : BinLDrivers_DocumentRetrievalDriver
58 //purpose  : Constructor
59 //=======================================================================
60
61 BinLDrivers_DocumentRetrievalDriver::BinLDrivers_DocumentRetrievalDriver ()
62 {
63   myReaderStatus = PCDM_RS_OK;
64 }
65
66 //=======================================================================
67 //function : Read
68 //purpose  :
69 //=======================================================================
70 void BinLDrivers_DocumentRetrievalDriver::Read
71                          (const TCollection_ExtendedString& theFileName,
72                           const Handle(CDM_Document)&       theNewDocument,
73                           const Handle(CDM_Application)&    theApplication,
74                           const Message_ProgressRange&      theRange)
75 {
76   std::ifstream aFileStream;
77   OSD_OpenStream (aFileStream, theFileName, std::ios::in | std::ios::binary);
78
79   if (aFileStream.is_open() && aFileStream.good())
80   {
81     Handle(Storage_Data) dData;
82     TCollection_ExtendedString aFormat = PCDM_ReadWriter::FileFormat (aFileStream, dData);
83
84     Read(aFileStream, dData, theNewDocument, theApplication, theRange);
85     if (!theRange.More())
86     {
87       myReaderStatus = PCDM_RS_UserBreak;
88       return;
89     }
90   }
91   else
92   {
93     myReaderStatus = PCDM_RS_OpenError;
94   }
95 }
96
97 #define MODIFICATION_COUNTER "MODIFICATION_COUNTER: "
98 #define REFERENCE_COUNTER "REFERENCE_COUNTER: "
99
100 #define START_TYPES "START_TYPES"
101 #define END_TYPES "END_TYPES"
102
103 //=======================================================================
104 //function : Read
105 //purpose  :
106 //=======================================================================
107 void BinLDrivers_DocumentRetrievalDriver::Read (Standard_IStream&               theIStream,
108                                                 const Handle(Storage_Data)&     theStorageData,
109                                                 const Handle(CDM_Document)&     theDoc,
110                                                 const Handle(CDM_Application)&  theApplication,
111                                                 const Message_ProgressRange&    theRange)
112 {
113   myReaderStatus = PCDM_RS_DriverFailure;
114   myMsgDriver = theApplication -> MessageDriver();
115
116   const TCollection_ExtendedString aMethStr
117     ("BinLDrivers_DocumentRetrievalDriver: ");
118
119   Handle(TDocStd_Document) aDoc =
120     Handle(TDocStd_Document)::DownCast(theDoc);
121   if (aDoc.IsNull()) {
122 #ifdef OCCT_DEBUG
123     myMsgDriver->Send (aMethStr + "error: null document", Message_Fail);
124 #endif
125     myReaderStatus = PCDM_RS_NoDocument;
126     return;
127   }
128
129   // 1. the information section
130   Handle(Storage_HeaderData) aHeaderData;
131   
132   if (!theStorageData.IsNull())
133   {
134     aHeaderData = theStorageData->HeaderData();
135   }
136
137   if (!aHeaderData.IsNull())
138   {
139     for (Standard_Integer i = 1; i <= aHeaderData->UserInfo().Length(); i++)
140     {
141       const TCollection_AsciiString& aLine = aHeaderData->UserInfo().Value(i);
142
143       if(aLine.Search(REFERENCE_COUNTER) != -1)
144       {
145         theDoc->SetReferenceCounter (aLine.Token(" ", 2).IntegerValue());
146       }
147       else if(aLine.Search(MODIFICATION_COUNTER) != -1)
148       {
149         theDoc->SetModifications (aLine.Token(" ", 2).IntegerValue());
150       }
151     }
152   }
153
154   // 1.a Version of writer
155   if (!aHeaderData->StorageVersion().IsIntegerValue()) {
156     // file has no format version
157     myMsgDriver->Send (aMethStr + "error: file has no format version", Message_Fail);
158     myReaderStatus = PCDM_RS_FormatFailure;
159     return;
160   }
161   Standard_Integer aFileVer = aHeaderData->StorageVersion().IntegerValue();
162   Standard_Integer aCurrVer = TDocStd_Document::CurrentStorageFormatVersion();
163   // maintain one-way compatibility starting from version 2+
164   if (!CheckDocumentVersion(aFileVer, aCurrVer)) {
165     myReaderStatus = PCDM_RS_NoVersion;
166     // file was written with another version
167     myMsgDriver->Send (aMethStr + "error: wrong file version: " +
168                  aHeaderData->StorageVersion() + " while current is " +
169                  TDocStd_Document::CurrentStorageFormatVersion(), Message_Fail);
170     return;
171   }
172
173   // 1.b Retrieve the Types table
174   TColStd_SequenceOfAsciiString aTypeNames; //Sequence of types in file
175   const TColStd_SequenceOfAsciiString& aUserInfo = aHeaderData->UserInfo();
176   Standard_Boolean begin = Standard_False;
177   Standard_Integer i;
178   for (i=1; i <= aUserInfo.Length(); i++) {
179     //const TCollection_AsciiString& aStr = aUserInfo(i);
180     TCollection_AsciiString aStr = aUserInfo(i);
181     if (aStr == START_TYPES)
182       begin = Standard_True;
183     else if (aStr == END_TYPES)
184       break;
185     else if (begin) {
186       if ( aFileVer < TDocStd_FormatVersion_VERSION_8) {
187 #ifdef DATATYPE_MIGRATION
188         TCollection_AsciiString  newName;       
189         if(Storage_Schema::CheckTypeMigration(aStr, newName)) {
190 #ifdef OCCT_DEBUG
191           std::cout << "CheckTypeMigration:OldType = " <<aStr << " Len = "<<aStr.Length()<<std::endl;
192           std::cout << "CheckTypeMigration:NewType = " <<newName  << " Len = "<< newName.Length()<<std::endl;
193 #endif
194           aStr = newName;
195         }
196 #endif  
197       } 
198       aTypeNames.Append (aStr);    
199     }
200   }
201   if (myDrivers.IsNull())
202     myDrivers = AttributeDrivers (myMsgDriver);
203   myDrivers->AssignIds (aTypeNames); 
204
205   // recognize types not supported by drivers
206   myMapUnsupported.Clear();
207   for (i=1; i <= aTypeNames.Length(); i++)
208     if (myDrivers->GetDriver(i).IsNull()) 
209       myMapUnsupported.Add(i);
210   if (!myMapUnsupported.IsEmpty()) {
211     myMsgDriver->Send (aMethStr + "warning: "
212                   "the following attributes have no driver:", Message_Warning);
213     for (i=1; i <= aTypeNames.Length(); i++)
214       if (myMapUnsupported.Contains(i))
215         myMsgDriver->Send (aTypeNames(i), Message_Warning);
216   }
217
218   // 2. Read document contents
219   // 2a. Retrieve data from the stream:
220   myRelocTable.Clear();
221   myRelocTable.SetHeaderData(aHeaderData);
222   mySections.Clear();
223   myPAtt.Init();
224   Handle(TDF_Data) aData = new TDF_Data();
225   std::streampos aDocumentPos = -1;
226
227   Message_ProgressScope aPS(theRange, "Reading data", 3);
228
229   // 2b. Read the TOC of Sections
230   if (aFileVer >= TDocStd_FormatVersion_VERSION_3) {
231     BinLDrivers_DocumentSection aSection;
232     do {
233       BinLDrivers_DocumentSection::ReadTOC (aSection, theIStream, aFileVer);
234       mySections.Append(aSection);
235     } while(!aSection.Name().IsEqual((Standard_CString)SHAPESECTION_POS) && !theIStream.eof());
236
237     if (theIStream.eof()) {
238       // There is no shape section in the file.
239       myMsgDriver->Send (aMethStr + "error: shape section is not found", Message_Fail);
240       myReaderStatus = PCDM_RS_ReaderException;
241       return;
242     }
243
244     aDocumentPos = theIStream.tellg(); // position of root label
245
246     BinLDrivers_VectorOfDocumentSection::Iterator anIterS (mySections);
247     for (; anIterS.More(); anIterS.Next()) {
248       BinLDrivers_DocumentSection& aCurSection = anIterS.ChangeValue();
249       if (aCurSection.IsPostRead() == Standard_False) {
250         theIStream.seekg ((std::streampos) aCurSection.Offset());
251         if (aCurSection.Name().IsEqual ((Standard_CString)SHAPESECTION_POS))
252         {
253           ReadShapeSection (aCurSection, theIStream, false, aPS.Next());
254           if (!aPS.More())
255           {
256             myReaderStatus = PCDM_RS_UserBreak;
257             return;
258           }
259         }
260         else
261           ReadSection (aCurSection, theDoc, theIStream);
262       }
263     }
264   } else { //aFileVer < 3
265     aDocumentPos = theIStream.tellg(); // position of root label
266
267     // retrieve SHAPESECTION_POS string
268     char aShapeSecLabel[SIZEOFSHAPELABEL + 1];
269     aShapeSecLabel[SIZEOFSHAPELABEL] = 0x00;
270     theIStream.read ((char*)&aShapeSecLabel, SIZEOFSHAPELABEL);// SHAPESECTION_POS
271     TCollection_AsciiString aShapeLabel(aShapeSecLabel);
272     // detect if a file was written in old fashion (version 2 without shapes)
273     // and if so then skip reading ShapeSection
274     if (aShapeLabel.Length() > 0) {
275       // version 2+(with shapes) and higher goes here
276       if(aShapeLabel.Length() <= 0 || aShapeLabel != SHAPESECTION_POS) {
277         myMsgDriver->Send (aMethStr + "error: Format failure", Message_Fail);
278         myReaderStatus = PCDM_RS_FormatFailure;
279         return;
280       }
281
282       // retrieve ShapeSection Position
283       Standard_Integer aShapeSectionPos; // go to ShapeSection
284       theIStream.read ((char*)&aShapeSectionPos, sizeof(Standard_Integer));
285
286 #if DO_INVERSE
287       aShapeSectionPos = InverseInt (aShapeSectionPos);
288 #endif
289 #ifdef OCCT_DEBUG
290       std::cout <<"aShapeSectionPos = " <<aShapeSectionPos <<std::endl;
291 #endif
292       if(aShapeSectionPos) { 
293         aDocumentPos = theIStream.tellg();
294         theIStream.seekg((std::streampos) aShapeSectionPos);
295
296         CheckShapeSection(aShapeSectionPos, theIStream);
297         // Read Shapes
298         BinLDrivers_DocumentSection aCurSection;
299         ReadShapeSection (aCurSection, theIStream, Standard_False, aPS.Next());
300         if (!aPS.More())
301         {
302           myReaderStatus = PCDM_RS_UserBreak;
303           return;
304         }
305       }
306     }
307   } // end of reading Sections or shape section
308
309   // Return to read of the Document structure
310   theIStream.seekg(aDocumentPos);
311
312   // read the header (tag) of the root label
313   Standard_Integer aTag;
314   theIStream.read ((char*)&aTag, sizeof(Standard_Integer));
315
316   // read sub-tree of the root label
317   Standard_Integer nbRead = ReadSubTree (theIStream, aData->Root(), aPS.Next());
318   if (!aPS.More()) 
319   {
320     myReaderStatus = PCDM_RS_UserBreak;
321     return;
322   }
323   
324   Clear();
325   if (!aPS.More())
326   {
327     myReaderStatus = PCDM_RS_UserBreak;
328     return;
329   }
330   aPS.Next();
331     
332   if (nbRead > 0) {
333     // attach data to the document
334     aDoc->SetData (aData);
335     TDocStd_Owner::SetDocument (aData, aDoc);
336     aDoc->SetComments(aHeaderData->Comments());
337     myReaderStatus = PCDM_RS_OK;
338   }
339
340   // Read Sections (post-reading type)
341   if (aFileVer >= TDocStd_FormatVersion_VERSION_3) {
342     BinLDrivers_VectorOfDocumentSection::Iterator aSectIter (mySections);
343     for (; aSectIter.More(); aSectIter.Next()) {
344       BinLDrivers_DocumentSection& aCurSection = aSectIter.ChangeValue();
345       if (aCurSection.IsPostRead()) {
346         theIStream.seekg ((std::streampos) aCurSection.Offset());
347         ReadSection (aCurSection, theDoc, theIStream); 
348       }
349     }
350   }
351 }
352
353 //=======================================================================
354 //function : ReadSubTree
355 //purpose  :
356 //=======================================================================
357
358 Standard_Integer BinLDrivers_DocumentRetrievalDriver::ReadSubTree
359                          (Standard_IStream& theIS,
360                           const TDF_Label&  theLabel,
361                           const Message_ProgressRange& theRange)
362 {
363   Standard_Integer nbRead = 0;
364   TCollection_ExtendedString aMethStr
365     ("BinLDrivers_DocumentRetrievalDriver: ");
366
367   Message_ProgressScope aPS(theRange, "Reading sub tree", 2, true);
368
369   // Read attributes:
370   theIS >> myPAtt;
371   while (theIS && myPAtt.TypeId() > 0 &&             // not an end marker ?
372          myPAtt.Id() > 0 &&                          // not a garbage ?
373          !theIS.eof())
374   {
375     if (!aPS.More())
376     {
377       myReaderStatus = PCDM_RS_UserBreak;
378       return -1;
379     }
380         
381     // get a driver according to TypeId
382     Handle(BinMDF_ADriver) aDriver = myDrivers->GetDriver (myPAtt.TypeId());
383     if (!aDriver.IsNull()) {
384       // create transient attribute
385       nbRead++;
386       Standard_Integer anID = myPAtt.Id();
387       Handle(TDF_Attribute) tAtt;
388       Standard_Boolean isBound = myRelocTable.IsBound(anID);
389       if (isBound)
390         tAtt = Handle(TDF_Attribute)::DownCast(myRelocTable.Find(anID));
391       else
392         tAtt = aDriver->NewEmpty();
393
394       if (tAtt->Label().IsNull())
395       {
396         try
397         {
398           theLabel.AddAttribute (tAtt);
399         }
400         catch (const Standard_DomainError&)
401         {
402           // For attributes that can have arbitrary GUID (e.g. TDataStd_Integer), exception
403           // will be raised in valid case if attribute of that type with default GUID is already
404           // present  on the same label; the reason is that actual GUID will be read later.
405           // To avoid this, set invalid (null) GUID to the newly added attribute (see #29669)
406           static const Standard_GUID fbidGuid;
407           tAtt->SetID (fbidGuid);
408           theLabel.AddAttribute (tAtt);
409         }
410       }
411       else
412         myMsgDriver->Send (aMethStr +
413                      "warning: attempt to attach attribute " +
414                      aDriver->TypeName() + " to a second label", Message_Warning);
415
416       Standard_Boolean ok = aDriver->Paste (myPAtt, tAtt, myRelocTable);
417       if (!ok) {
418         // error converting persistent to transient
419         myMsgDriver->Send (aMethStr + "warning: failure reading attribute " +
420                       aDriver->TypeName(), Message_Warning);
421       }
422       else if (!isBound)
423         myRelocTable.Bind (anID, tAtt);
424     }
425     else if (!myMapUnsupported.Contains(myPAtt.TypeId()))
426       myMsgDriver->Send (aMethStr + "warning: type ID not registered in header: "
427                     + myPAtt.TypeId(), Message_Warning);
428
429     // read next attribute
430     theIS >> myPAtt;
431   }
432   if (!theIS || myPAtt.TypeId() != BinLDrivers_ENDATTRLIST) {
433     // unexpected EOF or garbage data
434     myMsgDriver->Send (aMethStr + "error: unexpected EOF or garbage data", Message_Fail);
435     myReaderStatus = PCDM_RS_UnrecognizedFileFormat;
436     return -1;
437   }
438
439   // Read children:
440   // read the tag of a child label
441   Standard_Integer aTag = BinLDrivers_ENDLABEL;
442   theIS.read ((char*) &aTag, sizeof(Standard_Integer));
443 #if DO_INVERSE
444   aTag = InverseInt (aTag);
445 #endif
446   
447   while (theIS && aTag >= 0 && !theIS.eof()) { // not an end marker ?
448     // create sub-label
449     TDF_Label aLab = theLabel.FindChild (aTag, Standard_True);
450     if (!aPS.More())
451     {
452       myReaderStatus = PCDM_RS_UserBreak;
453       return -1;
454     }
455
456
457     // read sub-tree
458     Standard_Integer nbSubRead = ReadSubTree (theIS, aLab, aPS.Next());
459     // check for error
460     if (nbSubRead == -1)
461       return -1;
462     nbRead += nbSubRead;
463
464     // read the tag of the next child
465     theIS.read ((char*) &aTag, sizeof(Standard_Integer));
466 #if DO_INVERSE
467     aTag = InverseInt (aTag);
468 #endif
469   }
470
471   if (aTag != BinLDrivers_ENDLABEL) {
472     // invalid end label marker
473     myMsgDriver->Send (aMethStr + "error: invalid end label marker", Message_Fail);
474     myReaderStatus = PCDM_RS_UnrecognizedFileFormat;
475     return -1;
476   }
477
478   return nbRead;
479 }
480
481 //=======================================================================
482 //function : AttributeDrivers
483 //purpose  :
484 //=======================================================================
485
486 Handle(BinMDF_ADriverTable) BinLDrivers_DocumentRetrievalDriver::AttributeDrivers
487        (const Handle(Message_Messenger)& theMessageDriver)
488 {
489   return BinLDrivers::AttributeDrivers (theMessageDriver);
490 }
491
492 //=======================================================================
493 //function : ReadSection
494 //purpose  : 
495 //=======================================================================
496
497 void BinLDrivers_DocumentRetrievalDriver::ReadSection
498                                 (BinLDrivers_DocumentSection& /*theSection*/,
499                                  const Handle(CDM_Document)&  /*theDocument*/,
500                                  Standard_IStream&            /*theIS*/)
501 {
502   // empty; should be redefined in subclasses
503 }
504
505 //=======================================================================
506 //function : ReadShapeSection
507 //purpose  : 
508 //=======================================================================
509
510 void BinLDrivers_DocumentRetrievalDriver::ReadShapeSection
511                               (BinLDrivers_DocumentSection& theSection,
512                                Standard_IStream&            /*theIS*/,
513                                const Standard_Boolean isMess,
514                                const Message_ProgressRange &/*theRange*/)
515
516 {
517   if(isMess && theSection.Length()) {
518     const TCollection_ExtendedString aMethStr ("BinLDrivers_DocumentRetrievalDriver: ");
519     myMsgDriver->Send (aMethStr + "warning: Geometry is not supported by Lite schema. ", Message_Warning);
520   }
521 }
522
523 //=======================================================================
524 //function : CheckShapeSection
525 //purpose  : 
526 //=======================================================================
527 void BinLDrivers_DocumentRetrievalDriver::CheckShapeSection(
528                                           const Storage_Position& ShapeSectionPos, 
529                                           Standard_IStream& IS)
530 {
531   if (!IS.eof())
532   {
533     const std::streamoff endPos = IS.rdbuf()->pubseekoff(0L, std::ios_base::end, std::ios_base::in);
534 #ifdef OCCT_DEBUG
535     std::cout << "endPos = " << endPos <<std::endl;
536 #endif
537     if(ShapeSectionPos != endPos) {
538       const TCollection_ExtendedString aMethStr ("BinLDrivers_DocumentRetrievalDriver: ");
539       myMsgDriver->Send (aMethStr + "warning: Geometry is not supported by Lite schema. ", Message_Warning);
540     }
541   }
542 }
543
544 //=======================================================================
545 //function : Clear
546 //purpose  : 
547 //=======================================================================
548 void BinLDrivers_DocumentRetrievalDriver::Clear()
549 {
550   myPAtt.Destroy();    // free buffer
551   myRelocTable.Clear();
552   myMapUnsupported.Clear();
553 }
554
555 //=======================================================================
556 //function : CheckDocumentVersion
557 //purpose  : 
558 //=======================================================================
559 Standard_Boolean BinLDrivers_DocumentRetrievalDriver::CheckDocumentVersion(
560                                                           const Standard_Integer theFileVersion,
561                                                           const Standard_Integer theCurVersion)
562 {
563   if (theFileVersion < TDocStd_FormatVersion_VERSION_2 || theFileVersion > theCurVersion) {
564     // file was written with another version
565     return Standard_False;
566   }
567   return Standard_True;
568 }