5 #include "dispatcher_p.h"
8 #include "application.h"
11 #include "controller.h"
12 #include "controller_p.h"
14 #include "request_p.h"
15 #include "dispatchtypepath.h"
16 #include "dispatchtypechained.h"
20 #include <QMetaMethod>
25 , d_ptr(new DispatcherPrivate(this))
31 Dispatcher::~Dispatcher()
36 void Dispatcher::setupActions(
const QVector<Controller*> &controllers,
const QVector<Cutelyst::DispatchType *> &dispatchers,
bool printActions)
44 bool instanceUsed =
false;
45 const auto actions = controller->actions();
46 for (
Action *action : actions) {
47 bool registered =
false;
48 if (!d->actions.contains(action->reverse())) {
49 if (!action->attributes().contains(QStringLiteral(
"Private"))) {
52 if (
dispatch->registerAction(action)) {
66 const QString name = action->ns() + QLatin1Char(
'/') + action->name();
67 d->actions.insert(name, { name, action });
68 d->actionContainer[action->ns()] << action;
69 registeredActions.append(action);
72 qCDebug(CUTELYST_DISPATCHER) <<
"The action" << action->name() <<
"of"
73 << action->controller()->objectName()
74 <<
"controller was not registered in any dispatcher."
75 " If you still want to access it internally (via actionFor())"
76 " you may make it's method private.";
81 d->controllers.insert(controller->objectName(), controller);
90 d->rootActions = d->actionContainer.value(QLatin1String(
""));
93 controller->d_ptr->setupFinished();
100 if (!type->
inUse()) {
101 d->dispatchers.removeAt(i);
110 qCDebug(CUTELYST_DISPATCHER) <<
dispatch->list().constData();
117 Action *action = c->action();
121 const QString path = c->req()->path();
122 if (path.isEmpty()) {
123 c->
error(c->
translate(
"Cutelyst::Dispatcher",
"No default action defined"));
125 c->
error(c->
translate(
"Cutelyst::Dispatcher",
"Unknown resource '%1'.").arg(path));
143 Action *action = d->command2Action(c, opname, c->request()->args());
148 qCCritical(CUTELYST_DISPATCHER) <<
"Action not found" << opname << c->request()->args();
156 Request *request = c->request();
157 d->prepareAction(c, request->path());
159 static const auto &log = CUTELYST_DISPATCHER();
160 if (log.isDebugEnabled()) {
161 if (!request->match().isEmpty()) {
162 qCDebug(log) <<
"Path is" << request->match();
165 if (!request->args().isEmpty()) {
166 qCDebug(log) <<
"Arguments are" << request->args().join(QLatin1Char(
'/'));
171 void DispatcherPrivate::prepareAction(
Context *c,
const QString &requestPath)
const
173 QString path = normalizePath(requestPath);
184 if (type->match(c, path, args) == DispatchType::ExactMatch) {
190 if (path.isEmpty()) {
194 int pos = path.lastIndexOf(u
'/');
196 const QString arg = path.mid(pos + 1);
207 if (name.isEmpty()) {
211 if (nameSpace.isEmpty()) {
212 const QString normName = u
'/' + name;
213 return d->actions.value(normName).action;
216 const QString ns = DispatcherPrivate::cleanNamespace(nameSpace);
224 QString _path = path;
225 int slashes = _path.count(u
'/');
228 }
else if (_path.startsWith(u
'/') && slashes != 1) {
231 return d->actions.value(_path).action;
240 if (name.isEmpty()) {
244 const QString ns = DispatcherPrivate::cleanNamespace(nameSpace);
245 const ActionList containers = d->getContainers(ns);
246 auto rIt = containers.rbegin();
247 while (rIt != containers.rend()) {
248 if ((*rIt)->name() == name) {
259 return d->controllers;
267 ret =
dispatch->uriForAction(action, captures);
270 ret = QStringLiteral(
"/");
283 if (expandedAction) {
284 return expandedAction;
293 return d->dispatchers;
296 QString DispatcherPrivate::cleanNamespace(
const QString &ns)
299 bool lastWasSlash =
true;
300 int nsSize = ns.size();
301 for (
int i = 0; i < nsSize; ++i) {
306 if (ret.at(i) == u
'/') {
314 lastWasSlash =
false;
320 QString DispatcherPrivate::normalizePath(
const QString &path)
323 bool lastSlash =
true;
325 while (i < ret.size()) {
326 if (ret.at(i) == u
'/') {
338 if (ret.endsWith(u
'/')) {
339 ret.resize(ret.size() - 1);
344 void DispatcherPrivate::printActions()
const
346 QVector<QStringList> table;
348 auto keys = actions.keys();
349 std::sort(keys.begin(), keys.end());
350 for (
const auto &key : keys) {
351 Action *action = actions.value(key).action;
352 QString path = key.toString();
353 if (!path.startsWith(u
'/')) {
360 row.append(action->
name());
364 qCDebug(CUTELYST_DISPATCHER) << Utils::buildTable(table, {
365 QLatin1String(
"Private"),
366 QLatin1String(
"Class"),
367 QLatin1String(
"Method")
369 QLatin1String(
"Loaded Private actions:")).constData();
372 ActionList DispatcherPrivate::getContainers(
const QString &ns)
const
376 if (ns.compare(u
"/") != 0) {
381 ret.append(actionContainer.value(ns.mid(0, pos)));
382 pos = ns.lastIndexOf(QLatin1Char(
'/'), pos - 1);
386 ret.append(rootActions);
391 Action *DispatcherPrivate::command2Action(
Context *c,
const QString &command,
const QStringList &args)
const
393 auto it = actions.constFind(command);
394 if (it != actions.constEnd()) {
395 return it.value().action;
398 return invokeAsPath(c, command, args);
401 Action *DispatcherPrivate::invokeAsPath(
Context *c,
const QString &relativePath,
const QStringList &args)
const
407 QString path = DispatcherPrivate::actionRel2Abs(c, relativePath);
409 int pos = path.lastIndexOf(QLatin1Char(
'/'));
410 int lastPos = path.size();
413 ret = q->getAction(path, QString());
418 const QString name = path.mid(pos + 1, lastPos);
419 path = path.mid(0, pos);
420 ret = q->getAction(name, path);
427 pos = path.indexOf(QLatin1Char(
'/'), pos - 1);
433 QString DispatcherPrivate::actionRel2Abs(
Context *c,
const QString &path)
436 if (path.startsWith(QLatin1Char(
'/'))) {
441 const QString ns = qobject_cast<Action *>(c->
stack().constLast())->
ns();
445 ret = ns + QLatin1Char(
'/') + path;
450 #include "moc_dispatcher.cpp"
This class represents a Cutelyst Action.
QString ns() const noexcept
bool dispatch(Context *c)
QString className() const
Controller * controller() const
The Cutelyst Component base class.
QStack< Component * > stack() const noexcept
QString translate(const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
bool execute(Component *code)
bool error() const noexcept
Returns true if an error was set.
Cutelyst Controller base class
bool _DISPATCH(Context *c)
QMap< QString, Controller * > controllers() const
void setupActions(const QVector< Controller * > &controllers, const QVector< DispatchType * > &dispatchers, bool printActions)
Action * getAction(const QString &name, const QString &nameSpace=QString()) const
QVector< DispatchType * > dispatchers() const
Action * getActionByPath(const QString &path) const
QString uriForAction(Action *action, const QStringList &captures) const
bool forward(Context *c, Component *component)
Dispatcher(QObject *parent=nullptr)
ActionList getActions(const QString &name, const QString &nameSpace) const
void prepareAction(Context *c)
bool dispatch(Context *c)
Action * expandAction(const Context *c, Action *action) const
The Cutelyst namespace holds all public Cutelyst API.
QVector< Action * > ActionList