summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--project2/Jamfile.jam2
-rw-r--r--project2/appEngine.cpp3
-rw-r--r--project2/appEngine.h5
-rw-r--r--project2/cgi/cgiAppEngine.cpp28
-rw-r--r--project2/cgi/cgiAppEngine.h13
-rw-r--r--project2/config.cpp81
-rw-r--r--project2/config.h43
-rw-r--r--project2/console/consoleAppEngine.cpp25
-rw-r--r--project2/console/consoleAppEngine.h9
-rw-r--r--project2/sqlCheck.cpp6
-rw-r--r--project2/sqlCheck.h2
-rw-r--r--project2/sqlMergeTask.cpp4
-rw-r--r--project2/sqlMergeTask.h66
-rw-r--r--project2/sqlRows.cpp5
-rw-r--r--project2/sqlRows.h2
-rw-r--r--project2/sqlTask.cpp6
-rw-r--r--project2/sqlTask.h2
-rw-r--r--project2/variables.cpp21
18 files changed, 272 insertions, 51 deletions
diff --git a/project2/Jamfile.jam b/project2/Jamfile.jam
index e2eaf42..d42e278 100644
--- a/project2/Jamfile.jam
+++ b/project2/Jamfile.jam
@@ -33,7 +33,7 @@ lib p2common :
iterate.cpp paramChecker.cpp presenter.cpp rawView.cpp dumpTask.cpp
sourceObject.cpp task.cpp variables.cpp view.cpp xmlObjectLoader.cpp exceptions.cpp
sessionClearTask.cpp session.cpp sessionSetTask.cpp commonObjects.cpp xmlPresenter.cpp
- rowView.cpp rowSet.cpp rowUser.cpp rowProcessor.cpp
+ rowView.cpp rowSet.cpp rowUser.cpp rowProcessor.cpp config.cpp
:
<library>../libmisc//misc
<library>libxmlpp
diff --git a/project2/appEngine.cpp b/project2/appEngine.cpp
index 3961ffe..1489cd1 100644
--- a/project2/appEngine.cpp
+++ b/project2/appEngine.cpp
@@ -3,7 +3,8 @@
ApplicationEngine * ApplicationEngine::currentEngine = NULL;
-ApplicationEngine::ApplicationEngine()
+ApplicationEngine::ApplicationEngine(const Glib::ustring & engineKeys) :
+ Configuration(engineKeys)
{
if (currentEngine) {
throw std::runtime_error("One application at a time, please");
diff --git a/project2/appEngine.h b/project2/appEngine.h
index 04fe486..2921b2d 100644
--- a/project2/appEngine.h
+++ b/project2/appEngine.h
@@ -4,10 +4,11 @@
#include "environment.h"
#include "session.h"
#include "presenter.h"
+#include "config.h"
-class ApplicationEngine {
+class ApplicationEngine : public Configuration {
public:
- ApplicationEngine();
+ ApplicationEngine(const Glib::ustring & engineKeys);
virtual ~ApplicationEngine() = 0;
virtual void process() const = 0;
diff --git a/project2/cgi/cgiAppEngine.cpp b/project2/cgi/cgiAppEngine.cpp
index d1c1579..3a9e362 100644
--- a/project2/cgi/cgiAppEngine.cpp
+++ b/project2/cgi/cgiAppEngine.cpp
@@ -7,6 +7,7 @@
#include "../xmlObjectLoader.h"
#include "../iterate.h"
#include <boost/bind.hpp>
+#include <boost/regex.hpp>
#include "../sessionXml.h"
const std::string SESSIONID = "sessionID";
@@ -15,8 +16,10 @@ typedef std::string SValue;
SessionContainer * sessionsContainer = new SessionContainerXml();
+class UnknownDomain : public std::exception { };
+
CgiApplicationEngine::CgiApplicationEngine(const CgiEnvironment * e) :
- ApplicationEngine(),
+ ApplicationEngine("web/host"),
_env(e),
header(NULL)
{
@@ -181,3 +184,26 @@ CgiApplicationEngine::session() const
return sessionsContainer->GetSession(sessionID);
}
+bool
+CgiApplicationEngine::checkDomain(const CgiApplicationEngine::DomainPlatforms::value_type & i) const
+{
+ const std::string & h = _env->getServerName();
+ return boost::regex_match(h.begin(), h.end(), boost::regex(i.first.raw()));
+}
+
+Glib::ustring
+CgiApplicationEngine::resolveCurrentConfig() const
+{
+ DomainPlatforms::const_iterator i = std::find_if(domplat.begin(), domplat.end(), boost::bind(&CgiApplicationEngine::checkDomain, this, _1));
+ if (i != domplat.end()) {
+ return i->second;
+ }
+ throw UnknownDomain();
+}
+
+void
+CgiApplicationEngine::loadEngineSection(const xmlpp::Element * e) const
+{
+ domplat.push_back(DomainPlatforms::value_type(e->get_attribute_value("name"), e->get_attribute_value("platform")));
+}
+
diff --git a/project2/cgi/cgiAppEngine.h b/project2/cgi/cgiAppEngine.h
index 532b74d..5a21e98 100644
--- a/project2/cgi/cgiAppEngine.h
+++ b/project2/cgi/cgiAppEngine.h
@@ -31,12 +31,22 @@ class CgiApplicationEngine : public ApplicationEngine {
}
const Environment * env() const;
SessionPtr session() const;
+ virtual Glib::ustring resolveCurrentConfig() const;
void addAppData(const Presenter * p) const;
const CgiEnvironment * _env;
+
protected:
mutable cgicc::HTTPContentHeader * header;
+
private:
+ typedef std::pair<Glib::ustring, Glib::ustring> DomainPlatform;
+ typedef std::list<DomainPlatform> DomainPlatforms;
+ mutable DomainPlatforms domplat;
+ bool checkDomain(const DomainPlatforms::value_type & i) const;
+ void loadEngineSection(const xmlpp::Element *) const;
+
mutable boost::shared_ptr<xmlpp::Document> doc;
+
class Stage;
typedef boost::intrusive_ptr<Stage> StagePtr;
class Stage : public virtual CommonObjects {
@@ -47,6 +57,7 @@ class CgiApplicationEngine : public ApplicationEngine {
protected:
const CgiApplicationEngine * appEngine;
};
+
class RequestStage : public Stage {
public:
RequestStage(const CgiApplicationEngine *, const std::string & id);
@@ -57,12 +68,14 @@ class CgiApplicationEngine : public ApplicationEngine {
OrderedParamCheckers parameterChecks;
NoOutputExecutes tasks;
};
+
class PresentStage : public Stage, public XmlPresenter {
public:
PresentStage(const CgiApplicationEngine *, const std::string & id);
PresentStage(const CgiApplicationEngine *, const std::string & group, const std::string & id);
virtual ~PresentStage();
virtual StagePtr run();
+
};
mutable StagePtr currentStage;
mutable UUID sessionID;
diff --git a/project2/config.cpp b/project2/config.cpp
new file mode 100644
index 0000000..f973c05
--- /dev/null
+++ b/project2/config.cpp
@@ -0,0 +1,81 @@
+#include "config.h"
+#include "exceptions.h"
+#include <boost/filesystem/operations.hpp>
+#include <boost/foreach.hpp>
+#include <libxml/xinclude.h>
+#include <libxml++/parsers/domparser.h>
+
+class NoSuchPlatform : public std::exception { };
+class NoSuchConfigurationValue : public std::exception { };
+
+Configuration::Configuration(const Glib::ustring & ek) :
+ loaded(false),
+ engineKeys(ek)
+{
+}
+
+void
+Configuration::load() const
+{
+ if (loaded) {
+ return;
+ }
+ loaded = true;
+ if (!boost::filesystem::exists("config.xml")) {
+ return;
+ }
+ xmlpp::DomParser configxml("config.xml");
+ while (xmlXIncludeProcessFlags(configxml.get_document()->cobj(), XML_PARSE_NOXINCNODE) > 0);
+ xmlpp::NodeSet ps(configxml.get_document()->get_root_node()->find("platform"));
+ BOOST_FOREACH(const xmlpp::Node * p, ps) {
+ const xmlpp::Element * pe = dynamic_cast<const xmlpp::Element *>(p);
+ if (pe) {
+ platforms[pe->get_attribute_value("name")] = new Platform(pe);
+ }
+ }
+ xmlpp::NodeSet eks(configxml.get_document()->get_root_node()->find(engineKeys));
+ BOOST_FOREACH(const xmlpp::Node * ek, eks) {
+ const xmlpp::Element * eke = dynamic_cast<const xmlpp::Element *>(ek);
+ if (eke) {
+ loadEngineSection(eke);
+ }
+ }
+}
+
+Configuration::~Configuration()
+{
+}
+
+Configuration::PlatformPtr
+Configuration::getCurrentConfig() const
+{
+ load();
+ Glib::ustring platformName(resolveCurrentConfig());
+ Platforms::const_iterator i = platforms.find(platformName);
+ if (i == platforms.end()) {
+ throw NoSuchPlatform();
+ }
+ return i->second;
+}
+
+const Glib::ustring &
+Configuration::Platform::getValue(const Glib::ustring & key) const
+{
+ Values::const_iterator i = values.find(key);
+ if (i == values.end()) {
+ throw NoSuchConfigurationValue();
+ }
+ return i->second;
+}
+
+Configuration::Platform::Platform(const xmlpp::Element * e)
+{
+ xmlpp::NodeSet vs(e->find("variable"));
+ BOOST_FOREACH(const xmlpp::Node * v, vs) {
+ const xmlpp::Element * ve = dynamic_cast<const xmlpp::Element *>(v);
+ if (ve) {
+ values.insert(Values::value_type(ve->get_attribute_value("name"), ve->get_attribute_value("value")));
+ }
+ }
+}
+
diff --git a/project2/config.h b/project2/config.h
new file mode 100644
index 0000000..9f6cd5d
--- /dev/null
+++ b/project2/config.h
@@ -0,0 +1,43 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <string>
+#include <libxml++/nodes/element.h>
+#include "intrusivePtrBase.h"
+#include <boost/function.hpp>
+
+class Configuration {
+ public:
+ class Platform : public virtual IntrusivePtrBase {
+ public:
+ typedef std::map<Glib::ustring, const Glib::ustring> Values;
+
+ Platform(const xmlpp::Element * instanceRoot);
+ const Glib::ustring & getValue(const Glib::ustring & key) const;
+
+ private:
+ Values values;
+ };
+ typedef boost::intrusive_ptr<const Platform> PlatformPtr;
+
+ typedef std::map<Glib::ustring, PlatformPtr> Platforms;
+
+ Configuration(const Glib::ustring & engineKeys);
+ virtual ~Configuration() = 0;
+
+ PlatformPtr getCurrentConfig() const;
+
+ protected:
+ virtual Glib::ustring resolveCurrentConfig() const = 0;
+ void load() const;
+
+ private:
+ virtual void loadEngineSection(const xmlpp::Element *) const = 0;
+
+ mutable bool loaded;
+ mutable Platforms platforms;
+ const Glib::ustring engineKeys;
+};
+
+#endif
+
diff --git a/project2/console/consoleAppEngine.cpp b/project2/console/consoleAppEngine.cpp
index bd17458..43b58c9 100644
--- a/project2/console/consoleAppEngine.cpp
+++ b/project2/console/consoleAppEngine.cpp
@@ -3,6 +3,9 @@
#include "../iterate.h"
#include <stdexcept>
#include <libxml/xinclude.h>
+#include <libxml++/parsers/domparser.h>
+
+class UnknownPlatformAlias : public std::exception { };
class ConsoleSession : public Session {
public:
@@ -49,7 +52,7 @@ class ConsoleSession : public Session {
};
ConsoleApplicationEngine::ConsoleApplicationEngine(const ConsoleEnvironment * env, const boost::filesystem::path & f) :
- ApplicationEngine(),
+ ApplicationEngine("console/environment"),
_env(env),
runtime(new ConsoleSession())
{
@@ -138,3 +141,23 @@ ConsoleApplicationEngine::ConsolePresenter::popSub() const
indent -= 2;
}
+Glib::ustring
+ConsoleApplicationEngine::resolveCurrentConfig() const
+{
+ const char * e = getenv("P2_PLATFORM");
+ if (!e) {
+ e = "";
+ }
+ ConsolePlatforms::const_iterator i = conplat.find(e);
+ if (i != conplat.end()) {
+ return i->second;
+ }
+ throw UnknownPlatformAlias();
+}
+
+void
+ConsoleApplicationEngine::loadEngineSection(const xmlpp::Element * e) const
+{
+ conplat.insert(ConsolePlatforms::value_type(e->get_attribute_value("setting"), e->get_attribute_value("platform")));
+}
+
diff --git a/project2/console/consoleAppEngine.h b/project2/console/consoleAppEngine.h
index 9d4ee96..abc471f 100644
--- a/project2/console/consoleAppEngine.h
+++ b/project2/console/consoleAppEngine.h
@@ -9,7 +9,6 @@
#include <boost/intrusive_ptr.hpp>
#include <boost/filesystem/path.hpp>
#include <libxml++/document.h>
-#include <libxml++/parsers/domparser.h>
class ConsoleEnvironment;
@@ -21,10 +20,17 @@ class ConsoleApplicationEngine : public ApplicationEngine, public CommonObjects
void process() const;
const Environment * env() const;
SessionPtr session() const;
+ virtual Glib::ustring resolveCurrentConfig() const;
virtual void addAppData(const Presenter * p) const;
+
protected:
const ConsoleEnvironment * _env;
+
private:
+ typedef std::map<Glib::ustring, const Glib::ustring> ConsolePlatforms;
+ mutable ConsolePlatforms conplat;
+ void loadEngineSection(const xmlpp::Element *) const;
+
class ConsolePresenter : public Presenter {
public:
ConsolePresenter(const ConsoleApplicationEngine *, const std::string & id);
@@ -37,6 +43,7 @@ class ConsoleApplicationEngine : public ApplicationEngine, public CommonObjects
private:
mutable unsigned int indent;
};
+
OrderedParamCheckers parameterChecks;
NoOutputExecutes tasks;
SessionPtr runtime;
diff --git a/project2/sqlCheck.cpp b/project2/sqlCheck.cpp
index 3537134..9a5bb94 100644
--- a/project2/sqlCheck.cpp
+++ b/project2/sqlCheck.cpp
@@ -6,6 +6,7 @@
#include <boost/regex.hpp>
#include "commonObjects.h"
#include "sqlVariableBinder.h"
+#include "genericVisitor.h"
ElementLoaderImpl<SqlCheck> sqlCheckLoader("sqlcheck");
@@ -13,7 +14,7 @@ SqlCheck::SqlCheck(const xmlpp::Element * p) :
SourceObject(p),
IHaveParameters(p),
ParamChecker(p),
- dataSource(p->get_attribute_value("datasource")),
+ dataSource(p, "datasource"),
sql(xmlChildText(p, "sql")),
testOp(p->get_attribute_value("testOp")),
testValue(atof(p->get_attribute_value("testValue").c_str())),
@@ -29,7 +30,8 @@ SqlCheck::~SqlCheck()
void
SqlCheck::loadComplete(const CommonObjects * co)
{
- query = new ODBC::SelectCommand(co->dataSource<RdbmsDataSource>(dataSource)->getReadonly(), sql);
+ query = new ODBC::SelectCommand(LexicalCall<std::string, const RdbmsDataSource *>(boost::bind(
+ &CommonObjects::dataSource<RdbmsDataSource>, co, _1), dataSource)->getReadonly(), sql);
}
bool
diff --git a/project2/sqlCheck.h b/project2/sqlCheck.h
index e4f79ee..bfc7b37 100644
--- a/project2/sqlCheck.h
+++ b/project2/sqlCheck.h
@@ -14,7 +14,7 @@ class SqlCheck : public IHaveParameters, public ParamChecker {
virtual void loadComplete(const CommonObjects *);
bool performCheck() const;
- const std::string dataSource;
+ const Variable dataSource;
const Glib::ustring sql;
const std::string testOp;
const double testValue;
diff --git a/project2/sqlMergeTask.cpp b/project2/sqlMergeTask.cpp
index 2436f1f..6a5ebf7 100644
--- a/project2/sqlMergeTask.cpp
+++ b/project2/sqlMergeTask.cpp
@@ -49,7 +49,7 @@ SqlMergeTask::SqlMergeTask(const xmlpp::Element * p) :
tempTableCreated(false),
insCmd(NULL),
destdb(NULL),
- dataSource(p->get_attribute_value("datasource")),
+ dataSource(p, "datasource"),
dtable(p->get_attribute_value("targettable")),
dtablet(stringf("tmp_%s_%d", dtable.c_str(), getpid()))
{
@@ -89,7 +89,7 @@ SqlMergeTask::~SqlMergeTask()
void
SqlMergeTask::loadComplete(const CommonObjects * co)
{
- destdb = &co->dataSource<RdbmsDataSource>(dataSource)->getWritable();
+ destdb = &LexicalCall<std::string, const RdbmsDataSource *>(boost::bind(&CommonObjects::dataSource<RdbmsDataSource>, co, _1), dataSource)->getWritable();
insCmd = insertCommand();
BOOST_FOREACH(const Iterates::value_type & i, sources) {
attach(i.second, insCmd);
diff --git a/project2/sqlMergeTask.h b/project2/sqlMergeTask.h
index 08246d0..c7a7e4e 100644
--- a/project2/sqlMergeTask.h
+++ b/project2/sqlMergeTask.h
@@ -15,9 +15,9 @@
#include <list>
class SqlMergeTask : public Task {
- public:
- typedef std::string Table;
- typedef std::string Column;
+ public:
+ typedef std::string Table;
+ typedef std::string Column;
class TargetColumn;
typedef boost::intrusive_ptr<TargetColumn> TargetColumnPtr;
class TargetColumn : public virtual IntrusivePtrBase {
@@ -33,44 +33,44 @@ class SqlMergeTask : public Task {
Table maptable;
};
- typedef std::set<TargetColumnPtr, TargetColumn::Sort> Columns;
+ typedef std::set<TargetColumnPtr, TargetColumn::Sort> Columns;
- typedef std::set<Column> Keys;
+ typedef std::set<Column> Keys;
- SqlMergeTask(const xmlpp::Element * p);
- virtual ~SqlMergeTask();
+ SqlMergeTask(const xmlpp::Element * p);
+ virtual ~SqlMergeTask();
- virtual void loadComplete(const CommonObjects *);
- void execute() const;
- Columns cols;
- Keys keys;
- Keys indexes;
- const Variable updateWhere;
- const std::string patchOrder;
- const bool earlyKeys;
- const bool useView;
+ virtual void loadComplete(const CommonObjects *);
+ void execute() const;
+ Columns cols;
+ Keys keys;
+ Keys indexes;
+ const Variable updateWhere;
+ const std::string patchOrder;
+ const bool earlyKeys;
+ const bool useView;
- private:
- virtual void copyToTempTable() const;
- void createTempTable() const;
- void dropTempTable() const;
- void createTempKey() const;
+ private:
+ virtual void copyToTempTable() const;
+ void createTempTable() const;
+ void dropTempTable() const;
+ void createTempKey() const;
- mutable bool tempTableCreated;
- Iterates sources;
+ mutable bool tempTableCreated;
+ Iterates sources;
std::list<std::string> sqls;
- protected:
- ModifyCommand * insertCommand() const;
- ModifyCommand * insCmd;
+ protected:
+ ModifyCommand * insertCommand() const;
+ ModifyCommand * insCmd;
- public:
- ODBC::Connection * destdb;
- const std::string dataSource;
- const Table dtable;
- const Table dtablet;
+ public:
+ ODBC::Connection * destdb;
+ const Variable dataSource;
+ const Table dtable;
+ const Table dtablet;
- static unsigned int defaultVerbosity;
- static bool defaultUseTempTable;
+ static unsigned int defaultVerbosity;
+ static bool defaultUseTempTable;
};
#endif
diff --git a/project2/sqlRows.cpp b/project2/sqlRows.cpp
index 31948df..f592337 100644
--- a/project2/sqlRows.cpp
+++ b/project2/sqlRows.cpp
@@ -9,6 +9,7 @@
#include "xmlObjectLoader.h"
#include "commonObjects.h"
#include "sqlVariableBinder.h"
+#include "genericVisitor.h"
#include <boost/date_time/gregorian/gregorian_types.hpp>
ElementLoaderImpl<SqlRows> sqlviewLoader("sqlrows");
@@ -16,7 +17,7 @@ ElementLoaderImpl<SqlRows> sqlviewLoader("sqlrows");
SqlRows::SqlRows(const xmlpp::Element * p) :
SourceObject(p),
RowSet(p),
- dataSource(p->get_attribute_value("datasource")),
+ dataSource(p, "datasource"),
sqlCommand(dynamic_cast<xmlpp::Element *>(p->get_children("sql").front())),
query(NULL),
db(NULL)
@@ -30,7 +31,7 @@ SqlRows::~SqlRows()
void
SqlRows::loadComplete(const CommonObjects * co)
{
- db = co->dataSource<RdbmsDataSource>(dataSource);
+ db = LexicalCall<std::string, const RdbmsDataSource *>(boost::bind(&CommonObjects::dataSource<RdbmsDataSource>, co, _1), dataSource);
}
void
diff --git a/project2/sqlRows.h b/project2/sqlRows.h
index 9c6dadb..942da5d 100644
--- a/project2/sqlRows.h
+++ b/project2/sqlRows.h
@@ -25,7 +25,7 @@ class SqlRows : public RowSet {
bool isNull(unsigned int col) const;
bool isNull(const Glib::ustring & id) const;
- const std::string dataSource;
+ const Variable dataSource;
private:
class SqlWriter;
diff --git a/project2/sqlTask.cpp b/project2/sqlTask.cpp
index d5810be..3c54660 100644
--- a/project2/sqlTask.cpp
+++ b/project2/sqlTask.cpp
@@ -5,6 +5,7 @@
#include "rdbmsDataSource.h"
#include "commonObjects.h"
#include "sqlVariableBinder.h"
+#include "genericVisitor.h"
ElementLoaderImpl<SqlTask> sqltaskLoader("sqltask");
@@ -12,7 +13,7 @@ SqlTask::SqlTask(const xmlpp::Element * p) :
SourceObject(p),
Task(p),
IHaveParameters(p),
- dataSource(p->get_attribute_value("datasource")),
+ dataSource(p, "datasource"),
sql(xmlChildText(p, "sql")),
modify(NULL)
{
@@ -26,7 +27,8 @@ SqlTask::~SqlTask()
void
SqlTask::loadComplete(const CommonObjects * co)
{
- modify = new ODBC::ModifyCommand(co->dataSource<RdbmsDataSource>(dataSource)->getWritable(), sql);
+ modify = new ODBC::ModifyCommand(LexicalCall<std::string, const RdbmsDataSource *>(boost::bind(
+ &CommonObjects::dataSource<RdbmsDataSource>, co, _1), dataSource)->getWritable(), sql);
}
void
diff --git a/project2/sqlTask.h b/project2/sqlTask.h
index 3cd93c6..6432022 100644
--- a/project2/sqlTask.h
+++ b/project2/sqlTask.h
@@ -16,7 +16,7 @@ class SqlTask : public Task, public IHaveParameters {
virtual void loadComplete(const CommonObjects *);
virtual void execute() const;
- const std::string dataSource;
+ const Variable dataSource;
const std::string sql;
protected:
mutable ODBC::ModifyCommand * modify;
diff --git a/project2/variables.cpp b/project2/variables.cpp
index 130d210..20b4fa2 100644
--- a/project2/variables.cpp
+++ b/project2/variables.cpp
@@ -252,6 +252,25 @@ class VariableFixed : public VariableImpl {
VariableType var;
};
+class VariableConfig : public VariableImplDyn {
+ public:
+ VariableConfig(const xmlpp::Element * e) :
+ VariableImplDyn(e),
+ name(e->get_attribute_value("name"))
+ {
+ }
+ const VariableType & value() const
+ {
+ if (!cacheValid) {
+ cache = ApplicationEngine::getCurrent()->getCurrentConfig()->getValue(name);
+ cacheValid = true;
+ }
+ return cache;
+ }
+ private:
+ const Glib::ustring name;
+};
+
Variable::Variable(const xmlpp::Element * e, const Glib::ustring & n, bool required, VariableType def)
{
xmlpp::Attribute * a = e->get_attribute(n);
@@ -272,6 +291,8 @@ Variable::Variable(const xmlpp::Element * e, const Glib::ustring & n, bool requi
var = new VariableUri(c);
else if (source == "param")
var = new VariableParam(c);
+ else if (source == "config")
+ var = new VariableConfig(c);
else if (source == "literal" || source.empty())
var = new VariableLiteral(c->get_attribute_value("value"), c->get_attribute_value("type"));
else