Bump XML format version to 0.2
[stream:uboots-stream.git] / stromx / runtime / impl / XmlWriterImpl.cpp
1 /* 
2  *  Copyright 2011 Thomas Fidler
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  */
16
17 #include <boost/lexical_cast.hpp>
18 #include <fstream>
19 #include <iostream>
20 #include <xercesc/framework/MemBufFormatTarget.hpp>
21 #include "stromx/runtime/Config.h"
22 #include "stromx/runtime/Data.h"
23 #include "stromx/runtime/Exception.h"
24 #include "stromx/runtime/FileOutput.h"
25 #include "stromx/runtime/Input.h"
26 #include "stromx/runtime/Operator.h"
27 #include "stromx/runtime/Thread.h"
28 #include "stromx/runtime/impl/XmlUtilities.h"
29 #include "stromx/runtime/impl/XmlWriterImpl.h"
30
31 using namespace xercesc;
32
33 namespace stromx
34 {
35     namespace runtime
36     {
37         namespace impl
38         {
39             
40             const unsigned int XmlWriterImpl::XML_FORMAT_VERSION_MAJOR = 0;
41             const unsigned int XmlWriterImpl::XML_FORMAT_VERSION_MINOR = 2;
42             const unsigned int XmlWriterImpl::XML_FORMAT_VERSION_REVISION = 0;
43                 
44             XmlWriterImpl::XmlWriterImpl() 
45                 : m_stream(0),
46                   m_opList(0),
47                   m_output(0), 
48                   m_filename(""), 
49                   m_impl(0), 
50                   m_doc(0), 
51                   m_stromxElement(0), 
52                   m_strElement(0),
53                   m_parsElement(0)
54             {
55                 try
56                 {
57                     XMLPlatformUtils::Initialize();  // Initialize Xerces infrastructure
58                 }
59                 catch (const XMLException& toCatch)
60                 {
61                     char* message = XMLString::transcode(toCatch.getMessage());
62                     std::cerr << "Error during initialization! :\n"
63                         << message << "\n";
64                     XMLString::release(&message);
65                     return;
66                 }
67                 
68                 try
69                 {
70                     m_impl = DOMImplementationRegistry::getDOMImplementation(Str2Xml(""));
71                 }
72                 catch(DOMException&)
73                 {
74                     throw InternalError("Error in Xerces-C.");
75                 }
76                 catch(XMLException&)
77                 {
78                     throw InternalError("Error in Xerces-C.");
79                 }
80             }
81             
82             void XmlWriterImpl::createDoc()
83             {
84                 m_doc = m_impl->createDocument(0, Str2Xml("Stromx"), 0);
85             }
86             
87             void XmlWriterImpl::createComm()
88             {
89                 DOMComment* comment = m_doc->createComment(Str2Xml("XML generated automatically by XmlWriter of the open source library Stromx"));
90                 m_doc->appendChild(comment); 
91             }
92
93             void XmlWriterImpl::createStromx()
94             {
95                 //Create Stromx branch           
96                 m_stromxElement = m_doc->getDocumentElement();              
97             
98                 //Create attribute version of Stromx
99                 DOMAttr* verAttr = m_doc->createAttribute(Str2Xml("version"));
100                 Version version(XML_FORMAT_VERSION_MAJOR, XML_FORMAT_VERSION_MINOR, XML_FORMAT_VERSION_REVISION);
101                 std::string str = boost::lexical_cast<std::string>(version);
102                 verAttr->setValue(Str2Xml(str.c_str()));
103                 m_stromxElement->setAttributeNode(verAttr);
104             }
105
106             
107             unsigned int XmlWriterImpl::translateOperatorPointerToID(const Operator* const op) const
108             {
109                 if (m_opList.empty())
110                 {
111                     throw InternalError("No operator list available");
112                 }
113                 
114                 unsigned int count_op = 0;
115                 for(std::vector<const Operator*>::const_iterator iter_op = m_opList.begin();
116                         iter_op != m_opList.end();
117                         ++iter_op, ++count_op)
118                 {
119                     if ((*iter_op) == op)
120                     {
121                         return count_op;
122                     }
123                 }
124                 
125                 throw InternalError("Operator does not exist.");
126             }
127             
128             void XmlWriterImpl::createInputConnectors(const Thread* const currThr, DOMElement* const thrElement)
129             {
130                 //Add InputConnector branches (tree structure: Stream:Thread:InputConnector)
131                 //Processed for each InputConnector belonging to Thread (multiple entries for each Thread possible)
132                 for(std::vector<Input>::const_iterator iter_inConnectors = currThr->inputSequence().begin();
133                     iter_inConnectors != currThr->inputSequence().end();
134                     ++iter_inConnectors)
135                 {
136                     //Create current InputNode being child of Thread 
137                     DOMElement* inConnectorElement = m_doc->createElement(Str2Xml("InputConnector"));
138                     thrElement->appendChild(inConnectorElement);
139                     
140                     //Create attribute operator of current InputNode (one for each InputNode possible)
141                     DOMAttr* opAttr = m_doc->createAttribute(Str2Xml("operator"));
142                     unsigned int opId = translateOperatorPointerToID((*iter_inConnectors).op());
143                     opAttr->setValue(Str2Xml(boost::lexical_cast<std::string>(opId).c_str()));
144                     inConnectorElement->setAttributeNode(opAttr);
145                     
146                     //Create attribute input of current InputNode (one for each InputNode possible)
147                     DOMAttr* inputAttr = m_doc->createAttribute(Str2Xml("input"));
148                     inputAttr->setValue(Str2Xml(boost::lexical_cast<std::string>((*iter_inConnectors).id()).c_str()));
149                     inConnectorElement->setAttributeNode(inputAttr);
150                 }
151             }
152             
153             void XmlWriterImpl::createThreads(const std::vector<Thread*> & threads)
154             {
155                 //Add Thread branches (tree structure: Stream:Thread)
156                 //Processed for each thread belonging to the strom object (multiple entries for strom possible)
157                 for(std::vector<Thread*>::const_iterator iter_thr = threads.begin();
158                         iter_thr != threads.end();
159                         ++iter_thr)
160                 {
161                     //Create current Thread being child of Stream (one for each Thread possible)
162                     DOMElement* thrElement = m_doc->createElement(Str2Xml("Thread"));
163                     m_strElement->appendChild(thrElement);
164                     
165                     //Create attribute name of Thread (one for each Thread possible)
166                     DOMAttr* nameAttr = m_doc->createAttribute(Str2Xml("name"));
167                     nameAttr->setValue(Str2Xml((*iter_thr)->name().c_str()));
168                     thrElement->setAttributeNode(nameAttr);
169                     
170                     //Create InputNodes of Thread (multiple entries for each Thread possible)
171                     createInputConnectors((*iter_thr), thrElement);
172                 }
173             }
174             
175             void XmlWriterImpl::createParameters(const Operator* const currOp, DOMElement* const opElement)
176             {
177                 //Add parameter branches (tree structure: strom:operator:parameter)
178                 //Processed for each parameter belonging to current operator currOp (multiple entries for each operator possible)
179                 for(std::vector<const Parameter*>::const_iterator iter_par = currOp->info().parameters().begin();
180                             iter_par != currOp->info().parameters().end();
181                             ++iter_par)
182                 {
183                     try
184                     {
185                         // Try to access the parameter in question. 
186                         currOp->getParameter((*iter_par)->id());
187                     }
188                     catch(ParameterAccessViolation&)
189                     {
190                         // If the access fails continue with the next parameter.
191                         continue;
192                     }
193                     
194                     //Create current parameter entry param being child of current operator op (one for each parameter possible)
195                     DOMElement* parElement = m_doc->createElement(Str2Xml("Parameter"));
196                     opElement->appendChild(parElement);
197                     
198                     //Create attribute id of current parameter param (one for each parameter possible)
199                     DOMAttr* id = m_doc->createAttribute(Str2Xml("id"));
200                     id->setValue(Str2Xml(boost::lexical_cast<std::string>((*iter_par)->id()).c_str()));
201                     parElement->setAttributeNode(id);
202                     
203                     createData(*iter_par, currOp, parElement);
204                 }
205             }
206             
207             void XmlWriterImpl::createData(const Parameter*const currPar, const Operator*const currOp, DOMElement*const parElement)
208             {
209                 //Add Data branch
210                 //Create Data entry data being child of current param (one for each parameter possible)
211                 DOMElement* dataElement = m_doc->createElement(Str2Xml("Data"));
212                 parElement->appendChild(dataElement);
213                 
214                 //Create attribute type of data (one for each data possible)
215                 DOMAttr* typeAttr = m_doc->createAttribute(Str2Xml("type"));
216                 typeAttr->setValue(Str2Xml(currOp->getParameter(currPar->id()).type().c_str()));
217                 dataElement->setAttributeNode(typeAttr);
218                 
219                 //Create attribute package of data (one for each data possible)
220                 DOMAttr* packageAttr = m_doc->createAttribute(Str2Xml("package"));
221                 packageAttr->setValue(Str2Xml(currOp->getParameter(currPar->id()).package().c_str()));
222                 dataElement->setAttributeNode(packageAttr);
223                 
224                 //Create attribute version of data (one for each data possible)
225                 DOMAttr* verAttr = m_doc->createAttribute(Str2Xml("version"));
226                 std::string str1 = boost::lexical_cast<std::string>(currOp->getParameter(currPar->id()).version());
227                 verAttr->setValue(Str2Xml(str1.c_str()));
228                 dataElement->setAttributeNode(verAttr);
229                 
230                 //Create value of current parameter param (one for each parameter possible)
231                 //First, create unique input parameter name for function Data::serialize()
232                 std::string filename = m_filename +
233                                         "_op" + boost::lexical_cast<std::string>(translateOperatorPointerToID(currOp)) + 
234                                         "_parameter" + boost::lexical_cast<std::string>(currPar->id());
235                                     
236                 m_output->initialize(filename);
237                 DataRef data = currOp->getParameter(currPar->id());
238                 
239                 try
240                 {
241                     data.serialize(*m_output);
242                 }
243                 catch(FileException &)
244                 {
245                     throw;
246                 }
247                 catch(std::exception & e)
248                 {
249                     throw SerializationError(data.package(), data.type(), e.what());
250                 }
251                 
252                 //Create attribute for file
253                 if(! m_output->getFilename().empty())
254                 { 
255                     DOMAttr* fileAttr = m_doc->createAttribute(Str2Xml("file"));
256                     fileAttr->setValue(Str2Xml(m_output->getFilename().c_str()));
257                     dataElement->setAttributeNode(fileAttr);
258                 }
259                 
260                 DOMText* value = m_doc->createTextNode(Str2Xml(m_output->getText().c_str()));
261                 dataElement->appendChild(value);
262             }
263             
264             void XmlWriterImpl::createInputs(const Operator*const currOp, DOMElement*const opElement)
265             {
266                 for(std::vector<const Description*>::const_iterator iter_in = currOp->info().inputs().begin();
267                     iter_in != currOp->info().inputs().end();
268                     ++iter_in)
269                 {
270                     //Get the source node
271                     Output node = m_stream->connectionSource(currOp, (*iter_in)->id());
272                     
273                     //Create Input only for connected operators
274                     if (node.valid())
275                     {
276                         //Create Input entry in being child of current operator op (one for each parameter possible)
277                         DOMElement* inElement = m_doc->createElement(Str2Xml("Input"));
278                         opElement->appendChild(inElement);
279                         
280                         //Create attribute id of current input in (one for each input possible)
281                         DOMAttr* idAttr = m_doc->createAttribute(Str2Xml("id"));
282                         idAttr->setValue(Str2Xml(boost::lexical_cast<std::string>((*iter_in)->id()).c_str()));
283                         inElement->setAttributeNode(idAttr);
284                         
285                         //Get the id of the source operator
286                         unsigned sourceOp = translateOperatorPointerToID(node.op());
287                         
288                         //Write the id of the source operator
289                         DOMAttr* opAttr = m_doc->createAttribute(Str2Xml("operator"));
290                         opAttr->setValue(Str2Xml(boost::lexical_cast<std::string>(sourceOp).c_str()));
291                         inElement->setAttributeNode(opAttr);
292                         
293                         //Write the id of the output
294                         DOMAttr* outAttr = m_doc->createAttribute(Str2Xml("output"));
295                         outAttr->setValue(Str2Xml(boost::lexical_cast<std::string>(node.id()).c_str()));
296                         inElement->setAttributeNode(outAttr);
297                     }
298                 }
299             }
300             
301             void XmlWriterImpl::createOperators(const std::vector<const Operator*> & operators, xercesc::DOMElement* const parentElement)
302             {
303                 //Add operator branches (tree structure: strom:operator)
304                 //Processed for each operator belonging to the strom object (multiple entries for strom possible)
305                 for(std::vector<const Operator*>::const_iterator iter_op = operators.begin();
306                     iter_op != operators.end();
307                     ++iter_op)
308                 {
309                     //Create current operator entry op being child of strom (one for each operator possible)
310                     DOMElement* opElement = m_doc->createElement(Str2Xml("Operator"));
311                     parentElement->appendChild(opElement);
312                     
313                     //Create attribute id of current operator op (one for each operator possible)
314                     DOMAttr* idAttr = m_doc->createAttribute(Str2Xml("id"));
315                     idAttr->setValue(Str2Xml(boost::lexical_cast<std::string>(translateOperatorPointerToID(*iter_op)).c_str()));
316                     opElement->setAttributeNode(idAttr);
317                     
318                     //Create attribute package of current operator op (one for each operator possible)
319                     DOMAttr* packAttr = m_doc->createAttribute(Str2Xml("package"));
320                     packAttr->setValue(Str2Xml((*iter_op)->info().package().c_str()));
321                     opElement->setAttributeNode(packAttr);
322                     
323                     //Create attribute type of current operator op (one for each operator possible)
324                     DOMAttr* typeAttr = m_doc->createAttribute(Str2Xml("type"));
325                     typeAttr->setValue(Str2Xml((*iter_op)->info().type().c_str()));
326                     opElement->setAttributeNode(typeAttr);
327                     
328                     //Create attribute name of current operator op (one for each operator possible)
329                     DOMAttr* nameAttr = m_doc->createAttribute(Str2Xml("name"));
330                     nameAttr->setValue(Str2Xml((*iter_op)->name().c_str()));
331                     opElement->setAttributeNode(nameAttr);
332                     
333                     //Create attribute version of current operator op (one for each operator possible)
334                     DOMAttr* verAttr = m_doc->createAttribute(Str2Xml("version"));
335                     std::string str = boost::lexical_cast<std::string>((*iter_op)->info().version());
336                     verAttr->setValue(Str2Xml(str.c_str()));
337                     opElement->setAttributeNode(verAttr);
338                     
339                     //Create attribute version of current operator op (one for each operator possible)
340                     DOMAttr* initAttr = m_doc->createAttribute(Str2Xml("isInitialized"));
341                     initAttr->setValue(Str2Xml((*iter_op)->status() == Operator::NONE ? "false" : "true"));
342                     opElement->setAttributeNode(initAttr);
343                     
344                     createParameters((*iter_op), opElement);
345                     
346                     if (m_stream != 0 && (*iter_op)->status() != Operator::NONE)
347                     {
348                         createInputs((*iter_op), opElement);
349                     }
350                 }
351             }
352         
353             void XmlWriterImpl::writeStream(FileOutput & output, const std::string & basename, const Stream& stream)
354             {
355                 if (basename.empty())
356                 {
357                     throw FileAccessFailed(basename, "Invalid file name.");
358                 }
359                 
360                 m_stream = &stream;
361                 m_output = &output;
362                 m_filename = basename;
363                 
364                 std::vector<const Operator*> operators(m_stream->operators().begin(), m_stream->operators().end());
365                 m_opList = operators;
366                 
367                 try
368                 {
369                     createDoc();
370                     createComm();                    
371                     createStromx();
372                                         
373                     //Create Stream branch
374                     m_strElement = m_doc->createElement(Str2Xml("Stream"));
375                     m_stromxElement->appendChild(m_strElement);
376                     
377                     //Create attribute name of Stream
378                     DOMAttr* nameAttr = m_doc->createAttribute(Str2Xml("name"));
379                     nameAttr->setValue(Str2Xml(m_stream->name().c_str()));
380                     m_strElement->setAttributeNode(nameAttr);
381
382                     createOperators(m_opList,m_strElement);
383                     //Create Threads of Stream (multiple entries for Stream possible)
384                     createThreads(m_stream->threads());
385                     
386                     DOMLSOutput* output = m_impl->createLSOutput();
387                     MemBufFormatTarget formatTarget;
388                     output->setByteStream(&formatTarget);
389                     DOMLSSerializer* serializer = m_impl->createLSSerializer();
390                     serializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true);
391                     
392                     serializer->write(m_doc, output);
393                     
394                     try
395                     {
396                         m_output->initialize(basename);
397                         m_output->openFile("xml");
398                         m_output->file().write((const char*)(formatTarget.getRawBuffer()),
399                                                formatTarget.getLen());
400                     }
401                     catch(Exception&)
402                     {
403                         output->release();
404                         serializer->release();
405                         throw; 
406                     }
407                     
408                     output->release();
409                     serializer->release();
410
411                     // done with the document, must call release() to release the entire document resources
412                     m_doc->release();
413                 }
414                 catch(DOMException&)
415                 {
416                     throw InternalError("Error in Xerces-C.");
417                 }
418                 catch(XMLException&)
419                 {
420                     throw InternalError("Error in Xerces-C.");
421                 }
422                             
423                 try
424                 {
425                     XMLPlatformUtils::Terminate();  // Terminate after release of memory
426                 }
427                 catch(XMLException&)
428                 {
429                 }
430             }
431             
432             void XmlWriterImpl::writeParameters(FileOutput& output, 
433                                                 const std::string& basename, 
434                                                 const std::vector< const stromx::runtime::Operator* >& operators)
435             {
436                 if (basename.empty())
437                 {
438                     throw FileAccessFailed(basename, "Invalid base name.");
439                 }
440                 
441                 m_output = &output;
442                 m_filename = basename;
443                 m_opList = operators;
444                 
445                 try
446                 {
447                     createDoc();
448                     createComm();
449                     createStromx();
450                     
451                     //Create Parameters branch
452                     m_parsElement = m_doc->createElement(Str2Xml("Parameters"));
453                     m_stromxElement->appendChild(m_parsElement);
454                     
455                     //Create Operator branches
456                     createOperators(m_opList, m_parsElement);
457                     
458                     DOMLSOutput* output = m_impl->createLSOutput();
459                     MemBufFormatTarget formatTarget;
460                     output->setByteStream(&formatTarget);
461                     DOMLSSerializer* serializer = m_impl->createLSSerializer();
462                     serializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true);
463                     
464                     serializer->write(m_doc, output);
465                     
466                     try
467                     {
468                         m_output->initialize(basename);
469                         m_output->openFile("xml");
470                         m_output->file().write((const char*)(formatTarget.getRawBuffer()),
471                                                formatTarget.getLen());
472                     }
473                     catch(Exception&)
474                     {
475                         output->release();
476                         serializer->release();
477
478                         // done with the document, must call release() to release the entire document resources
479                         m_doc->release();
480                     
481                         throw; 
482                     }
483                     
484                     output->release();
485                     serializer->release();
486
487                     // done with the document, must call release() to release the entire document resources
488                     m_doc->release();
489                 }
490                 catch(DOMException&)
491                 {
492                     throw InternalError("Error in Xerces-C.");
493                 }
494                 catch(XMLException&)
495                 {
496                     throw InternalError("Error in Xerces-C.");
497                 }
498                             
499                 try
500                 {
501                     XMLPlatformUtils::Terminate();  // Terminate after release of memory
502                 }
503                 catch(XMLException&)
504                 {
505                 }
506             }
507         }
508     } 
509 }