18 #include "controller_p.h"
20 #include "application.h"
21 #include "dispatcher.h"
25 #include "context_p.h"
27 #include <QMetaClassInfo>
28 #include <QRegularExpression>
33 , d_ptr(new ControllerPrivate(this))
37 Controller::~Controller()
40 qDeleteAll(d->actionList);
53 Action *ret = d->actions.value(name);
57 return d->dispatcher->getAction(name, d->pathPrefix);
68 return !qstrcmp(metaObject()->className(), className);
83 ControllerPrivate::ControllerPrivate(
Controller *parent) :
92 dispatcher = _dispatcher;
98 const QMetaObject *meta = q->metaObject();
99 const QString className = QString::fromLatin1(meta->className());
100 q->setObjectName(className);
102 bool namespaceFound =
false;
103 for (
int i = meta->classInfoCount() - 1; i >= 0; --i) {
104 if (qstrcmp(meta->classInfo(i).name(),
"Namespace") == 0) {
105 pathPrefix = QString::fromLatin1(meta->classInfo(i).value());
107 pathPrefix.remove(0, 1);
109 namespaceFound =
true;
114 if (!namespaceFound) {
116 bool lastWasUpper =
true;
118 for (
int i = 0; i < className.length(); ++i) {
119 const QChar c = className.at(i);
120 if (c.isLower() || c.isDigit()) {
121 controlerNS.append(c);
122 lastWasUpper =
false;
124 controlerNS.append(c);
131 controlerNS.append(c.toLower());
136 pathPrefix = controlerNS;
139 registerActionMethods(meta, q, app);
142 void ControllerPrivate::setupFinished()
146 const ActionList beginList = dispatcher->getActions(QStringLiteral(
"Begin"), pathPrefix);
147 if (!beginList.isEmpty()) {
148 beginAutoList.append(beginList.last());
151 beginAutoList.append(dispatcher->getActions(QStringLiteral(
"Auto"), pathPrefix));
153 const ActionList endList = dispatcher->getActions(QStringLiteral(
"End"), pathPrefix);
154 if (!endList.isEmpty()) {
155 end = endList.last();
158 const auto actions = actionList;
159 for (
Action *action : actions) {
160 action->dispatcherReady(dispatcher, q);
163 q->preFork(qobject_cast<Application *>(q->parent()));
172 int &asyncDetached = c->d_ptr->asyncDetached;
175 const auto beginAutoList = d->beginAutoList;
176 for (
Action *action : beginAutoList) {
178 c->d_ptr->pendingAsync.append(action);
179 }
else if (!action->dispatch(c)) {
188 c->d_ptr->pendingAsync.append(c->action());
189 }
else if (!c->action()->
dispatch(c)) {
197 c->d_ptr->pendingAsync.append(d->end);
198 }
else if (!d->end->dispatch(c)) {
206 Action *ControllerPrivate::actionClass(
const QVariantHash &args)
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"
219 <<
"is not an ActionClass";
226 Action *ControllerPrivate::createAction(
const QVariantHash &args,
const QMetaMethod &method,
Controller *controller,
Application *app)
228 Action *action = actionClass(args);
233 QStack<Component *> roles = gatherActionRoles(args);
234 for (
int i = 0; i < roles.size(); ++i) {
236 code->
init(app, args);
237 code->setParent(action);
242 action->
setName(args.value(QStringLiteral(
"name")).toString());
243 action->
setReverse(args.value(QStringLiteral(
"reverse")).toString());
249 void ControllerPrivate::registerActionMethods(
const QMetaObject *meta,
Controller *controller,
Application *app)
252 for (
int i = 0; i < meta->methodCount(); ++i) {
253 const QMetaMethod method = meta->method(i);
254 const QByteArray name = method.
name();
259 if (method.isValid() &&
260 (method.methodType() == QMetaMethod::Method || method.methodType() == QMetaMethod::Slot) &&
261 (method.parameterCount() && method.parameterType(0) == qMetaTypeId<Cutelyst::Context *>())) {
264 QByteArray attributeArray;
265 for (
int i2 = meta->classInfoCount() - 1; i2 >= 0; --i2) {
266 QMetaClassInfo classInfo = meta->classInfo(i2);
267 if (name == classInfo.name()) {
268 attributeArray.append(classInfo.value());
274 if (controller->
ns().isEmpty()) {
275 reverse = QString::fromLatin1(name);
277 reverse = controller->
ns() +
QLatin1Char(
'/') + QString::fromLatin1(name);
280 Action *action = createAction({
281 {QStringLiteral(
"name"), QVariant::fromValue(name)},
282 {QStringLiteral(
"reverse"), QVariant::fromValue(reverse)},
283 {QStringLiteral(
"namespace"), QVariant::fromValue(controller->
ns())},
284 {QStringLiteral(
"attributes"), QVariant::fromValue(attrs)}
290 actions.insertMulti(action->
reverse(), action);
291 actionList.append(action);
296 QMap<QString, QString> ControllerPrivate::parseAttributes(
const QMetaMethod &method,
const QByteArray &str,
const QByteArray &name)
299 std::vector<std::pair<QString, QString> > attributes;
306 int size = str.
size();
313 if (str.at(pos) ==
':') {
314 int keyStart = ++pos;
317 if (str.at(pos) ==
'(') {
319 int valueStart = ++pos;
321 if (str.at(pos) ==
')') {
324 if (++pos < size && str.at(pos) ==
':') {
327 value = QString::fromLatin1(str.mid(valueStart, valueEnd - valueStart));
329 }
else if (pos >= size) {
332 value = QString::fromLatin1(str.mid(valueStart, valueEnd - valueStart));
342 }
else if (str.at(pos) ==
':') {
351 key = QString::fromLatin1(str.mid(keyStart, keyLength));
354 if (!value.isEmpty()) {
358 value.remove(value.size() - 1, 1);
363 attributes.push_back({ key, value });
371 auto i = attributes.crbegin();
372 const auto end = attributes.crend();
374 QString key = i->first;
375 QString value = i->second;
377 key = QStringLiteral(
"Path");
378 value = parsePathAttr(
QLatin1Char(
'/') + QString::fromLatin1(name));
380 key = QStringLiteral(
"Path");
381 value = parsePathAttr(QString::fromLatin1(name));
383 value = parsePathAttr(value);
385 QString args = value;
386 if (!args.isEmpty()) {
387 value = args.remove(QRegularExpression(QStringLiteral(
"\\D")));
390 QString captureArgs = value;
391 value = captureArgs.remove(QRegularExpression(QStringLiteral(
"\\D")));
393 value = parseChainedAttr(value);
401 if (!ret.
contains(QStringLiteral(
"Args")) && !ret.
contains(QStringLiteral(
"CaptureArgs")) &&
402 (ret.
contains(QStringLiteral(
"AutoArgs")) || ret.
contains(QStringLiteral(
"AutoCaptureArgs")))) {
403 if (ret.
contains(QStringLiteral(
"AutoArgs")) && ret.
contains(QStringLiteral(
"AutoCaptureArgs"))) {
404 qFatal(
"Action '%s' has both AutoArgs and AutoCaptureArgs, which is not allowed", name.constData());
406 QString parameterName;
407 if (ret.
contains(QStringLiteral(
"AutoArgs"))) {
408 ret.
remove(QStringLiteral(
"AutoArgs"));
409 parameterName = QStringLiteral(
"Args");
411 ret.
remove(QStringLiteral(
"AutoCaptureArgs"));
412 parameterName = QStringLiteral(
"CaptureArgs");
416 if (!(method.parameterCount() == 2 && method.parameterType(1) == QMetaType::QStringList)) {
417 int parameterCount = 0;
418 for (
int i2 = 1; i2 < method.parameterCount(); ++i2) {
419 int typeId = method.parameterType(i2);
420 if (typeId == QMetaType::QString) {
424 ret.
insert(parameterName, QString::number(parameterCount));
431 if (!ret.
contains(QStringLiteral(
"Private")) && method.access() == QMetaMethod::Private) {
432 ret.
insert(QStringLiteral(
"Private"), QString());
438 QStack<Component *> ControllerPrivate::gatherActionRoles(
const QVariantHash &args)
440 QStack<Component *> roles;
442 auto doesIt = attributes.
constFind(QStringLiteral(
"Does"));
443 while (doesIt != attributes.constEnd() && doesIt.
key() ==
QLatin1String(
"Does")) {
444 QObject *
object = instantiateClass(doesIt.
value(), QByteArrayLiteral(
"Cutelyst::Component"));
446 roles.push(qobject_cast<Component *>(
object));
453 QString ControllerPrivate::parsePathAttr(
const QString &value)
455 QString ret = pathPrefix;
458 }
else if (!value.isEmpty()) {
464 QString ControllerPrivate::parseChainedAttr(
const QString &attr)
466 QString ret = QStringLiteral(
"/");
467 if (attr.isEmpty()) {
471 if (attr == QStringLiteral(
".")) {
472 ret.append(pathPrefix);
474 if (!pathPrefix.isEmpty()) {
487 QObject *ControllerPrivate::instantiateClass(
const QString &name,
const QByteArray &super)
489 QString instanceName = name;
490 if (!instanceName.isEmpty()) {
491 instanceName.remove(QRegularExpression(QStringLiteral(
"\\W")));
493 int id = QMetaType::type(instanceName.toLatin1().data());
499 id = QMetaType::type(instanceName.toLatin1().data());
500 if (!
id && !instanceName.startsWith(QStringLiteral(
"Cutelyst::"))) {
502 id = QMetaType::type(instanceName.toLatin1().data());
507 const QMetaObject *metaObj = QMetaType::metaObjectForType(
id);
509 if (!superIsClassName(metaObj->superClass(), super)) {
510 qCWarning(CUTELYST_CONTROLLER)
513 <<
"is not a derived class of"
517 QObject *
object = metaObj->newInstance();
519 qCWarning(CUTELYST_CONTROLLER)
520 <<
"Could create a new instance of"
522 <<
"make sure it's default constructor is "
523 "marked with the Q_INVOKABLE macro";
529 Component *component = application->createComponentPlugin(name);
534 component = application->createComponentPlugin(instanceName);
541 qFatal(
"Could not create component '%s', you can register it with qRegisterMetaType<%s>(); or set a proper CUTELYST_PLUGINS_DIR",
542 qPrintable(instanceName), qPrintable(instanceName));
548 bool ControllerPrivate::superIsClassName(
const QMetaObject *super,
const QByteArray &className)
551 if (super->className() == className) {
554 return superIsClassName(super->superClass(), className);
559 #include "moc_controller.cpp"