<commit_before>/*
  Copyright (c) 2013 Timothy Reaves treaves@silverfieldstech.com

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
  */
#include "classhandlermanager.h"

#include "classhandler.h"

#include <QtCore/QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QMetaObject>
#include <QMetaMethod>
#include <QMetaType>
#include <QtPlugin>
#include <QPluginLoader>
#include <QSet>
#include <QString>
#include <QUrl>
#include <QVariant>

#include "httpserverrequest.h"
#include "headers.h"

namespace Tufao {

// Initialize static members.
QStringList ClassHandlerManager::pluginLocations;

/* ****************************************************************************************************************** */
#pragma mark -
#pragma mark Object lifecycle
/* ****************************************************************************************************************** */
ClassHandlerManager::ClassHandlerManager(QString pluginID, QString context, QObject * parent) :
    QObject(parent),
    pluginID(pluginID),
    m_context(context)
{
    // Set up the search locations
    if(pluginLocations.isEmpty()) {
        // Add standard locations to pluginLocations
        // First, the typical app config dir's
#ifdef Q_OS_WIN
        // Code can be added here, but, for the time being, I'm leaving it empty.
#elif defined(Q_OS_MAC)
        pluginLocations.append(QDir::homePath() + "/Library/Application Support/Tufao");
        pluginLocations.append("/Library/Application Support/Tufao");
#else
        pluginLocations.append(QDir::homePath() + "/.tufao");
#endif

        // The standard library locations
        foreach (QString libraryPath, QCoreApplication::libraryPaths()) {
            QDir testDir(libraryPath + QDir::separator() + "Tufao");
            if(testDir.exists()){
                pluginLocations.append(testDir.absolutePath());
            }
        }

        // Finally, the install location
        QFileInfo installDir(QCoreApplication::applicationDirPath());
        if(installDir.isDir()) {
            pluginLocations.append(installDir.absolutePath());
        }

        qDebug() << "pluginLocations:" << pluginLocations;
    }

    // Now load the plugin of interest
    // First list all static plugins.
    foreach (QObject * pluginInterface, QPluginLoader::staticInstances()){
        ClassHandler * plugin = qobject_cast<ClassHandler *>(pluginInterface);
        if (plugin){
            registerHandler(plugin);
        }
    }
///
    // Then list dynamic libraries from the plugins/ directory
    QStringList contents;
#ifdef Q_OS_WIN
    QString pluginExtension = ".dll";
#else
#ifdef Q_OS_MAC
    QString pluginExtension = ".dylib";
#else
    QString pluginExtension = ".so";
#endif
#endif
    // retrieve a list of all dynamic libraries from the search paths
    foreach (QString path, pluginLocations) {
        QFileInfo thisPath(QDir(path).filePath("plugins"));
        if(thisPath.isDir()){
            QDir thisDir(thisPath.absoluteFilePath());
            qDebug() << "Search " << thisPath.absolutePath() << " for plugins.";
            foreach (const QString entry, thisDir.entryList()) {
                if (!(entry == "..") && !(entry == ".") && entry.endsWith(pluginExtension)){
                    contents.append(thisDir.filePath(entry));
                }
            }
        }
    }

    // Check each dynamic library to see if it is a plugin
    foreach (QString pluginPath, contents) {
        QPluginLoader loader(pluginPath);
        // If we were constructed with a pluginID, we need to chech each plugin.
        if(pluginID.isEmpty() || pluginID == loader.metaData().value("IID").toString()) {
            if (!loader.load()) {
                qWarning() << "Couldn't load the dynamic library: "
                           << QDir::toNativeSeparators(pluginPath)
                           << ": "
                           << loader.errorString();
                continue;
            }

            QObject* obj = loader.instance();
            if (!obj) {
                qWarning() << "Couldn't open the dynamic library: "
                           << QDir::toNativeSeparators(pluginPath)
                           << ": "
                           << loader.errorString();
                continue;
            }

            ClassHandler * plugin = qobject_cast<ClassHandler *>(obj);
            if (plugin) {
                if (plugin){
                    registerHandler(plugin);
                }
            }
        }

    }
}

ClassHandlerManager::~ClassHandlerManager()
{
    foreach (ClassHandlerManager::PluginDescriptor * descriptor, handlers) {
        delete descriptor;
    }
    handlers.clear();
}

/* ****************************************************************************************************************** */
#pragma mark -
#pragma mark Accessors & mutators
/* ****************************************************************************************************************** */
QString ClassHandlerManager::context(void) const
{
    return m_context;
}

/* ****************************************************************************************************************** */
#pragma mark -
#pragma mark Static Methods
/* ****************************************************************************************************************** */
void ClassHandlerManager::addPluginLocation(const QString location)
{
    if(!pluginLocations.contains(location)){
        pluginLocations.append(location);
    }
}

/* ****************************************************************************************************************** */
#pragma mark -
#pragma mark Private Methods
/* ****************************************************************************************************************** */
void ClassHandlerManager::dispatchVoidMethod(QMetaMethod method,
                                             ClassHandler * handler,
                                             const QGenericArgument * args) const
{
    method.invoke(handler,
                  Qt::DirectConnection,
                  args[0],
                  args[1],
                  args[2],
                  args[3],
                  args[4],
                  args[5],
                  args[6],
                  args[7],
                  args[8],
                  args[9]
                  );
}

void ClassHandlerManager::dispatchJSONMethod(HttpServerResponse & response,
                                             QMetaMethod method,
                                             ClassHandler *handler,
                                             const QGenericArgument *args) const
{
    QJsonObject result;
    bool wasInvoked = method.invoke(handler,
                                    Qt::DirectConnection,
                                    Q_RETURN_ARG(QJsonObject, result),
                                    args[0],
                                    args[1],
                                    args[2],
                                    args[3],
                                    args[4],
                                    args[5],
                                    args[6],
                                    args[7],
                                    args[8],
                                    args[9]
                                    );
    if(wasInvoked) {
        HttpResponseStatus status = HttpResponseStatus::OK;
        QJsonObject jsonResponse = result;
        if(result.contains(HttpResponseStatusKey)) {
            status = HttpResponseStatus(result[HttpResponseStatusKey].toInt());
            jsonResponse = result[JsonResponseKey].toObject();
        }

        response.writeHead(status);
        response.headers().replace("Content-Type", "application/json; charset=utf-8");
        response.end(QJsonDocument(jsonResponse).toJson());
    }
}

bool ClassHandlerManager::processRequest(HttpServerRequest & request,
                                         HttpServerResponse & response,
                                         const QString className,
                                         const QString methodName,
                                         const QHash<QString, QString> arguments)
{
    bool handled = false;
    bool canHandle = true;
    int methodIndex = selectMethod(className, methodName, arguments);
    if(methodIndex > -1) {
        ClassHandlerManager::PluginDescriptor * handler = handlers[className];
        QMetaMethod method = handler->handler->metaObject()->method(methodIndex);

        // Create the arguments
        QGenericArgument argumentTable[10];
        argumentTable[0] = Q_ARG(Tufao::HttpServerRequest, request);
        argumentTable[1] = Q_ARG(Tufao::HttpServerResponse, response);

        // We need this to keep objects in scope until the actual invoke() call.
        QVariant variants[10];

        int argumentIndex = 2;
        while(argumentIndex < method.parameterCount()){
            QString parameterName = method.parameterNames()[argumentIndex];
            qDebug() << "Processing " << parameterName;
            variants[argumentIndex] = QVariant::fromValue(arguments.value(parameterName));
            int methodType = method.parameterType(argumentIndex);
            if(variants[argumentIndex].canConvert(methodType)) {
                variants[argumentIndex].convert(methodType);
                argumentTable[argumentIndex] = QGenericArgument(variants[argumentIndex].typeName(),
                                                                variants[argumentIndex].data());
                qDebug() << "Converted "
                         << arguments.value(parameterName)
                         << " to type "
                         << QVariant::typeToName(methodType)
                         << " index "
                         << argumentIndex;
            } else {
                qWarning() << "Can not convert "
                           << arguments.value(parameterName)
                           << " to type " << QVariant::typeToName(methodType);
            }
            argumentIndex+=1;
        }
        if(canHandle) {
            if(method.returnType() == QMetaType::QJsonObject) {
                this->dispatchJSONMethod(response, method, handler->handler, argumentTable);
            } else {
                this->dispatchVoidMethod(method, handler->handler, argumentTable);
            }
            handled = true;
        }
    } else {
        qWarning() << "Cound not find a method named with a matching signature.";
    }

    return handled;
}

void ClassHandlerManager::registerHandler(ClassHandler * handler)
{
    // Only process plugins that have not already been registered.
    if (!handlers.contains(handler->objectName())){
        qDebug() << "Registering " << handler->objectName() << " as a handler.";
        bool canDispathTo = false;
        const QMetaObject* metaObject = handler->metaObject();
        for(int methodIndex = metaObject->methodOffset(); methodIndex < metaObject->methodCount(); ++methodIndex) {
            QMetaMethod method = metaObject->method(methodIndex);
            // We only want public slots whos first two arguements are request & response
            if(method.methodType() == QMetaMethod::Slot && method.access() == QMetaMethod::Public) {
                QList<QByteArray> parameterNames = method.parameterNames();
                if(parameterNames[0] == QByteArray("request") && parameterNames[1] == QByteArray("response")) {
                    canDispathTo = true;
                    ClassHandlerManager::PluginDescriptor * pluginDescriptor = handlers[handler->objectName()];
                    if(pluginDescriptor == NULL) {
                        pluginDescriptor = new ClassHandlerManager::PluginDescriptor();
                        handlers[handler->objectName()] = pluginDescriptor;
                    }
                    pluginDescriptor->className = handler->objectName();
                    pluginDescriptor->handler = handler;

                    uint parameterHash = qHash(QString::fromLatin1(method.name()));
                    foreach (QByteArray nameBytes, parameterNames) {
                        parameterHash += qHash(QString::fromLatin1(nameBytes));
                    }
                    pluginDescriptor->methods.insert(parameterHash, methodIndex);
                    pluginDescriptor->methodNames.append(QString::fromLatin1(method.name()));

                    QString signature = QString::fromLatin1(method.methodSignature());
                    qDebug() << signature << " is a dispatchable endpoint.";
                }
            }
        }
        if(canDispathTo) {
            handler->init();
        }
    }

}

int ClassHandlerManager::selectMethod(const QString className,
                                      const QString methodName,
                                      const QHash<QString, QString> arguments) const
{
    int methodIndex = -1;
    uint parameterHash = qHash(methodName);
    parameterHash += qHash(QString("request"));
    parameterHash += qHash(QString("response"));
    foreach (QString key, arguments.keys()) {
        parameterHash += qHash(key);
    }
    PluginDescriptor * pluginDescriptor = handlers[className];
    if (pluginDescriptor->methods.contains(parameterHash)) {
        methodIndex = pluginDescriptor->methods.value(parameterHash);
    }
    return methodIndex;
}

/* ****************************************************************************************************************** */
#pragma mark -
#pragma mark Override Tufao::AbstractHttpServerRequestHandler Methods
/* ****************************************************************************************************************** */
bool ClassHandlerManager::handleRequest(Tufao::HttpServerRequest & request, Tufao::HttpServerResponse & response)
{
    bool wasHandled = false;
    QStringList pathComponents = request.url().toString().split("/", QString::SkipEmptyParts);

    //Is the request for our context?
    bool useContext = !m_context.isEmpty();
    // There must be at least two path components (class & method), and 3 if a context is specified.
    int minimumPathComponents = useContext ? 3 : 2;
    if (pathComponents.length() < minimumPathComponents) {
        qWarning() << "Request was dispatched to handler, but too few path components found.  The path components are"
                   << pathComponents;
    } else if(pathComponents.length() > minimumPathComponents  + 16) {
        // We also can not have too many arguments; 16 is max, as that is 8 argumetns plus request & response
        qWarning() << "Request was dispatched to handler, but too many path components found.  The path components are"
                   << pathComponents;
    } else {
        if(!useContext || m_context == pathComponents[0]) {
            // Add the context to the request
            request.setContext(m_context);

            int pathIndex = useContext ? 1 : 0;
            QString className = pathComponents[pathIndex++];
            QString methodName = pathComponents[pathIndex++];
            // We need to have an even number of path components left
            if((pathComponents.length() - pathIndex) % 2 == 0) {
                // See if we have a class handler with a matching method
                if(handlers.contains(className) && handlers[className]->methodNames.contains(methodName)) {
                    // Convert the remaining path components into an argument hash
                    QHash<QString, QString> arguments;
                    while(pathIndex < pathComponents.length()){
                        arguments[pathComponents[pathIndex]] = pathComponents[pathIndex + 1];
                        pathIndex += 2;
                    }
                    wasHandled = processRequest(request, response, className, methodName, arguments);
                } else {
                    if(handlers.contains(className)) {
                        qWarning() << "The class" << className << "has no method named" << methodName;
                    }
                }
            } else {
                qWarning() << "Can not dispath as an odd number of parameter components were supplied.";
            }

        }
    }

    return wasHandled;
}

} // namespace Tufao
<commit_msg>Updated JSON handling code<commit_after>/*
  Copyright (c) 2013 Timothy Reaves treaves@silverfieldstech.com

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
  */
