<commit_before>/*
 * Copyright (C) 2013 <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "dispatcher_p.h"

#include "common.h"
#include "context.h"
#include "controller.h"
#include "action.h"
#include "request_p.h"
#include "dispatchtypepath.h"

#include <QUrl>
#include <QMetaMethod>
#include <QStringBuilder>
#include <QDebug>

using namespace Cutelyst;

Dispatcher::Dispatcher(QObject *parent) :
    QObject(parent),
    d_ptr(new DispatcherPrivate)
{
}

Dispatcher::~Dispatcher()
{
    delete d_ptr;
}

void Dispatcher::setupActions(const QList<Controller*> &controllers)
{
    Q_D(Dispatcher);

    if (d->dispatchers.isEmpty()) {
        registerDispatchType(new DispatchTypePath(this));
    }

    ActionList registeredActions;
    Q_FOREACH (Controller *controller, controllers) {
        bool instanceUsed = false;
        Q_FOREACH (Action *action, controller->actions()) {
            bool registered = false;
            if (action->isValid() && !d->actionHash.contains(action->privateName())) {
                if (!action->attributes().contains("Private")) {
                    // Register the action with each dispatcher
                    Q_FOREACH (DispatchType *dispatch, d->dispatchers) {
                        if (dispatch->registerAction(action)) {
                            registered = true;
                        }
                    }
                } else {
                    // We register private actions
                    registered = true;
                }
            }

            // The Begin, Auto, End actions are not
            // registered by Dispatchers but we need them
            // as private actions anyway
            if (registered) {
                d->actionHash.insert(action->privateName(), action);
                d->containerHash[action->ns()] << action;
                registeredActions.append(action);
                instanceUsed = true;
            } else if (action->name() != "_DISPATCH" &&
                       action->name() != "_BEGIN" &&
                       action->name() != "_AUTO" &&
                       action->name() != "_ACTION" &&
                       action->name() != "_END") {
                qCDebug(CUTELYST_DISPATCHER) << "The action" << action->name() << "of"
                                             << action->controller()->objectName()
                                             << "controller was not registered in any dispatcher."
                                                " If you still want to access it internally (via actionFor())"
                                                " you may make it's method private.";
            } else if (d->showInternalActions) {
                qCDebug(CUTELYST_DISPATCHER) << "The action" << action->name() << "of"
                                             << action->controller()->objectName()
                                             << "controller was alread registered by the"
                                             << d->actionHash.value(action->privateName())->controller()->objectName()
                                             << "controller.";
            }
        }

        if (instanceUsed) {
            d->constrollerHash.insert(controller->objectName().toLatin1(), controller);
        }
    }

    // Cache root actions, BEFORE the controllers set them
    d->rootActions = d->containerHash.value("");

    Q_FOREACH (Controller *controller, controllers) {
        controller->setupActions(this);
    }

    Q_FOREACH (Action *action, registeredActions) {
        action->dispatcherReady(this);
    }

    qCDebug(CUTELYST_DISPATCHER) << endl << printActions().data() << endl;
}

bool Dispatcher::dispatch(Context *ctx)
{
    if (ctx->action()) {
        QByteArray action = ctx->ns();
        action.reserve(action.size() + 12);
        action.prepend('/');
        action.append("/_DISPATCH", 10);
        return forward(ctx, action);
    } else {
        const QString &path = ctx->req()->path();
        if (path.isEmpty()) {
            ctx->error(tr("No default action defined"));
        } else {
            ctx->error(tr("Unknown resource \"%1\".").arg(path));
        };
    }
    return false;
}

bool Dispatcher::forward(Context *ctx, const Action *action, const QStringList &arguments)
{
    Q_ASSERT(action);
    return action->dispatch(ctx);
}

bool Dispatcher::forward(Context *ctx, const QByteArray &opname, const QStringList &arguments)
{
    const Action *action = command2Action(ctx, opname);
    if (action) {
        return action->dispatch(ctx);
    }

    qCCritical(CUTELYST_DISPATCHER) << "Action not found" << action;
    return false;
}

void Dispatcher::prepareAction(Context *ctx)
{
    Q_D(Dispatcher);

    Request *request = ctx->request();
    QByteArray path = request->path();
    QList<QByteArray> pathParts = path.split('/');
    QStringList args;

    int pos = path.size();
    QByteArray actionPath;

    //  "/foo/bar"
    //  "/foo/" skip
    //  "/foo"
    //  "/"
    do {
        actionPath = path.mid(1, pos);
        Q_FOREACH (DispatchType *type, d->dispatchers) {
            if (type->match(ctx, actionPath, args)) {
                request->d_ptr->args = args;

                if (!request->match().isEmpty()) {
                    qCDebug(CUTELYST_DISPATCHER) << "Path is" << request->match();
                }

                if (!args.isEmpty()) {
                    qCDebug(CUTELYST_DISPATCHER) << "Arguments are" << args.join(QLatin1Char('/'));
                }
                return;
            }
        }

        // leave the loop if we are at the root "/"
        if (pos <= 1) {
            break;
        }

        pos = path.lastIndexOf('/', pos);
        if (pos != 0) {
            // Remove trailing '/'
            --pos;
        }

        args.prepend(QUrl::fromPercentEncoding(pathParts.takeLast()));
    } while (pos != -2);
}

const Action *Dispatcher::getAction(const QByteArray &name, const QByteArray &nameSpace) const
{
    Q_D(const Dispatcher);

    if (name.isEmpty()) {
        return 0;
    }

    QByteArray action = cleanNamespace(nameSpace);
    action.reserve(action.size() + name.size() + 1);
    action.append('/');
    action.append(name);

    return d->actionHash.value(action);
}

const Action *Dispatcher::getActionByPath(const QByteArray &path) const
{
    Q_D(const Dispatcher);

    QByteArray _path = path;
    if (!_path.startsWith('/')) {
        _path.prepend('/');
    }
    return d->actionHash.value(_path);
}

ActionList Dispatcher::getActions(const QByteArray &name, const QByteArray &nameSpace) const
{
    Q_D(const Dispatcher);

    ActionList ret;
    if (name.isEmpty()) {
        return ret;
    }

    QByteArray _ns = cleanNamespace(nameSpace);

    ActionList containers = d->getContainers(_ns);
    Q_FOREACH (Action *action, containers) {
        if (action->name() == name) {
            ret.prepend(action);
        }
    }

    return ret;
}
<commit_msg>Internal actions don't start with / remove it from the getActionByPath<commit_after>/*
 * Copyright (C) 2013 <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "dispatcher_p.h"

#include "common.h"
#include "context.h"
#include "controller.h"
#include "action.h"
#include "request_p.h"
#include "dispatchtypepath.h"

#include <QUrl>
#include <QMetaMethod>
#include <QStringBuilder>
#include <QDebug>

using namespace Cutelyst;

Dispatcher::Dispatcher(QObject *parent) :
    QObject(parent),
    d_ptr(new DispatcherPrivate)
{
}

Dispatcher::~Dispatcher()
{
    delete d_ptr;
}

void Dispatcher::setupActions(const QList<Controller*> &controllers)
{
    Q_D(Dispatcher);

    if (d->dispatchers.isEmpty()) {
        registerDispatchType(new DispatchTypePath(this));
    }

    ActionList registeredActions;
    Q_FOREACH (Controller *controller, controllers) {
        bool instanceUsed = false;
        Q_FOREACH (Action *action, controller->actions()) {
            bool registered = false;
            if (action->isValid() && !d->actionHash.contains(action->privateName())) {
                if (!action->attributes().contains("Private")) {
                    // Register the action with each dispatcher
                    Q_FOREACH (DispatchType *dispatch, d->dispatchers) {
                        if (dispatch->registerAction(action)) {
                            registered = true;
                        }
                    }
                } else {
                    // We register private actions
                    registered = true;
                }
            }

            // The Begin, Auto, End actions are not
            // registered by Dispatchers but we need them
            // as private actions anyway
            if (registered) {
                d->actionHash.insert(action->privateName(), action);
                d->containerHash[action->ns()] << action;
                registeredActions.append(action);
                instanceUsed = true;
            } else if (action->name() != "_DISPATCH" &&
                       action->name() != "_BEGIN" &&
                       action->name() != "_AUTO" &&
                       action->name() != "_ACTION" &&
                       action->name() != "_END") {
                qCDebug(CUTELYST_DISPATCHER) << "The action" << action->name() << "of"
                                             << action->controller()->objectName()
                                             << "controller was not registered in any dispatcher."
                                                " If you still want to access it internally (via actionFor())"
                                                " you may make it's method private.";
            } else if (d->showInternalActions) {
                qCDebug(CUTELYST_DISPATCHER) << "The action" << action->name() << "of"
                                             << action->controller()->objectName()
                                             << "controller was alread registered by the"
                                             << d->actionHash.value(action->privateName())->controller()->objectName()
                                             << "controller.";
            }
        }

        if (instanceUsed) {
            d->constrollerHash.insert(controller->objectName().toLatin1(), controller);
        }
    }

    // Cache root actions, BEFORE the controllers set them
    d->rootActions = d->containerHash.value("");

    Q_FOREACH (Controller *controller, controllers) {
        controller->setupActions(this);
    }

    Q_FOREACH (Action *action, registeredActions) {
        action->dispatcherReady(this);
    }

    qCDebug(CUTELYST_DISPATCHER) << endl << printActions().data() << endl;
}

bool Dispatcher::dispatch(Context *ctx)
{
    if (ctx->action()) {
        QByteArray action = ctx->ns();
        action.reserve(action.size() + 12);
        action.prepend('/');
        action.append("/_DISPATCH", 10);
        return forward(ctx, action);
    } else {
        const QString &path = ctx->req()->path();
        if (path.isEmpty()) {
            ctx->error(tr("No default action defined"));
        } else {
            ctx->error(tr("Unknown resource \"%1\".").arg(path));
        };
    }
    return false;
}

bool Dispatcher::forward(Context *ctx, const Action *action, const QStringList &arguments)
{
    Q_ASSERT(action);
    return action->dispatch(ctx);
}

bool Dispatcher::forward(Context *ctx, const QByteArray &opname, const QStringList &arguments)
{
    const Action *action = command2Action(ctx, opname);
    if (action) {
        return action->dispatch(ctx);
    }

    qCCritical(CUTELYST_DISPATCHER) << "Action not found" << action;
    return false;
}

void Dispatcher::prepareAction(Context *ctx)
{
    Q_D(Dispatcher);

    Request *request = ctx->request();
    QByteArray path = request->path();
    QList<QByteArray> pathParts = path.split('/');
    QStringList args;

    int pos = path.size();
    QByteArray actionPath;

    //  "/foo/bar"
    //  "/foo/" skip
    //  "/foo"
    //  "/"
    do {
        actionPath = path.mid(1, pos);
        Q_FOREACH (DispatchType *type, d->dispatchers) {
            if (type->match(ctx, actionPath, args)) {
                request->d_ptr->args = args;

                if (!request->match().isEmpty()) {
                    qCDebug(CUTELYST_DISPATCHER) << "Path is" << request->match();
                }

                if (!args.isEmpty()) {
                    qCDebug(CUTELYST_DISPATCHER) << "Arguments are" << args.join(QLatin1Char('/'));
                }
                return;
            }
        }

        // leave the loop if we are at the root "/"
        if (pos <= 1) {
            break;
        }

        pos = path.lastIndexOf('/', pos);
        if (pos != 0) {
            // Remove trailing '/'
            --pos;
        }

        args.prepend(QUrl::fromPercentEncoding(pathParts.takeLast()));
    } while (pos != -2);
}

const Action *Dispatcher::getAction(const QByteArray &name, const QByteArray &nameSpace) const
{
    Q_D(const Dispatcher);

    if (name.isEmpty()) {
        return 0;
    }

    QByteArray action = cleanNamespace(nameSpace);
    action.reserve(action.size() + name.size() + 1);
    action.append('/');
    action.append(name);

    return d->actionHash.value(action);
}

const Action *Dispatcher::getActionByPath(const QByteArray &path) const
{
    Q_D(const Dispatcher);

    QByteArray _path = path;
    if (_path.startsWith('/')) {
        _path.remove(0, 1);
    }
    return d->actionHash.value(_path);
}

ActionList Dispatcher::getActions(const QByteArray &name, const QByteArray &nameSpace) const
{
    Q_D(const Dispatcher);

    ActionList ret;
    if (name.isEmpty()) {
        return ret;
    }

    QByteArray _ns = cleanNamespace(nameSpace);

    ActionList containers = d->getContainers(_ns);
    Q_FOREACH (Action *action, containers) {
        if (action->name() == name) {
            ret.prepend(action);
        }
    }

    return ret;
}

{'fraction_non_alphanumeric': 0.12640051513200257, 'fraction_numerical': 0.003992273019961365, 'mean_word_length': 1.9890300230946882, 'pattern_counts': {'":': 0, '<': 111, '<?xml version=': 0, '>': 129, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 32, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '734684', 'n_tokens_mistral': 4318, 'n_tokens_neox': 4129, 'n_words': 1096}