6#include "application.h"
9#include "controller_p.h"
10#include "dispatcher.h"
14#include <QMetaClassInfo>
15#include <QRegularExpression>
245 , d_ptr(new ControllerPrivate(this))
249Controller::~Controller()
252 qDeleteAll(d->actionList);
259 return d->pathPrefix;
265 auto it = d->actions.constFind(name);
266 if (it != d->actions.constEnd()) {
269 return d->dispatcher->getAction(name.
toString(), d->pathPrefix);
275 return d->actionList;
280 return !qstrcmp(
metaObject()->className(), className);
295ControllerPrivate::ControllerPrivate(
Controller *parent)
306 dispatcher = _dispatcher;
314 q->setObjectName(className);
316 bool namespaceFound =
false;
320 while (pathPrefix.startsWith(u
'/')) {
321 pathPrefix.remove(0, 1);
323 namespaceFound =
true;
328 if (!namespaceFound) {
330 bool lastWasUpper =
true;
332 for (
int i = 0; i < className.
length(); ++i) {
333 const QChar c = className.
at(i);
336 lastWasUpper =
false;
337 }
else if (c == u
'_') {
350 pathPrefix = controlerNS;
353 registerActionMethods(meta, q, app);
356void ControllerPrivate::setupFinished()
360 const ActionList beginList = dispatcher->getActions(u
"Begin"_s, pathPrefix);
365 beginAutoList.append(dispatcher->getActions(u
"Auto"_s, pathPrefix));
367 const ActionList endList = dispatcher->getActions(u
"End"_s, pathPrefix);
369 end = endList.
last();
372 for (
Action *action : std::as_const(actionList)) {
373 action->dispatcherReady(dispatcher, q);
376 q->preFork(qobject_cast<Application *>(q->parent()));
385 const int &actionRefCount = c->d_ptr->actionRefCount;
388 const auto beginAutoList = d->beginAutoList;
389 for (
Action *action : beginAutoList) {
390 if (actionRefCount) {
391 c->d_ptr->pendingAsync.enqueue(action);
392 }
else if (!action->dispatch(c)) {
400 if (actionRefCount) {
401 c->d_ptr->pendingAsync.enqueue(c->
action());
409 if (actionRefCount) {
410 c->d_ptr->pendingAsync.enqueue(d->end);
411 }
else if (!d->end->dispatch(c)) {
416 if (actionRefCount) {
417 c->d_ptr->engineRequest->status |= EngineRequest::Async;
423Action *ControllerPrivate::actionClass(
const QVariantHash &args)
425 const auto attributes = args.value(u
"attributes"_s).value<
ParamsMultiMap>();
426 const QString actionClass = attributes.value(u
"ActionClass"_s);
428 QObject *
object = instantiateClass(actionClass,
"Cutelyst::Action");
430 if (
auto action = qobject_cast<Cutelyst::Action *>(
object); action) {
433 qCWarning(CUTELYST_CONTROLLER) <<
"ActionClass" << actionClass <<
"is not an ActionClass"
434 <<
object->metaObject()->superClass()->className();
441Action *ControllerPrivate::createAction(
const QVariantHash &args,
446 Action *action = actionClass(args);
452 for (
int i = 0; i < roles.
size(); ++i) {
454 code->
init(app, args);
460 action->
setName(args.value(u
"name"_s).toString());
461 action->
setReverse(args.value(u
"reverse"_s).toString());
467void ControllerPrivate::registerActionMethods(
const QMetaObject *meta,
483 method.
parameterType(0) == qMetaTypeId<Cutelyst::Context *>())) {
489 if (name == classInfo.
name()) {
493 ParamsMultiMap attrs = parseAttributes(method, attributeArray, name);
511 actionList.append(action);
521 std::vector<std::pair<QString, QString>> attributes;
528 int size = str.
size();
532 if (str.
at(pos) ==
':') {
535 int keyStart = ++pos;
538 if (str.
at(pos) ==
'(') {
540 int valueStart = ++pos;
542 if (str.
at(pos) ==
')') {
546 if (pos < size && str.
at(pos) ==
':') {
552 }
else if (pos >= size) {
565 }
else if (str.
at(pos) ==
':') {
577 if (!value.isEmpty()) {
578 if ((value.startsWith(u
'\'') && value.endsWith(u
'\'')) ||
579 (value.startsWith(u
'"') && value.endsWith(u
'"'))) {
581 value.remove(value.size() - 1, 1);
586 attributes.emplace_back(key, value);
595 std::ranges::for_each(attributes | std::views::reverse, [&](
const auto &pair) {
598 if (key.
compare(u
"Global") == 0) {
600 value = parsePathAttr(u
'/' + QString::fromLatin1(name));
601 }
else if (key.
compare(u
"Local") == 0) {
603 value = parsePathAttr(QString::fromLatin1(name));
604 }
else if (key.
compare(u
"Path") == 0) {
605 value = parsePathAttr(value);
606 }
else if (key.
compare(u
"Args") == 0) {
607 QString args = value;
608 if (!args.isEmpty()) {
609 value = args.remove(digitRE);
611 }
else if (key.
compare(u
"CaptureArgs") == 0) {
612 QString captureArgs = value;
613 value = captureArgs.remove(digitRE);
614 }
else if (key.
compare(u
"Chained") == 0) {
615 value = parseChainedAttr(value);
625 qFatal(
"Action '%s' has both AutoArgs and AutoCaptureArgs, which is not allowed",
630 ret.
remove(u
"AutoArgs"_s);
631 parameterName = u
"Args"_s;
633 ret.
remove(u
"AutoCaptureArgs"_s);
634 parameterName = u
"CaptureArgs"_s;
640 int parameterCount = 0;
654 ret.
insert(u
"Private"_s, {});
664 auto doesIt = attributes.
constFind(u
"Does"_s);
665 while (doesIt != attributes.constEnd() && doesIt.
key().compare(u
"Does") == 0) {
667 instantiateClass(doesIt.
value(), QByteArrayLiteral(
"Cutelyst::Component"));
669 roles.
push(qobject_cast<Component *>(
object));
679 if (value.startsWith(u
'/')) {
681 }
else if (!value.isEmpty()) {
682 ret = pathPrefix + u
'/' + value;
697 if (!pathPrefix.isEmpty()) {
698 ret.
append(pathPrefix + u
'/' + attr);
715 instanceName.
remove(nonWordsRE);
720 instanceName.
append(u
'*');
724 if (!
id.isValid() && !instanceName.
startsWith(u
"Cutelyst::")) {
725 instanceName = u
"Cutelyst::" + instanceName;
733 if (!superIsClassName(metaObj->
superClass(), super)) {
734 qCWarning(CUTELYST_CONTROLLER)
735 <<
"Class name" << instanceName <<
"is not a derived class of" << super;
740 qCWarning(CUTELYST_CONTROLLER)
741 <<
"Could create a new instance of" << instanceName
742 <<
"make sure it's default constructor is "
743 "marked with the Q_INVOKABLE macro";
749 Component *component = application->createComponentPlugin(name);
754 component = application->createComponentPlugin(instanceName);
761 qCCritical(CUTELYST_CONTROLLER,
762 "Could not create component '%s', you can register it with "
763 "qRegisterMetaType<%s>(); or set a proper CUTELYST_PLUGINS_DIR",
764 qPrintable(instanceName),
765 qPrintable(instanceName));
778 return superIsClassName(super->
superClass(), className);
783#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)
QString reverse() const noexcept
void setName(const QString &name)
Cutelyst Controller base class.
virtual bool preFork(Application *app)
virtual bool postFork(Application *app)
bool operator==(const char *className)
QString ns() const noexcept
ActionList actions() const noexcept
bool _DISPATCH(Context *c)
Controller(QObject *parent=nullptr)
Action * actionFor(QStringView name) const
The Cutelyst namespace holds all public Cutelyst API.
QByteArray & append(QByteArrayView data)
char at(qsizetype i) const const
const char * constData() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
qsizetype size() const const
bool isDigit(char32_t ucs4)
bool isLower(char32_t ucs4)
char32_t toLower(char32_t ucs4)
void append(QList::parameter_type value)
QList::const_reference at(qsizetype i) const const
bool isEmpty() const const
qsizetype size() const const
T value(qsizetype i) const const
QMultiMap::const_iterator constFind(const Key &key) const const
bool contains(const Key &key) const const
QMultiMap::iterator insert(QMultiMap::const_iterator pos, const Key &key, const T &value)
Key key(const T &value, const Key &defaultKey) const const
QMultiMap::size_type remove(const Key &key)
QMultiMap::iterator replace(const Key &key, const T &value)
T value(const Key &key, const T &defaultValue) const const
void setParent(QObject *parent)
QString & append(QChar ch)
const QChar at(qsizetype position) const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString first(qsizetype n) const const
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toLatin1() const const
QString toString() const const
QVariant fromValue(T &&value)