#include "classhandlermanager.h"

#include "classhandler.h"

#include <QtCore/QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QMetaObject>
#include <QMetaMethod>
#include <QMetaType>
#include <QtPlugin>
#include <QPluginLoader>
#include <QSet>
#include <QString>
#include <QUrl>
#include <QVariant>

#include "httpserverrequest.h"
#include "headers.h"

namespace Tufao {

// Initialize static members.
QStringList ClassHandlerManager::pluginLocations;

/* ****************************************************************************************************************** */
#pragma mark -
#pragma mark Object lifecycle
/* ****************************************************************************************************************** */
ClassHandlerManager::ClassHandlerManager(QString pluginID, QString context, QObject * parent) :
    QObject(parent),
    pluginID(pluginID),
    m_context(context)
{
    // Set up the search locations
    if(pluginLocations.isEmpty()) {
        // Add standard locations to pluginLocations
        // First, the typical app config dir's
#ifdef Q_OS_WIN
        // Code can be added here, but, for the time being, I'm leaving it empty.
#elif defined(Q_OS_MAC)
        pluginLocations.append(QDir::homePath() + "/Library/Application Support/Tufao");
        pluginLocations.append("/Library/Application Support/Tufao");
#else
        pluginLocations.append(QDir::homePath() + "/.tufao");
#endif

        // The standard library locations
        foreach (QString libraryPath, QCoreApplication::libraryPaths()) {
            QDir testDir(libraryPath + QDir::separator() + "Tufao");
            if(testDir.exists()){
                pluginLocations.append(testDir.absolutePath());
            }
        }

        // Finally, the install location
        QFileInfo installDir(QCoreApplication::applicationDirPath());
        if(installDir.isDir()) {
            pluginLocations.append(installDir.absolutePath());
        }

        qDebug() << "pluginLocations:" << pluginLocations;
    }

    // Now load the plugin of interest
    // First list all static plugins.
    foreach (QObject * pluginInterface, QPluginLoader::staticInstances()){
        ClassHandler * plugin = qobject_cast<ClassHandler *>(pluginInterface);
        if (plugin){
            registerHandler(plugin);
        }
    }
///
    // Then list dynamic libraries from the plugins/ directory
    QStringList contents;
#ifdef Q_OS_WIN
    QString pluginExtension = ".dll";
#else
#ifdef Q_OS_MAC
    QString pluginExtension = ".dylib";
#else
    QString pluginExtension = ".so";
#endif
#endif
    // retrieve a list of all dynamic libraries from the search paths
    foreach (QString path, pluginLocations) {
        QFileInfo thisPath(QDir(path).filePath("plugins"));
        if(thisPath.isDir()){
            QDir thisDir(thisPath.absoluteFilePath());
            qDebug() << "Search " << thisPath.absolutePath() << " for plugins.";
            foreach (const QString entry, thisDir.entryList()) {
                if (!(entry == "..") && !(entry == ".") && entry.endsWith(pluginExtension)){
                    contents.append(thisDir.filePath(entry));
                }
            }
        }
    }

    // Check each dynamic library to see if it is a plugin
    foreach (QString pluginPath, contents) {
        QPluginLoader loader(pluginPath);
        // If we were constructed with a pluginID, we need to chech each plugin.
        if(pluginID.isEmpty() || pluginID == loader.metaData().value("IID").toString()) {
            if (!loader.load()) {
                qWarning() << "Couldn't load the dynamic library: "
                           << QDir::toNativeSeparators(pluginPath)
                           << ": "
                           << loader.errorString();
                continue;
            }

            QObject* obj = loader.instance();
            if (!obj) {
                qWarning() << "Couldn't open the dynamic library: "
                           << QDir::toNativeSeparators(pluginPath)
                           << ": "
                           << loader.errorString();
                continue;
            }

            ClassHandler * plugin = qobject_cast<ClassHandler *>(obj);
            if (plugin) {
                if (plugin){
                    registerHandler(plugin);
                }
            }
        }

    }
}

ClassHandlerManager::~ClassHandlerManager()
{
    foreach (ClassHandlerManager::PluginDescriptor * descriptor, handlers) {
        delete descriptor;
    }
    handlers.clear();
}

/* ****************************************************************************************************************** */
#pragma mark -
#pragma mark Accessors & mutators
/* ****************************************************************************************************************** */
QString ClassHandlerManager::context(void) const
{
    return m_context;
}

/* ****************************************************************************************************************** */
#pragma mark -
#pragma mark Static Methods
/* ****************************************************************************************************************** */
void ClassHandlerManager::addPluginLocation(const QString location)
{
    if(!pluginLocations.contains(location)){
        pluginLocations.append(location);
    }
}

/* ****************************************************************************************************************** */
#pragma mark -
#pragma mark Private Methods
/* ****************************************************************************************************************** */
void ClassHandlerManager::dispatchVoidMethod(QMetaMethod method,
                                             ClassHandler * handler,
                                             const QGenericArgument * args) const
{
    method.invoke(handler,
                  Qt::DirectConnection,
                  args[0],
                  args[1],
                  args[2],
                  args[3],
                  args[4],
                  args[5],
                  args[6],
                  args[7],
                  args[8],
                  args[9]
                  );
}

void ClassHandlerManager::dispatchJSONMethod(HttpServerResponse & response,
                                             QMetaMethod method,
                                             ClassHandler *handler,
                                             const QGenericArgument *args) const
{
    QJsonObject result;
    bool wasInvoked = method.invoke(handler,
                                    Qt::DirectConnection,
                                    Q_RETURN_ARG(QJsonObject, result),
                                    args[0],
                                    args[1],
                                    args[2],
                                    args[3],
                                    args[4],
                                    args[5],
                                    args[6],
                                    args[7],
                                    args[8],
                                    args[9]
                                    );
    if(wasInvoked) {
        HttpResponseStatus status = HttpResponseStatus::OK;
        QJsonDocument jsonDocument;
        if(result.contains(HttpResponseStatusKey)) {
            status = HttpResponseStatus(result[HttpResponseStatusKey].toInt());
            //The response will either be an JsonObject, or a JsonArray
            if(result[JsonResponseKey].isArray()) {
                jsonDocument.setArray(result[JsonResponseKey].toArray());
            } else {
                jsonDocument = QJsonDocument(result[JsonResponseKey].toObject());
            }
        }
        response.writeHead(status);
        response.headers().replace("Content-Type", "application/json");
        response.end(jsonDocument.toJson());
    }
}

bool ClassHandlerManager::processRequest(HttpServerRequest & request,
                                         HttpServerResponse & response,
                                         const QString className,
                                         const QString methodName,
                                         const QHash<QString, QString> arguments)
{
    bool handled = false;
    bool canHandle = true;
    int methodIndex = selectMethod(className, methodName, arguments);
    if(methodIndex > -1) {
        ClassHandlerManager::PluginDescriptor * handler = handlers[className];
        QMetaMethod method = handler->handler->metaObject()->method(methodIndex);

        // Create the arguments
        QGenericArgument argumentTable[10];
        argumentTable[0] = Q_ARG(Tufao::HttpServerRequest, request);
        argumentTable[1] = Q_ARG(Tufao::HttpServerResponse, response);

        // We need this to keep objects in scope until the actual invoke() call.
        QVariant variants[10];

        int argumentIndex = 2;
        while(argumentIndex < method.parameterCount()){
            QString parameterName = method.parameterNames()[argumentIndex];
            // qDebug() << "Processing " << parameterName;
            variants[argumentIndex] = QVariant::fromValue(arguments.value(parameterName));
            int methodType = method.parameterType(argumentIndex);
            if(variants[argumentIndex].canConvert(methodType)) {
                variants[argumentIndex].convert(methodType);
                argumentTable[argumentIndex] = QGenericArgument(variants[argumentIndex].typeName(),
                                                                variants[argumentIndex].data());
                // qDebug() << "Converted "
                //          << arguments.value(parameterName)
                //          << " to type "
                //          << QVariant::typeToName(methodType)
                //          << " index "
                //          << argumentIndex;
            } else {
                qWarning() << "Can not convert "
                           << arguments.value(parameterName)
                           << " to type " << QVariant::typeToName(methodType);
            }
            argumentIndex+=1;
        }
        if(canHandle) {
            if(method.returnType() == QMetaType::QJsonObject) {
                this->dispatchJSONMethod(response, method, handler->handler, argumentTable);
            } else {
                this->dispatchVoidMethod(method, handler->handler, argumentTable);
            }
            handled = true;
        }
    } else {
        qWarning() << "Cound not find a method named with a matching signature.";
    }

    return handled;
}

void ClassHandlerManager::registerHandler(ClassHandler * handler)
{
    // Only process plugins that have not already been registered.
    if (!handlers.contains(handler->objectName())){
        qDebug() << "Registering " << handler->objectName() << " as a handler.";
        bool canDispathTo = false;
        const QMetaObject* metaObject = handler->metaObject();
        for(int methodIndex = metaObject->methodOffset(); methodIndex < metaObject->methodCount(); ++methodIndex) {
            QMetaMethod method = metaObject->method(methodIndex);
            // We only want public slots whos first two arguements are request & response
            if(method.methodType() == QMetaMethod::Slot && method.access() == QMetaMethod::Public) {
                QList<QByteArray> parameterNames = method.parameterNames();
                if(parameterNames[0] == QByteArray("request") && parameterNames[1] == QByteArray("response")) {
                    canDispathTo = true;
                    ClassHandlerManager::PluginDescriptor * pluginDescriptor = handlers[handler->objectName()];
                    if(pluginDescriptor == NULL) {
                        pluginDescriptor = new ClassHandlerManager::PluginDescriptor();
                        handlers[handler->objectName()] = pluginDescriptor;
                    }
                    pluginDescriptor->className = handler->objectName();
                    pluginDescriptor->handler = handler;

                    uint parameterHash = qHash(QString::fromLatin1(method.name()));
                    foreach (QByteArray nameBytes, parameterNames) {
                        parameterHash += qHash(QString::fromLatin1(nameBytes));
                    }
                    pluginDescriptor->methods.insert(parameterHash, methodIndex);
                    pluginDescriptor->methodNames.append(QString::fromLatin1(method.name()));

                    QString signature = QString::fromLatin1(method.methodSignature());
                    qDebug() << signature << " is a dispatchable endpoint.";
                }
            }
        }
        if(canDispathTo) {
            handler->init();
        }
    }

}

int ClassHandlerManager::selectMethod(const QString className,
                                      const QString methodName,
                                      const QHash<QString, QString> arguments) const
{
    int methodIndex = -1;
    uint parameterHash = qHash(methodName);
    parameterHash += qHash(QString("request"));
    parameterHash += qHash(QString("response"));
    foreach (QString key, arguments.keys()) {
        parameterHash += qHash(key);
    }
    PluginDescriptor * pluginDescriptor = handlers[className];
    if (pluginDescriptor->methods.contains(parameterHash)) {
        methodIndex = pluginDescriptor->methods.value(parameterHash);
    }
    return methodIndex;
}

/* ****************************************************************************************************************** */
#pragma mark -
#pragma mark Override Tufao::AbstractHttpServerRequestHandler Methods
/* ****************************************************************************************************************** */
bool ClassHandlerManager::handleRequest(Tufao::HttpServerRequest & request, Tufao::HttpServerResponse & response)
{
    bool wasHandled = false;
    QStringList pathComponents = request.url().toString().split("/", QString::SkipEmptyParts);

    //Is the request for our context?
    bool useContext = !m_context.isEmpty();
    // There must be at least two path components (class & method), and 3 if a context is specified.
    int minimumPathComponents = useContext ? 3 : 2;
    if (pathComponents.length() < minimumPathComponents) {
        qWarning() << "Request was dispatched to handler, but too few path components found.  The path components are"
                   << pathComponents;
    } else if(pathComponents.length() > minimumPathComponents  + 16) {
        // We also can not have too many arguments; 16 is max, as that is 8 argumetns plus request & response
        qWarning() << "Request was dispatched to handler, but too many path components found.  The path components are"
                   << pathComponents;
    } else {
        if(!useContext || m_context == pathComponents[0]) {
            // Add the context to the request
            request.setContext(m_context);

            int pathIndex = useContext ? 1 : 0;
            QString className = pathComponents[pathIndex++];
            QString methodName = pathComponents[pathIndex++];
            // We need to have an even number of path components left
            if((pathComponents.length() - pathIndex) % 2 == 0) {
                // See if we have a class handler with a matching method
                if(handlers.contains(className) && handlers[className]->methodNames.contains(methodName)) {
                    // Convert the remaining path components into an argument hash
                    QHash<QString, QString> arguments;
                    while(pathIndex < pathComponents.length()){
                        arguments[pathComponents[pathIndex]] = pathComponents[pathIndex + 1];
                        pathIndex += 2;
                    }
                    wasHandled = processRequest(request, response, className, methodName, arguments);
                } else {
                    if(handlers.contains(className)) {
                        qWarning() << "The class" << className << "has no method named" << methodName;
                    }
                }
            } else {
                qWarning() << "Can not dispath as an odd number of parameter components were supplied.";
            }

        }
    }

    return wasHandled;
}

} // namespace Tufao
<|endoftext|>


