18 #include "dispatcher_p.h"
21 #include "application.h"
24 #include "controller.h"
25 #include "controller_p.h"
27 #include "request_p.h"
28 #include "dispatchtypepath.h"
29 #include "dispatchtypechained.h"
33 #include <QMetaMethod>
38 , d_ptr(new DispatcherPrivate(this))
44 Dispatcher::~Dispatcher()
49 void Dispatcher::setupActions(
const QVector<Controller*> &controllers,
const QVector<Cutelyst::DispatchType *> &dispatchers,
bool printActions)
57 bool instanceUsed =
false;
58 const auto actions = controller->actions();
59 for (
Action *action : actions) {
60 bool registered =
false;
61 if (!d->actions.contains(action->reverse())) {
62 if (!action->attributes().contains(QLatin1String(
"Private"))) {
65 if (
dispatch->registerAction(action)) {
79 d->actions.insert(action->ns() + QLatin1Char(
'/') + action->name(), action);
80 d->actionContainer[action->ns()] << action;
81 registeredActions.append(action);
84 qCDebug(CUTELYST_DISPATCHER) <<
"The action" << action->name() <<
"of"
85 << action->controller()->objectName()
86 <<
"controller was not registered in any dispatcher."
87 " If you still want to access it internally (via actionFor())"
88 " you may make it's method private.";
93 d->controllers.insert(controller->objectName(), controller);
102 d->rootActions = d->actionContainer.value(QLatin1String(
""));
105 controller->d_ptr->setupFinished();
112 if (!type->
inUse()) {
113 d->dispatchers.removeAt(i);
122 qCDebug(CUTELYST_DISPATCHER) <<
dispatch->list().constData();
129 Action *action = c->action();
133 const QString path = c->req()->path();
134 if (path.isEmpty()) {
135 c->
error(c->
translate(
"Cutelyst::Dispatcher",
"No default action defined"));
137 c->
error(c->
translate(
"Cutelyst::Dispatcher",
"Unknown resource '%1'.").arg(path));
155 Action *action = d->command2Action(c, opname, c->request()->args());
160 qCCritical(CUTELYST_DISPATCHER) <<
"Action not found" << opname << c->request()->args();
168 Request *request = c->request();
169 d->prepareAction(c, request->path());
171 static const auto &log = CUTELYST_DISPATCHER();
172 if (log.isDebugEnabled()) {
173 if (!request->match().isEmpty()) {
174 qCDebug(log) <<
"Path is" << request->match();
177 if (!request->args().isEmpty()) {
178 qCDebug(log) <<
"Arguments are" << request->args().join(QLatin1Char(
'/'));
183 void DispatcherPrivate::prepareAction(
Context *c,
const QString &requestPath)
const
185 QString path = normalizePath(requestPath);
196 if (type->match(c, path, args) == DispatchType::ExactMatch) {
202 if (path.isEmpty()) {
206 int pos = path.lastIndexOf(QLatin1Char(
'/'));
208 const QString arg = path.mid(pos + 1);
219 if (name.isEmpty()) {
223 if (nameSpace.isEmpty()) {
224 return d->actions.value(QLatin1Char(
'/') + name);
227 const QString ns = DispatcherPrivate::cleanNamespace(nameSpace);
235 QString _path = path;
236 int slashes = _path.count(QLatin1Char(
'/'));
238 _path.prepend(QLatin1Char(
'/'));
239 }
else if (_path.startsWith(QLatin1Char(
'/')) && slashes != 1) {
242 return d->actions.value(_path);
251 if (name.isEmpty()) {
255 const QString ns = DispatcherPrivate::cleanNamespace(nameSpace);
256 const ActionList containers = d->getContainers(ns);
257 auto rIt = containers.rbegin();
258 while (rIt != containers.rend()) {
259 if ((*rIt)->name() == name) {
270 return d->controllers;
278 ret =
dispatch->uriForAction(action, captures);
281 ret = QStringLiteral(
"/");
294 if (expandedAction) {
295 return expandedAction;
304 return d->dispatchers;
307 QString DispatcherPrivate::cleanNamespace(
const QString &ns)
310 bool lastWasSlash =
true;
311 int nsSize = ns.size();
312 for (
int i = 0; i < nsSize; ++i) {
317 if (ret.at(i) == QLatin1Char(
'/')) {
325 lastWasSlash =
false;
331 QString DispatcherPrivate::normalizePath(
const QString &path)
334 bool lastSlash =
true;
336 while (i < ret.size()) {
337 if (ret.at(i) == QLatin1Char(
'/')) {
349 if (ret.endsWith(QLatin1Char(
'/'))) {
350 ret.resize(ret.size() - 1);
355 void DispatcherPrivate::printActions()
const
357 QVector<QStringList> table;
359 QStringList keys = actions.keys();
360 keys.sort(Qt::CaseInsensitive);
361 for (
const QString &key : keys) {
362 Action *action = actions.value(key);
364 if (!path.startsWith(QLatin1Char(
'/'))) {
365 path.prepend(QLatin1Char(
'/'));
371 row.append(action->
name());
375 qCDebug(CUTELYST_DISPATCHER) << Utils::buildTable(table, {
376 QLatin1String(
"Private"),
377 QLatin1String(
"Class"),
378 QLatin1String(
"Method")
380 QLatin1String(
"Loaded Private actions:")).constData();
383 ActionList DispatcherPrivate::getContainers(
const QString &ns)
const
387 if (ns != QLatin1String(
"/")) {
392 ret.append(actionContainer.value(ns.mid(0, pos)));
393 pos = ns.lastIndexOf(QLatin1Char(
'/'), pos - 1);
397 ret.append(rootActions);
402 Action *DispatcherPrivate::command2Action(
Context *c,
const QString &command,
const QStringList &args)
const
404 auto it = actions.constFind(command);
405 if (it != actions.constEnd()) {
409 return invokeAsPath(c, command, args);
412 Action *DispatcherPrivate::invokeAsPath(
Context *c,
const QString &relativePath,
const QStringList &args)
const
417 QString path = DispatcherPrivate::actionRel2Abs(c, relativePath);
419 int pos = path.lastIndexOf(QLatin1Char(
'/'));
420 int lastPos = path.size();
423 ret = q->getAction(path, QString());
428 const QString name = path.mid(pos + 1, lastPos);
429 path = path.mid(0, pos);
430 ret = q->getAction(name, path);
437 pos = path.indexOf(QLatin1Char(
'/'), pos - 1);
443 QString DispatcherPrivate::actionRel2Abs(
Context *c,
const QString &path)
446 if (path.startsWith(QLatin1Char(
'/'))) {
451 const QString ns = qobject_cast<Action *>(c->
stack().constLast())->
ns();
455 ret = ns + QLatin1Char(
'/') + path;
460 #include "moc_dispatcher.cpp"