18 #include "dispatchtypechained_p.h"
20 #include "actionchain.h"
24 #include <QtCore/QUrl>
26 using namespace Cutelyst;
29 , d_ptr(new DispatchTypeChainedPrivate)
34 DispatchTypeChained::~DispatchTypeChained()
44 Actions endPoints = d->endPoints;
45 std::sort(endPoints.begin(), endPoints.end(), [](
Action *a,
Action *b) ->
bool {
46 return a->
reverse() < b->reverse();
51 for (
Action *endPoint : endPoints) {
53 if (endPoint->numberOfArgs() == -1) {
56 for (
int i = 0; i < endPoint->numberOfArgs(); ++i) {
62 QString extra = DispatchTypeChainedPrivate::listExtraHttpMethods(endPoint);
63 QString consumes = DispatchTypeChainedPrivate::listExtraConsumes(endPoint);
65 Action *current = endPoint;
73 for (
const QString &part : pathParts) {
74 if (!part.isEmpty()) {
80 current = d->actions.value(parent);
94 unattachedTable.
append(row);
99 for (
Action *p : parents) {
102 QString extraHttpMethod = DispatchTypeChainedPrivate::listExtraHttpMethods(p);
103 if (!extraHttpMethod.
isEmpty()) {
107 const auto attributes = p->attributes();
108 auto it = attributes.constFind(
QLatin1String(
"CaptureArgs"));
109 if (it != attributes.constEnd()) {
115 QString ct = DispatchTypeChainedPrivate::listExtraConsumes(p);
120 if (p != parents[0]) {
135 if (endPoint->numberOfArgs() == -1) {
152 if (!paths.isEmpty()) {
157 if (!unattachedTable.
isEmpty()) {
173 const BestActionMatch ret = d->recurseMatch(args.
size(), QStringLiteral(
"/"), path.
split(
QLatin1Char(
'/')));
175 if (ret.isNull || chain.
isEmpty()) {
181 for (
const QString &arg : parts) {
183 decodedArgs.
append(Utils::decodePercentEncoding(&aux));
187 Request *request = c->request();
206 if (chainedList.
size() > 1) {
207 qCCritical(CUTELYST_DISPATCHER_CHAINED)
208 <<
"Multiple Chained attributes not supported registering" << action->
reverse();
214 qCCritical(CUTELYST_DISPATCHER_CHAINED)
215 <<
"Actions cannot chain to themselves registering /" << action->
name();
223 if (pathPart.
size() == 1 && !pathPart[0].
isEmpty()) {
225 }
else if (pathPart.
size() > 1) {
226 qCCritical(CUTELYST_DISPATCHER_CHAINED)
227 <<
"Multiple PathPart attributes not supported registering"
233 qCCritical(CUTELYST_DISPATCHER_CHAINED)
234 <<
"Absolute parameters to PathPart not allowed registering"
239 attributes.
insert(QStringLiteral(
"PathPart"), part);
242 auto &childrenOf = d->childrenOf[chainedTo][part];
243 childrenOf.insert(childrenOf.begin(), action);
253 qCCritical(CUTELYST_DISPATCHER_CHAINED)
254 <<
"Combining Args and CaptureArgs attributes not supported registering"
272 if (!(attributes.
contains(QStringLiteral(
"Chained")) &&
273 !attributes.
contains(QStringLiteral(
"CaptureArgs")))) {
274 qCWarning(CUTELYST_DISPATCHER_CHAINED) <<
"uriForAction: action is not an end point" << action;
284 if (curr_attributes.
contains(QStringLiteral(
"CaptureArgs"))) {
287 qCWarning(CUTELYST_DISPATCHER_CHAINED) <<
"uriForAction: not enough captures" << curr->
numberOfCaptures() << captures.
size();
295 const QString pp = curr_attributes.
value(QStringLiteral(
"PathPart"));
300 parent = curr_attributes.
value(QStringLiteral(
"Chained"));
301 curr = d->actions.value(parent);
306 qCWarning(CUTELYST_DISPATCHER_CHAINED) <<
"uriForAction: dangling action" <<
parent;
310 if (!localCaptures.
isEmpty()) {
312 qCWarning(CUTELYST_DISPATCHER_CHAINED) <<
"uriForAction: too many captures" << localCaptures;
325 if (qobject_cast<ActionChain*>(action)) {
330 if (!action->
attributes().contains(QStringLiteral(
"Chained"))) {
340 curr = d->actions.value(parent);
343 return new ActionChain(chain, const_cast<Context*>(c));
350 if (d->actions.isEmpty()) {
359 BestActionMatch DispatchTypeChainedPrivate::recurseMatch(
int reqArgsSize,
const QString &parent,
const QStringList &pathParts)
const
361 BestActionMatch bestAction;
362 auto it = childrenOf.constFind(parent);
363 if (it == childrenOf.constEnd()) {
367 const StringActionsMap &children = it.value();
371 return b.size() < a.
size();
374 for (
const QString &tryPart : keys) {
376 if (!tryPart.isEmpty()) {
384 parts = parts.
mid(tryPartCount);
387 const Actions tryActions = children.value(tryPart);
388 for (
Action *action : tryActions) {
390 if (attributes.
contains(QStringLiteral(
"CaptureArgs"))) {
391 const int captureCount = action->numberOfCaptures();
393 if (parts.
size() < captureCount) {
401 if (!action->matchCaptures(captures.
size())) {
408 const BestActionMatch ret = recurseMatch(reqArgsSize,
QLatin1Char(
'/') + action->reverse(), localParts);
416 int bestActionParts = bestAction.parts.
size();
418 if (!actions.isEmpty() &&
419 (bestAction.isNull ||
420 actionParts.
size() < bestActionParts ||
421 (actionParts.
size() == bestActionParts &&
422 actionCaptures.
size() < bestAction.captures.size() &&
423 ret.n_pathParts > bestAction.n_pathParts))) {
424 actions.prepend(action);
425 int pathparts = attributes.
value(QStringLiteral(
"PathPart")).count(
QLatin1Char(
'/')) + 1;
426 bestAction.actions = actions;
427 bestAction.captures = captures + actionCaptures;
428 bestAction.parts = actionParts;
429 bestAction.n_pathParts = pathparts + ret.n_pathParts;
430 bestAction.isNull =
false;
433 if (!action->match(reqArgsSize + parts.
size())) {
437 const QString argsAttr = attributes.
value(QStringLiteral(
"Args"));
438 const int pathparts = attributes.
value(QStringLiteral(
"PathPart")).count(
QLatin1Char(
'/')) + 1;
446 if (bestAction.isNull ||
447 parts.
size() < bestAction.parts.size() ||
448 (parts.
isEmpty() && !argsAttr.
isEmpty() && action->numberOfArgs() == 0)) {
449 bestAction.actions = { action };
451 bestAction.parts = parts;
452 bestAction.n_pathParts = pathparts;
453 bestAction.isNull =
false;
462 bool DispatchTypeChainedPrivate::checkArgsAttr(
Action *action,
const QString &name)
const
470 if (values.
size() > 1) {
471 qCCritical(CUTELYST_DISPATCHER_CHAINED)
474 <<
"attributes not supported registering"
482 qCCritical(CUTELYST_DISPATCHER_CHAINED)
484 << name <<
"(" << args <<
") for action"
486 <<
"(use '" << name <<
"' or '" << name <<
"(<number>)')";
493 QString DispatchTypeChainedPrivate::listExtraHttpMethods(
Action *action)
504 QString DispatchTypeChainedPrivate::listExtraConsumes(
Action *action)
515 #include "moc_dispatchtypechained.cpp"
void setupMatchedAction(Context *c, Action *action) const
Action * expandAction(const Context *c, Action *action) const final
QString & append(QChar ch)
virtual bool inUse() override
virtual bool registerAction(Action *action) override
registerAction
bool contains(const Key &key) const
QList< T > values() const
void append(const T &value)
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
QString & prepend(QChar ch)
Holds a chain of Cutelyst Actions.
QString join(const QString &separator) const
void setAttributes(const QMap< QString, QString > &attributes)
void setMatch(const QString &match)
This class represents a Cutelyst Action.
QString number(int n, int base)
int count(const T &value) const
void append(const T &value)
QString & insert(int position, QChar ch)
DispatchTypeChained(QObject *parent=nullptr)
int toInt(bool *ok, int base) const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
virtual MatchType match(Context *c, const QString &path, const QStringList &args) const override
void setCaptures(const QStringList &captures)
virtual QString uriForAction(Action *action, const QStringList &captures) const override
void setArguments(const QStringList &arguments)
QMap< QString, QString > attributes() const
virtual qint8 numberOfCaptures() const
void prepend(const T &value)
QList< T > mid(int pos, int length) const
void prepend(const T &value)
virtual QByteArray list() const override
list the registered actions To be implemented by subclasses
QString attribute(const QString &name, const QString &defaultValue=QString()) const
const T value(const Key &key, const T &defaultValue) const