<commit_before>#ifndef ROBOCUP2DSIM_CLIENT_BOT_RECEIVER_HPP
#define ROBOCUP2DSIM_CLIENT_BOT_RECEIVER_HPP

#include <memory>
#include <thread>
#include <asio/error_code.hpp>
#include <asio/io_service.hpp>
#include <asio/posix/stream_descriptor.hpp>
#include <beam/message/capnproto.hpp>
#include <turbo/container/spsc_ring_queue.hpp>
#include <turbo/ipc/posix/pipe.hpp>
#include <robocup2Dsim/bcprotocol/protocol.capnp.h>
#include "specification.hpp"

namespace robocup2Dsim {
namespace client {

class bot_receiver
{
public:
    bot_receiver(turbo::ipc::posix::pipe::front&& bot_stdout, bot_out_queue_type::producer& producer);
    ~bot_receiver();
    bool running() const { return thread_ != nullptr; }
    void reset();
    void start();
    void stop();
private:
    bot_receiver(const bot_receiver& other) = delete;
    bot_receiver& operator=(const bot_receiver& other) = delete;
    void run();
    void receive(const asio::error_code& error, std::size_t bytes_received);
    turbo::ipc::posix::pipe::front&& bot_stdout_;
    bot_out_queue_type::producer& producer_;
    std::thread* thread_;
    asio::io_service service_;
    asio::posix::stream_descriptor stream_;
};

} // namespace client
} // namespace robocup2Dsim

#endif
<commit_msg>bot_stdout_ should be a lvalue<commit_after>#ifndef ROBOCUP2DSIM_CLIENT_BOT_RECEIVER_HPP
#define ROBOCUP2DSIM_CLIENT_BOT_RECEIVER_HPP

#include <memory>
#include <thread>
#include <asio/error_code.hpp>
#include <asio/io_service.hpp>
#include <asio/posix/stream_descriptor.hpp>
#include <beam/message/capnproto.hpp>
#include <turbo/container/spsc_ring_queue.hpp>
#include <turbo/ipc/posix/pipe.hpp>
#include <robocup2Dsim/bcprotocol/protocol.capnp.h>
#include "specification.hpp"

namespace robocup2Dsim {
namespace client {

class bot_receiver
{
public:
    bot_receiver(turbo::ipc::posix::pipe::front&& bot_stdout, bot_out_queue_type::producer& producer);
    ~bot_receiver();
    bool running() const { return thread_ != nullptr; }
    void reset();
    void start();
    void stop();
private:
    bot_receiver(const bot_receiver& other) = delete;
    bot_receiver& operator=(const bot_receiver& other) = delete;
    void run();
    void receive(const asio::error_code& error, std::size_t bytes_received);
    turbo::ipc::posix::pipe::front bot_stdout_;
    bot_out_queue_type::producer& producer_;
    std::thread* thread_;
    asio::io_service service_;
    asio::posix::stream_descriptor stream_;
};

} // namespace client
} // namespace robocup2Dsim

#endif
<|endoftext|>


<commit_before>/*
  cl_main_frame.cc -- Copyright (c) 2013 Noel Cower. All rights reserved.
  See COPYING under the project root for the source code license. If this file
  is not present, refer to <https://raw.github.com/nilium/snow/master/COPYING>.
*/
#include "cl_main.hh"
#include "../game/system.hh"
#include "../game/console_pane.hh"
#include "../renderer/gl_error.hh"
#include "../timing.hh"
#include <thread>

namespace snow {

namespace {

static void cl_log_callback(const char *msg, size_t len, void *ctx)
{
  console_pane_t *console = (console_pane_t *)ctx;
  if (!ctx) {
    return;
  }
  console->write_log(string(msg, len));
}

std::chrono::milliseconds cl_frameloop_sleep_duration()
{
  return std::chrono::milliseconds(16);
}

} // namespace <anon>

/*==============================================================================
  pump_netevents

    Reads events from the server or other connections and inserts them into the
    event queue.
==============================================================================*/
#if USE_SERVER
void client_t::pump_netevents(double timeslice)
{
  #define NET_TIMEOUT 1

  ENetEvent event;
  int error = 0;
  while ((error = enet_host_service(host_, &event, NET_TIMEOUT)) > 0) {
    if (event.type == ENET_EVENT_TYPE_RECEIVE) {
      if (event.packet) {
        const auto index = netevent_pool_.allocate();
        netevent_t &netevent = netevent_pool_[index];
        netevent.read_from(event.packet);
        enet_packet_destroy(event.packet);
        event_t emitted = {
          EVENT_SENDER_NET,
          { .sender = this },
          NET_EVENT,
          timeslice
        };
        emitted.net = &netevent;
        event_queue_.emit_event(emitted);
        s_log_note("Emitted net event");
      }
    }
  }

  if (error < 0) {
    s_log_error("Error checking for ENet events: %d", error);
  }
}
#endif

/*==============================================================================
  read_events(timeslice)

    Reads events that have occurred prior to the given timeslice. Events that
    occur afterward must be ignored and left in the event queue.

    Any events valid to the timeslice must be propagated through all systems.
    Systems may receive events in an out-of-order manner due to netevents being
    emitted after polling for OS events.
==============================================================================*/
void client_t::read_events(double timeslice)
{
  event_queue_.set_frame_time(sim_time_);

  // And run through events
  event_t event;
  auto sys_end = logic_systems_.cend();
  auto sys_begin = logic_systems_.cbegin();
  while (read_socket_.recv(&event, sizeof(event), ZMQ_DONTWAIT) == sizeof(event)) {
    switch (event.kind) {
      case WINDOW_FOCUS_EVENT:
        wnd_focused->seti(event.focused);
        // fall-through

      default: {
        auto sys_iter = sys_begin;
        event.time -= base_time_;
        while (sys_iter != sys_end) {
          system_t *sys = sys_iter->second;
          if (sys->active() && !sys->event(event)) {
            break;
          }
          ++sys_iter;
        }
        break;
      }
    }
  }
}

/*==============================================================================
  do_frame(step, timeslice)

    Perform a single frame for as much time has passed as step. Barring changes
    to gameplay, step should be considered a constant some fraction of a second
    measured in seconds.
==============================================================================*/
void client_t::do_frame(double step, double timeslice)
{
  for (const auto &spair : logic_systems_) {
    if (spair.second->active()) {
      spair.second->frame(step, timeslice);
    }
  }
}

/*==============================================================================
  run_frameloop

    Kick off the frameloop. This just calls frameloop() and should not allocate
    any resources itself -- if resources must be allocated, it should be done
    so in or beneath frameloop() and freed before frameloop() ends. This is to
    prevent async hiccups when terminate() is called on the main thread.
==============================================================================*/
void client_t::run_frameloop()
{
  frameloop();
  terminate();
}

/*==============================================================================
  frameloop

    Body of the frameloop. Handles incoming events, signals system updates, and
    frame rendering.
==============================================================================*/
void client_t::frameloop()
{
  // FIXME: Almost all of this crap should be moved to game-specific code.
  console_pane_t &console = default_console();
  s_set_log_callback(cl_log_callback, &console);

  cvars_.clear();
  cvars_.register_ccmd(&cmd_quit_);

  cl_willQuit = cvars_.get_cvar( "cl_willQuit", 0, CVAR_READ_ONLY | CVAR_DELAYED | CVAR_INVISIBLE );
  wnd_focused = cvars_.get_cvar( "wnd_focused", 1, CVAR_READ_ONLY | CVAR_DELAYED | CVAR_INVISIBLE );
  wnd_mouseMode = cvars_.get_cvar("wnd_mouseMode", true, CVAR_DELAYED | CVAR_INVISIBLE);
  r_drawFrame = cvars_.get_cvar( "r_drawFrame", 1, CVAR_READ_ONLY | CVAR_DELAYED );
  r_clearFrame = cvars_.get_cvar("r_clearFrame", 1, CVAR_READ_ONLY | CVAR_DELAYED );

  console.set_cvar_set(&cvars_);

  glfwShowWindow(window_);
  glfwMakeContextCurrent(window_);
  glfwSwapInterval(0);    // Don't needlessly limit rendering speed

  // Set this to true before getting the base time since it might loop a couple
  // times setting it.
  running_ = true;
  sim_time_ = 0;
  // Don't call glfwSetTime because that might throw other clients out of sync
  // (even though really the chance of there being other clients is zero)
  base_time_ = glfwGetTime();
  unsigned frame, last_frame;
  frame = 1; last_frame = 0;

  // FIXME: move these somewhere else, probably?
  glClearColor(0, 0, 0, 1);
  glEnable(GL_BLEND);

  add_system(&console, 16777216, -16777216);

  while (running_.load()) {
#if HIDE_CURSOR_ON_CONSOLE_CLOSE
    int mousemode = -1;
#endif

    // Do any frames that would have passed since the last rendering point
    const double cur_time = glfwGetTime() - base_time_;
    while (sim_time_ < cur_time) {
      sim_time_ += FRAME_SEQ_TIME;
      ++frame;
      read_events(sim_time_);
      do_frame(FRAME_SEQ_TIME, sim_time_);

#if HIDE_CURSOR_ON_CONSOLE_CLOSE
      if (wnd_mouseMode->has_flags(CVAR_MODIFIED)) {
        wnd_mouseMode->update();
        mousemode = wnd_mouseMode->geti();
      }
#endif

      cvars_.update_cvars();
    }

#if HIDE_CURSOR_ON_CONSOLE_CLOSE
    switch (mousemode) {
    case 0:
      glfwSetInputMode(window_, GLFW_CURSOR_MODE, GLFW_CURSOR_HIDDEN);
      glfwSetInputMode(window_, GLFW_STICKY_KEYS, 1);
      break;
    case 1:
      glfwSetInputMode(window_, GLFW_CURSOR_MODE, GLFW_CURSOR_NORMAL);
      glfwSetInputMode(window_, GLFW_STICKY_KEYS, 0);
      break;
    default: break;
    }
#endif

    if (frame != last_frame && r_drawFrame->geti()) {
      last_frame = frame;

      if (r_clearFrame->geti()) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        assert_gl("Clearing buffers");
      }

      for (auto &spair : draw_systems_) {
        if (spair.second->active()) {
          spair.second->draw(sim_time_);
        }
      }

      glfwSwapBuffers(window_);
    }

    if (cl_willQuit->geti()) {
      running_ = false;
    } else if (!wnd_focused->geti()) {
      std::this_thread::sleep_for(cl_frameloop_sleep_duration());
    }
  } // while (running)

