<commit_before>/* Copyright (c) 2011-2017, <NAME>
 * All rights reserved.
 *
 * This software may be used and distributed according to the terms of
 * the New BSD License, which is incorporated herein by reference.
 */

#include <QFile>
#include <QTextStream>
#include <QRegExp>
#include <QMap>
#include <TWebApplication>
#include <TSystemGlobal>
#include <THttpUtility>
#include "turlroute.h"


class RouteDirectiveHash : public QMap<QString, int>
{
public:
    RouteDirectiveHash() : QMap<QString, int>()
    {
        insert("match",    TRoute::Match);
        insert("get",      TRoute::Get);
        insert("post",     TRoute::Post);
        insert("put",      TRoute::Put);
        insert("patch",    TRoute::Patch);
        insert("delete",   TRoute::Delete);
        insert("trace",    TRoute::Trace);
        insert("connect",  TRoute::Connect);
        insert("patch",    TRoute::Patch);
    }
};
Q_GLOBAL_STATIC(RouteDirectiveHash, directiveHash)

static TUrlRoute *urlRoute = nullptr;

/*!
 * Initializes.
 * Call this in main thread.
 */
void TUrlRoute::instantiate()
{
    if (!urlRoute) {
        urlRoute = new TUrlRoute();
        urlRoute->parseConfigFile();

        qAddPostRoutine([] {
            if (urlRoute) {
                delete urlRoute;
                urlRoute = nullptr;
            }
        });
    }
}


const TUrlRoute &TUrlRoute::instance()
{
    Q_CHECK_PTR(urlRoute);
    return *urlRoute;
}


bool TUrlRoute::parseConfigFile()
{
    QFile routesFile(Tf::app()->routesConfigFilePath());
    if (!routesFile.open(QIODevice::ReadOnly)) {
        tSystemError("failed to read file : %s", qPrintable(routesFile.fileName()));
        return false;
    }

    int cnt = 0;
    QTextStream ts(&routesFile);
    while (!ts.atEnd()) {
        QString line = ts.readLine().simplified();
        ++cnt;

        if (!line.isEmpty() && !line.startsWith('#')) {
            if (!addRouteFromString(line)) {
                tError("Error parsing route %s [line: %d]", qPrintable(line), cnt);
            }
        }
    }
    return true;
}


bool TUrlRoute::addRouteFromString(const QString &line)
{
    QStringList items = line.simplified().split(' ');
    if (items.count() != 3) {
       tError("Invalid directive, '%s'", qPrintable(line));
       return false;
    }

    // Trimm quotes
    items[1] = THttpUtility::trimmedQuotes(items[1]);
    items[2] = THttpUtility::trimmedQuotes(items[2]);
    const QString &path = items[1];

    if (path.contains(":params") && !path.endsWith(":params")) {
       tError(":params must be specified as last directive.");
       return false;
    }

    TRoute rt;

    // Check method
    rt.method = directiveHash()->value(items[0].toLower(), TRoute::Invalid);
    if (rt.method == TRoute::Invalid) {
        tError("Invalid directive, '%s'", qPrintable(items[0]));
        return false;
    }

    // parse path
    rt.componentList = splitPath(path);
    rt.paramNum = rt.componentList.count(":param");
    rt.hasVariableParams = rt.componentList.contains(":params");

    for (int i = 0; i < rt.componentList.count(); ++i) {
        const QString &c = rt.componentList[i];
        if (c.startsWith(":")) {
            if (c != ":param" && c != ":params") {
                return false;
            }
        } else {
            rt.keywordIndexes << i;
        }
    }

    if (items[2].startsWith("/")) {
        // static file
        rt.controller = items[2].toUtf8();
    } else {
        // parse controller and action
        QStringList list = items[2].split(QRegExp("[#\\.]"));
        if (list.count() == 2) {
            rt.controller = list[0].toLower().toLatin1() + "controller";
            rt.action = list[1].toLatin1();
        } else {
            tError("Invalid action, '%s'", qPrintable(items[2]));
            return false;
        }
    }

    _routes << rt;
    tSystemDebug("route: method:%d path:%s  ctrl:%s action:%s params:%d",
        rt.method, qPrintable(QLatin1String("/") + rt.componentList.join("/")), rt.controller.data(),
        rt.action.data(), rt.hasVariableParams);
    return true;
}


