6#include "application.h"
9#include "controller_p.h"
10#include "dispatcher.h"
12#include <QMetaClassInfo>
13#include <QRegularExpression>
19 , d_ptr(new ControllerPrivate(this))
23Controller::~Controller()
26 qDeleteAll(d->actionList);
39 auto it = d->actions.constFind(name);
40 if (it != d->actions.constEnd()) {
43 return d->dispatcher->getAction(name, d->pathPrefix);
49 auto it = d->actions.constFind(name);
50 if (it != d->actions.constEnd()) {
53 return d->dispatcher->getAction(name.
toString(), d->pathPrefix);
64 return !qstrcmp(
metaObject()->className(), className);
79ControllerPrivate::ControllerPrivate(
Controller *parent)
88 dispatcher = _dispatcher;
96 q->setObjectName(className);
98 bool namespaceFound =
false;
102 while (pathPrefix.startsWith(u
'/')) {
103 pathPrefix.remove(0, 1);
105 namespaceFound =
true;
110 if (!namespaceFound) {
112 bool lastWasUpper =
true;
114 for (
int i = 0; i < className.
length(); ++i) {
115 const QChar c = className.
at(i);
118 lastWasUpper =
false;
119 }
else if (c == u
'_') {
132 pathPrefix = controlerNS;
135 registerActionMethods(meta, q, app);
138void ControllerPrivate::setupFinished()
142 const ActionList beginList = dispatcher->getActions(QStringLiteral(
"Begin"), pathPrefix);
144 beginAutoList.append(beginList.
last());
147 beginAutoList.append(dispatcher->getActions(QStringLiteral(
"Auto"), pathPrefix));
149 const ActionList endList = dispatcher->getActions(QStringLiteral(
"End"), pathPrefix);
151 end = endList.
last();
154 const auto actions = actionList;
155 for (
Action *action : actions) {
156 action->dispatcherReady(dispatcher, q);
159 q->preFork(qobject_cast<Application *>(q->parent()));
168 int &actionRefCount = c->d_ptr->actionRefCount;
171 const auto beginAutoList = d->beginAutoList;
172 for (
Action *action : beginAutoList) {
173 if (actionRefCount) {
174 c->d_ptr->pendingAsync.enqueue(action);
175 }
else if (!action->dispatch(c)) {
183 if (actionRefCount) {
184 c->d_ptr->pendingAsync.enqueue(c->action());
192 if (actionRefCount) {
193 c->d_ptr->pendingAsync.enqueue(d->end);
194 }
else if (!d->end->dispatch(c)) {
199 if (actionRefCount) {
200 c->d_ptr->engineRequest->status |= EngineRequest::Async;
206Action *ControllerPrivate::actionClass(
const QVariantHash &args)
208 const auto attributes = args.value(QStringLiteral(
"attributes")).value<
ParamsMultiMap>();
209 const QString actionClass = attributes.value(QStringLiteral(
"ActionClass"));
211 QObject *
object = instantiateClass(actionClass,
"Cutelyst::Action");
213 Action *action = qobject_cast<Action *>(
object);
217 qCWarning(CUTELYST_CONTROLLER) <<
"ActionClass" << actionClass <<
"is not an ActionClass";
224Action *ControllerPrivate::createAction(
const QVariantHash &args,
225 const QMetaMethod &method,
229 Action *action = actionClass(args);
234 QStack<Component *> roles = gatherActionRoles(args);
235 for (
int i = 0; i < roles.
size(); ++i) {
237 code->
init(app, args);
243 action->
setName(args.value(QStringLiteral(
"name")).toString());
244 action->
setReverse(args.value(QStringLiteral(
"reverse")).toString());
250void ControllerPrivate::registerActionMethods(
const QMetaObject *meta,
256 const QMetaMethod method = meta->
method(i);
257 const QByteArray name = method.
name();
266 method.
parameterType(0) == qMetaTypeId<Cutelyst::Context *>())) {
269 QByteArray attributeArray;
271 QMetaClassInfo classInfo = meta->
classInfo(i2);
272 if (name == classInfo.
name()) {
276 ParamsMultiMap attrs = parseAttributes(method, attributeArray, name);
294 actions.insert(action->
reverse(), {action->reverse(), action});
295 actionList.append(action);
300ParamsMultiMap ControllerPrivate::parseAttributes(
const QMetaMethod &method,
301 const QByteArray &str,
302 const QByteArray &name)
305 std::vector<std::pair<QString, QString>> attributes;
312 int size = str.
size();
319 if (str.
at(pos) ==
':') {
320 int keyStart = ++pos;
323 if (str.
at(pos) ==
'(') {
325 int valueStart = ++pos;
327 if (str.
at(pos) ==
')') {
330 if (++pos < size && str.
at(pos) ==
':') {
336 }
else if (pos >= size) {
350 }
else if (str.
at(pos) ==
':') {
362 if (!value.isEmpty()) {
363 if ((value.startsWith(u
'\'') && value.endsWith(u
'\'')) ||
364 (value.startsWith(u
'"') && value.endsWith(u
'"'))) {
366 value.remove(value.size() - 1, 1);
371 attributes.emplace_back(std::make_pair(key, value));
379 for (
const auto &pair : attributes) {
380 QString key = pair.first;
381 QString value = pair.second;
382 if (key.
compare(u
"Global") == 0) {
383 key = QStringLiteral(
"Path");
385 }
else if (key.
compare(u
"Local") == 0) {
386 key = QStringLiteral(
"Path");
388 }
else if (key.
compare(u
"Path") == 0) {
389 value = parsePathAttr(value);
390 }
else if (key.
compare(u
"Args") == 0) {
391 QString args = value;
393 value = args.
remove(QRegularExpression(QStringLiteral(
"\\D")));
395 }
else if (key.
compare(u
"CaptureArgs") == 0) {
396 QString captureArgs = value;
397 value = captureArgs.
remove(QRegularExpression(QStringLiteral(
"\\D")));
398 }
else if (key.
compare(u
"Chained") == 0) {
399 value = parseChainedAttr(value);
406 if (!ret.
contains(QStringLiteral(
"Args")) && !ret.
contains(QStringLiteral(
"CaptureArgs")) &&
407 (ret.
contains(QStringLiteral(
"AutoArgs")) ||
408 ret.
contains(QStringLiteral(
"AutoCaptureArgs")))) {
409 if (ret.
contains(QStringLiteral(
"AutoArgs")) &&
410 ret.
contains(QStringLiteral(
"AutoCaptureArgs"))) {
411 qFatal(
"Action '%s' has both AutoArgs and AutoCaptureArgs, which is not allowed",
414 QString parameterName;
415 if (ret.
contains(QStringLiteral(
"AutoArgs"))) {
416 ret.
remove(QStringLiteral(
"AutoArgs"));
417 parameterName = QStringLiteral(
"Args");
419 ret.
remove(QStringLiteral(
"AutoCaptureArgs"));
420 parameterName = QStringLiteral(
"CaptureArgs");
426 int parameterCount = 0;
440 ret.
replace(QStringLiteral(
"Private"), QString());
446QStack<Component *> ControllerPrivate::gatherActionRoles(
const QVariantHash &args)
448 QStack<Component *> roles;
450 auto doesIt = attributes.constFind(QStringLiteral(
"Does"));
451 while (doesIt != attributes.constEnd() && doesIt.key().compare(u
"Does") == 0) {
453 instantiateClass(doesIt.value(), QByteArrayLiteral(
"Cutelyst::Component"));
455 roles.
push(qobject_cast<Component *>(
object));
462QString ControllerPrivate::parsePathAttr(
const QString &value)
464 QString ret = pathPrefix;
465 if (value.startsWith(u
'/')) {
467 }
else if (!value.isEmpty()) {
468 ret = pathPrefix + u
'/' + value;
473QString ControllerPrivate::parseChainedAttr(
const QString &attr)
475 QString ret = QStringLiteral(
"/");
483 if (!pathPrefix.isEmpty()) {
484 ret.
append(pathPrefix + u
'/' + attr);
496QObject *ControllerPrivate::instantiateClass(
const QString &name,
const QByteArray &super)
498 QString instanceName = name;
500 instanceName.
remove(QRegularExpression(QStringLiteral(
"\\W")));
502#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
503 QMetaType
id = QMetaType::fromName(instanceName.
toLatin1().
data());
505 if (!instanceName.
endsWith(QLatin1Char(
'*'))) {
506 instanceName.
append(QLatin1Char(
'*'));
509 id = QMetaType::fromName(instanceName.
toLatin1().
data());
510 if (!
id.isValid() && !instanceName.
startsWith(u
"Cutelyst::")) {
511 instanceName = QLatin1String(
"Cutelyst::") + instanceName;
512 id = QMetaType::fromName(instanceName.
toLatin1().
data());
517 const QMetaObject *metaObj =
id.metaObject();
519 if (!superIsClassName(metaObj->
superClass(), super)) {
520 qCWarning(CUTELYST_CONTROLLER)
521 <<
"Class name" << instanceName <<
"is not a derived class of" << super;
526 qCWarning(CUTELYST_CONTROLLER)
527 <<
"Could create a new instance of" << instanceName
528 <<
"make sure it's default constructor is "
529 "marked with the Q_INVOKABLE macro";
535 Component *component = application->createComponentPlugin(name);
540 component = application->createComponentPlugin(instanceName);
547 qCCritical(CUTELYST_CONTROLLER,
548 "Could not create component '%s', you can register it with "
549 "qRegisterMetaType<%s>(); or set a proper CUTELYST_PLUGINS_DIR",
550 qPrintable(instanceName),
551 qPrintable(instanceName));
557 if (!instanceName.
endsWith(QLatin1Char(
'*'))) {
558 instanceName.
append(QLatin1Char(
'*'));
562 if (!
id && !instanceName.
startsWith(u
"Cutelyst::")) {
563 instanceName = QLatin1String(
"Cutelyst::") + instanceName;
571 if (!superIsClassName(metaObj->
superClass(), super)) {
572 qCWarning(CUTELYST_CONTROLLER)
573 <<
"Class name" << instanceName <<
"is not a derived class of" << super;
578 qCWarning(CUTELYST_CONTROLLER)
579 <<
"Could create a new instance of" << instanceName
580 <<
"make sure it's default constructor is "
581 "marked with the Q_INVOKABLE macro";
587 Component *component = application->createComponentPlugin(name);
592 component = application->createComponentPlugin(instanceName);
599 qCCritical(CUTELYST_CONTROLLER,
600 "Could not create component '%s', you can register it with "
601 "qRegisterMetaType<%s>(); or set a proper CUTELYST_PLUGINS_DIR",
602 qPrintable(instanceName),
603 qPrintable(instanceName));
610bool ControllerPrivate::superIsClassName(
const QMetaObject *super,
const QByteArray &className)
616 return superIsClassName(super->
superClass(), className);
621#include "moc_controller.cpp"
This class represents a Cutelyst Action.
void setupAction(const QVariantHash &args, Application *app)
bool dispatch(Context *c)
void setMethod(const QMetaMethod &method)
void setController(Controller *controller)
The Cutelyst Application.
The Cutelyst Component base class.
void setReverse(const QString &reverse)
virtual bool init(Application *application, const QVariantHash &args)
void applyRoles(const QStack< Component * > &roles)
void setName(const QString &name)
Cutelyst Controller base class
virtual bool preFork(Application *app)
Action * actionFor(const QString &name) const
ActionList actions() const
virtual bool postFork(Application *app)
bool operator==(const char *className)
bool _DISPATCH(Context *c)
Controller(QObject *parent=nullptr)
The Cutelyst namespace holds all public Cutelyst API.
QMultiMap< QString, QString > ParamsMultiMap
QVector< Action * > ActionList
QByteArray & append(char ch)
char at(int i) const const
const char * constData() const const
QByteArray mid(int pos, int len) const const
bool isDigit() const const
bool isLower() const const
QChar toLower() const const
bool contains(const Key &key, const T &value) const const
typename QMap< Key, T >::iterator insert(const Key &key, const T &value)
int remove(const Key &key, const T &value)
typename QMap< Key, T >::iterator replace(const Key &key, const T &value)
QObject * parent() const const
void setParent(QObject *parent)
QString & append(QChar ch)
const QChar at(int position) const const
int compare(const QString &other, Qt::CaseSensitivity cs) const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString fromLatin1(const char *str, int size)
bool isEmpty() const const
QString number(int n, int base)
QString & remove(int position, int n)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QByteArray toLatin1() const const
QString toString() const const
QVariant fromValue(const T &value)
const T & at(int i) const const
bool isEmpty() const const
T value(int i) const const