  s_set_log_callback(nullptr, nullptr);

  res_->release_all();

  glfwMakeContextCurrent(NULL);
} // frameloop

} // namespace snow
<commit_msg>client: Wrap resource loading in block to drop references first.<commit_after>/*
  cl_main_frame.cc -- Copyright (c) 2013 Noel Cower. All rights reserved.
  See COPYING under the project root for the source code license. If this file
  is not present, refer to <https://raw.github.com/nilium/snow/master/COPYING>.
*/
#include "cl_main.hh"
#include "../game/system.hh"
#include "../game/console_pane.hh"
#include "../renderer/gl_error.hh"
#include "../timing.hh"
#include <thread>

namespace snow {

namespace {

static void cl_log_callback(const char *msg, size_t len, void *ctx)
{
  console_pane_t *console = (console_pane_t *)ctx;
  if (!ctx) {
    return;
  }
  console->write_log(string(msg, len));
}

std::chrono::milliseconds cl_frameloop_sleep_duration()
{
  return std::chrono::milliseconds(16);
}

} // namespace <anon>

/*==============================================================================
  pump_netevents

    Reads events from the server or other connections and inserts them into the
    event queue.
==============================================================================*/
#if USE_SERVER
void client_t::pump_netevents(double timeslice)
{
  #define NET_TIMEOUT 1

  ENetEvent event;
  int error = 0;
  while ((error = enet_host_service(host_, &event, NET_TIMEOUT)) > 0) {
    if (event.type == ENET_EVENT_TYPE_RECEIVE) {
      if (event.packet) {
        const auto index = netevent_pool_.allocate();
        netevent_t &netevent = netevent_pool_[index];
        netevent.read_from(event.packet);
        enet_packet_destroy(event.packet);
        event_t emitted = {
          EVENT_SENDER_NET,
          { .sender = this },
          NET_EVENT,
          timeslice
        };
        emitted.net = &netevent;
        event_queue_.emit_event(emitted);
        s_log_note("Emitted net event");
      }
    }
  }

  if (error < 0) {
    s_log_error("Error checking for ENet events: %d", error);
  }
}
#endif

/*==============================================================================
  read_events(timeslice)

    Reads events that have occurred prior to the given timeslice. Events that
    occur afterward must be ignored and left in the event queue.

    Any events valid to the timeslice must be propagated through all systems.
    Systems may receive events in an out-of-order manner due to netevents being
    emitted after polling for OS events.
==============================================================================*/
void client_t::read_events(double timeslice)
{
  event_queue_.set_frame_time(sim_time_);

  // And run through events
  event_t event;
  auto sys_end = logic_systems_.cend();
  auto sys_begin = logic_systems_.cbegin();
  while (read_socket_.recv(&event, sizeof(event), ZMQ_DONTWAIT) == sizeof(event)) {
    switch (event.kind) {
      case WINDOW_FOCUS_EVENT:
        wnd_focused->seti(event.focused);
        // fall-through

      default: {
        auto sys_iter = sys_begin;
        event.time -= base_time_;
        while (sys_iter != sys_end) {
          system_t *sys = sys_iter->second;
          if (sys->active() && !sys->event(event)) {
            break;
          }
          ++sys_iter;
        }
        break;
      }
    }
  }
}

/*==============================================================================
  do_frame(step, timeslice)

    Perform a single frame for as much time has passed as step. Barring changes
    to gameplay, step should be considered a constant some fraction of a second
    measured in seconds.
==============================================================================*/
void client_t::do_frame(double step, double timeslice)
{
  for (const auto &spair : logic_systems_) {
    if (spair.second->active()) {
      spair.second->frame(step, timeslice);
    }
  }
}

/*==============================================================================
  run_frameloop

    Kick off the frameloop. This just calls frameloop() and should not allocate
    any resources itself -- if resources must be allocated, it should be done
    so in or beneath frameloop() and freed before frameloop() ends. This is to
    prevent async hiccups when terminate() is called on the main thread.
==============================================================================*/
void client_t::run_frameloop()
{
  frameloop();
  terminate();
}

/*==============================================================================
  frameloop

    Body of the frameloop. Handles incoming events, signals system updates, and
    frame rendering.
==============================================================================*/
void client_t::frameloop()
{
  // FIXME: Almost all of this crap should be moved to game-specific code.
  {
    console_pane_t &console = default_console();
    s_set_log_callback(cl_log_callback, &console);

    cvars_.clear();
    cvars_.register_ccmd(&cmd_quit_);

    cl_willQuit = cvars_.get_cvar( "cl_willQuit", 0, CVAR_READ_ONLY | CVAR_DELAYED | CVAR_INVISIBLE );
    wnd_focused = cvars_.get_cvar( "wnd_focused", 1, CVAR_READ_ONLY | CVAR_DELAYED | CVAR_INVISIBLE );
    wnd_mouseMode = cvars_.get_cvar("wnd_mouseMode", true, CVAR_DELAYED | CVAR_INVISIBLE);
    r_drawFrame = cvars_.get_cvar( "r_drawFrame", 1, CVAR_READ_ONLY | CVAR_DELAYED );
    r_clearFrame = cvars_.get_cvar("r_clearFrame", 1, CVAR_READ_ONLY | CVAR_DELAYED );

    console.set_cvar_set(&cvars_);

    glfwShowWindow(window_);
    glfwMakeContextCurrent(window_);
    glfwSwapInterval(0);    // Don't needlessly limit rendering speed

    // FIXME: move these somewhere else, probably?
    glClearColor(0, 0, 0, 1);
    glEnable(GL_BLEND);

    add_system(&console, 16777216, -16777216);

    // Set this to true before getting the base time since it might loop a couple
    // times setting it.
    running_ = true;
    sim_time_ = 0;
    // Don't call glfwSetTime because that might throw other clients out of sync
    // (even though really the chance of there being other clients is zero)
    base_time_ = glfwGetTime();
    unsigned frame, last_frame;
    frame = 1; last_frame = 0;

    while (running_.load()) {
  #if HIDE_CURSOR_ON_CONSOLE_CLOSE
      int mousemode = -1;
  #endif

      // Do any frames that would have passed since the last rendering point
      const double cur_time = glfwGetTime() - base_time_;
      while (sim_time_ < cur_time) {
        sim_time_ += FRAME_SEQ_TIME;
        ++frame;
        read_events(sim_time_);
        do_frame(FRAME_SEQ_TIME, sim_time_);

  #if HIDE_CURSOR_ON_CONSOLE_CLOSE
        if (wnd_mouseMode->has_flags(CVAR_MODIFIED)) {
          wnd_mouseMode->update();
          mousemode = wnd_mouseMode->geti();
        }
  #endif

        cvars_.update_cvars();
      }

  #if HIDE_CURSOR_ON_CONSOLE_CLOSE
      switch (mousemode) {
      case 0:
        glfwSetInputMode(window_, GLFW_CURSOR_MODE, GLFW_CURSOR_HIDDEN);
        glfwSetInputMode(window_, GLFW_STICKY_KEYS, 1);
        break;
      case 1:
        glfwSetInputMode(window_, GLFW_CURSOR_MODE, GLFW_CURSOR_NORMAL);
        glfwSetInputMode(window_, GLFW_STICKY_KEYS, 0);
        break;
      default: break;
      }
  #endif

      if (frame != last_frame && r_drawFrame->geti()) {
        last_frame = frame;

        if (r_clearFrame->geti()) {
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
          assert_gl("Clearing buffers");
        }

        for (auto &spair : draw_systems_) {
          if (spair.second->active()) {
            spair.second->draw(sim_time_);
          }
        }

        glfwSwapBuffers(window_);
      }

      if (cl_willQuit->geti()) {
        running_ = false;
      } else if (!wnd_focused->geti()) {
        std::this_thread::sleep_for(cl_frameloop_sleep_duration());
      }
    } // while (running)
  } // resource dropping scope

  s_set_log_callback(nullptr, nullptr);

  res_->release_all();

  glfwMakeContextCurrent(NULL);
} // frameloop

} // namespace snow
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <algorithm>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/Language.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-base/util/SimpleRateLimit.h"
#include "fnord-base/InternMap.h"
#include "fnord-json/json.h"
#include "fnord-mdb/MDB.h"
#include "fnord-mdb/MDBUtil.h"
#include "fnord-sstable/sstablereader.h"
#include "fnord-sstable/sstablewriter.h"
#include "fnord-sstable/SSTableColumnSchema.h"
#include "fnord-sstable/SSTableColumnReader.h"
#include "fnord-sstable/SSTableColumnWriter.h"
#include <fnord-fts/fts.h>
#include <fnord-fts/fts_common.h>
#include "fnord-eventdb/TableReader.h"
#include "common.h"
#include "schemas.h"
#include "CustomerNamespace.h"
#include "FeatureSchema.h"
#include "JoinedQuery.h"
#include "CTRCounter.h"
#include "IndexReader.h"
#include "analytics/ReportBuilder.h"
#include "analytics/AnalyticsTableScanSource.h"
#include "analytics/JoinedQueryTableSource.h"
#include "analytics/CTRByPositionMapper.h"
#include "analytics/CTRByPageMapper.h"
#include "analytics/CTRBySearchQueryMapper.h"
#include "analytics/CTRByQueryAttributeMapper.h"
#include "analytics/CTRBySearchTermCrossCategoryMapper.h"
#include "analytics/CTRStatsMapper.h"
#include "analytics/CTRCounterMergeReducer.h"
#include "analytics/CTRCounterTableSink.h"
#include "analytics/CTRCounterTableSource.h"
#include "analytics/RelatedTermsMapper.h"
#include "analytics/TopCategoriesByTermMapper.h"
#include "analytics/TermInfoMergeReducer.h"

