0028691: Storage of Ocaf documents in XML file format in old document version
[occt.git] / src / XmlLDrivers / XmlLDrivers_DocumentStorageDriver.cxx
1 // Created on: 2001-07-09
2 // Created by: Julia DOROVSKIKH
3 // Copyright (c) 2001-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 <CDM_Application.hxx>
18 #include <CDM_Document.hxx>
19 #include <CDM_MessageDriver.hxx>
20 #include <CDM_NullMessageDriver.hxx>
21 #include <LDOM_DocumentType.hxx>
22 #include <LDOM_LDOMImplementation.hxx>
23 #include <LDOM_XmlWriter.hxx>
24 #include <OSD_Environment.hxx>
25 #include <OSD_File.hxx>
26 #include <OSD_OpenFile.hxx>
27 #include <PCDM.hxx>
28 #include <PCDM_ReadWriter.hxx>
29 #include <Standard_ErrorHandler.hxx>
30 #include <Standard_Failure.hxx>
31 #include <Standard_Type.hxx>
32 #include <Storage_Data.hxx>
33 #include <TCollection_AsciiString.hxx>
34 #include <TCollection_ExtendedString.hxx>
35 #include <TColStd_SequenceOfAsciiString.hxx>
36 #include <TDocStd_Document.hxx>
37 #include <XmlLDrivers.hxx>
38 #include <XmlLDrivers_DocumentStorageDriver.hxx>
39 #include <XmlLDrivers_NamespaceDef.hxx>
40 #include <XmlMDF.hxx>
41 #include <XmlMDF_ADriverTable.hxx>
42 #include <XmlObjMgt.hxx>
43 #include <XmlObjMgt_Document.hxx>
44 #include <XmlObjMgt_SRelocationTable.hxx>
45
46 #include <locale.h>
47 IMPLEMENT_STANDARD_RTTIEXT(XmlLDrivers_DocumentStorageDriver,PCDM_StorageDriver)
48
49 #define STORAGE_VERSION      "STORAGE_VERSION: "
50 #define REFERENCE_COUNTER    "REFERENCE_COUNTER: "
51 #define MODIFICATION_COUNTER "MODIFICATION_COUNTER: "
52 #define START_REF            "START_REF"
53 #define END_REF              "END_REF"
54
55 #define FAILSTR "Failed to write xsi:schemaLocation : "
56
57 //#define TAKE_TIMES
58 static void take_time (const Standard_Integer, const char *,
59                        const Handle(CDM_MessageDriver)&)
60 #ifdef TAKE_TIMES
61 ;
62 #else
63 {}
64 #endif
65
66 //=======================================================================
67 //function : XmlLDrivers_DocumentStorageDriver
68 //purpose  : Constructor
69 //=======================================================================
70 XmlLDrivers_DocumentStorageDriver::XmlLDrivers_DocumentStorageDriver
71                                 (const TCollection_ExtendedString& theCopyright)
72      : myCopyright (theCopyright)
73
74 }
75
76 //=======================================================================
77 //function : AddNamespace
78 //purpose  : 
79 //=======================================================================
80
81 void XmlLDrivers_DocumentStorageDriver::AddNamespace
82                                 (const TCollection_AsciiString& thePrefix,
83                                  const TCollection_AsciiString& theURI)
84 {
85   for (Standard_Integer i = 1; i <= mySeqOfNS.Length(); i++)
86     if (thePrefix == mySeqOfNS(i).Prefix()) return;
87   mySeqOfNS.Append (XmlLDrivers_NamespaceDef(thePrefix, theURI));
88 }
89
90 //=======================================================================
91 //function : Write
92 //purpose  : 
93 //=======================================================================
94 void XmlLDrivers_DocumentStorageDriver::Write (const Handle(CDM_Document)&       theDocument,
95                                                const TCollection_ExtendedString& theFileName)
96 {
97   myFileName = theFileName;
98
99   std::ofstream aFileStream;
100   OSD_OpenStream (aFileStream, theFileName, std::ios::out);
101
102   if (aFileStream.is_open() && aFileStream.good())
103   {
104     Write (theDocument, aFileStream);
105   }
106   else
107   {
108     SetIsError (Standard_True);
109     SetStoreStatus(PCDM_SS_WriteFailure);
110     
111     TCollection_ExtendedString aMsg = TCollection_ExtendedString("Error: the file ") +
112                                       theFileName + " cannot be opened for writing";
113
114     theDocument->Application()->MessageDriver()->Write (aMsg.ToExtString());
115     throw Standard_Failure("File cannot be opened for writing");
116   }
117 }
118
119 //=======================================================================
120 //function : Write
121 //purpose  : 
122 //=======================================================================
123 Standard_EXPORT void XmlLDrivers_DocumentStorageDriver::Write (const Handle(CDM_Document)& theDocument,
124                                                                Standard_OStream&           theOStream)
125 {
126   Handle(CDM_MessageDriver) aMessageDriver = theDocument->Application()->MessageDriver();
127   ::take_time (~0, " +++++ Start STORAGE procedures ++++++", aMessageDriver);
128
129   // Create new DOM_Document
130   XmlObjMgt_Document aDOMDoc = XmlObjMgt_Document::createDocument ("document");
131
132   // Fill the document with data
133   XmlObjMgt_Element anElement = aDOMDoc.getDocumentElement();
134
135   if (WriteToDomDocument (theDocument, anElement) == Standard_False) {
136
137     LDOM_XmlWriter aWriter;
138     aWriter.SetIndentation(1);
139   
140     if (theOStream.good())
141     {
142       aWriter.Write (theOStream, aDOMDoc);
143     }
144     else
145     {
146       SetIsError (Standard_True);
147       SetStoreStatus(PCDM_SS_WriteFailure);
148
149       TCollection_ExtendedString aMsg = TCollection_ExtendedString("Error: the stream is bad and") +
150                                         " cannot be used for writing";
151       theDocument->Application()->MessageDriver()->Write (aMsg.ToExtString());
152       
153       throw Standard_Failure("File cannot be opened for writing");
154     }
155
156     ::take_time (0, " +++++ Fin formatting to XML : ", aMessageDriver);
157   }
158 }
159
160 //=======================================================================
161 //function : WriteToDomDocument
162 //purpose  : management of the macro-structure of XML document data
163 //remark   : If the application needs to use myRelocTable to store additional
164 //           data to XML, this method should be reimplemented avoiding step 3
165 //=======================================================================
166
167 Standard_Boolean XmlLDrivers_DocumentStorageDriver::WriteToDomDocument (const Handle(CDM_Document)&  theDocument,
168                                                                         XmlObjMgt_Element&           theElement)
169 {
170   SetIsError(Standard_False);
171   Handle(CDM_MessageDriver) aMessageDriver =
172     theDocument -> Application() -> MessageDriver();
173   // 1. Write header information
174   Standard_Integer i;
175   XmlObjMgt_Document aDOMDoc = theElement.getOwnerDocument();
176
177   // 1.a File Format
178   TCollection_AsciiString aStorageFormat (theDocument->StorageFormat(), '?');
179   theElement.setAttribute ("format", aStorageFormat.ToCString());
180 //  theElement.setAttribute ("schema", "XSD");
181
182   theElement.setAttribute ("xmlns" , "http://www.opencascade.org/OCAF/XML");
183   for (i = 1; i <= mySeqOfNS.Length(); i++) {
184     TCollection_AsciiString aPrefix =
185       TCollection_AsciiString("xmlns:") + mySeqOfNS(i).Prefix().ToCString();
186     theElement.setAttribute (aPrefix.ToCString(),
187                              mySeqOfNS(i).URI().ToCString());
188   }
189   theElement.setAttribute ("xmlns:xsi",
190                            "http://www.w3.org/2001/XMLSchema-instance");
191   //mkv 15.09.05 OCC10001
192   //theElement.setAttribute ("xsi:schemaLocation",
193   //                         "http://www.opencascade.org/OCAF/XML"
194   //                         " http://www.nnov.matra-dtv.fr/~agv/XmlOcaf.xsd");
195   //
196   // the order of search : by CSF_XmlOcafResource and then by CASROOT
197   TCollection_AsciiString anHTTP = "http://www.opencascade.org/OCAF/XML";
198   Standard_Boolean aToSetCSFVariable = Standard_False;
199   const char * aCSFVariable [2] = {
200     "CSF_XmlOcafResource",
201     "CASROOT"
202   };
203   OSD_Environment anEnv (aCSFVariable[0]);
204   TCollection_AsciiString aResourceDir = anEnv.Value();
205   if (aResourceDir.IsEmpty()) {
206     // now try by CASROOT
207     OSD_Environment anEnv2(aCSFVariable[1]);
208     aResourceDir = anEnv2.Value();
209     if ( !aResourceDir.IsEmpty() ) {
210       aResourceDir += "/src/XmlOcafResource" ;
211       aToSetCSFVariable = Standard_True; //CSF variable to be set later
212     }
213 #ifdef OCCT_DEBUG
214     else {
215       TCollection_ExtendedString aWarn = FAILSTR "Neither ";
216       aWarn = (aWarn + aCSFVariable[0] + ", nor " + aCSFVariable[1]
217                + " variables have been set");
218       aMessageDriver->Write (aWarn.ToExtString());
219     }
220 #endif
221   }
222   if (!aResourceDir.IsEmpty()) {
223     TCollection_AsciiString aResourceFileName =  aResourceDir + "/XmlOcaf.xsd";
224     // search directory name that has been constructed, now check whether
225     // it and the file exist
226     OSD_File aResourceFile ( aResourceFileName );
227     if ( aResourceFile.Exists() ) {
228       if (aToSetCSFVariable) {
229         OSD_Environment aCSFVarEnv ( aCSFVariable[0], aResourceDir );
230         aCSFVarEnv.Build();
231 #ifdef OCCT_DEBUG
232         TCollection_ExtendedString aWarn1 = "Variable ";
233         aWarn1 = (aWarn1 + aCSFVariable[0]
234                   + " has not been explicitly defined. Set to " + aResourceDir);
235         aMessageDriver->Write (aWarn1.ToExtString());
236 #endif
237         if ( aCSFVarEnv.Failed() ) {
238           TCollection_ExtendedString aWarn = FAILSTR "Failed to initialize ";
239           aWarn = aWarn + aCSFVariable[0] + " with " + aResourceDir;
240           aMessageDriver->Write (aWarn.ToExtString());
241         }
242       }
243     }
244 #ifdef OCCT_DEBUG
245     else {
246       TCollection_ExtendedString aWarn = FAILSTR "Schema definition file ";
247       aWarn += (aResourceFileName + " was not found");
248       aMessageDriver->Write (aWarn.ToExtString());
249     }
250 #endif
251     anHTTP = anHTTP + ' ' + aResourceFileName;
252   }
253   theElement.setAttribute ("xsi:schemaLocation", anHTTP.ToCString() );
254
255   // 1.b Info section
256   XmlObjMgt_Element anInfoElem = aDOMDoc.createElement("info");
257   theElement.appendChild(anInfoElem);
258
259   TCollection_AsciiString aCreationDate = XmlLDrivers::CreationDate();
260
261 //  anInfoElem.setAttribute("dbv", 0);
262   anInfoElem.setAttribute("date", aCreationDate.ToCString());
263   anInfoElem.setAttribute("schemav", 0);
264 //  anInfoElem.setAttribute("appv", anAppVersion.ToCString());
265
266   // Document version
267   anInfoElem.setAttribute("DocVersion", XmlLDrivers::StorageVersion());
268  
269   // User info with Copyright
270   TColStd_SequenceOfAsciiString aUserInfo;
271   if (myCopyright.Length() > 0)
272     aUserInfo.Append (TCollection_AsciiString(myCopyright,'?'));
273
274   Handle(Storage_Data) theData = new Storage_Data;
275   //PCDM_ReadWriter::WriteFileFormat( theData, theDocument );
276   PCDM_ReadWriter::Writer()->WriteReferenceCounter(theData,theDocument);
277   PCDM_ReadWriter::Writer()->WriteReferences(theData,theDocument, myFileName);
278   PCDM_ReadWriter::Writer()->WriteExtensions(theData,theDocument);
279   PCDM_ReadWriter::Writer()->WriteVersion(theData,theDocument);
280
281   const TColStd_SequenceOfAsciiString& aRefs = theData->UserInfo();
282   for(i = 1; i <= aRefs.Length(); i++)
283     aUserInfo.Append(aRefs.Value(i));
284
285   for (i = 1; i <= aUserInfo.Length(); i++)
286   {
287     XmlObjMgt_Element aUIItem = aDOMDoc.createElement ("iitem");
288     anInfoElem.appendChild (aUIItem);
289     LDOM_Text aUIText = aDOMDoc.createTextNode (aUserInfo(i).ToCString());
290     aUIItem.appendChild (aUIText);
291   }
292
293   // 1.c Comments section
294   TColStd_SequenceOfExtendedString aComments;
295   theDocument->Comments(aComments);
296
297   XmlObjMgt_Element aCommentsElem = aDOMDoc.createElement ("comments");
298   theElement.appendChild (aCommentsElem);
299
300   for (i = 1; i <= aComments.Length(); i++)
301   {
302     XmlObjMgt_Element aCItem = aDOMDoc.createElement ("citem");
303     aCommentsElem.appendChild (aCItem);
304     XmlObjMgt::SetExtendedString (aCItem, aComments(i));
305   }
306
307   // 2a. Write document contents
308   Standard_Integer anObjNb = 0;
309   {
310     try
311     {
312       OCC_CATCH_SIGNALS
313       anObjNb = MakeDocument(theDocument, theElement);
314     }
315     catch (Standard_Failure const& anException)
316     {
317       SetIsError (Standard_True);
318       SetStoreStatus(PCDM_SS_Failure);
319       TCollection_ExtendedString anErrorString (anException.GetMessageString());
320       aMessageDriver -> Write (anErrorString.ToExtString());
321     }
322   }
323   if (anObjNb <= 0 && IsError() == Standard_False) {
324     SetIsError (Standard_True);
325     SetStoreStatus(PCDM_SS_No_Obj);
326     TCollection_ExtendedString anErrorString ("error occurred");
327     aMessageDriver -> Write (anErrorString.ToExtString());
328   }
329   // 2b. Write number of objects into the info section
330   anInfoElem.setAttribute("objnb", anObjNb);
331   ::take_time (0, " +++++ Fin DOM data for OCAF : ", aMessageDriver);
332
333   // 3. Clear relocation table
334   //    If the application needs to use myRelocTable to store additional
335   //    data to XML, this method should be reimplemented avoiding this step
336   myRelocTable.Clear();
337
338   // 4. Write Shapes section
339   if(WriteShapeSection(theElement))
340     ::take_time (0, " +++ Fin DOM data for Shapes : ", aMessageDriver);
341   return IsError();
342 }
343
344 //=======================================================================
345 //function : MakeDocument
346 //purpose  : 
347 //=======================================================================
348 Standard_Integer XmlLDrivers_DocumentStorageDriver::MakeDocument
349                                     (const Handle(CDM_Document)& theTDoc,
350                                      XmlObjMgt_Element&          theElement) 
351 {  
352   TCollection_ExtendedString aMessage;
353   Handle(TDocStd_Document) TDOC = Handle(TDocStd_Document)::DownCast(theTDoc);  
354   myRelocTable.Clear();
355   if (!TDOC.IsNull()) 
356   {
357 //    myRelocTable.SetDocument (theElement.getOwnerDocument());
358     Handle(TDF_Data) aTDF = TDOC->GetData();
359
360 //      Find MessageDriver and pass it to AttributeDrivers()
361     Handle(CDM_Application) anApplication= theTDoc -> Application();
362     Handle(CDM_MessageDriver) aMessageDriver;
363     if (anApplication.IsNull())
364       aMessageDriver = new CDM_NullMessageDriver;
365     else
366       aMessageDriver = anApplication -> MessageDriver();
367     if (myDrivers.IsNull()) myDrivers = AttributeDrivers (aMessageDriver);
368
369 //      Retrieve from DOM_Document
370     XmlMDF::FromTo (aTDF, theElement, myRelocTable, myDrivers); 
371 #ifdef OCCT_DEBUG
372     aMessage = "First step successfull";
373     aMessageDriver -> Write (aMessage.ToExtString());
374 #endif
375     return myRelocTable.Extent();
376   }
377 #ifdef OCCT_DEBUG
378   cout << "First step failed" << endl;  // No MessageDriver available
379 #endif
380   return -1; // error
381 }
382
383 //=======================================================================
384 //function : AttributeDrivers
385 //purpose  : 
386 //=======================================================================
387 Handle(XmlMDF_ADriverTable) XmlLDrivers_DocumentStorageDriver::AttributeDrivers
388        (const Handle(CDM_MessageDriver)& theMessageDriver) 
389 {
390   return XmlLDrivers::AttributeDrivers (theMessageDriver);
391 }
392
393 //=======================================================================
394 //function : take_time
395 //class    : static
396 //purpose  : output astronomical time elapsed
397 //=======================================================================
398 #ifdef TAKE_TIMES
399 #include <time.h>
400 #include <sys/timeb.h>
401 #include <sys/types.h>
402 #include <stdio.h>
403 #ifndef _WIN32
404 extern "C" int ftime (struct timeb *tp);
405 #endif
406 struct timeb  tmbuf0;
407
408 static void take_time (const Standard_Integer isReset, const char * aHeader,
409                        const Handle(CDM_MessageDriver)& aMessageDriver)
410 {
411   struct timeb  tmbuf;
412   ftime (&tmbuf);
413   TCollection_ExtendedString aMessage ((Standard_CString)aHeader);
414   if (isReset) tmbuf0 = tmbuf;
415   else {
416     char take_tm_buf [64];
417     Sprintf (take_tm_buf, "%9.2f s ++++",
418              double(tmbuf.time - tmbuf0.time) +
419              double(tmbuf.millitm - tmbuf0.millitm)/1000.);
420     aMessage += take_tm_buf;
421   }
422   aMessageDriver -> Write (aMessage.ToExtString());
423 }
424 #endif
425
426 //=======================================================================
427 //function : WriteShapeSection
428 //purpose  : defines WriteShapeSection
429 //=======================================================================
430 Standard_Boolean XmlLDrivers_DocumentStorageDriver::WriteShapeSection
431                                 (XmlObjMgt_Element&  /*theElement*/)
432 {
433   // empty; should be redefined in subclasses
434   return Standard_False;
435 }
436