Cutelyst  1.11.0
dispatchtypepath.cpp
1 /*
2  * Copyright (C) 2013-2017 Daniel Nicoletti <dantti12@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 #include "dispatchtypepath_p.h"
19 
20 #include "common.h"
21 #include "controller.h"
22 #include "utils.h"
23 
24 #include <QBuffer>
25 #include <QRegularExpression>
26 #include <QDebug>
27 
28 using namespace Cutelyst;
29 
31  DispatchType(parent),
32  d_ptr(new DispatchTypePathPrivate)
33 {
34 }
35 
36 DispatchTypePath::~DispatchTypePath()
37 {
38  delete d_ptr;
39 }
40 
41 QByteArray DispatchTypePath::list() const
42 {
43  Q_D(const DispatchTypePath);
44 
45  QRegularExpression multipleSlashes(QLatin1String("/{1,}"));
46 
47  QVector<QStringList> table;
48 
49  QStringList keys = d->paths.keys();
50  keys.sort(Qt::CaseInsensitive);
51  for (const QString &path : keys) {
52  const auto paths = d->paths.value(path);
53  for (Action *action : paths) {
54  QString _path = QLatin1Char('/') + path;
55  if (action->attribute(QLatin1String("Args")).isEmpty()) {
56  _path.append(QLatin1String("/..."));
57  } else {
58  for (int i = 0; i < action->numberOfArgs(); ++i) {
59  _path.append(QLatin1String("/*"));
60  }
61  }
62  _path.replace(multipleSlashes, QLatin1String("/"));
63 
64  QString privateName = action->reverse();
65  if (!privateName.startsWith(QLatin1Char('/'))) {
66  privateName.prepend(QLatin1Char('/'));
67  }
68 
69  table.append({ _path, privateName });
70  }
71  }
72 
73  return Utils::buildTable(table, { QLatin1String("Path"), QLatin1String("Private") },
74  QLatin1String("Loaded Path actions:"));
75 }
76 
77 Cutelyst::DispatchType::MatchType DispatchTypePath::match(Context *c, const QString &path, const QStringList &args) const
78 {
79  Q_D(const DispatchTypePath);
80 
81  QString _path = path;
82  if (_path.isEmpty()) {
83  _path = QStringLiteral("/");
84  }
85 
86  const auto it = d->paths.constFind(_path);
87  if (it == d->paths.constEnd()) {
88  return NoMatch;
89  }
90 
91  MatchType ret = NoMatch;
92  int numberOfArgs = args.size();
93  for (Action *action : it.value()) {
94  // If the number of args is -1 (not defined)
95  // it will slurp all args so we don't care
96  // about how many args was passed
97  if (action->numberOfArgs() == numberOfArgs) {
98  Request *request = c->request();
99  request->setArguments(args);
100  request->setMatch(_path);
101  setupMatchedAction(c, action);
102  return ExactMatch;
103  } else if (action->numberOfArgs() == -1 &&
104  !c->action()) {
105  // Only setup partial matches if no action is
106  // currently set
107  Request *request = c->request();
108  request->setArguments(args);
109  request->setMatch(_path);
110  setupMatchedAction(c, action);
111  ret = PartialMatch;
112  }
113  }
114  return ret;
115 }
116 
118 {
119  Q_D(DispatchTypePath);
120 
121  bool ret = false;
122  const auto attributes = action->attributes();
123  const auto range = attributes.equal_range(QLatin1String("Path"));
124  for (auto i = range.first; i != range.second; ++i) {
125  if (d->registerPath(*i, action)) {
126  ret = true;
127  }
128  }
129 
130  // We always register valid actions
131  return ret;
132 }
133 
135 {
136  Q_D(const DispatchTypePath);
137  return !d->paths.isEmpty();
138 }
139 
140 QString DispatchTypePath::uriForAction(Cutelyst::Action *action, const QStringList &captures) const
141 {
142  QString ret;
143  if (captures.isEmpty()) {
144  const auto attributes = action->attributes();
145  auto it = attributes.constFind(QStringLiteral("Path"));
146  if (it != attributes.constEnd()) {
147  const QString path = it.value();
148  if (path.isEmpty()) {
149  ret = QStringLiteral("/");
150  } else if (!path.startsWith(QLatin1Char('/'))) {
151  ret = QLatin1Char('/') + path;
152  } else {
153  ret = path;
154  }
155  }
156  }
157  return ret;
158 }
159 
160 bool DispatchTypePathPrivate::registerPath(const QString &path, Action *action)
161 {
162  QString _path = path;
163  if (_path.startsWith(QLatin1Char('/')) && !_path.isEmpty()) {
164  _path.remove(0, 1);
165  }
166  if (_path.isEmpty()) {
167  _path = QStringLiteral("/");
168  }
169 
170  auto it = paths.find(_path);
171  if (it != paths.end()) {
172  int actionNumberOfArgs = action->numberOfArgs();
173  for (const Action *regAction : it.value()) {
174  if (regAction->numberOfArgs() == actionNumberOfArgs) {
175  qCCritical(CUTELYST_DISPATCHER_PATH) << "Not registering Action"
176  << action->name()
177  << "of controller"
178  << action->controller()->objectName()
179  << "because it conflicts with"
180  << regAction->name()
181  << "of controller"
182  << regAction->controller()->objectName();
183  return false;
184  }
185  }
186 
187  it.value().push_back(action);
188  std::sort(it.value().begin(), it.value().end(), [](Action *a, Action *b) -> bool {
189  return a->numberOfArgs() < b->numberOfArgs();
190  });
191  } else {
192  paths.insert(_path, { action });
193  }
194  return true;
195 }
196 
197 #include "moc_dispatchtypepath.cpp"
void setupMatchedAction(Context *c, Action *action) const
virtual bool inUse() override
DispatchTypePath(QObject *parent=nullptr)
QString name() const
Definition: component.cpp:39
virtual bool registerAction(Action *action) override
registerAction
virtual MatchType match(Context *c, const QString &path, const QStringList &args) const override
Controller * controller() const
Definition: action.cpp:105
virtual qint8 numberOfArgs() const
Definition: action.cpp:137
void setMatch(const QString &match)
Definition: request.cpp:157
This class represents a Cutelyst Action.
Definition: action.h:47
The Cutelyst Context.
Definition: context.h:50
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
void setArguments(const QStringList &arguments)
Definition: request.cpp:169
virtual QByteArray list() const override
list the registered actions To be implemented by subclasses
QMap< QString, QString > attributes() const
Definition: action.cpp:81
virtual QString uriForAction(Action *action, const QStringList &captures) const override