using namespace fnord;
using namespace cm;

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  fnord::cli::FlagParser flags;

  //flags.defineFlag(
  //    "conf",
  //    cli::FlagParser::T_STRING,
  //    false,
  //    NULL,
  //    "./conf",
  //    "conf directory",
  //    "<path>");

  flags.defineFlag(
      "index",
      cli::FlagParser::T_STRING,
      false,
      NULL,
      NULL,
      "index directory",
      "<path>");

  flags.defineFlag(
      "replica",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "replica id",
      "<id>");

  flags.defineFlag(
      "datadir",
      cli::FlagParser::T_STRING,
      false,
      NULL,
      NULL,
      "artifact directory",
      "<path>");

  flags.defineFlag(
      "tempdir",
      cli::FlagParser::T_STRING,
      false,
      NULL,
      NULL,
      "artifact directory",
      "<path>");

  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  auto index_path = flags.getString("index");
  //auto conf_path = flags.getString("conf");
  auto tempdir = flags.getString("tempdir");
  auto datadir = flags.getString("datadir");

  /* open index */
  auto index_reader = cm::IndexReader::openIndex(index_path);
  //auto analyzer = RefPtr<fts::Analyzer>(new fts::Analyzer(conf_path));

  /* set up reportbuilder */
  cm::ReportBuilder report_builder;

  auto buildid = 0;

  auto table = eventdb::TableReader::open(
      "dawanda_joined_sessions",
      flags.getString("replica"),
      flags.getString("datadir"),
      joinedSessionsSchema());

  auto snap = table->getSnapshot();

  Set<String> searchterm_x_e1_tables;
  Set<String> related_terms_tables;

  for (const auto& c : snap->head->chunks) {
    auto input_table = StringUtil::format(
        "$0/$1.$2.$3.cst",
        datadir,
        "dawanda_joined_sessions",
        c.replica_id,
        c.chunk_id);

    /* map related terms */
    auto related_terms_table = StringUtil::format(
        "$0/dawanda_related_terms.$1.$2.sst",
        tempdir,
        c.replica_id,
        c.chunk_id);

    related_terms_tables.emplace(related_terms_table);
    report_builder.addReport(
        new RelatedTermsMapper(
            new AnalyticsTableScanSource(input_table),
            new CTRCounterTableSink(0, 0, related_terms_table)));

    /* map serchterm x e1 */
    auto searchterm_x_e1_table = StringUtil::format(
        "$0/dawanda_ctr_by_searchterm_cross_e1.$1.$2.sst",
        tempdir,
        c.replica_id,
        c.chunk_id);

    searchterm_x_e1_tables.emplace(searchterm_x_e1_table);
    report_builder.addReport(
        new CTRBySearchTermCrossCategoryMapper(
            new AnalyticsTableScanSource(input_table),
            new CTRCounterTableSink(0, 0, searchterm_x_e1_table),
            "category1",
            index_reader));
  }

  report_builder.addReport(
      new TermInfoMergeReducer(
          new TermInfoTableSource(related_terms_tables),
          new TermInfoTableSink(
              StringUtil::format(
                  "$0/dawanda_related_terms.$1.sstable",
                  tempdir,
                  buildid))));

  report_builder.addReport(
      new TermInfoMergeReducer(
          new TermInfoTableSource(searchterm_x_e1_tables),
          new TermInfoTableSink(
              StringUtil::format(
                  "$0/dawanda_top_cats_by_searchterm_e1.$1.sstable",
                  tempdir,
                  buildid))));

  report_builder.addReport(
      new TermInfoMergeReducer(
          new TermInfoTableSource(Set<String> {
            StringUtil::format(
                  "$0/dawanda_related_terms.$1.sstable",
                  tempdir,
                  buildid),
            StringUtil::format(
                  "$0/dawanda_top_cats_by_searchterm_e1.$1.sstable",
                  tempdir,
                  buildid)
          }),
          new TermInfoTableSink(
              StringUtil::format(
                  "$0/dawanda_termstats.$1.sstable",
                  tempdir,
                  buildid))));

  report_builder.buildAll();

  fnord::logInfo(
      "cm.reportbuild",
      "Build completed: dawanda_termstats.$0.sstable",
      buildid);

  return 0;
}

<commit_msg>RelatedTermsMapper...<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <algorithm>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/Language.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-base/util/SimpleRateLimit.h"
#include "fnord-base/InternMap.h"
#include "fnord-json/json.h"
#include "fnord-mdb/MDB.h"
#include "fnord-mdb/MDBUtil.h"
#include "fnord-sstable/sstablereader.h"
#include "fnord-sstable/sstablewriter.h"
#include "fnord-sstable/SSTableColumnSchema.h"
#include "fnord-sstable/SSTableColumnReader.h"
#include "fnord-sstable/SSTableColumnWriter.h"
#include <fnord-fts/fts.h>
#include <fnord-fts/fts_common.h>
#include "fnord-eventdb/TableReader.h"
#include "common.h"
#include "schemas.h"
#include "CustomerNamespace.h"
#include "FeatureSchema.h"
#include "JoinedQuery.h"
#include "CTRCounter.h"
#include "IndexReader.h"
#include "analytics/ReportBuilder.h"
#include "analytics/AnalyticsTableScanSource.h"
#include "analytics/JoinedQueryTableSource.h"
#include "analytics/CTRByPositionMapper.h"
#include "analytics/CTRByPageMapper.h"
#include "analytics/CTRBySearchQueryMapper.h"
#include "analytics/CTRByQueryAttributeMapper.h"
#include "analytics/CTRBySearchTermCrossCategoryMapper.h"
#include "analytics/CTRStatsMapper.h"
#include "analytics/CTRCounterMergeReducer.h"
#include "analytics/CTRCounterTableSink.h"
#include "analytics/CTRCounterTableSource.h"
#include "analytics/RelatedTermsMapper.h"
#include "analytics/TopCategoriesByTermMapper.h"
#include "analytics/TermInfoMergeReducer.h"

using namespace fnord;
using namespace cm;

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  fnord::cli::FlagParser flags;

  //flags.defineFlag(
  //    "conf",
  //    cli::FlagParser::T_STRING,
  //    false,
  //    NULL,
  //    "./conf",
  //    "conf directory",
  //    "<path>");

  flags.defineFlag(
      "index",
      cli::FlagParser::T_STRING,
      false,
      NULL,
      NULL,
      "index directory",
      "<path>");

  flags.defineFlag(
      "replica",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "replica id",
      "<id>");

  flags.defineFlag(
      "datadir",
      cli::FlagParser::T_STRING,
      false,
      NULL,
      NULL,
      "artifact directory",
      "<path>");

  flags.defineFlag(
      "tempdir",
      cli::FlagParser::T_STRING,
      false,
      NULL,
      NULL,
      "artifact directory",
      "<path>");

  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  auto index_path = flags.getString("index");
  //auto conf_path = flags.getString("conf");
  auto tempdir = flags.getString("tempdir");
  auto datadir = flags.getString("datadir");

  /* open index */
  auto index_reader = cm::IndexReader::openIndex(index_path);
  //auto analyzer = RefPtr<fts::Analyzer>(new fts::Analyzer(conf_path));

  /* set up reportbuilder */
  cm::ReportBuilder report_builder;

  auto buildid = 0;

  auto table = eventdb::TableReader::open(
      "dawanda_joined_sessions",
      flags.getString("replica"),
      flags.getString("datadir"),
      joinedSessionsSchema());

  auto snap = table->getSnapshot();

  Set<String> searchterm_x_e1_tables;
  Set<String> related_terms_tables;

  for (const auto& c : snap->head->chunks) {
    auto input_table = StringUtil::format(
        "$0/$1.$2.$3.cst",
        datadir,
        "dawanda_joined_sessions",
        c.replica_id,
        c.chunk_id);

    /* map related terms */
    auto related_terms_table = StringUtil::format(
        "$0/dawanda_related_terms.$1.$2.sst",
        tempdir,
        c.replica_id,
        c.chunk_id);

    related_terms_tables.emplace(related_terms_table);
    report_builder.addReport(
        new RelatedTermsMapper(
            new AnalyticsTableScanSource(input_table),
            new TermInfoTableSink(related_terms_table)));

    /* map serchterm x e1 */
    auto searchterm_x_e1_table = StringUtil::format(
        "$0/dawanda_ctr_by_searchterm_cross_e1.$1.$2.sst",
        tempdir,
        c.replica_id,
        c.chunk_id);

    searchterm_x_e1_tables.emplace(searchterm_x_e1_table);
    report_builder.addReport(
        new CTRBySearchTermCrossCategoryMapper(
            new AnalyticsTableScanSource(input_table),
            new CTRCounterTableSink(0, 0, searchterm_x_e1_table),
            "category1",
            index_reader));
  }

  report_builder.addReport(
      new TermInfoMergeReducer(
          new TermInfoTableSource(related_terms_tables),
          new TermInfoTableSink(
              StringUtil::format(
                  "$0/dawanda_related_terms.$1.sstable",
                  tempdir,
                  buildid))));

  report_builder.addReport(
      new TopCategoriesByTermMapper(
          new CTRCounterTableSource(searchterm_x_e1_tables),
          new TermInfoTableSink(
              StringUtil::format(
                  "$0/dawanda_top_cats_by_searchterm_e1.$1.sstable",
                  tempdir,
                  buildid)),
          "e1-"));

  report_builder.addReport(
      new TermInfoMergeReducer(
          new TermInfoTableSource(Set<String> {
            StringUtil::format(
                  "$0/dawanda_related_terms.$1.sstable",
                  tempdir,
                  buildid),
            StringUtil::format(
                  "$0/dawanda_top_cats_by_searchterm_e1.$1.sstable",
                  tempdir,
                  buildid)
          }),
          new TermInfoTableSink(
              StringUtil::format(
                  "$0/dawanda_termstats.$1.sstable",
                  tempdir,
                  buildid))));

  report_builder.buildAll();

  fnord::logInfo(
      "cm.reportbuild",
      "Build completed: dawanda_termstats.$0.sstable",
      buildid);

  return 0;
}

<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <algorithm>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-base/util/SimpleRateLimit.h"
#include "fnord-mdb/MDB.h"
#include "fnord-mdb/MDBUtil.h"
#include "fnord-sstable/sstablereader.h"
#include "fnord-sstable/sstablewriter.h"
#include "fnord-sstable/SSTableColumnSchema.h"
#include "fnord-sstable/SSTableColumnReader.h"
#include "fnord-sstable/SSTableColumnWriter.h"
#include "common.h"
#include "CustomerNamespace.h"
#include "FeatureSchema.h"
#include "JoinedQuery.h"
#include "CTRCounter.h"

