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