- updated & documented all about serializers & visitors

git-svn-id: svn://svn.gna.org/svn/sgpemv2/trunk@764 3ecf2c5c-341e-0410-92b4-d18e462d057c
This commit is contained in:
paolo 2006-07-15 01:28:35 +00:00
parent 35ae7f4eae
commit 88d5ca2fe1
6 changed files with 359 additions and 103 deletions

View File

@ -19,29 +19,17 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "xml_serializer.hh"
#include "xml_visitor.hh"
#include "xml_serializer_factory.hh"
#include "xml_visitor.hh"
#include "string_utils.hh"
#include "environment.hh"
#include "history.hh"
/*
#include "backend/concrete_environment.hh"
#include "backend/concrete_history.hh"
*/
#include "backend/environment.hh"
#include "backend/history.hh"
#include "backend/process.hh"
#include "backend/serializer_error.hh"
#include "backend/string_utils.hh"
using namespace sgpem;
#include <iostream>
using namespace std;
XMLSerializer::~XMLSerializer()
{
}
@ -81,7 +69,6 @@ void XMLSerializer::restore_snapshot(const Glib::ustring& filename, History& his
{
// TODO - all to do!!
// DEBUG - remove me when finished
XMLSerializerFactory fact(hist);
#ifdef LIBXML_SAX1_ENABLED
xmlDocPtr doc;
@ -97,6 +84,10 @@ void XMLSerializer::restore_snapshot(const Glib::ustring& filename, History& his
xmlCleanupParser();
throw SerializerError("Parsing Error: doc is invalid.");
}
clear_history(hist);
XMLSerializerFactory fact(hist);
// read all elements and fill hist
read_doc(doc, fact);
@ -121,13 +112,13 @@ void XMLSerializer::restore_snapshot(const Glib::ustring& filename, History& his
const Glib::ustring XMLSerializer::get_filename_extension()
{
return Glib::ustring("ocio");
return Glib::ustring("xsgp");
}
const Glib::ustring XMLSerializer::get_filename_description()
{
return Glib::ustring("SGPEMv2 XML formatted snapshot save file");
return Glib::ustring("SGPEMv2 XML savefile");
}
void XMLSerializer::fill_doc(xmlDocPtr doc, const History& hist)
@ -142,22 +133,27 @@ void XMLSerializer::fill_doc(xmlDocPtr doc, const History& hist)
/*
* Creates a DTD declaration. Isn't mandatory.
*/
// dtd = xmlCreateIntSubset(doc, BAD_CAST "root", NULL, BAD_CAST "tree2.dtd");
xmlDtdPtr dtd = xmlCreateIntSubset(doc, (const xmlChar *) "sgpem", NULL, (const xmlChar *) "sgpem.dtd");
/*
* xmlNewChild() creates a new node, which is "attached" as child node
* of root_node node.
*/
XMLVisitor xvisit(root_node);
xvisit.from_history(hist);
/*
//
// xmlNewChild() creates a new node, which is "attached" as child node
// of root_node node.
//
xmlNodePtr resources_node = xmlNewChild(root_node, NULL, (const xmlChar *) "resources", NULL);
/*
* The same as above, but the new child node doesn't have a content
*/
//
// The same as above, but the new child node doesn't have a content
//
xmlNodePtr schedulables_node = xmlNewChild(root_node, NULL, (const xmlChar *) "schedulables", NULL);
fill_resources(resources_node, hist);
fill_schedulables(schedulables_node, hist);
*/
}
/*
void XMLSerializer::fill_resources(xmlNodePtr resources_node, const History& hist)
{
@ -197,24 +193,33 @@ void XMLSerializer::fill_schedulables(xmlNodePtr schedulables_node, const Histor
iter++;
}
}
*/
void XMLSerializer::clear_history(History& hist)
{
/*
const Environment& env = hist.get_last_environment();
const Environment::Processes& pvect = env.get_processes();
typedef std::vector<Process*>::const_iterator proc_iterator;
proc_iterator iter = pvect.begin();
proc_iterator end = pvect.end();
while(iter!=end)
while(iter!=pvect.end())
{
xvisit.from_process(*(*iter));
pvect.
hist.remove(*(*iter));
iter = pvect.begin();
}
*/
const Environment::Resources& rvect = env.get_resources();
typedef Environment::Resources::const_iterator res_iterator;
res_iterator riter = rvect.begin();
while(riter!=rvect.end())
{
hist.remove((*riter).first);
riter = rvect.begin();
}
}
void XMLSerializer::read_doc(xmlDocPtr doc, XMLSerializerFactory& fact)
@ -230,14 +235,10 @@ void XMLSerializer::read_doc(xmlDocPtr doc, XMLSerializerFactory& fact)
throw SerializerError("Reading Error: xml doc is empty.");
}
// cout << "ROOT: " << root->name << endl;
xmlNodePtr cur;
cur = root->children;
while(cur!=NULL)
{
// cout << "NODE: " << cur->name << endl;
Glib::ustring name((const char *)cur->name);
if(name=="resources")
{
@ -258,25 +259,16 @@ XMLSerializerFactory::Parameters* read_properties(xmlAttrPtr prop)
XMLSerializerFactory::Parameters* par=new XMLSerializerFactory::Parameters();
while (prop != NULL) {
// // cout << "PROP: " << prop->name;
if(prop->children && xmlNodeIsText(prop->children)){
xmlChar *key = xmlNodeGetContent (prop->children);
// xmlChar *key = xmlNodeListGetString(doc, prop->children, 1);
if(key!=NULL)
{
// // cout << " VALUE: " << key;
std::pair<Glib::ustring, Glib::ustring> key_value(Glib::ustring((const char *)prop->name), Glib::ustring((const char *)key));
par->insert(key_value);
// c.insert(typename Cont::value_type(new_key, pos->second));
// cout << " pair PROP: " << key_value.first << " VALUE: " << key_value.second << endl;
xmlFree(key);
}
// else
// cout << " !VALUE IS NULL! ";
}
// cout << endl;
prop = prop->next;
}
return par;
@ -291,8 +283,6 @@ void XMLSerializer::read_resources(xmlNodePtr resources_node, XMLSerializerFacto
Glib::ustring node_name((const char *)cur->name);
if(node_name=="resource")
{
// cout << "read_resources NODE: " << cur->name << endl;
xmlAttrPtr prop = cur->properties;
XMLSerializerFactory::Parameters* par=read_properties(prop);
if(par!=NULL)
@ -317,8 +307,6 @@ void XMLSerializer::read_schedulables(xmlNodePtr schedulables_node, XMLSerialize
Glib::ustring node_name((const char *)cur->name);
if(node_name=="process")
{
// cout << "read_schedulables NODE: " << cur->name << endl;
xmlAttrPtr prop = cur->properties;
XMLSerializerFactory::Parameters* par=read_properties(prop);
if(par!=NULL)
@ -345,8 +333,6 @@ void XMLSerializer::read_threads(xmlNodePtr threads_node, XMLSerializerFactory&
Glib::ustring node_name((const char *)cur->name);
if(node_name=="thread")
{
// cout << "read_threads NODE: " << cur->name << endl;
xmlAttrPtr prop = cur->properties;
XMLSerializerFactory::Parameters* par=read_properties(prop);
if(par!=NULL)
@ -365,7 +351,6 @@ void XMLSerializer::read_requests(xmlNodePtr requests_node, XMLSerializerFactory
{
if(requests_node==NULL)
{
// cout << "read_requests NULL" << endl;
return;
}
@ -376,8 +361,6 @@ void XMLSerializer::read_requests(xmlNodePtr requests_node, XMLSerializerFactory
Glib::ustring node_name((const char *)cur->name);
if(node_name=="request")
{
// cout << "read_requests NODE: " << cur->name << endl;
xmlAttrPtr prop = cur->properties;
XMLSerializerFactory::Parameters* par=read_properties(prop);
if(par!=NULL)
@ -396,7 +379,6 @@ void XMLSerializer::read_subrequests(xmlNodePtr subrequest_node, XMLSerializerFa
{
if(subrequest_node==NULL)
{
// cout << "read_subrequest NULL" << endl;
return;
}
@ -407,8 +389,6 @@ void XMLSerializer::read_subrequests(xmlNodePtr subrequest_node, XMLSerializerFa
Glib::ustring node_name((const char *)cur->name);
if(node_name=="subrequest")
{
// cout << "read_subrequest NODE: " << cur->name << endl;
xmlAttrPtr prop = cur->properties;
XMLSerializerFactory::Parameters* par=read_properties(prop);
if(par!=NULL)

View File

@ -25,8 +25,10 @@
#include "serializer.hh"
#include <glibmm/ustring.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>
namespace sgpem
{
class XMLSerializerFactory;
@ -36,28 +38,137 @@ namespace sgpem
{
class XMLSerializer;
/**
\brief Loads and saves from/to XML
Serialization should be done to temporary files, which are then moved in place when serialization has finished without errors.
See mkstemp(3).
*/
class XMLSerializer : public Serializer
{
public:
XMLSerializer();
virtual ~XMLSerializer();
/**
Tries to open filename for writing, else throws SerializerError.
Respecting the SGPEMv2 DTD for the snapshots,
creates a new XML document node and write to disk
using a new xmlsave::XMLVisitor.
Calls fill_doc to accomplish this task.
\throws backend::SerializerError on error
*/
virtual void save_snapshot(const Glib::ustring& filename, const History& hist);
/**
\brief Re-initialize system status from a saved XML snapshot
Tries to open filename for reading, else throws SerializerError.
First validates file versus the SGPEMv2 DTD for snapshots, then reads it.
Calls read_doc to relize the work.
\throws backend::SerializerError
*/
virtual void restore_snapshot(const Glib::ustring& filename, History& hist);
/**
\return Constant string "xsgp"
*/
virtual const Glib::ustring get_filename_extension();
/**
\return Constant string "SGPEMv2 XML savefile"
*/
virtual const Glib::ustring get_filename_description();
protected:
private:
void fill_doc(xmlDocPtr doc, const History& hist);
void fill_resources(xmlNodePtr resources_node, const History& hist);
void fill_schedulables(xmlNodePtr schedulables_node, const History& hist);
/**
\brief Create the document root and fill the data
Create the document root and children "resources" and "schedulables",
then calls fill_resources and fill_schedulables to fill all with data.
*/
void fill_doc(xmlDocPtr doc, const History& hist);
/**
\brief Take a resources node and fill it with the data
For each resource in hist create a "resource" node and fill with data.
Uses an XMLVisitor object to do the task.
*/
// void fill_resources(xmlNodePtr resources_node, const History& hist);
/**
\brief Take a schedulables node and fill it with the data
For each resource in hist create a "schedulable" node and fill with data.
Also all schedulable sub nodes are generated too.
Uses an XMLVisitor object to do the task.
*/
// void fill_schedulables(xmlNodePtr schedulables_node, const History& hist);
/**
\brief Clear the passed history
For each process in history deletes it.
For each resource in history deletes it.
*/
void clear_history(History& hist);
/**
\brief Restore the snapshot from the passed xml document
Traverse the passed (previously readed) xml document and
rebuild the correct image using the XMLSerializerFactory object.
*/
void read_doc(xmlDocPtr doc, XMLSerializerFactory& fact);
/**
\brief Restore all the resources from the passed xml node
Traverse the passed (previously readed) xml node and
rebuild the correct resources image using the XMLSerializerFactory
object.
*/
void read_resources(xmlNodePtr resources_node, XMLSerializerFactory& fact);
/**
\brief Restore all the schedulables (processes) from the passed xml node
Traverse the passed (previously readed) xml node and
rebuild the correct processes (and sub objects) images using the
XMLSerializerFactory object.
*/
void read_schedulables(xmlNodePtr schedulables_node, XMLSerializerFactory& fact);
/**
\brief Restore all threads from the passed xml node
Traverse the passed (previously readed) xml node and
rebuild the correct threads (and sub objects) images using the
XMLSerializerFactory object.
*/
void read_threads(xmlNodePtr schedulables_node, XMLSerializerFactory& fact);
/**
\brief Restore all the requests from the passed xml node
Traverse the passed (previously readed) xml node and
rebuild the correct requests (and sub objects) images using the
XMLSerializerFactory object.
*/
void read_requests(xmlNodePtr schedulables_node, XMLSerializerFactory& fact);
/**
\brief Restore all the subrequests from the passed xml node
Traverse the passed (previously readed) xml node and
rebuild the correct subrequests image using the
XMLSerializerFactory object.
*/
void read_subrequests(xmlNodePtr schedulables_node, XMLSerializerFactory& fact);
};
}

View File

@ -187,7 +187,7 @@ XMLSerializerFactory::create_thread(Parameters& parameters)
}
// read "arrival-time" property
pos = parameters.find(Glib::ustring("arrival-time"));
pos = parameters.find(Glib::ustring("arrival-delta"));
if (pos != parameters.end()) {
string_to_int(pos->second, arrival_time);
}

View File

@ -41,32 +41,72 @@ namespace sgpem
namespace sgpem
{
class XMLSerializerFactory;
/**
\brief Creates objects given their class name and an array of their properties
Resources should come first in deserialization, so save pointers to new objects to a backend::Resource into a std::map<Glib::ustring,backend::Resource*>. This will come really handy when deserializing Requests.
*/
class XMLSerializerFactory
{
public:
typedef std::map<Glib::ustring, Glib::ustring> Parameters;
/**
\brief Contructor takes an history as readed data destination
*/
XMLSerializerFactory(History& hist);
/**
\brief A destructor, nothing else
*/
~XMLSerializerFactory();
/**
\return The data destination history associated with this factory
*/
History* get_history();
typedef Environment::resource_key_t resource_key_t;
// typedef const std::pair<resource_key_t, Resource*> ResourcePair;
//NOUSE typedef const std::map<const Glib::ustring&, const Glib::ustring&> Parameters;
// associate old keys with new ones
typedef std::map<Glib::ustring, Glib::ustring> Parameters;
// associate old keys with new ones
//typedef pair<resource_key_t, resource_key_t> TempPair;
typedef std::map<resource_key_t, resource_key_t> TempMap;
/**
\brief Creates objects from their name plus parameters
This method recognizes a class type and calls the appropriate
creator method, which must also take care of inserting the
new object in its right container, if necessary.
\throw SerializerError If not all necessary parameters for an object creation are provided
*/
void factory_method(const Glib::ustring& class_name, Parameters& parameters);
protected:
private:
typedef Environment::resource_key_t resource_key_t;
// associate old keys with new ones
typedef std::map<resource_key_t, resource_key_t> TempMap;
/**
Resource factory from given parameters
*/
History::ResourcePair create_resource(Parameters& parameters);
/**
Process factory from given parameters
*/
Process& create_process(Parameters& parameters);
/**
Thread factory from given parameters
*/
Thread& create_thread(Parameters& parameters);
/**
Request factory from given parameters
*/
Request& create_request(Parameters& parameters);
/**
\throw A SerializerError if there's no existing associated resource
for a given ID parameter (it means the savefile is corrupted).
*/
SubRequest& create_subrequest(Parameters& parameters);
// history object to add to resources processes etc...

View File

@ -20,25 +20,20 @@
#include "xml_visitor.hh"
#include "string_utils.hh"
#include "resource.hh"
#include "gettext.h"
#include "environment.hh"
#include "history.hh"
#include "process.hh"
#include "thread.hh"
#include "request.hh"
#include "sub_request.hh"
#include "resource.hh"
#include "serializer_error.hh"
#include <iostream>
using namespace std;
#include "string_utils.hh"
#include "sub_request.hh"
#include "thread.hh"
using namespace sgpem;
/*
#include <iostream>
using namespace std;
*/
XMLVisitor::XMLVisitor(xmlNodePtr current)
: _current(current)
@ -51,6 +46,21 @@ XMLVisitor::~XMLVisitor()
void XMLVisitor::from_resource(const Resource& obj)
{
throw SerializerError(
_("XMLVisitor: unsupported method from_resource(const Resource& obj)")
);
}
void XMLVisitor::from_history(const History& obj)
{
from_history(_current, obj);
}
void XMLVisitor::from_environment(const Environment& obj)
{
from_environment(_current, obj);
}
@ -68,11 +78,13 @@ void XMLVisitor::from_thread(const Thread& obj)
void XMLVisitor::from_request(const Request& obj)
{
from_request(_current, obj);
}
void XMLVisitor::from_subrequest(const SubRequest& obj)
{
from_subrequest(_current, obj);
}
void XMLVisitor::from_resource(const Resource& obj, const Glib::ustring& key)
@ -85,11 +97,66 @@ void XMLVisitor::from_resource(const Resource& obj, const Glib::ustring& key)
/*
void XMLVisitor::from_resource(xmlNodePtr parent, const Resource& obj)
void XMLVisitor::from_history(xmlNodePtr parent, const History& hist)
{
if(parent!=NULL)
{
from_environment(parent, hist.get_last_environment());
}
else
{
throw SerializerError(_("Error trying to add data to empty XML node."));
}
}
*/
void XMLVisitor::from_environment(xmlNodePtr parent, const Environment& env)
{
if(parent==NULL)
{
throw SerializerError(_("Error trying to add data to empty XML node."));
}
//
//Enclosing block - save resources
//
{
xmlNodePtr resources_node = xmlNewChild(parent, NULL, (const xmlChar *) "resources", NULL);
const Environment::Resources& rvect = env.get_resources();
typedef Environment::Resources::const_iterator res_iterator;
res_iterator iter = rvect.begin();
res_iterator end = rvect.end();
while(iter!=end)
{
//XMLVisitor xvisit(resources_node);
Glib::ustring key;
int_to_string((int)(*iter).first, key);
//xvisit.from_resource(*((*iter).second), key);
from_resource(resources_node, *((*iter).second), key);
iter++;
}
}
//
//Enclosing block - save schedulables
//
{
xmlNodePtr schedulables_node = xmlNewChild(parent, NULL, (const xmlChar *) "schedulables", NULL);
const Environment::Processes& pvect = env.get_processes();
typedef std::vector<Process*>::const_iterator proc_iterator;
proc_iterator iter = pvect.begin();
proc_iterator end = pvect.end();
while(iter!=end)
{
// XMLVisitor xvisit(schedulables_node);
// xvisit.from_process(*(*iter));
from_process(schedulables_node, *(*iter));
iter++;
}
}
}
void XMLVisitor::from_resource(xmlNodePtr parent, const Resource& obj, const Glib::ustring& key)
{
@ -109,7 +176,7 @@ void XMLVisitor::from_resource(xmlNodePtr parent, const Resource& obj, const Gli
}
else
{
throw SerializerError("Error trying to add resource to empty XML node.");
throw SerializerError(_("Error trying to add resource to empty XML node."));
}
}
@ -131,7 +198,6 @@ void XMLVisitor::from_process(xmlNodePtr parent, const Process& obj)
// make a threads subnode
xmlNodePtr threads_node = xmlNewChild(process_node, NULL, (const xmlChar *) "threads", NULL);
// cout << "PROCESS: " << obj.get_name() << endl;
// iterate on threads
typedef std::vector<Thread*> Threads;
typedef std::vector<Thread*>::const_iterator thr_iterator;
@ -141,7 +207,6 @@ void XMLVisitor::from_process(xmlNodePtr parent, const Process& obj)
while(iter!=end)
{
const Thread* t = *iter;
// cout << "THREAD: " << t->get_name() << endl;
from_thread(threads_node, *(*iter));
iter++;
@ -149,7 +214,7 @@ void XMLVisitor::from_process(xmlNodePtr parent, const Process& obj)
}
else
{
throw SerializerError("Error trying to add process to empty XML node.");
throw SerializerError(_("Error trying to add process to empty XML node."));
}
}
@ -174,7 +239,6 @@ void XMLVisitor::from_thread(xmlNodePtr parent, const Thread& obj)
// make a requests subnode
xmlNodePtr requests_node = xmlNewChild(thread_node, NULL, (const xmlChar *) "requests", NULL);
// cout << "PROCESS: " << obj.get_name() << endl;
// iterate on requests
typedef std::vector<Request*> Requests;
typedef std::vector<Request*>::const_iterator req_iterator;
@ -184,7 +248,6 @@ void XMLVisitor::from_thread(xmlNodePtr parent, const Thread& obj)
while(iter!=end)
{
const Request* r = *iter;
// cout << "REQUEST: " << r->get_instant() << endl;
from_request(requests_node, *(*iter));
iter++;
@ -192,7 +255,7 @@ void XMLVisitor::from_thread(xmlNodePtr parent, const Thread& obj)
}
else
{
throw SerializerError("Error trying to add thread to empty XML node.");
throw SerializerError(_("Error trying to add thread to empty XML node."));
}
}
@ -210,7 +273,6 @@ void XMLVisitor::from_request(xmlNodePtr parent, const Request& obj)
// make a requests subnode
// xmlNodePtr subrequests_node = xmlNewChild(thread_node, NULL, (const xmlChar *) "requests", NULL);
// cout << "PROCESS: " << obj.get_name() << endl;
// iterate on subrequests
typedef std::vector<SubRequest*> SubRequests;
typedef std::vector<SubRequest*>::const_iterator subreq_iterator;
@ -220,14 +282,13 @@ void XMLVisitor::from_request(xmlNodePtr parent, const Request& obj)
while(iter!=end)
{
const SubRequest* sr = *iter;
// cout << "SUB REQUEST: " << sr->get_resource_key() << " places: " << sr->get_places() << " length: " << sr->get_length() << endl;
from_subrequest(request_node, *(*iter));
iter++;
}
}
else
{
throw SerializerError("Error trying to add request to empty XML node.");
throw SerializerError(_("Error trying to add request to empty XML node."));
}
}
@ -251,6 +312,6 @@ void XMLVisitor::from_subrequest(xmlNodePtr parent, const SubRequest& obj)
}
else
{
throw SerializerError("Error trying to add subrequest to empty XML node.");
throw SerializerError(_("Error trying to add subrequest to empty XML node."));
}
}

View File

@ -31,7 +31,7 @@ namespace sgpem
}
#include "config.h"
#include "serializer_visitor.hh"
#include "serialize_visitor.hh"
#include <glibmm/ustring.h>
#include <libxml/xmlmemory.h>
@ -41,20 +41,84 @@ namespace sgpem
{
class XMLVisitor;
class XMLVisitor : public SerializerVisitor
/**
\brief Serialize objects to XML
This class is a concrete class derived from SerializeVisitor
and implements each methodto serialize
into an xml tree.
The pointer to the sgpem (root) node must be passed as constructor parameter.
*/
class XMLVisitor : public SerializeVisitor
{
public:
/**
\brief Class constructor to save to the passed XML tree
Build an XMLVisitor taking a pointer to an xml node as parameter.
Every called method see this node as his relative root.
*/
XMLVisitor(xmlNodePtr current);
/**
\brief A pretty virtual destructor
*/
virtual ~XMLVisitor();
/**
\brief Add output to the serializer taking data from history
Wrapper method: call from_history(xmlNodePtr parent, const History& obj);
*/
virtual void from_history(const History& obj);
/**
\brief Add output to the serializer taking data from environment
Wrapper method: call from_environment(xmlNodePtr parent, const Environment& obj);
*/
virtual void from_environment(const Environment& obj);
/**
\brief Add output to the serializer taking data from resource
BUG: a resource must be saved with her own associated key.
Throw an exception.
*/
virtual void from_resource(const Resource& obj);
/**
\brief Add output to the serializer taking data from resource and key
BUG FIXED: This save a resource with her own associated key.
Wrapper method: call from_resource(xmlNodePtr parent, const Resource& obj, const Glib::ustring& key);
*/
virtual void from_resource(const Resource& obj, const Glib::ustring& key);
/**
\brief Add output to the serializer taking data from process
Wrapper method: call from_process(xmlNodePtr parent, const Process& obj);
*/
virtual void from_process(const Process& obj);
/**
\brief Add output to the serializer taking data from thread
Wrapper method: call from_thread(xmlNodePtr parent, const Thread& obj);
*/
virtual void from_thread(const Thread& obj);
/**
\brief Add output to the serializer taking data from request
Wrapper method: call from_request(xmlNodePtr parent, const Request& obj);
*/
virtual void from_request(const Request& obj);
/**
\brief Add output to the serializer taking data from subrequest
Wrapper method: call from_subrequest(xmlNodePtr parent, const SubRequest& obj);
*/
virtual void from_subrequest(const SubRequest& obj);
virtual void from_resource(const Resource& obj, const Glib::ustring& key);
private:
void from_history(xmlNodePtr parent, const History& obj);
void from_environment(xmlNodePtr parent, const Environment& obj);
void from_resource(xmlNodePtr parent, const Resource& obj, const Glib::ustring& key);
void from_process(xmlNodePtr parent, const Process& obj);
void from_thread(xmlNodePtr parent, const Thread& obj);