using namespace fnord;

typedef Tuple<
    String,
    String,
    uint64_t,
    uint64_t,
    double,
    double,
    double,
    double> OutputRow;

typedef HashMap<String, cm::CTRCounter> CounterMap;

/* read all input sstables */
void importInputTables(const Vector<String> sstables, CounterMap* counters) {
  for (int tbl_idx = 0; tbl_idx < sstables.size(); ++tbl_idx) {
    const auto& sstable = sstables[tbl_idx];
    fnord::logInfo("cm.ctrstats", "Importing sstable: $0", sstable);

    /* read sstable heade r*/
    sstable::SSTableReader reader(File::openFile(sstable, File::O_READ));
    sstable::SSTableColumnSchema schema;
    schema.loadIndex(&reader);

    if (reader.bodySize() == 0) {
      fnord::logCritical("cm.ctrstats", "unfinished sstable: $0", sstable);
      exit(1);
    }

    /* get sstable cursor */
    auto cursor = reader.getCursor();
    auto body_size = reader.bodySize();
    int row_idx = 0;

    /* status line */
    util::SimpleRateLimitedFn status_line(kMicrosPerSecond, [&] () {
      fnord::logInfo(
          "cm.ctrstats",
          "[$1/$2] [$0%] Reading sstable... rows=$3",
          (size_t) ((cursor->position() / (double) body_size) * 100),
          tbl_idx + 1, sstables.size(), row_idx);
    });

    /* read sstable rows */
    for (; cursor->valid(); ++row_idx) {
      status_line.runMaybe();

      auto key = cursor->getKeyString();
      auto val = cursor->getDataBuffer();
      sstable::SSTableColumnReader cols(&schema, val);

      auto& counter = (*counters)[key];
      auto num_views = cols.getUInt64Column(schema.columnID("num_views"));
      auto num_clicks= cols.getUInt64Column(schema.columnID("num_clicks"));
      counter.num_views += num_views;
      counter.num_clicks += num_clicks;

      if (!cursor->next()) {
        break;
      }
    }

    status_line.runForce();
  }
}

/* write output table */
void writeOutputTable(const String& filename, const Vector<OutputRow>& rows) {
  /* prepare output sstable schema */
  sstable::SSTableColumnSchema sstable_schema;
  sstable_schema.addColumn("num_views", 1, sstable::SSTableColumnType::UINT64);
  sstable_schema.addColumn("num_clicks", 2, sstable::SSTableColumnType::UINT64);
  sstable_schema.addColumn("p_view_base", 3, sstable::SSTableColumnType::FLOAT);
  sstable_schema.addColumn("p_view_dim1", 4, sstable::SSTableColumnType::FLOAT);
  sstable_schema.addColumn("p_click", 5, sstable::SSTableColumnType::FLOAT);
  sstable_schema.addColumn(
      "p_click_normalized",
      6,
      sstable::SSTableColumnType::FLOAT);

  /* open output sstable */
  fnord::logInfo("cm.ctrstats", "Writing results to: $0", filename);
  auto sstable_writer = sstable::SSTableWriter::create(
      filename,
      sstable::IndexProvider{},
      nullptr,
      0);

  /* write output sstable */
  for (const auto& r : rows) {
    sstable::SSTableColumnWriter cols(&sstable_schema);

    cols.addStringColumn(1, std::get<1>(r));
    cols.addUInt64Column(2, std::get<2>(r));
    cols.addUInt64Column(3, std::get<3>(r));
    cols.addFloatColumn(4, std::get<4>(r));
    cols.addFloatColumn(5, std::get<5>(r));
    cols.addFloatColumn(6, std::get<6>(r));
    cols.addFloatColumn(7, std::get<7>(r));

    sstable_writer->appendRow(std::get<0>(r), cols);
  }

  sstable_schema.writeIndex(sstable_writer.get());
  sstable_writer->finalize();
}

/* aggregate ctr counters (flat) */
void aggregateCounters(CounterMap* counters, Vector<OutputRow>* rows) {
  const auto& global_counter_iter = counters->find("__GLOBAL");
  if (global_counter_iter == counters->end()) {
    fnord::logCritical("cm.ctrstatsexport", "missing global counter");
    exit(1);
  }
  const auto& global_counter = global_counter_iter->second;

  for (const auto& row : *counters) {
    const auto& ctr = row.second;

    auto sep = row.first.find("~");
    if (sep == std::string::npos) {
      continue;
    }
    auto dim1 = row.first.substr(0, sep);
    auto dim2 = row.first.substr(sep + 1);

    if (dim1.length() == 0 || dim2.length() == 0) {
      continue;
    }

    const auto& group_counter_iter = counters->find(dim1);
    if (group_counter_iter == counters->end()) {
      fnord::logWarning("cm.ctrstatsexport", "missing row: $0", dim1);
      continue;
    }
    const auto& group_counter = group_counter_iter->second;

    double p_base = ctr.num_views / (double) global_counter.num_views;
    double p_base_dim2 = ctr.num_views / (double) group_counter.num_views;
    double p_click = ctr.num_clicks / (double) ctr.num_views;
    double p_click_n = ctr.num_clicks / (double) group_counter.num_clicks;

    rows->emplace_back(
        dim1,
        dim2,
        row.second.num_views,
        row.second.num_clicks,
        p_base,
        p_base_dim2,
        p_click,
        p_click_n);
  }
}

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  fnord::cli::FlagParser flags;

  flags.defineFlag(
      "output_file",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "output file path",
      "<path>");

  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  /* read input tables */
  HashMap<String, cm::CTRCounter> counters;
  importInputTables(flags.getArgv(), &counters);

  /* aggregate counters */
  Vector<OutputRow> rows;
  aggregateCounters(&counters, &rows);

  /* write output table */
  writeOutputTable(flags.getString("output_file"), rows);

  return 0;
}

<commit_msg>correct output sstable schema<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <algorithm>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-base/util/SimpleRateLimit.h"
#include "fnord-mdb/MDB.h"
#include "fnord-mdb/MDBUtil.h"
#include "fnord-sstable/sstablereader.h"
#include "fnord-sstable/sstablewriter.h"
#include "fnord-sstable/SSTableColumnSchema.h"
#include "fnord-sstable/SSTableColumnReader.h"
#include "fnord-sstable/SSTableColumnWriter.h"
#include "common.h"
#include "CustomerNamespace.h"
#include "FeatureSchema.h"
#include "JoinedQuery.h"
#include "CTRCounter.h"

using namespace fnord;

typedef Tuple<
    String,
    String,
    uint64_t,
    uint64_t,
    double,
    double,
    double,
    double> OutputRow;

typedef HashMap<String, cm::CTRCounter> CounterMap;

/* read all input sstables */
void importInputTables(const Vector<String> sstables, CounterMap* counters) {
  for (int tbl_idx = 0; tbl_idx < sstables.size(); ++tbl_idx) {
    const auto& sstable = sstables[tbl_idx];
    fnord::logInfo("cm.ctrstats", "Importing sstable: $0", sstable);

    /* read sstable heade r*/
    sstable::SSTableReader reader(File::openFile(sstable, File::O_READ));
    sstable::SSTableColumnSchema schema;
    schema.loadIndex(&reader);

    if (reader.bodySize() == 0) {
      fnord::logCritical("cm.ctrstats", "unfinished sstable: $0", sstable);
      exit(1);
    }

    /* get sstable cursor */
    auto cursor = reader.getCursor();
    auto body_size = reader.bodySize();
    int row_idx = 0;

    /* status line */
    util::SimpleRateLimitedFn status_line(kMicrosPerSecond, [&] () {
      fnord::logInfo(
          "cm.ctrstats",
          "[$1/$2] [$0%] Reading sstable... rows=$3",
          (size_t) ((cursor->position() / (double) body_size) * 100),
          tbl_idx + 1, sstables.size(), row_idx);
    });

    /* read sstable rows */
    for (; cursor->valid(); ++row_idx) {
      status_line.runMaybe();

      auto key = cursor->getKeyString();
      auto val = cursor->getDataBuffer();
      sstable::SSTableColumnReader cols(&schema, val);

      auto& counter = (*counters)[key];
      auto num_views = cols.getUInt64Column(schema.columnID("num_views"));
      auto num_clicks= cols.getUInt64Column(schema.columnID("num_clicks"));
      counter.num_views += num_views;
      counter.num_clicks += num_clicks;

      if (!cursor->next()) {
        break;
      }
    }

    status_line.runForce();
  }
}

/* write output table */
void writeOutputTable(const String& filename, const Vector<OutputRow>& rows) {
  /* prepare output sstable schema */
  sstable::SSTableColumnSchema sstable_schema;
  sstable_schema.addColumn("dim1", 1, sstable::SSTableColumnType::STRING);
  sstable_schema.addColumn("num_views", 2, sstable::SSTableColumnType::UINT64);
  sstable_schema.addColumn("num_clicks", 3, sstable::SSTableColumnType::UINT64);
  sstable_schema.addColumn("p_view_base", 4, sstable::SSTableColumnType::FLOAT);
  sstable_schema.addColumn("p_view_dim1", 5, sstable::SSTableColumnType::FLOAT);
  sstable_schema.addColumn("p_click", 6, sstable::SSTableColumnType::FLOAT);
  sstable_schema.addColumn(
      "p_click_normalized",
      7,
      sstable::SSTableColumnType::FLOAT);

  /* open output sstable */
  fnord::logInfo("cm.ctrstats", "Writing results to: $0", filename);
  auto sstable_writer = sstable::SSTableWriter::create(
      filename,
      sstable::IndexProvider{},
      nullptr,
      0);

  /* write output sstable */
  for (const auto& r : rows) {
    sstable::SSTableColumnWriter cols(&sstable_schema);

    cols.addStringColumn(1, std::get<1>(r));
    cols.addUInt64Column(2, std::get<2>(r));
    cols.addUInt64Column(3, std::get<3>(r));
    cols.addFloatColumn(4, std::get<4>(r));
    cols.addFloatColumn(5, std::get<5>(r));
    cols.addFloatColumn(6, std::get<6>(r));
    cols.addFloatColumn(7, std::get<7>(r));

    sstable_writer->appendRow(std::get<0>(r), cols);
  }

  sstable_schema.writeIndex(sstable_writer.get());
  sstable_writer->finalize();
}

/* aggregate ctr counters (flat) */
void aggregateCounters(CounterMap* counters, Vector<OutputRow>* rows) {
  const auto& global_counter_iter = counters->find("__GLOBAL");
  if (global_counter_iter == counters->end()) {
    fnord::logCritical("cm.ctrstatsexport", "missing global counter");
    exit(1);
  }
  const auto& global_counter = global_counter_iter->second;

  for (const auto& row : *counters) {
    const auto& ctr = row.second;

    auto sep = row.first.find("~");
    if (sep == std::string::npos) {
      continue;
    }
    auto dim1 = row.first.substr(0, sep);
    auto dim2 = row.first.substr(sep + 1);

    if (dim1.length() == 0 || dim2.length() == 0) {
      continue;
    }

    const auto& group_counter_iter = counters->find(dim1);
    if (group_counter_iter == counters->end()) {
      fnord::logWarning("cm.ctrstatsexport", "missing row: $0", dim1);
      continue;
    }
    const auto& group_counter = group_counter_iter->second;

    double p_base = ctr.num_views / (double) global_counter.num_views;
    double p_base_dim2 = ctr.num_views / (double) group_counter.num_views;
    double p_click = ctr.num_clicks / (double) ctr.num_views;
    double p_click_n = ctr.num_clicks / (double) group_counter.num_clicks;

    rows->emplace_back(
        dim1,
        dim2,
        row.second.num_views,
        row.second.num_clicks,
        p_base,
        p_base_dim2,
        p_click,
        p_click_n);
  }
}

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  fnord::cli::FlagParser flags;

  flags.defineFlag(
      "output_file",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "output file path",
      "<path>");

  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  /* read input tables */
  HashMap<String, cm::CTRCounter> counters;
  importInputTables(flags.getArgv(), &counters);

  /* aggregate counters */
  Vector<OutputRow> rows;
  aggregateCounters(&counters, &rows);

  /* write output table */
  writeOutputTable(flags.getString("output_file"), rows);

  return 0;
}