TRouting TUrlRoute::findRouting(Tf::HttpMethod method, const QStringList &components) const
{
    if (_routes.isEmpty()) {
        TRouting();
    }

    for (const auto &rt : _routes) {
        // Too long or short?
        if (rt.hasVariableParams) {<commit_msg>fixed typo<commit_after>/* Copyright (c) 2011-2017, <NAME>
 * All rights reserved.
 *
 * This software may be used and distributed according to the terms of
 * the New BSD License, which is incorporated herein by reference.
 */

#include <QFile>
#include <QTextStream>
#include <QRegExp>
#include <QMap>
#include <TWebApplication>
#include <TSystemGlobal>
#include <THttpUtility>
#include "turlroute.h"


class RouteDirectiveHash : public QMap<QString, int>
{
public:
    RouteDirectiveHash() : QMap<QString, int>()
    {
        insert("match",    TRoute::Match);
        insert("get",      TRoute::Get);
        insert("post",     TRoute::Post);
        insert("put",      TRoute::Put);
        insert("patch",    TRoute::Patch);
        insert("delete",   TRoute::Delete);
        insert("trace",    TRoute::Trace);
        insert("connect",  TRoute::Connect);
        insert("patch",    TRoute::Patch);
    }
};
Q_GLOBAL_STATIC(RouteDirectiveHash, directiveHash)

static TUrlRoute *urlRoute = nullptr;

/*!
 * Initializes.
 * Call this in main thread.
 */
void TUrlRoute::instantiate()
{
    if (!urlRoute) {
        urlRoute = new TUrlRoute();
        urlRoute->parseConfigFile();

        qAddPostRoutine([] {
            if (urlRoute) {
                delete urlRoute;
                urlRoute = nullptr;
            }
        });
    }
}


const TUrlRoute &TUrlRoute::instance()
{
    Q_CHECK_PTR(urlRoute);
    return *urlRoute;
}


bool TUrlRoute::parseConfigFile()
{
    QFile routesFile(Tf::app()->routesConfigFilePath());
    if (!routesFile.open(QIODevice::ReadOnly)) {
        tSystemError("failed to read file : %s", qPrintable(routesFile.fileName()));
        return false;
    }

    int cnt = 0;
    QTextStream ts(&routesFile);
    while (!ts.atEnd()) {
        QString line = ts.readLine().simplified();
        ++cnt;

        if (!line.isEmpty() && !line.startsWith('#')) {
            if (!addRouteFromString(line)) {
                tError("Error parsing route %s [line: %d]", qPrintable(line), cnt);
            }
        }
    }
    return true;
}


bool TUrlRoute::addRouteFromString(const QString &line)
{
    QStringList items = line.simplified().split(' ');
    if (items.count() != 3) {
       tError("Invalid directive, '%s'", qPrintable(line));
       return false;
    }

    // Trimm quotes
    items[1] = THttpUtility::trimmedQuotes(items[1]);
    items[2] = THttpUtility::trimmedQuotes(items[2]);
    const QString &path = items[1];

    if (path.contains(":params") && !path.endsWith(":params")) {
       tError(":params must be specified as last directive.");
       return false;
    }

    TRoute rt;

    // Check method
    rt.method = directiveHash()->value(items[0].toLower(), TRoute::Invalid);
    if (rt.method == TRoute::Invalid) {
        tError("Invalid directive, '%s'", qPrintable(items[0]));
        return false;
    }

    // parse path
    rt.componentList = splitPath(path);
    rt.paramNum = rt.componentList.count(":param");
    rt.hasVariableParams = rt.componentList.contains(":params");

    for (int i = 0; i < rt.componentList.count(); ++i) {
        const QString &c = rt.componentList[i];
        if (c.startsWith(":")) {
            if (c != ":param" && c != ":params") {
                return false;
            }
        } else {
            rt.keywordIndexes << i;
        }
    }

    if (items[2].startsWith("/")) {
        // static file
        rt.controller = items[2].toUtf8();
    } else {
        // parse controller and action
        QStringList list = items[2].split(QRegExp("[#\\.]"));
        if (list.count() == 2) {
            rt.controller = list[0].toLower().toLatin1() + "controller";
            rt.action = list[1].toLatin1();
        } else {
            tError("Invalid action, '%s'", qPrintable(items[2]));
            return false;
        }
    }

    _routes << rt;
    tSystemDebug("route: method:%d path:%s  ctrl:%s action:%s params:%d",
        rt.method, qPrintable(QLatin1String("/") + rt.componentList.join("/")), rt.controller.data(),
        rt.action.data(), rt.hasVariableParams);
    return true;
}


TRouting TUrlRoute::findRouting(Tf::HttpMethod method, const QStringList &components) const
{
    if (_routes.isEmpty()) {
        return TRouting();
    }

    for (const auto &rt : _routes) {
        // Too long or short?
        if (rt.hasVariableParams) {
{'fraction_non_alphanumeric': 0.15793087663080796, 'fraction_numerical': 0.006637674525062943, 'mean_word_length': 2.5223700120918986, 'pattern_counts': {'":': 16, '<': 33, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7084672', 'n_tokens_mistral': 2750, 'n_tokens_neox': 2705, 'n_words': 596}