diff options
author | randomdan <randomdan@localhost> | 2010-07-11 23:32:11 +0000 |
---|---|---|
committer | randomdan <randomdan@localhost> | 2010-07-11 23:32:11 +0000 |
commit | ef626d76076bae21718bf22508bb91c7661bfc14 (patch) | |
tree | fec3389474f5ac1dd843970061209830bb18d79e | |
parent | Implement variables (prefixed strings to find values elsewhere) (diff) | |
download | project2-ef626d76076bae21718bf22508bb91c7661bfc14.tar.bz2 project2-ef626d76076bae21718bf22508bb91c7661bfc14.tar.xz project2-ef626d76076bae21718bf22508bb91c7661bfc14.zip |
Use master DB after writes
Split presentation code into it's own class
Implement sending emails using the presentation class and transforming it internally
-rw-r--r-- | project2/Jamfile.jam | 6 | ||||
-rw-r--r-- | project2/appEngine.h | 4 | ||||
-rw-r--r-- | project2/cgiAppEngine.cpp | 54 | ||||
-rw-r--r-- | project2/cgiAppEngine.h | 10 | ||||
-rw-r--r-- | project2/cgiEnvironment.h | 2 | ||||
-rw-r--r-- | project2/environment.h | 7 | ||||
-rw-r--r-- | project2/presenter.cpp | 38 | ||||
-rw-r--r-- | project2/presenter.h | 27 | ||||
-rw-r--r-- | project2/rdbmsDataSource.cpp | 15 | ||||
-rw-r--r-- | project2/rdbmsDataSource.h | 1 | ||||
-rw-r--r-- | project2/sendmailTask.cpp | 107 | ||||
-rw-r--r-- | project2/sendmailTask.h | 42 | ||||
-rw-r--r-- | project2/sqlIterate.cpp | 1 | ||||
-rw-r--r-- | project2/sqlIterate.h | 1 | ||||
-rw-r--r-- | project2/task.cpp | 3 | ||||
-rw-r--r-- | project2/xmlObjectLoader.h | 2 |
16 files changed, 278 insertions, 42 deletions
diff --git a/project2/Jamfile.jam b/project2/Jamfile.jam index 4fdd54c..771a473 100644 --- a/project2/Jamfile.jam +++ b/project2/Jamfile.jam @@ -3,15 +3,20 @@ using gcc ; alias libxmlpp : : : : <cflags>"`pkg-config --cflags libxml++-2.6`" <linkflags>"`pkg-config --libs libxml++-2.6`" ; +alias libxslt : : : : + <cflags>"`pkg-config --cflags libexslt`" + <linkflags>"`pkg-config --libs libexslt`" ; lib fcgi : : <name>fcgi ; lib fcgi++ : : <name>fcgi++ ; lib odbc : : <name>odbc ; lib boost_regex : : <name>boost_regex ; lib cgicc : : <name>cgicc ; +lib esmtp : : <name>esmtp ; exe p2web : libxmlpp + libxslt [ glob *.cpp ] ../libmisc ../libodbcpp : @@ -19,6 +24,7 @@ exe p2web : <include>../libodbcpp/ <library>boost_regex <library>odbc + <library>esmtp <library>cgicc <library>fcgi++ <library>fcgi ; diff --git a/project2/appEngine.h b/project2/appEngine.h index 59e0d4d..7f37cfd 100644 --- a/project2/appEngine.h +++ b/project2/appEngine.h @@ -4,6 +4,7 @@ #include "environment.h" #include "dataSource.h" #include "session.h" +#include "presenter.h" class ApplicationEngine { public: @@ -16,6 +17,7 @@ class ApplicationEngine { virtual void process() const = 0; virtual const Environment * env() const = 0; virtual SessionPtr session() const = 0; + virtual PresenterPtr getPresenter(const std::string & group, const std::string & id) const = 0; template <class DataSourceType> const DataSourceType * dataSource(const std::string & name) const @@ -31,7 +33,7 @@ class ApplicationEngine { return s; } static ApplicationEngine * getCurrent() { return currentEngine; } - protected: + // protected: mutable DataSources datasources; private: static ApplicationEngine * currentEngine; diff --git a/project2/cgiAppEngine.cpp b/project2/cgiAppEngine.cpp index a419be0..e041d20 100644 --- a/project2/cgiAppEngine.cpp +++ b/project2/cgiAppEngine.cpp @@ -72,18 +72,13 @@ CgiApplicationEngine::Stage::~Stage() CgiApplicationEngine::PresentStage::PresentStage(const CgiApplicationEngine * e, const std::string & id) : CgiApplicationEngine::Stage(e), - present("present/" + id + ".xml") + Presenter("present", id) +{ +} +CgiApplicationEngine::PresentStage::PresentStage(const CgiApplicationEngine * e, const std::string & group, const std::string & id) : + CgiApplicationEngine::Stage(e), + Presenter(group, id) { - while (xmlXIncludeProcessFlags(present.get_document()->cobj(), XML_PARSE_NOXINCNODE) > 0); - xmlpp::Element * presentRoot = present.get_document()->get_root_node(); - responseRootNodeName = presentRoot->get_attribute_value("root"); - responseStyle = presentRoot->get_attribute_value("style"); - - Loaders loaders; - _DataSource::AddLoaders(loaders, appEngine->datasources); - _ParamChecker::AddLoaders(loaders, parameterChecks); - _View::AddLoaders(loaders, views); - _LoaderBase::collectAll(loaders, "project2", presentRoot, true, true); } CgiApplicationEngine::PresentStage::~PresentStage() { @@ -91,20 +86,16 @@ CgiApplicationEngine::PresentStage::~PresentStage() CgiApplicationEngine::Stage * CgiApplicationEngine::PresentStage::run() { - BOOST_FOREACH(OrderedParamCheckers::value_type pc, parameterChecks) { - if (!pc.second->performCheck(appEngine)) { - return new PresentStage(appEngine, pc.second->present); - } - } - boost::shared_ptr<xmlpp::Document> responseDoc = boost::shared_ptr<xmlpp::Document>(new xmlpp::Document("1.0")); - xmlpp::Element * responseRoot = responseDoc->create_root_node(responseRootNodeName); - BOOST_FOREACH(Views::value_type s, views) { - s.second->execute(responseRoot, this->appEngine); - } - // These were for debug... but why not pass them on? - xmlNewNs(responseRoot->cobj(), BAD_CAST "http://project2.randomdan.homeip.net/", BAD_CAST "project2"); - responseRoot->add_child("fqdn", "project2")->set_child_text(appEngine->_env->getServerName()); - responseRoot->add_child("requesturi", "project2")->set_child_text(appEngine->_env->getScriptName()); + appEngine->doc = getDataDocument(); + sessionsContainer->CleanUp(); + return NULL; +} + +Presenter::XmlDocumentPtr +CgiApplicationEngine::PresentStage::getDataDocument() const +{ + XmlDocumentPtr responseDoc = Presenter::getDataDocument(); + xmlpp::Element * responseRoot = responseDoc->get_root_node(); // URL elements xmlpp::Element * uriElems = responseRoot->add_child("uriElems", "project2"); BOOST_FOREACH(std::string s, appEngine->_env->elems) { @@ -136,12 +127,11 @@ CgiApplicationEngine::PresentStage::run() xmlNewDocPI(responseDoc->cobj(), BAD_CAST "xml-stylesheet", BAD_CAST buf)); } free(buf); - appEngine->doc = responseDoc; - sessionsContainer->CleanUp(); - return NULL; + return responseDoc; } -CgiApplicationEngine::RequestStage::RequestStage(const CgiApplicationEngine * e, const std::string & id) : CgiApplicationEngine::Stage(e) +CgiApplicationEngine::RequestStage::RequestStage(const CgiApplicationEngine * e, const std::string & id) : + CgiApplicationEngine::Stage(e) { xmlpp::DomParser request("request/" + id + ".xml"); while (xmlXIncludeProcessFlags(request.get_document()->cobj(), XML_PARSE_NOXINCNODE) > 0); @@ -191,3 +181,9 @@ CgiApplicationEngine::session() const return sessionsContainer->GetSession(sessionID); } +PresenterPtr +CgiApplicationEngine::getPresenter(const std::string & group, const std::string & id) const +{ + return PresenterPtr(new PresentStage(this, group, id)); +} + diff --git a/project2/cgiAppEngine.h b/project2/cgiAppEngine.h index 0172eee..d2a1f44 100644 --- a/project2/cgiAppEngine.h +++ b/project2/cgiAppEngine.h @@ -4,6 +4,7 @@ #include "appEngine.h" #include "task.h" #include "paramChecker.h" +#include "presenter.h" #include <boost/shared_ptr.hpp> #include <boost/uuid/uuid.hpp> #include <libxml++/document.h> @@ -28,6 +29,7 @@ class CgiApplicationEngine : public ApplicationEngine { } const Environment * env() const; SessionPtr session() const; + PresenterPtr getPresenter(const std::string & group, const std::string & id) const; const CgiEnvironment * _env; protected: cgicc::HTTPContentHeader * header; @@ -51,16 +53,14 @@ class CgiApplicationEngine : public ApplicationEngine { protected: NoOutputExecutes tasks; }; - class PresentStage : public Stage { + class PresentStage : public Stage, public Presenter { public: PresentStage(const CgiApplicationEngine *, const std::string & id); + PresentStage(const CgiApplicationEngine *, const std::string & group, const std::string & id); virtual ~PresentStage(); virtual Stage * run(); protected: - Views views; - Glib::ustring responseRootNodeName; - Glib::ustring responseStyle; - xmlpp::DomParser present; + XmlDocumentPtr getDataDocument() const; }; mutable Stage * currentStage; mutable boost::uuids::uuid sessionID; diff --git a/project2/cgiEnvironment.h b/project2/cgiEnvironment.h index 7d9b820..7df65c9 100644 --- a/project2/cgiEnvironment.h +++ b/project2/cgiEnvironment.h @@ -17,6 +17,8 @@ class CgiEnvironment : public Environment, public cgicc::CgiEnvironment { Glib::ustring getParamUri(const std::string & idx) const; Glib::ustring getParamQuery(const std::string & idx) const; + std::string getServerName() const { return cgicc::CgiEnvironment::getServerName(); } + std::string getScriptName() const { return cgicc::CgiEnvironment::getScriptName(); } std::vector<std::string> elems; const cgicc::Cgicc * const cgi; diff --git a/project2/environment.h b/project2/environment.h index b5e9307..2925f13 100644 --- a/project2/environment.h +++ b/project2/environment.h @@ -11,8 +11,11 @@ class Environment { Environment(); virtual ~Environment() = 0; - virtual Glib::ustring getParamUri(const std::string & idx) const; - virtual Glib::ustring getParamQuery(const std::string & idx) const; + virtual Glib::ustring getParamUri(const std::string & idx) const = 0; + virtual Glib::ustring getParamQuery(const std::string & idx) const = 0; + + virtual std::string getServerName() const = 0; + virtual std::string getScriptName() const = 0; private: }; diff --git a/project2/presenter.cpp b/project2/presenter.cpp new file mode 100644 index 0000000..95ac53d --- /dev/null +++ b/project2/presenter.cpp @@ -0,0 +1,38 @@ +#include "presenter.h" +#include <libxml/xinclude.h> +#include "dataSource.h" +#include "appEngine.h" + +Presenter::Presenter(const std::string & group, const std::string & file) : + present(group + "/" + file + ".xml"), + responseRootNodeName(present.get_document()->get_root_node()->get_attribute_value("root")), + responseStyle(present.get_document()->get_root_node()->get_attribute_value("style")) +{ + while (xmlXIncludeProcessFlags(present.get_document()->cobj(), XML_PARSE_NOXINCNODE) > 0); + + Loaders loaders; + _DataSource::AddLoaders(loaders, ApplicationEngine::getCurrent()->datasources); + _View::AddLoaders(loaders, views); + _LoaderBase::collectAll(loaders, "project2", present.get_document()->get_root_node(), true, true); +} + +Presenter::~Presenter() +{ +} + +Presenter::XmlDocumentPtr +Presenter::getDataDocument() const +{ + ApplicationEngine * appEngine = ApplicationEngine::getCurrent(); + XmlDocumentPtr responseDoc = boost::shared_ptr<xmlpp::Document>(new xmlpp::Document("1.0")); + xmlpp::Element * responseRoot = responseDoc->create_root_node(responseRootNodeName); + BOOST_FOREACH(Views::value_type s, views) { + s.second->execute(responseRoot, appEngine); + } + // These were for debug... but why not pass them on? + xmlNewNs(responseRoot->cobj(), BAD_CAST "http://project2.randomdan.homeip.net/", BAD_CAST "project2"); + responseRoot->add_child("servername", "project2")->set_child_text(appEngine->env()->getServerName()); + responseRoot->add_child("scriptname", "project2")->set_child_text(appEngine->env()->getScriptName()); + return responseDoc; +} + diff --git a/project2/presenter.h b/project2/presenter.h new file mode 100644 index 0000000..d862f1f --- /dev/null +++ b/project2/presenter.h @@ -0,0 +1,27 @@ +#ifndef PRESENTER_H +#define PRESENTER_H + +#include <boost/shared_ptr.hpp> +#include <glibmm/ustring.h> +#include <libxml++/parsers/domparser.h> +#include "view.h" + +class Presenter { + public: + typedef boost::shared_ptr<xmlpp::Document> XmlDocumentPtr; + Presenter(const std::string & group, const std::string & file); + ~Presenter(); + + virtual XmlDocumentPtr getDataDocument() const; + + protected: + Views views; + xmlpp::DomParser present; + public: + const Glib::ustring responseRootNodeName; + const Glib::ustring responseStyle; +}; +typedef boost::shared_ptr<Presenter> PresenterPtr; + +#endif + diff --git a/project2/rdbmsDataSource.cpp b/project2/rdbmsDataSource.cpp index ab515a1..e9c0089 100644 --- a/project2/rdbmsDataSource.cpp +++ b/project2/rdbmsDataSource.cpp @@ -12,7 +12,8 @@ _RdbmsDataSource::_RdbmsDataSource(const xmlpp::Element * p) : _SourceObject(p), _DataSource(p), masterDsn(xmlChildText(p, "masterdsn")), - preferLocal(p->get_attribute_value("preferlocal") != "false") + preferLocal(p->get_attribute_value("preferlocal") != "false"), + changesMade(false) { BOOST_FOREACH(const xmlpp::Node * node, p->find("readonly/dsn")) { const xmlpp::Element * elem = dynamic_cast<const xmlpp::Element *>(node); @@ -29,16 +30,21 @@ _RdbmsDataSource::getWritable() const if (!master->inTx()) { master->beginTx(); } + changesMade = true; return *master; } ODBC::Connection & _RdbmsDataSource::getReadonly() const { + if (changesMade) { + return *connectTo(masterDsn); + } if (localhost.length() == 0 && preferLocal) { struct utsname name; if (uname(&name)) { - syslog(LOG_WARNING, "%s: Unable to determine local host name (%d:%s)", __FUNCTION__, errno, strerror(errno)); + syslog(LOG_WARNING, "%s: Unable to determine local host name (%d:%s)", + __FUNCTION__, errno, strerror(errno)); localhost = "unknown"; } else { @@ -48,7 +54,8 @@ _RdbmsDataSource::getReadonly() const if (preferLocal) { ReadonlyDSNs::const_iterator ro = roDSNs.find(localhost); if (ro == roDSNs.end()) { - syslog(LOG_WARNING, "%s: No database host makes local host name (%s) Will use master DSN", __FUNCTION__, localhost.c_str()); + syslog(LOG_WARNING, "%s: No database host makes local host name (%s) Will use master DSN", + __FUNCTION__, localhost.c_str()); return *connectTo(masterDsn); } return *connectTo(ro->second); @@ -81,7 +88,9 @@ _RdbmsDataSource::rollback() if (m != dbhosts.end() && m->second->inTx()) { m->second->rollbackTx(); } + changesMade = false; } + _RdbmsDataSource::ConnectionPtr _RdbmsDataSource::connectTo(const std::string & dsn) { diff --git a/project2/rdbmsDataSource.h b/project2/rdbmsDataSource.h index e5709b7..57a607f 100644 --- a/project2/rdbmsDataSource.h +++ b/project2/rdbmsDataSource.h @@ -23,6 +23,7 @@ class _RdbmsDataSource : public _DataSource { protected: static ConnectionPtr connectTo(const std::string & dsn); ReadonlyDSNs roDSNs; + mutable bool changesMade; private: mutable std::string localhost; diff --git a/project2/sendmailTask.cpp b/project2/sendmailTask.cpp new file mode 100644 index 0000000..bb11017 --- /dev/null +++ b/project2/sendmailTask.cpp @@ -0,0 +1,107 @@ +#include "sendmailTask.h" +#include <boost/foreach.hpp> +#include <stdexcept> +#include "xmlObjectLoader.h" +#include "modifycommand.h" +#include "appEngine.h" +#include "rdbmsDataSource.h" +#include "presenter.h" +#include <libxslt/transform.h> +#include <libesmtp.h> +#include <libxslt/xsltutils.h> +_SendMailTask::_SendMailTask(const xmlpp::Element * p) : + _SourceObject(p), + _Task(p), + to(p->get_attribute("to")), + subject(p->get_attribute("subject")), + from(p->get_attribute("from")), + server(p->get_attribute("server")), + present(p->get_attribute_value("present").raw()), + body(NULL) +{ +} + +_SendMailTask::~_SendMailTask() +{ + if (body) { + xmlFree(body); + } +} + +const char * +_SendMailTask::writeMailWrapper(void ** buf, int * len, void * arg) +{ + return static_cast<const _SendMailTask*>(arg)->writeMail(buf, len); +} + +void +_SendMailTask::execute(const ApplicationEngine * ep, const PerRowValues * parent) const +{ + PresenterPtr p = ep->getPresenter("emails", present); + Presenter::XmlDocumentPtr data = p->getDataDocument(); + typedef boost::shared_ptr<xsltStylesheet> XsltStyleSheetPtr; + typedef boost::shared_ptr<xmlDoc> XmlDocumentPtr; + + // Do transform + XsltStyleSheetPtr cur = XsltStyleSheetPtr(xsltParseStylesheetFile(BAD_CAST p->responseStyle.c_str()), + xsltFreeStylesheet); + if (!cur) { throw std::runtime_error("Failed to load stylesheet"); } + XmlDocumentPtr result = XmlDocumentPtr(xsltApplyStylesheet(cur.get(), data->cobj(), NULL), xmlFreeDoc); + if (!result) { throw std::runtime_error("Failed to perform transformation"); } + xmlDocDumpFormatMemoryEnc(result.get(), &body, &bodyLength, "utf-8", 0); + + headers[-1] = HeaderPtr(new Header("To", to)); + headers[-2] = HeaderPtr(new Header("From", from)); + headers[-3] = HeaderPtr(new Header("Subject", subject)); + headers[-4] = HeaderPtr(new Header("Content-Type", "multipart/alternative; boundary=\"<<divider>>\"")); + headers[-5] = HeaderPtr(new Header("MIME-Version", "1.0")); + headers[-6] = HeaderPtr(new Header("Content-Transfer-Encoding", "binary")); + position = -1; + + // Write email + smtp_session_t session = smtp_create_session(); + smtp_message_t message = smtp_add_message(session); + smtp_set_server(session, server().c_str()); + smtp_set_header(message, "To", NULL, NULL); + smtp_add_recipient(message, to().c_str()); + smtp_set_messagecb(message, writeMailWrapper, (_SendMailTask*)this); + if (!smtp_start_session(session)) { + char buf[BUFSIZ]; + smtp_strerror(smtp_errno(), buf, sizeof buf); + throw std::runtime_error(buf); + } +} + +const char * +_SendMailTask::writeMail(void ** buf, int * len) const +{ + if (!len) { + position = -1; + return NULL; + } + Headers::const_iterator h = headers.find(position); + if (h != headers.end()) { + *len = asprintf((char**)buf, "%s: %s\r\n", h->second->first.c_str(), h->second->second.c_str()); + position -= 1; + return (const char *)*buf; + } + else if (position < 0) { + position = 0; + *len = 51; + return "\r\n--<<divider>>\r\nContent-Type: text/html; utf-8\r\n\r\n"; + } + else if (position == bodyLength) { + position = bodyLength + 1; + *len = 17; + return "--<<divider>>--\r\n"; + } + else if (position >= 0 && position < bodyLength) { + *buf = NULL; + *len = bodyLength; + position = bodyLength; + return (const char *)body; + } + else { + return NULL; + } +} diff --git a/project2/sendmailTask.h b/project2/sendmailTask.h new file mode 100644 index 0000000..e3504b6 --- /dev/null +++ b/project2/sendmailTask.h @@ -0,0 +1,42 @@ +#ifndef SENDMAILTASK_H +#define SENDMAILTASK_H + +#include <libxml++/nodes/element.h> +#include <boost/shared_ptr.hpp> +#include <map> +#include "task.h" +#include "variables.h" + +class ApplicationEngine; +class _View; +typedef unsigned char xmlChar; + +class _SendMailTask : public _Task { + public: + _SendMailTask(const xmlpp::Element * p); + virtual ~_SendMailTask(); + virtual void execute(const ApplicationEngine *, const PerRowValues * parent = NULL) const; + + protected: + const Variable to; + const Variable subject; + const Variable from; + const Variable server; + const std::string present; + + private: + static const char * writeMailWrapper(void ** buf, int * len, void * arg); + const char * writeMail(void ** buf, int * len) const; + + typedef std::pair<std::string, const Glib::ustring> Header; + typedef boost::shared_ptr<Header> HeaderPtr; + typedef std::map<int, HeaderPtr> Headers; + mutable Headers headers; + mutable xmlChar * body; + mutable int bodyLength; + mutable int position; +}; +typedef boost::shared_ptr<_SendMailTask> SendMailTask; +typedef std::map<std::string, SendMailTask> SendMailTasks; + +#endif diff --git a/project2/sqlIterate.cpp b/project2/sqlIterate.cpp index 5499940..81f1561 100644 --- a/project2/sqlIterate.cpp +++ b/project2/sqlIterate.cpp @@ -1,6 +1,7 @@ #include "sqlIterate.h" #include "xml.h" #include "selectcommand.h" +#include "rdbmsDataSource.h" #include "column.h" #include <string.h> #include <syslog.h> diff --git a/project2/sqlIterate.h b/project2/sqlIterate.h index 41b5648..0c26bdd 100644 --- a/project2/sqlIterate.h +++ b/project2/sqlIterate.h @@ -6,7 +6,6 @@ #include <map> #include "selectcommand.h" #include "iterate.h" -#include "rdbmsDataSource.h" #include "iHaveParameters.h" class ApplicationEngine; diff --git a/project2/task.cpp b/project2/task.cpp index a84ad7e..65ce359 100644 --- a/project2/task.cpp +++ b/project2/task.cpp @@ -4,6 +4,7 @@ #include "sqlTask.h" #include "sessionClearTask.h" #include "sessionSetTask.h" +#include "sendmailTask.h" _Task::_Task(const xmlpp::Element * p) : _SourceObject(p), @@ -21,6 +22,7 @@ _Task::AddLoaders(Loaders & l, OrderedTasks & tasks) l.insert(LoadersVT("sqltask", _LoaderBase::Make<_SqlTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks))); l.insert(LoadersVT("sessionclear", _LoaderBase::Make<_SessionClearTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks))); l.insert(LoadersVT("sessionset", _LoaderBase::Make<_SessionSetTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks))); + l.insert(LoadersVT("sendmail", _LoaderBase::Make<_SendMailTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks))); } void @@ -29,5 +31,6 @@ _Task::AddLoaders(Loaders & l, NoOutputExecutes & tasks) l.insert(LoadersVT("sqltask", _LoaderBase::Make<_SqlTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks))); l.insert(LoadersVT("sessionclear", _LoaderBase::Make<_SessionClearTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks))); l.insert(LoadersVT("sessionset", _LoaderBase::Make<_SessionSetTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks))); + l.insert(LoadersVT("sendmail", _LoaderBase::Make<_SendMailTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks))); } diff --git a/project2/xmlObjectLoader.h b/project2/xmlObjectLoader.h index 59c4f1c..e68a7d8 100644 --- a/project2/xmlObjectLoader.h +++ b/project2/xmlObjectLoader.h @@ -29,7 +29,7 @@ class _LoaderBase { void storeByX(std::map<KeyT, boost::shared_ptr<T> > * m, boost::shared_ptr<T> o) { - if (o) { + if (o && m->find(o.get()->*Var) == m->end()) { (*m)[o.get()->*Var] = o; } } |