<|endoftext|>


<commit_before>#include "cinder/app/AppBasic.h"
#include "cinder/Path2d.h"
#include "cinder/cairo/Cairo.h"
#include "cinder/ip/Fill.h"

#include <vector>

using namespace ci;
using namespace ci::app;
using namespace std;

class Path2dApp : public AppBasic {
 public:
	Path2dApp() : mTrackedPoint( -1 ) {}

	void mouseDown( MouseEvent event );
	void mouseUp( MouseEvent event );
	void mouseDrag( MouseEvent event );
	void keyDown( KeyEvent event );
	void draw();

	Path2d	mPath;
	int		mTrackedPoint;
};

void Path2dApp::mouseDown( MouseEvent event )
{
	if( event.isLeftDown() ) { // line
		if( mPath.empty() ) {
			mPath.moveTo( event.getPos() );
			mTrackedPoint = 0;
		}
		else
			mPath.lineTo( event.getPos() );
	}
}

void Path2dApp::mouseDrag( MouseEvent event )
{
	if( mTrackedPoint >= 0 ) {
		mPath.setPoint( mTrackedPoint, event.getPos() );
	}
	else { // first bit of dragging, so switch our line to a cubic or a quad if Shift is down
		// we want to preserve the end of our current line, because it will still be the end of our curve
		Vec2f endPt = mPath.getPoint( mPath.getNumPoints() - 1 );
		// and now we'll delete that line and replace it with a curve
		mPath.removeSegment( mPath.getNumSegments() - 1 );

		Path2d::SegmentType prevType = ( mPath.getNumSegments() == 0 ) ? Path2d::MOVETO : mPath.getSegmentType( mPath.getNumSegments() - 1 );

		if( event.isShiftDown() || prevType == Path2d::MOVETO ) { // add a quadratic curve segment
			mPath.quadTo( event.getPos(), endPt );
		}
		else { // add a cubic curve segment
			Vec2f tan1;
			if( prevType == Path2d::CUBICTO ) { 		// if the segment before was cubic, let's replicate and reverse its tangent
				Vec2f prevDelta = mPath.getPoint( mPath.getNumPoints() - 2 ) - mPath.getPoint( mPath.getNumPoints() - 1 );
				tan1 = mPath.getPoint( mPath.getNumPoints() - 1 ) - prevDelta;
			}
			else if( prevType == Path2d::QUADTO ) {
				// we can figure out what the equivalent cubic tangent would be using a little math
				Vec2f quadTangent = mPath.getPoint( mPath.getNumPoints() - 2 );
				Vec2f quadEnd = mPath.getPoint( mPath.getNumPoints() - 1 );
				Vec2f prevDelta = ( quadTangent + ( quadEnd - quadTangent ) * 0.3333333f ) - quadEnd;
				tan1 = quadEnd - prevDelta;
			}
			else
				tan1 = mPath.getPoint( mPath.getNumPoints() - 1 );

			mPath.curveTo( tan1, event.getPos(), endPt );
		}

		// our second-to-last point is the tangent next to the end, and we'll track that
		mTrackedPoint = mPath.getNumPoints() - 2;
	}
}

void Path2dApp::mouseUp( MouseEvent event )
{
	mTrackedPoint = -1;
}

void Path2dApp::keyDown( KeyEvent event ) {
	if( event.getCode() == KeyEvent::KEY_f ) {
		setFullScreen( ! isFullScreen() );
	}
	else if( event.getCode() == KeyEvent::KEY_ESCAPE ) {
		setFullScreen( false );
	}
	else if( event.getChar() == 'x' ) {
		mPath.clear();
	}
}

void Path2dApp::draw()
{
	// clear to the background color
	cairo::Context ctx( cairo::createWindowSurface() );
	ctx.setSourceRgb( 0.0f, 0.1f, 0.2f );
	ctx.paint();

	// draw the control points
	ctx.setSourceRgb( 1.0f, 1.0f, 0.0f );
	for( size_t p = 0; p < mPath.getNumPoints(); ++p ) {
		ctx.newSubPath();
		ctx.arc( mPath.getPoint( p ), 2.5, 0, 2 * 3.14159 );
	}
	ctx.stroke();

	// draw the curve itself
	ctx.setSourceRgb( 1.0f, 0.5f, 0.25f );
	ctx.appendPath( mPath );
	ctx.stroke();
}

CINDER_APP_BASIC( Path2dApp, Renderer2d )
<commit_msg>bezierPathApp renders with OpenGL<commit_after>#include "cinder/app/AppBasic.h"
#include "cinder/Path2d.h"
#include "cinder/gl/gl.h"

#include <vector>

using namespace ci;
using namespace ci::app;
using namespace std;

class Path2dApp : public AppBasic {
 public:
	Path2dApp() : mTrackedPoint( -1 ) {}

	void mouseDown( MouseEvent event );
	void mouseUp( MouseEvent event );
	void mouseDrag( MouseEvent event );
	void keyDown( KeyEvent event );
	void draw();

	Path2d	mPath;
	int		mTrackedPoint;
};

void Path2dApp::mouseDown( MouseEvent event )
{
	if( event.isLeftDown() ) { // line
		if( mPath.empty() ) {
			mPath.moveTo( event.getPos() );
			mTrackedPoint = 0;
		}
		else
			mPath.lineTo( event.getPos() );
	}
}

void Path2dApp::mouseDrag( MouseEvent event )
{
	if( mTrackedPoint >= 0 ) {
		mPath.setPoint( mTrackedPoint, event.getPos() );
	}
	else { // first bit of dragging, so switch our line to a cubic or a quad if Shift is down
		// we want to preserve the end of our current line, because it will still be the end of our curve
		Vec2f endPt = mPath.getPoint( mPath.getNumPoints() - 1 );
		// and now we'll delete that line and replace it with a curve
		mPath.removeSegment( mPath.getNumSegments() - 1 );

		Path2d::SegmentType prevType = ( mPath.getNumSegments() == 0 ) ? Path2d::MOVETO : mPath.getSegmentType( mPath.getNumSegments() - 1 );

		if( event.isShiftDown() || prevType == Path2d::MOVETO ) { // add a quadratic curve segment
			mPath.quadTo( event.getPos(), endPt );
		}
		else { // add a cubic curve segment
			Vec2f tan1;
			if( prevType == Path2d::CUBICTO ) { 		// if the segment before was cubic, let's replicate and reverse its tangent
				Vec2f prevDelta = mPath.getPoint( mPath.getNumPoints() - 2 ) - mPath.getPoint( mPath.getNumPoints() - 1 );
				tan1 = mPath.getPoint( mPath.getNumPoints() - 1 ) - prevDelta;
			}
			else if( prevType == Path2d::QUADTO ) {
				// we can figure out what the equivalent cubic tangent would be using a little math
				Vec2f quadTangent = mPath.getPoint( mPath.getNumPoints() - 2 );
				Vec2f quadEnd = mPath.getPoint( mPath.getNumPoints() - 1 );
				Vec2f prevDelta = ( quadTangent + ( quadEnd - quadTangent ) / 3 ) - quadEnd;
				tan1 = quadEnd - prevDelta;
			}
			else
				tan1 = mPath.getPoint( mPath.getNumPoints() - 1 );

			mPath.curveTo( tan1, event.getPos(), endPt );
		}

		// our second-to-last point is the tangent next to the end, and we'll track that
		mTrackedPoint = mPath.getNumPoints() - 2;
	}
}

void Path2dApp::mouseUp( MouseEvent event )
{
	mTrackedPoint = -1;
}

void Path2dApp::keyDown( KeyEvent event )
{
	if( event.getChar() == 'x' )
		mPath.clear();
}

void Path2dApp::draw()
{
	gl::clear( Color( 0.0f, 0.1f, 0.2f ) );

	// draw the control points
	gl::color( Color( 1, 1, 0 ) );
	for( size_t p = 0; p < mPath.getNumPoints(); ++p )
		gl::drawSolidCircle( mPath.getPoint( p ), 2.5f );

	// draw the curve itself
	gl::color( Color( 1.0f, 0.5f, 0.25f ) );
	gl::draw( mPath );
}

CINDER_APP_BASIC( Path2dApp, RendererGl )
<|endoftext|>


<commit_before>/*!
	@file
	@author		Albert Semenov
	@date		08/2010
*/
#include "Precompiled.h"
#include "PropertyFieldManager.h"
#include "PropertyFieldComboBox.h"
#include "PropertyFieldEditBox.h"
#include "PropertyFieldSkin.h"
#include "PropertyFieldType.h"
#include "PropertyFieldFont.h"
#include "PropertyFieldFileName.h"
#include "PropertyFieldNumeric.h"
#include "PropertyFieldAlpha.h"
#include "PropertyFieldPosition.h"
#include "PropertyFieldColour.h"

template <> tools::PropertyFieldManager* MyGUI::Singleton<tools::PropertyFieldManager>::msInstance = nullptr;
template <> const char* MyGUI::Singleton<tools::PropertyFieldManager>::mClassTypeName("PropertyFieldManager");

namespace tools
{

	template <typename Type>
	class GenericFactory
	{
	public:
		typedef MyGUI::delegates::CDelegate2<IPropertyField*&, MyGUI::Widget*> Delegate;
		static typename Delegate::IDelegate* getFactory()
		{
			return MyGUI::newDelegate(createFromFactory);
		}

	private:
		static void createFromFactory(IPropertyField*& _instance, MyGUI::Widget* _parent)
		{
			_instance = new Type(_parent);
		}
	};

	PropertyFieldManager::PropertyFieldManager()
	{
	}

	PropertyFieldManager::~PropertyFieldManager()
	{
	}

