* splitted midi++/event.h in header and implementation
authorHans Baier <hansfbaier@googlemail.com>
Sun, 25 May 2008 23:35:23 +0000 (23:35 +0000)
committerHans Baier <hansfbaier@googlemail.com>
Sun, 25 May 2008 23:35:23 +0000 (23:35 +0000)
* added to_string(), to_xml() and from_xml() to MIDI::Event
* added partial support for midnam-Patchfiles (http://www.sonosphere.com/dtds/MIDINameDocument.dtd): midnam_patch.h/.cc
* added validation support to xml++.cc/.h
* added XMLNode::add_property(const char *name, const long value)
* added test to pbd/tests/xpath.cc

git-svn-id: svn://localhost/ardour2/branches/3.0@3412 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/midi++2/SConscript
libs/midi++2/event.cc [new file with mode: 0644]
libs/midi++2/midi++/event.h
libs/midi++2/midi++/midnam_patch.h [new file with mode: 0644]
libs/midi++2/midnam_patch.cc [new file with mode: 0644]
libs/pbd/pbd/xml++.h
libs/pbd/tests/Makefile
libs/pbd/tests/xpath.cc
libs/pbd/xml++.cc

index 3e2e204fbd778cacbf32c243ad8cbce552de289e..0915db9f3f32a9d4b38ff9eee3ab8d5f53e8e91b 100644 (file)
@@ -25,12 +25,14 @@ midi2.Append(DOMAIN=domain,MAJOR=2,MINOR=1,MICRO=1)
 sources = Split("""
 fd_midiport.cc
 fifomidi.cc
+event.cc
 midi.cc
 midichannel.cc
 midifactory.cc
 midimanager.cc
 midiparser.cc
 midiport.cc
+midnam_patch.cc
 mmc.cc
 mtc.cc
 version.cc
diff --git a/libs/midi++2/event.cc b/libs/midi++2/event.cc
new file mode 100644 (file)
index 0000000..09e3fcf
--- /dev/null
@@ -0,0 +1,97 @@
+#include "midi++/event.h"
+
+namespace MIDI {
+
+#ifdef MIDI_EVENT_ALLOW_ALLOC
+Event::Event(double t, uint32_t s, uint8_t* b, bool owns_buffer)
+       : _time(t)
+       , _size(s)
+       , _buffer(b)
+       , _owns_buffer(owns_buffer)
+{
+       if (owns_buffer) {
+               _buffer = (uint8_t*)malloc(_size);
+               if (b) {
+                       memcpy(_buffer, b, _size);
+               } else {
+                       memset(_buffer, 0, _size);
+               }
+       }
+}
+
+Event::Event(const XMLNode& event)
+{
+       string name = event.name();
+       
+       if (name == "ControlChange") {
+               
+       } else if (name == "ProgramChange") {
+               
+       }
+}
+
+Event::Event(const Event& copy, bool owns_buffer)
+       : _time(copy._time)
+       , _size(copy._size)
+       , _buffer(copy._buffer)
+       , _owns_buffer(owns_buffer)
+{
+       if (owns_buffer) {
+               _buffer = (uint8_t*)malloc(_size);
+               if (copy._buffer) {
+                       memcpy(_buffer, copy._buffer, _size);
+               } else {
+                       memset(_buffer, 0, _size);
+               }
+       }
+}
+
+Event::~Event() {
+       if (_owns_buffer) {
+               free(_buffer);
+       }
+}
+
+
+#endif // MIDI_EVENT_ALLOW_ALLOC
+
+std::string      
+Event::to_string() const 
+{
+       std::ostringstream result(std::ios::ate);
+       result << "MIDI::Event type:" << std::hex << "0x" << int(type()) << "   buffer: ";
+       
+       for(uint32_t i = 0; i < size(); ++i) {
+               result << " 0x" << int(_buffer[i]); 
+       }
+       return result.str();
+}
+
+boost::shared_ptr<XMLNode> 
+Event::to_xml() const
+{
+       XMLNode *result = 0;
+       
+       switch (type()) {
+               case MIDI_CMD_CONTROL:
+                       result = new XMLNode("ControlChange");
+                       result->add_property("Channel", channel());
+                       result->add_property("Control", cc_number());
+                       result->add_property("Value",   cc_value());
+                       break;
+                       
+               case MIDI_CMD_PGM_CHANGE:
+                       result = new XMLNode("ProgramChange");
+                       result->add_property("Channel", channel());
+                       result->add_property("number",  pgm_number());
+                       break;
+               
+               default:
+                       // The implementation is continued as needed
+                       break;
+       }
+       
+       return boost::shared_ptr<XMLNode>(result);
+}
+
+} // namespace MIDI
index 123ff8f7fcac6d61ecdd19bfc4c48f1cf67b03e5..64f99090ad344e223c0b2c470c12a934e81e165c 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <midi++/types.h>
 #include <midi++/events.h>
+#include <pbd/xml++.h>
 
 /** If this is not defined, all methods of MidiEvent are RT safe
  * but MidiEvent will never deep copy and (depending on the scenario)
@@ -46,21 +47,7 @@ namespace MIDI {
  */
 struct Event {
 #ifdef MIDI_EVENT_ALLOW_ALLOC
-       Event(double t=0, uint32_t s=0, uint8_t* b=NULL, bool owns_buffer=false)
-               : _time(t)
-               , _size(s)
-               , _buffer(b)
-               , _owns_buffer(owns_buffer)
-       {
-               if (owns_buffer) {
-                       _buffer = (uint8_t*)malloc(_size);
-                       if (b) {
-                               memcpy(_buffer, b, _size);
-                       } else {
-                               memset(_buffer, 0, _size);
-                       }
-               }
-       }
+       Event(double t=0, uint32_t s=0, uint8_t* b=NULL, bool owns_buffer=false);
        
        /** Copy \a copy.
         * 
@@ -68,27 +55,14 @@ struct Event {
         * is NOT REALTIME SAFE.  Otherwise both events share a buffer and
         * memory management semantics are the caller's problem.
         */
-       Event(const Event& copy, bool owns_buffer)
-               : _time(copy._time)
-               , _size(copy._size)
-               , _buffer(copy._buffer)
-               , _owns_buffer(owns_buffer)
-       {
-               if (owns_buffer) {
-                       _buffer = (uint8_t*)malloc(_size);
-                       if (copy._buffer) {
-                               memcpy(_buffer, copy._buffer, _size);
-                       } else {
-                               memset(_buffer, 0, _size);
-                       }
-               }
-       }
+       Event(const Event& copy, bool owns_buffer);
        
-       ~Event() {
-               if (_owns_buffer) {
-                       free(_buffer);
-               }
-       }
+       /**
+        * see the MIDI XML specification: http://www.midi.org/dtds/MIDIEvents10.dtd
+        */
+       Event(const XMLNode &event);
+       
+       ~Event();
 
        inline const Event& operator=(const Event& copy) {
                _time = copy._time;
@@ -179,6 +153,7 @@ struct Event {
                _size = size;
        }
 
+
 #else
 
        inline void set_buffer(uint8_t* buf) { _buffer = buf; }
@@ -218,15 +193,16 @@ struct Event {
        inline bool        is_sysex()              const { return _buffer[0] == 0xF0 || _buffer[0] == 0xF7; }
        inline const uint8_t* buffer()             const { return _buffer; }
        inline uint8_t*&      buffer()                   { return _buffer; }
-       inline std::string      to_string()             const {
-               std::ostringstream result(std::ios::ate);
-               result << "MIDI::Event type:" << std::hex << "0x" << int(type()) << "   buffer: ";
-               
-               for(uint32_t i = 0; i < size(); ++i) {
-                       result << " 0x" << int(_buffer[i]); 
-               }
-               return result.str();
-       }
+       
+              /**
+               * mainly used for debugging purposes
+               */
+              std::string    to_string()          const;
+              
+              /**
+               * see the MIDI XML specification: http://www.midi.org/dtds/MIDIEvents10.dtd
+               */
+              boost::shared_ptr<XMLNode> to_xml() const;
 
 private:
        double   _time;   /**< Sample index (or beat time) at which event is valid */
diff --git a/libs/midi++2/midi++/midnam_patch.h b/libs/midi++2/midi++/midnam_patch.h
new file mode 100644 (file)
index 0000000..492eacd
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef MIDNAM_PATCH_H_
+#define MIDNAM_PATCH_H_
+
+#include "pbd/stateful.h"
+#include "midi++/event.h"
+#include "pbd/xml++.h"
+
+#include <string>
+#include <list>
+#include <set>
+
+namespace MIDI
+{
+
+namespace Name
+{
+
+class Patch : public PBD::Stateful
+{
+public:
+       typedef std::list<MIDI::Event> PatchMidiCommands;
+       
+       Patch() {};
+       Patch(string a_number, string a_name) : _number(a_number), _name(a_name) {};
+       ~Patch() {};
+       
+       const string& name() const               { return _name; }
+       void set_name(const string a_name)       { _name = a_name; }
+       
+       const string& number() const             { return _number; }
+       void set_number(const string a_number)   { _number = a_number; }
+       
+       const PatchMidiCommands& patch_midi_commands() const { return _patch_midi_commands; }
+       
+       XMLNode& get_state (void);
+       int      set_state (const XMLNode& a_node);
+       
+private:
+       string _number;
+       string _name;
+       PatchMidiCommands _patch_midi_commands;
+};
+
+class PatchBank : public PBD::Stateful
+{
+public:
+       typedef std::list<Patch> PatchNameList;
+       
+       PatchBank() {};
+       virtual ~PatchBank() {};
+       PatchBank(string a_name) : _name(a_name) {};
+       
+       const string& name() const               { return _name; }
+       void set_name(const string a_name)       { _name = a_name; }    
+       
+       const PatchNameList& patch_name_list() const { return _patch_name_list; }
+       
+       XMLNode& get_state (void);
+       int      set_state (const XMLNode& a_node);
+       
+private:
+       string        _name;
+       PatchNameList _patch_name_list;
+};
+
+class ChannelNameSet : public PBD::Stateful
+{
+public:
+       typedef std::set<uint8_t>    AvailableForChannels;
+       typedef std::list<PatchBank> PatchBanks;
+       
+       ChannelNameSet() {};
+       virtual ~ChannelNameSet() {};
+       ChannelNameSet(string a_name) : _name(a_name) {};
+       
+       const string& name() const               { return _name; }
+       void set_name(const string a_name)       { _name = a_name; }            
+       
+       const AvailableForChannels& available_for_channels() const { return _available_for_channels; }
+       const PatchBanks&           patch_banks()            const { return _patch_banks; }
+       
+       XMLNode& get_state (void);
+       int      set_state (const XMLNode& a_node);
+       
+private:
+       string _name;
+       AvailableForChannels _available_for_channels;
+       PatchBanks           _patch_banks;
+};
+
+
+}
+
+}
+
+#endif /*MIDNAM_PATCH_H_*/
diff --git a/libs/midi++2/midnam_patch.cc b/libs/midi++2/midnam_patch.cc
new file mode 100644 (file)
index 0000000..c103237
--- /dev/null
@@ -0,0 +1,137 @@
+#include "midi++/midnam_patch.h"
+#include <algorithm>
+
+namespace MIDI
+{
+
+namespace Name
+{
+
+XMLNode& 
+Patch::get_state (void)
+{
+       XMLNode* node = new XMLNode("Patch");
+       node->add_property("Number", _number);
+       node->add_property("Name",   _name);
+       XMLNode* commands = node->add_child("PatchMIDICommands");
+       for (PatchMidiCommands::const_iterator event = _patch_midi_commands.begin(); 
+           event != _patch_midi_commands.end();
+           ++event) {
+               commands->add_child_copy(*(event->to_xml()));
+       }
+       
+       return *node;
+}
+
+int
+Patch::set_state (const XMLNode& node)
+{
+       assert(node.name() == "Patch");
+       _number = node.property("Number")->value();
+       _name   = node.property("Name")->value();
+       XMLNode* commands = node.child("PatchMIDICommands");
+       assert(commands);
+       const XMLNodeList events = commands->children();
+       for (XMLNodeList::const_iterator i = events.begin(); i != events.end(); ++i) {
+               _patch_midi_commands.push_back(*(new Event(*(*i))));
+       }
+       
+       return 0;
+}
+
+XMLNode& 
+PatchBank::get_state (void)
+{
+       XMLNode* node = new XMLNode("PatchBank");
+       node->add_property("Name",   _name);
+       XMLNode* patch_name_list = node->add_child("PatchNameList");
+       for (PatchNameList::iterator patch = _patch_name_list.begin(); 
+           patch != _patch_name_list.end();
+           ++patch) {
+               patch_name_list->add_child_nocopy(patch->get_state());
+       }
+       
+       return *node;
+}
+
+int
+PatchBank::set_state (const XMLNode& node)
+{
+       assert(node.name() == "PatchBank");
+       _name   = node.property("Name")->value();
+       XMLNode* patch_name_list = node.child("PatchNameList");
+       assert(patch_name_list);
+       const XMLNodeList patches = patch_name_list->children();
+       for (XMLNodeList::const_iterator i = patches.begin(); i != patches.end(); ++i) {
+               Patch patch;
+               patch.set_state(*(*i));
+               _patch_name_list.push_back(patch);
+       }
+       
+       return 0;
+}
+
+XMLNode& 
+ChannelNameSet::get_state (void)
+{
+       XMLNode* node = new XMLNode("ChannelNameSet");
+       node->add_property("Name",   _name);
+       
+       XMLNode* available_for_channels = node->add_child("AvailableForChannels");
+       assert(available_for_channels);
+       
+       for (uint8_t channel = 0; channel < 16; ++channel) {
+               XMLNode* available_channel = available_for_channels->add_child("AvailableChannel");
+               assert(available_channel);
+               
+               available_channel->add_property("Channel", (long) channel);
+               
+               if (_available_for_channels.find(channel) != _available_for_channels.end()) {
+                       available_channel->add_property("Available", "true");                   
+               } else {
+                       available_channel->add_property("Available", "false");                  
+               }
+       }
+       
+       for (PatchBanks::iterator patch_bank = _patch_banks.begin(); 
+           patch_bank != _patch_banks.end();
+           ++patch_bank) {
+               node->add_child_nocopy(patch_bank->get_state());
+       }
+       
+       return *node;
+}
+
+int
+ChannelNameSet::set_state (const XMLNode& node)
+{
+       assert(node.name() == "ChannelNameSet");
+       _name   = node.property("Name")->value();
+       const XMLNodeList children = node.children();
+       for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
+               XMLNode* node = *i;
+               assert(node);
+               if (node->name() == "AvailableForChannels") {
+                       boost::shared_ptr<XMLSharedNodeList> channels =
+                               node->find("//AvailableChannel[@Available = 'true']/@Channel");
+                       for(XMLSharedNodeList::const_iterator i = channels->begin();
+                           i != channels->end();
+                           ++i) {
+                               _available_for_channels.insert(atoi((*i)->attribute_value().c_str()));
+                       }
+               }
+               
+               if (node->name() == "PatchBank") {
+                       PatchBank bank;
+                       bank.set_state(*node);
+                       _patch_banks.push_back(bank);
+               }               
+       }
+       
+       return 0;
+}
+
+} //namespace Name
+
+} //namespace MIDI
+
index 062effcdfe4c4468a5cb5dffd2b78761b3a80938..290a449869128382604ad51c550bd79d0f4a06a8 100644 (file)
@@ -39,10 +39,11 @@ private:
   string _filename;
   XMLNode *_root;
   int _compression;
-
+  bool read_internal(bool validate);
+  
 public:
   XMLTree();
-  XMLTree(const string &fn);
+  XMLTree(const string &fn, bool validate = false);
   XMLTree(const XMLTree *);
   ~XMLTree();
 
@@ -55,8 +56,10 @@ public:
   int compression() const { return _compression; };
   int set_compression(int);
 
-  bool read();
-  bool read(const string &fn) { set_filename(fn); return read(); };
+  bool read() { return read_internal(false); };
+  bool read(const string &fn) { set_filename(fn); return read_internal(false); };
+  bool read_and_validate() { return read_internal(true); };
+  bool read_and_validate(const string &fn) { set_filename(fn); return read_internal(true); };
   bool read_buffer(const string &);
 
   bool write() const;
@@ -77,9 +80,9 @@ private:
   XMLPropertyMap _propmap;
 
 public:
-  XMLNode(const string &);
-  XMLNode(const string &, const string &);
-  XMLNode(const XMLNode&);
+  XMLNode(const string& name);
+  XMLNode(const string& name, const string& content);
+  XMLNode(const XMLNode& other);
   ~XMLNode();
 
   const string name() const { return _name; };
@@ -105,8 +108,9 @@ public:
        { return ((XMLNode *) this)->property(n); };
   const XMLProperty *property(const std::string& ns) const
        { return ((XMLNode *) this)->property(ns); };
-  XMLProperty *add_property(const char *, const string &);
-  XMLProperty *add_property(const char *, const char * = "");
+  XMLProperty *add_property(const char *name, const string& value);
+  XMLProperty *add_property(const char *name, const char *value = "");
+  XMLProperty *add_property(const char *name, const long value);
 
   void remove_property(const string &);
 
index 464bbbe13e42f85cfe4f7693d815aa4decfdf3a1..fc0da41812ef85bff1feca3d441a428c43696f8e 100644 (file)
@@ -1,6 +1,6 @@
 test: xpath
        LD_LIBRARY_PATH=..:../../sigc++2:../../glibmm2 ./xpath
-       LD_LIBRARY_PATH=..:../../sigc++2:../../glibmm2 gprof ./xpath
+       LD_LIBRARY_PATH=..:../../sigc++2:../../glibmm2 gprof ./xpath > gprof.out
 
 xpath: xpath.cc
-       gcc -o $@ -g -pg -I.. `xml2-config --libs --cflags` -L.. -L../../sigc++2 -L../../glibmm2 -lstdc++ -lpbd -lglibmm2 -lsigc++2  $< 
\ No newline at end of file
+       gcc -o $@ -g -pg -I.. `xml2-config --libs --cflags` -L.. -L../../sigc++2 -L../../glibmm2 -lstdc++ -lpbd -lglibmm2 -lsigc++2  $< 
index 6dc28fa91c36d193949f9fd29261377f8026eff0..54b844f16c2376a791f8d21d6a754ff029fe4b8b 100644 (file)
@@ -83,4 +83,16 @@ int main()
                cout << "\t found attribute node: " << node->name()  
                     << " value: " << node->attribute_value() << endl;
        }       
+       
+       cout << endl << endl << "Test 6: ProtoolsPatchFile.midnam: Find available channels on 'Name Set 1'" << endl;
+       result = doc3.root()->find(
+               "//ChannelNameSet[@Name = 'Name Set 1']//AvailableChannel[@Available = 'true']/@Channel");
+       
+       assert(result->size() == 15);
+       for(XMLSharedNodeList::const_iterator i = result->begin(); i != result->end(); ++i) {
+               boost::shared_ptr<XMLNode> node = (*i);
+               cout << "\t found available Channel: " << node->name()  
+                    << " value: " << node->attribute_value() << endl;
+       }       
+       
 }
index 522b1dc3bcd3316482d3e3841ec3d93862a2923c..ec1ca5d2b9e0ba5c4605106c99a067d4f4592e8c 100644 (file)
@@ -22,12 +22,12 @@ XMLTree::XMLTree()
 { 
 }
 
-XMLTree::XMLTree(const string &fn)
+XMLTree::XMLTree(const string &fn, bool validate)
        : _filename(fn), 
        _root(0), 
        _compression(0)
 { 
-       read(); 
+       read_internal(validate); 
 }
 
 XMLTree::XMLTree(const XMLTree * from)
@@ -48,9 +48,9 @@ int
 XMLTree::set_compression(int c)
 {
        if (c > 9) {
-               c = 9;
+               c = 9;
        } else if (c < 0) {
-               c = 0;
+               c = 0;
        }
        
        _compression = c;
@@ -59,24 +59,55 @@ XMLTree::set_compression(int c)
 }
 
 bool 
-XMLTree::read(void)
+XMLTree::read_internal(bool validate)
 {
-       xmlDocPtr doc;
-       
+       //shouldnt be used anywhere ATM, remove if so!
+       assert(!validate);
        if (_root) {
                delete _root;
                _root = 0;
        }
        
+       xmlParserCtxtPtr ctxt; /* the parser context */
+       xmlDocPtr doc; /* the resulting document tree */
+       
        xmlKeepBlanksDefault(0);
+       /* parse the file, activating the DTD validation option */
+       if(validate) {
+               /* create a parser context */
+               ctxt = xmlNewParserCtxt();
+               if (ctxt == NULL) {
+                       return false;
+               }
+               doc = xmlCtxtReadFile(ctxt, _filename.c_str(), NULL, XML_PARSE_DTDVALID);
+       } else {
+               doc = xmlParseFile(_filename.c_str());
+       }
        
-       doc = xmlParseFile(_filename.c_str());
-       if (!doc) {
-               return false;
+       /* check if parsing suceeded */
+       if (doc == NULL) {
+               if(validate) {
+                       xmlFreeParserCtxt(ctxt);
+               }
+               return false;
+       } else {
+       /* check if validation suceeded */
+               if (validate && ctxt->valid == 0) {
+                       xmlFreeParserCtxt(ctxt);
+                       xmlFreeDoc(doc);
+                       xmlCleanupParser();
+                       throw XMLException("Failed to validate document " + _filename);
+               }
        }
        
        _root = readnode(xmlDocGetRootElement(doc));
+       
+       /* free up the parser context */
+       if(validate) {
+               xmlFreeParserCtxt(ctxt);
+       }
        xmlFreeDoc(doc);
+       xmlCleanupParser();
        
        return true;
 }
@@ -129,15 +160,15 @@ XMLTree::write(void) const
 void
 XMLTree::debug(FILE* out) const
 {
-    xmlDocPtr doc;
-    XMLNodeList children;
+       xmlDocPtr doc;
+       XMLNodeList children;
 
-    xmlKeepBlanksDefault(0);
-    doc = xmlNewDoc((xmlChar *) XML_VERSION);
-    xmlSetDocCompressMode(doc, _compression);
-    writenode(doc, _root, doc->children, 1);
-    xmlDebugDumpDocument (out, doc);
-    xmlFreeDoc(doc);
+       xmlKeepBlanksDefault(0);
+       doc = xmlNewDoc((xmlChar *) XML_VERSION);
+       xmlSetDocCompressMode(doc, _compression);
+       writenode(doc, _root, doc->children, 1);
+       xmlDebugDumpDocument (out, doc);
+       xmlFreeDoc(doc);
 }
 
 const string & 
@@ -202,7 +233,7 @@ XMLNode::~XMLNode()
        for (curchild = _children.begin(); curchild != _children.end(); ++curchild) {
                delete *curchild;
        }
-           
+               
        for (curprop = _proplist.begin(); curprop != _proplist.end(); ++curprop) {
                delete *curprop;
        }
@@ -216,7 +247,7 @@ XMLNode::set_content(const string & c)
        } else {
                _is_content = true;
        }
-           
+               
        _content = c;
        
        return _content;
@@ -232,13 +263,13 @@ XMLNode::child (const char *name) const
        if (name == 0) {
                return 0;
        }
-           
+               
        for (cur = _children.begin(); cur != _children.end(); ++cur) {
                if ((*cur)->name() == name) {
                        return *cur;
                }
        }
-           
+               
        return 0;
 }
 
@@ -253,7 +284,7 @@ XMLNode::children(const string& n) const
        if (n.empty()) {
                return _children;
        }
-           
+               
        retval.erase(retval.begin(), retval.end());
        
        for (cur = _children.begin(); cur != _children.end(); ++cur) {
@@ -261,7 +292,7 @@ XMLNode::children(const string& n) const
                        retval.insert(retval.end(), *cur);
                }
        }
-           
+               
        return retval;
 }
 
@@ -370,6 +401,14 @@ XMLNode::add_property(const char * n, const char * v)
        return add_property(n, vs);
 }
 
+XMLProperty *
+XMLNode::add_property(const char *name, const long value)
+{
+       static char str[1024];
+       snprintf(str, 1024, "%ld", value);
+       return add_property(name, str);
+}
+
 void 
 XMLNode::remove_property(const string & n)
 {
@@ -492,7 +531,7 @@ writenode(xmlDocPtr doc, XMLNode * n, xmlNodePtr p, int root = 0)
        } else {
                node = xmlNewChild(p, 0, (xmlChar *) n->name().c_str(), 0);
        }
-           
+               
        if (n->is_content()) {
                node->type = XML_TEXT_NODE;
                xmlNodeSetContentLen(node, (const xmlChar *) n->content().c_str(), n->content().length());
@@ -502,7 +541,7 @@ writenode(xmlDocPtr doc, XMLNode * n, xmlNodePtr p, int root = 0)
        for (curprop = props.begin(); curprop != props.end(); ++curprop) {
                xmlSetProp(node, (xmlChar *) (*curprop)->name().c_str(), (xmlChar *) (*curprop)->value().c_str());
        }
-           
+               
        children = n->children();
        for (curchild = children.begin(); curchild != children.end(); ++curchild) {
                writenode(doc, *curchild, node);