	void PropertyFieldManager::initialise()
	{
		mFactories["Name"] = GenericFactory<PropertyFieldEditBox>::getFactory();
		mFactories["Type"] = GenericFactory<PropertyFieldType>::getFactory();
		mFactories["Skin"] = GenericFactory<PropertyFieldSkin>::getFactory();
		mFactories["Font"] = GenericFactory<PropertyFieldFont>::getFactory();
		mFactories["Position"] = GenericFactory<PropertyFieldPosition>::getFactory();
		mFactories["Layer"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["String"] = GenericFactory<PropertyFieldEditBox>::getFactory();
		mFactories["Align"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["FileName"] = GenericFactory<PropertyFieldFileName>::getFactory();
		mFactories["1 int"] = GenericFactory<PropertyFieldNumeric>::getFactory();
		mFactories["2 int"] = GenericFactory<PropertyFieldNumeric>::getFactory();
		mFactories["4 int"] = GenericFactory<PropertyFieldNumeric>::getFactory();
		mFactories["1 float"] = GenericFactory<PropertyFieldNumeric>::getFactory();
		mFactories["2 float"] = GenericFactory<PropertyFieldNumeric>::getFactory();
		mFactories["Alpha"] = GenericFactory<PropertyFieldAlpha>::getFactory();
		mFactories["Colour"] = GenericFactory<PropertyFieldColour>::getFactory();
		mFactories["Bool"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["TextAlign"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["FlowDirection"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["CanvasAlign"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["MenuItemType"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["ItemResizingPolicy"] = GenericFactory<PropertyFieldComboBox>::getFactory();
	}

	void PropertyFieldManager::shutdown()
	{
	}

	IPropertyField* PropertyFieldManager::createPropertyField(MyGUI::Widget* _window, const std::string& _type)
	{
		IPropertyField* result = nullptr;

		MapFactoryItem::iterator item = mFactories.find(_type);
		MYGUI_ASSERT(item != mFactories.end(), "Factory PropertyField '" << _type << "' not found.");

		(*item).second(result, _window);
		result->initialise(_type);

		return result;
	}

} // namespace tools
<commit_msg>LE: fix #112<commit_after>/*!
	@file
	@author		Albert Semenov
	@date		08/2010
*/
#include "Precompiled.h"
#include "PropertyFieldManager.h"
#include "PropertyFieldComboBox.h"
#include "PropertyFieldEditBox.h"
#include "PropertyFieldSkin.h"
#include "PropertyFieldType.h"
#include "PropertyFieldFont.h"
#include "PropertyFieldFileName.h"
#include "PropertyFieldNumeric.h"
#include "PropertyFieldAlpha.h"
#include "PropertyFieldPosition.h"
#include "PropertyFieldColour.h"

template <> tools::PropertyFieldManager* MyGUI::Singleton<tools::PropertyFieldManager>::msInstance = nullptr;
template <> const char* MyGUI::Singleton<tools::PropertyFieldManager>::mClassTypeName("PropertyFieldManager");

namespace tools
{

	template <typename Type>
	class GenericFactory
	{
	public:
		typedef MyGUI::delegates::CDelegate2<IPropertyField*&, MyGUI::Widget*> Delegate;
		static typename Delegate::IDelegate* getFactory()
		{
			return MyGUI::newDelegate(createFromFactory);
		}

	private:
		static void createFromFactory(IPropertyField*& _instance, MyGUI::Widget* _parent)
		{
			_instance = new Type(_parent);
		}
	};

	PropertyFieldManager::PropertyFieldManager()
	{
	}

	PropertyFieldManager::~PropertyFieldManager()
	{
	}

	void PropertyFieldManager::initialise()
	{
		mFactories["Name"] = GenericFactory<PropertyFieldEditBox>::getFactory();
		mFactories["Type"] = GenericFactory<PropertyFieldType>::getFactory();
		mFactories["Skin"] = GenericFactory<PropertyFieldSkin>::getFactory();
		mFactories["Font"] = GenericFactory<PropertyFieldFont>::getFactory();
		mFactories["Position"] = GenericFactory<PropertyFieldPosition>::getFactory();
		mFactories["Layer"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["String"] = GenericFactory<PropertyFieldEditBox>::getFactory();
		mFactories["Align"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["FileName"] = GenericFactory<PropertyFieldFileName>::getFactory();
		mFactories["1 int"] = GenericFactory<PropertyFieldNumeric>::getFactory();
		mFactories["2 int"] = GenericFactory<PropertyFieldNumeric>::getFactory();
		mFactories["4 int"] = GenericFactory<PropertyFieldNumeric>::getFactory();
		mFactories["1 float"] = GenericFactory<PropertyFieldNumeric>::getFactory();
		mFactories["2 float"] = GenericFactory<PropertyFieldNumeric>::getFactory();
		mFactories["Alpha"] = GenericFactory<PropertyFieldAlpha>::getFactory();
		mFactories["Colour"] = GenericFactory<PropertyFieldColour>::getFactory();
		mFactories["Bool"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["TextAlign"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["FlowDirection"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["CanvasAlign"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["MenuItemType"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["ItemResizingPolicy"] = GenericFactory<PropertyFieldComboBox>::getFactory();
		mFactories["Function"] = GenericFactory<PropertyFieldComboBox>::getFactory();
	}

	void PropertyFieldManager::shutdown()
	{
	}

	IPropertyField* PropertyFieldManager::createPropertyField(MyGUI::Widget* _window, const std::string& _type)
	{
		IPropertyField* result = nullptr;

		MapFactoryItem::iterator item = mFactories.find(_type);
		MYGUI_ASSERT(item != mFactories.end(), "Factory PropertyField '" << _type << "' not found.");

		(*item).second(result, _window);
		result->initialise(_type);

		return result;
	}

} // namespace tools
<|endoftext|>


<commit_before>int main(int argc, char *argv[])
{
   // Reads from the file build/version_number the new version
   // number and generates the header base/inc/RVersion.h.
   // To be executed as CINT script by build/unix/makeversion.sh.
   //
   // Author: Fons Rademakers   11/10/99

   const char *in  = "build/version_number";
   const char *inr = "etc/svninfo.txt";

   FILE *fp = fopen(in, "r");
   if (!fp) {
      printf("%s: can not open input file %s\n", argv[0], in);
      exit(1);
   }
   char vers[32];
   fgets(vers, sizeof(vers), fp);
   if (vers[strlen(vers)-1] == '\n') vers[strlen(vers)-1] = 0;
   fclose(fp);

   fp = fopen(inr, "r");
   if (!fp) {
      printf("%s: can not open input file %s\n", argv[0], in);
      exit(1);
   }
   char branch[2048];
   fgets(branch, sizeof(branch), fp);
   if (branch[strlen(branch)-1] == '\n') branch[strlen(branch)-1] = 0;
   char revs[32];
   fgets(revs, sizeof(revs), fp);
   if (revs[strlen(revs)-1] == '\n') revs[strlen(revs)-1] = 0;
   fclose(fp);

   const char *out = "core/base/inc/RVersion.h";
   fp = fopen(out, "w");
   if (!fp) {
      printf("%s: can not open output file %s\n", argv[0], out);
      exit(1);
   }

   fprintf(fp, "#ifndef ROOT_RVersion\n");
   fprintf(fp, "#define ROOT_RVersion\n\n");
   fprintf(fp, "/* Version information automatically generated by installer. */\n\n");
   fprintf(fp, "/*\n");
   fprintf(fp, " * These macros can be used in the following way:\n");
   fprintf(fp, " *\n");
   fprintf(fp, " *    #if ROOT_VERSION_CODE >= ROOT_VERSION(2,23,4)\n");
   fprintf(fp, " *       #include <newheader.h>\n");
   fprintf(fp, " *    #else\n");
   fprintf(fp, " *       #include <oldheader.h>\n");
   fprintf(fp, " *    #endif\n");
   fprintf(fp, " *\n");
   fprintf(fp, "*/\n\n");

   int xx, yy, zz, rev;
   char patch = '\0';
   sscanf(vers, "%d.%d/%d%c", &xx, &yy, &zz, &patch);
   int vers_code = (xx << 16) + (yy << 8) + zz;
   int full_vers_code = (xx << 24) + (yy << 16) + (zz << 8) + patch;
   sscanf(revs, "%d", &rev);

   fprintf(fp, "#define ROOT_RELEASE \"%s\"\n", vers);
   fprintf(fp, "#define ROOT_RELEASE_DATE \"%s\"\n", __DATE__);
   fprintf(fp, "#define ROOT_RELEASE_TIME \"%s\"\n", __TIME__);
   fprintf(fp, "#define ROOT_SVN_REVISION %d\n", rev);
   fprintf(fp, "#define ROOT_SVN_BRANCH \"%s\"\n", branch);
   fprintf(fp, "#define ROOT_VERSION_CODE %d\n", vers_code);
   fprintf(fp, "#define ROOT_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))\n");
   fprintf(fp, "#define ROOT_FULL_VERSION_CODE %d\n", full_vers_code);
   fprintf(fp, "#define ROOT_FULL_VERSION(a,b,c,p) (((a) << 24) + ((b) << 16) + ((c) << 8) + (p))\n");
   fprintf(fp, "\n#endif\n");

   fclose(fp);

   exit(0);
}
<commit_msg>remove ROOT_FULL_VERSION and ROOT_FULL_VERSION_CODE as we will not use the letter extensions anymore for patch releases.<commit_after>int main(int argc, char *argv[])
{
   // Reads from the file build/version_number the new version
   // number and generates the header base/inc/RVersion.h.
   // To be executed as CINT script by build/unix/makeversion.sh.
   //
   // Author: Fons Rademakers   11/10/99

   const char *in  = "build/version_number";
   const char *inr = "etc/svninfo.txt";

   FILE *fp = fopen(in, "r");
   if (!fp) {
      printf("%s: can not open input file %s\n", argv[0], in);
      exit(1);
   }
   char vers[32];
   fgets(vers, sizeof(vers), fp);
   if (vers[strlen(vers)-1] == '\n') vers[strlen(vers)-1] = 0;
   fclose(fp);

   fp = fopen(inr, "r");
   if (!fp) {
      printf("%s: can not open input file %s\n", argv[0], in);
      exit(1);
   }
   char branch[2048];
   fgets(branch, sizeof(branch), fp);
   if (branch[strlen(branch)-1] == '\n') branch[strlen(branch)-1] = 0;
   char revs[32];
   fgets(revs, sizeof(revs), fp);
   if (revs[strlen(revs)-1] == '\n') revs[strlen(revs)-1] = 0;
   fclose(fp);

   const char *out = "core/base/inc/RVersion.h";
   fp = fopen(out, "w");
   if (!fp) {
      printf("%s: can not open output file %s\n", argv[0], out);
      exit(1);
   }

   fprintf(fp, "#ifndef ROOT_RVersion\n");
   fprintf(fp, "#define ROOT_RVersion\n\n");
   fprintf(fp, "/* Version information automatically generated by installer. */\n\n");
   fprintf(fp, "/*\n");
   fprintf(fp, " * These macros can be used in the following way:\n");
   fprintf(fp, " *\n");
   fprintf(fp, " *    #if ROOT_VERSION_CODE >= ROOT_VERSION(2,23,4)\n");
   fprintf(fp, " *       #include <newheader.h>\n");
   fprintf(fp, " *    #else\n");
   fprintf(fp, " *       #include <oldheader.h>\n");
   fprintf(fp, " *    #endif\n");
   fprintf(fp, " *\n");
   fprintf(fp, "*/\n\n");

   int xx, yy, zz, rev;
   sscanf(vers, "%d.%d/%d", &xx, &yy, &zz);
   int vers_code = (xx << 16) + (yy << 8) + zz;
   sscanf(revs, "%d", &rev);

   fprintf(fp, "#define ROOT_RELEASE \"%s\"\n", vers);
   fprintf(fp, "#define ROOT_RELEASE_DATE \"%s\"\n", __DATE__);
   fprintf(fp, "#define ROOT_RELEASE_TIME \"%s\"\n", __TIME__);
   fprintf(fp, "#define ROOT_SVN_REVISION %d\n", rev);
   fprintf(fp, "#define ROOT_SVN_BRANCH \"%s\"\n", branch);
   fprintf(fp, "#define ROOT_VERSION_CODE %d\n", vers_code);
   fprintf(fp, "#define ROOT_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))\n");
   fprintf(fp, "\n#endif\n");

   fclose(fp);

   exit(0);
}
<|endoftext|>