<commit_before>/*
 * ConnectionHistory.cpp
 *
 * Copyright (C) 2020 by RStudio, PBC
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "ConnectionHistory.hpp"

#include <core/FileSerializer.hpp>

#include <session/SessionModuleContext.hpp>

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace connections {

namespace {

bool isConnection(const ConnectionId& id, json::Value valueJson)
{
   if (!json::isType<json::Object>(valueJson))
   {
      LOG_WARNING_MESSAGE("Connection JSON has unexpected format");
      return false;
   }

   const json::Object& connJson = valueJson.getObject();
   return hasConnectionId(id, connJson);
}

} // anonymous namespace

ConnectionHistory& connectionHistory()
{
   static ConnectionHistory instance;
   return instance;
}

ConnectionHistory::ConnectionHistory()
{
}

Error ConnectionHistory::initialize()
{
   // register to be notified when connections are changed
   connectionsDir_ = module_context::registerMonitoredUserScratchDir(
            "connection_history",
            boost::bind(&ConnectionHistory::onConnectionsChanged, this));

   return Success();
}

void ConnectionHistory::update(const Connection& connection)
{
   // read existing connections
   json::Array connectionsJson;
   Error error = readConnections(&connectionsJson);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // look for a matching connection and update it
   bool foundConnection = false;
   for (size_t i = 0; i<connectionsJson.getSize(); i++)
   {
      json::Value valueJson = connectionsJson[i];
      if (isConnection(connection.id, valueJson))
      {
         connectionsJson[i] = connectionJson(connection);
         foundConnection = true;
         break;
      }
   }

   // if we didn't find a connection then append
   if (!foundConnection)
      connectionsJson.push_back(connectionJson(connection));

   // write out the connections
   error = writeConnections(connectionsJson);
   if (error)
      LOG_ERROR(error);

   // fire event
   onConnectionsChanged();
}

void ConnectionHistory::remove(const ConnectionId &id)
{
   // read existing connections
   json::Array connectionsJson;
   Error error = readConnections(&connectionsJson);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // remove matching connection
   connectionsJson.erase(std::remove_if(connectionsJson.begin(),
                                        connectionsJson.end(),
                                        boost::bind(isConnection, id, _1)),
                         connectionsJson.end());

   // write out the connections
   error = writeConnections(connectionsJson);
   if (error)
      LOG_ERROR(error);
}

json::Array ConnectionHistory::connectionsAsJson()
{
   json::Array connectionsJson;
   Error error = readConnections(&connectionsJson);
   if (error)
      LOG_ERROR(error);
   return connectionsJson;
}

void ConnectionHistory::onConnectionsChanged()
{
   ClientEvent event(client_events::kConnectionListChanged,
                     connectionsAsJson());
   module_context::enqueClientEvent(event);
}

const char* const kConnectionListFile = "connection-history-database.json";

Error ConnectionHistory::readConnections(json::Array* pConnections)
{
   FilePath connectionListFile = connectionsDir_.completeChildPath(kConnectionListFile);
   if (!connectionListFile.exists())
      return Success();

   std::string contents;
   Error error = core::readStringFromFile(connectionListFile, &contents);
   if (error)
      return error;

   json::Value parsedJson;
   if (parsedJson.parse(contents) || !json::isType<json::Array>(parsedJson))
   {
      return systemError(boost::system::errc::protocol_error,
                         "Error parsing connections json file",
                         ERROR_LOCATION);
   }

   json::Array connections = parsedJson.getArray();
   for (auto&& connection : connections)
      if (connection.isObject())
         pConnections->push_back(connection);

   return Success();
}

Error ConnectionHistory::writeConnections(const json::Array& connectionsJson)
{
   FilePath connectionListFile = connectionsDir_.completeChildPath(kConnectionListFile);
   std::shared_ptr<std::ostream> pStream;
   Error error = connectionListFile.openForWrite(pStream);
   if (error)
      return error;

   connectionsJson.writeFormatted(*pStream);

   return Success();
}

} // namespace connections
} // namespace modules
} // namespace session
} // namespace rstudio

<commit_msg>accept JSON by reference (#8205)<commit_after>/*
 * ConnectionHistory.cpp
 *
 * Copyright (C) 2020 by RStudio, PBC
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "ConnectionHistory.hpp"

#include <core/FileSerializer.hpp>

#include <session/SessionModuleContext.hpp>

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace connections {

namespace {

bool isConnection(const ConnectionId& id, const json::Value& valueJson)
{
   if (!json::isType<json::Object>(valueJson))
   {
      LOG_WARNING_MESSAGE("Connection JSON has unexpected format");
      return false;
   }

   const json::Object& connJson = valueJson.getObject();
   return hasConnectionId(id, connJson);
}

} // anonymous namespace

ConnectionHistory& connectionHistory()
{
   static ConnectionHistory instance;
   return instance;
}

ConnectionHistory::ConnectionHistory()
{
}

Error ConnectionHistory::initialize()
{
   // register to be notified when connections are changed
   connectionsDir_ = module_context::registerMonitoredUserScratchDir(
            "connection_history",
            boost::bind(&ConnectionHistory::onConnectionsChanged, this));

   return Success();
}

void ConnectionHistory::update(const Connection& connection)
{
   // read existing connections
   json::Array connectionsJson;
   Error error = readConnections(&connectionsJson);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // look for a matching connection and update it
   bool foundConnection = false;
   for (size_t i = 0; i<connectionsJson.getSize(); i++)
   {
      json::Value valueJson = connectionsJson[i];
      if (isConnection(connection.id, valueJson))
      {
         connectionsJson[i] = connectionJson(connection);
         foundConnection = true;
         break;
      }
   }

   // if we didn't find a connection then append
   if (!foundConnection)
      connectionsJson.push_back(connectionJson(connection));

   // write out the connections
   error = writeConnections(connectionsJson);
   if (error)
      LOG_ERROR(error);

   // fire event
   onConnectionsChanged();
}

void ConnectionHistory::remove(const ConnectionId &id)
{
   // read existing connections
   json::Array connectionsJson;
   Error error = readConnections(&connectionsJson);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // remove matching connection
   connectionsJson.erase(std::remove_if(connectionsJson.begin(),
                                        connectionsJson.end(),
                                        boost::bind(isConnection, id, _1)),
                         connectionsJson.end());

   // write out the connections
   error = writeConnections(connectionsJson);
   if (error)
      LOG_ERROR(error);
}

json::Array ConnectionHistory::connectionsAsJson()
{
   json::Array connectionsJson;
   Error error = readConnections(&connectionsJson);
   if (error)
      LOG_ERROR(error);
   return connectionsJson;
}

void ConnectionHistory::onConnectionsChanged()
{
   ClientEvent event(client_events::kConnectionListChanged,
                     connectionsAsJson());
   module_context::enqueClientEvent(event);
}

const char* const kConnectionListFile = "connection-history-database.json";

Error ConnectionHistory::readConnections(json::Array* pConnections)
{
   FilePath connectionListFile = connectionsDir_.completeChildPath(kConnectionListFile);
   if (!connectionListFile.exists())
      return Success();

   std::string contents;
   Error error = core::readStringFromFile(connectionListFile, &contents);
   if (error)
      return error;

   json::Value parsedJson;
   if (parsedJson.parse(contents) || !json::isType<json::Array>(parsedJson))
   {
      return systemError(boost::system::errc::protocol_error,
                         "Error parsing connections json file",
                         ERROR_LOCATION);
   }

   json::Array connections = parsedJson.getArray();
   for (auto&& connection : connections)
      if (connection.isObject())
         pConnections->push_back(connection);

   return Success();
}

Error ConnectionHistory::writeConnections(const json::Array& connectionsJson)
{
   FilePath connectionListFile = connectionsDir_.completeChildPath(kConnectionListFile);
   std::shared_ptr<std::ostream> pStream;
   Error error = connectionListFile.openForWrite(pStream);
   if (error)
      return error;

   connectionsJson.writeFormatted(*pStream);

   return Success();
}

} // namespace connections
} // namespace modules
} // namespace session
} // namespace rstudio

<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/

#include "genericprojectwizard.h"
#include "filesselectionwizardpage.h"

#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/customwizard/customwizard.h>

#include <utils/filewizardpage.h>

#include <QtGui/QIcon>

#include <QtGui/QApplication>
#include <QtGui/QStyle>
#include <QtGui/QPainter>
#include <QtGui/QPixmap>

#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QtDebug>
#include <QtCore/QCoreApplication>

using namespace GenericProjectManager::Internal;
using namespace Utils;

//////////////////////////////////////////////////////////////////////////////
// GenericProjectWizardDialog
//////////////////////////////////////////////////////////////////////////////

GenericProjectWizardDialog::GenericProjectWizardDialog(QWidget *parent)
    : Utils::Wizard(parent)
{
    setWindowTitle(tr("Import Existing Project"));

    // first page
    m_firstPage = new FileWizardPage;
    m_firstPage->setTitle(tr("Project Name and Location"));
    m_firstPage->setFileNameLabel(tr("Project name:"));
    m_firstPage->setPathLabel(tr("Location:"));

    // second page
    m_secondPage = new FilesSelectionWizardPage(this);
    m_secondPage->setTitle(tr("File Selection"));

    const int firstPageId = addPage(m_firstPage);
    wizardProgress()->item(firstPageId)->setTitle(tr("Location"));

    const int secondPageId = addPage(m_secondPage);
    wizardProgress()->item(secondPageId)->setTitle(tr("Files"));
}

GenericProjectWizardDialog::~GenericProjectWizardDialog()
{ }

QString GenericProjectWizardDialog::path() const
{
    return m_firstPage->path();
}

QStringList GenericProjectWizardDialog::selectedPaths() const
{
    return m_secondPage->selectedPaths();
}

QStringList GenericProjectWizardDialog::selectedFiles() const
{
    return m_secondPage->selectedFiles();
}

void GenericProjectWizardDialog::setPath(const QString &path)
{
    m_firstPage->setPath(path);
}

QString GenericProjectWizardDialog::projectName() const
{
    return m_firstPage->fileName();
}

GenericProjectWizard::GenericProjectWizard()
    : Core::BaseFileWizard(parameters())
{ }

GenericProjectWizard::~GenericProjectWizard()
{ }

Core::BaseFileWizardParameters GenericProjectWizard::parameters()
{
    Core::BaseFileWizardParameters parameters(ProjectWizard);
    // TODO do something about the ugliness of standard icons in sizes different than 16, 32, 64, 128
    {
        QPixmap icon(22, 22);
        icon.fill(Qt::transparent);
        QPainter p(&icon);
        p.drawPixmap(3, 3, 16, 16, qApp->style()->standardIcon(QStyle::SP_DirIcon).pixmap(16));
        parameters.setIcon(icon);
    }
    parameters.setDisplayName(tr("Import Existing Project"));
    parameters.setId(QLatin1String("Z.Makefile"));
    parameters.setDescription(tr("Imports existing projects that do not use qmake or CMake. "
                                 "This allows you to use Qt Creator as a code editor."));
    parameters.setCategory(QLatin1String(ProjectExplorer::Constants::PROJECT_WIZARD_CATEGORY));
    parameters.setDisplayCategory(QCoreApplication::translate("ProjectExplorer", ProjectExplorer::Constants::PROJECT_WIZARD_TR_CATEGORY));
    return parameters;
}

QWizard *GenericProjectWizard::createWizardDialog(QWidget *parent,
                                                  const QString &defaultPath,
                                                  const WizardPageList &extensionPages) const
{
    GenericProjectWizardDialog *wizard = new GenericProjectWizardDialog(parent);
    setupWizard(wizard);

    wizard->setPath(defaultPath);

    foreach (QWizardPage *p, extensionPages)
        BaseFileWizard::applyExtensionPageShortTitle(wizard, wizard->addPage(p));

    return wizard;
}

void GenericProjectWizard::getFileList(const QDir &dir, const QString &projectRoot,
                                       const QStringList &suffixes,
                                       QStringList *files, QStringList *paths) const
{
    const QFileInfoList fileInfoList = dir.entryInfoList(QDir::Files |
                                                         QDir::Dirs |
                                                         QDir::NoDotAndDotDot |
                                                         QDir::NoSymLinks);

    foreach (const QFileInfo &fileInfo, fileInfoList) {
        QString filePath = fileInfo.absoluteFilePath();
        filePath = filePath.mid(projectRoot.length() + 1);

        if (fileInfo.isDir() && isValidDir(fileInfo)) {
            getFileList(QDir(fileInfo.absoluteFilePath()), projectRoot,
                        suffixes, files, paths);

            if (! paths->contains(filePath))
                paths->append(filePath);
        }

        else if (suffixes.contains(fileInfo.suffix()))
            files->append(filePath);
    }
}

bool GenericProjectWizard::isValidDir(const QFileInfo &fileInfo) const
{
    const QString fileName = fileInfo.fileName();
    const QString suffix = fileInfo.suffix();

    if (fileName.startsWith(QLatin1Char('.')))
        return false;

    else if (fileName == QLatin1String("CVS"))
        return false;

    // ### user include/exclude

    return true;
}

Core::GeneratedFiles GenericProjectWizard::generateFiles(const QWizard *w,
                                                         QString *errorMessage) const
{
    Q_UNUSED(errorMessage)

    const GenericProjectWizardDialog *wizard = qobject_cast<const GenericProjectWizardDialog *>(w);
    const QString projectPath = wizard->path();
    const QDir dir(projectPath);
    const QString projectName = wizard->projectName();
    const QString creatorFileName = QFileInfo(dir, projectName + QLatin1String(".creator")).absoluteFilePath();
    const QString filesFileName = QFileInfo(dir, projectName + QLatin1String(".files")).absoluteFilePath();
    const QString includesFileName = QFileInfo(dir, projectName + QLatin1String(".includes")).absoluteFilePath();
    const QString configFileName = QFileInfo(dir, projectName + QLatin1String(".config")).absoluteFilePath();
    const QStringList sources = wizard->selectedFiles();
    const QStringList paths = wizard->selectedPaths();

    Core::ICore *core = Core::ICore::instance();
    Core::MimeDatabase *mimeDatabase = core->mimeDatabase();

    Core::MimeType headerTy = mimeDatabase->findByType(QLatin1String("text/x-chdr"));

    QStringList nameFilters;
    foreach (const Core::MimeGlobPattern &gp, headerTy.globPatterns())
        nameFilters.append(gp.regExp().pattern());

    QStringList includePaths;
    foreach (const QString &path, paths) {
        QFileInfo fileInfo(dir, path);
        QDir thisDir(fileInfo.absoluteFilePath());

        if (! thisDir.entryList(nameFilters, QDir::Files).isEmpty())
            includePaths.append(path);
    }

    Core::GeneratedFile generatedCreatorFile(creatorFileName);
    generatedCreatorFile.setContents(QLatin1String("[General]\n"));
    generatedCreatorFile.setAttributes(Core::GeneratedFile::OpenProjectAttribute);

    Core::GeneratedFile generatedFilesFile(filesFileName);
    generatedFilesFile.setContents(sources.join(QLatin1String("\n")));

    Core::GeneratedFile generatedIncludesFile(includesFileName);
    generatedIncludesFile.setContents(includePaths.join(QLatin1String("\n")));

    Core::GeneratedFile generatedConfigFile(configFileName);
    generatedConfigFile.setContents(QLatin1String("// ADD PREDEFINED MACROS HERE!\n"));

    Core::GeneratedFiles files;
    files.append(generatedFilesFile);
    files.append(generatedIncludesFile);
    files.append(generatedConfigFile);
    files.append(generatedCreatorFile);

    return files;
}

bool GenericProjectWizard::postGenerateFiles(const QWizard *w, const Core::GeneratedFiles &l, QString *errorMessage)
{
    Q_UNUSED(w);
    return ProjectExplorer::CustomProjectWizard::postGenerateOpen(l, errorMessage);
}
<commit_msg>Generic Project Wizard adds files with relative path.<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/

#include "genericprojectwizard.h"
#include "filesselectionwizardpage.h"

#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/customwizard/customwizard.h>

#include <utils/filewizardpage.h>

#include <QtGui/QIcon>

#include <QtGui/QApplication>
#include <QtGui/QStyle>
#include <QtGui/QPainter>
#include <QtGui/QPixmap>

#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QtDebug>
#include <QtCore/QCoreApplication>

using namespace GenericProjectManager::Internal;
using namespace Utils;

//////////////////////////////////////////////////////////////////////////////
// GenericProjectWizardDialog
//////////////////////////////////////////////////////////////////////////////

GenericProjectWizardDialog::GenericProjectWizardDialog(QWidget *parent)
    : Utils::Wizard(parent)
{
    setWindowTitle(tr("Import Existing Project"));

    // first page
    m_firstPage = new FileWizardPage;
    m_firstPage->setTitle(tr("Project Name and Location"));
    m_firstPage->setFileNameLabel(tr("Project name:"));
    m_firstPage->setPathLabel(tr("Location:"));

    // second page
    m_secondPage = new FilesSelectionWizardPage(this);
    m_secondPage->setTitle(tr("File Selection"));

    const int firstPageId = addPage(m_firstPage);
    wizardProgress()->item(firstPageId)->setTitle(tr("Location"));

    const int secondPageId = addPage(m_secondPage);
    wizardProgress()->item(secondPageId)->setTitle(tr("Files"));
}

GenericProjectWizardDialog::~GenericProjectWizardDialog()
{ }

QString GenericProjectWizardDialog::path() const
{
    return m_firstPage->path();
}

QStringList GenericProjectWizardDialog::selectedPaths() const
{
    return m_secondPage->selectedPaths();
}

QStringList GenericProjectWizardDialog::selectedFiles() const
{
    return m_secondPage->selectedFiles();
}

void GenericProjectWizardDialog::setPath(const QString &path)
{
    m_firstPage->setPath(path);
}

QString GenericProjectWizardDialog::projectName() const
{
    return m_firstPage->fileName();
}

GenericProjectWizard::GenericProjectWizard()
    : Core::BaseFileWizard(parameters())
{ }

GenericProjectWizard::~GenericProjectWizard()
{ }

Core::BaseFileWizardParameters GenericProjectWizard::parameters()
{
    Core::BaseFileWizardParameters parameters(ProjectWizard);
    // TODO do something about the ugliness of standard icons in sizes different than 16, 32, 64, 128
    {
        QPixmap icon(22, 22);
        icon.fill(Qt::transparent);
        QPainter p(&icon);
        p.drawPixmap(3, 3, 16, 16, qApp->style()->standardIcon(QStyle::SP_DirIcon).pixmap(16));
        parameters.setIcon(icon);
    }
    parameters.setDisplayName(tr("Import Existing Project"));
    parameters.setId(QLatin1String("Z.Makefile"));
    parameters.setDescription(tr("Imports existing projects that do not use qmake or CMake. "
                                 "This allows you to use Qt Creator as a code editor."));
    parameters.setCategory(QLatin1String(ProjectExplorer::Constants::PROJECT_WIZARD_CATEGORY));
    parameters.setDisplayCategory(QCoreApplication::translate("ProjectExplorer", ProjectExplorer::Constants::PROJECT_WIZARD_TR_CATEGORY));
    return parameters;
}

QWizard *GenericProjectWizard::createWizardDialog(QWidget *parent,
                                                  const QString &defaultPath,
                                                  const WizardPageList &extensionPages) const
{
    GenericProjectWizardDialog *wizard = new GenericProjectWizardDialog(parent);
    setupWizard(wizard);

    wizard->setPath(defaultPath);

    foreach (QWizardPage *p, extensionPages)
        BaseFileWizard::applyExtensionPageShortTitle(wizard, wizard->addPage(p));

    return wizard;
}

void GenericProjectWizard::getFileList(const QDir &dir, const QString &projectRoot,
                                       const QStringList &suffixes,
                                       QStringList *files, QStringList *paths) const
{
    const QFileInfoList fileInfoList = dir.entryInfoList(QDir::Files |
                                                         QDir::Dirs |
                                                         QDir::NoDotAndDotDot |
                                                         QDir::NoSymLinks);

    foreach (const QFileInfo &fileInfo, fileInfoList) {
        QString filePath = fileInfo.absoluteFilePath();
        filePath = filePath.mid(projectRoot.length() + 1);

        if (fileInfo.isDir() && isValidDir(fileInfo)) {
            getFileList(QDir(fileInfo.absoluteFilePath()), projectRoot,
                        suffixes, files, paths);

            if (! paths->contains(filePath))
                paths->append(filePath);
        }

        else if (suffixes.contains(fileInfo.suffix()))
            files->append(filePath);
    }
}

bool GenericProjectWizard::isValidDir(const QFileInfo &fileInfo) const
{
    const QString fileName = fileInfo.fileName();
    const QString suffix = fileInfo.suffix();

    if (fileName.startsWith(QLatin1Char('.')))
        return false;

    else if (fileName == QLatin1String("CVS"))
        return false;

    // ### user include/exclude

    return true;
}

Core::GeneratedFiles GenericProjectWizard::generateFiles(const QWizard *w,
                                                         QString *errorMessage) const
{
    Q_UNUSED(errorMessage)

    const GenericProjectWizardDialog *wizard = qobject_cast<const GenericProjectWizardDialog *>(w);
    const QString projectPath = wizard->path();
    const QDir dir(projectPath);
    const QString projectName = wizard->projectName();
    const QString creatorFileName = QFileInfo(dir, projectName + QLatin1String(".creator")).absoluteFilePath();
    const QString filesFileName = QFileInfo(dir, projectName + QLatin1String(".files")).absoluteFilePath();
    const QString includesFileName = QFileInfo(dir, projectName + QLatin1String(".includes")).absoluteFilePath();
    const QString configFileName = QFileInfo(dir, projectName + QLatin1String(".config")).absoluteFilePath();
    const QStringList paths = wizard->selectedPaths();

    Core::ICore *core = Core::ICore::instance();
    Core::MimeDatabase *mimeDatabase = core->mimeDatabase();

    Core::MimeType headerTy = mimeDatabase->findByType(QLatin1String("text/x-chdr"));

    QStringList nameFilters;
    foreach (const Core::MimeGlobPattern &gp, headerTy.globPatterns())
        nameFilters.append(gp.regExp().pattern());

    QStringList includePaths;
    foreach (const QString &path, paths) {
        QFileInfo fileInfo(dir, path);
        QDir thisDir(fileInfo.absoluteFilePath());

        if (! thisDir.entryList(nameFilters, QDir::Files).isEmpty())
            includePaths.append(path);
    }

    Core::GeneratedFile generatedCreatorFile(creatorFileName);
    generatedCreatorFile.setContents(QLatin1String("[General]\n"));
    generatedCreatorFile.setAttributes(Core::GeneratedFile::OpenProjectAttribute);

    QStringList sources = wizard->selectedFiles();
    for (int i = 0; i < sources.length(); ++i)
        sources[i] = dir.relativeFilePath(sources[i]);

    Core::GeneratedFile generatedFilesFile(filesFileName);
    generatedFilesFile.setContents(sources.join(QLatin1String("\n")));

    Core::GeneratedFile generatedIncludesFile(includesFileName);
    generatedIncludesFile.setContents(includePaths.join(QLatin1String("\n")));

    Core::GeneratedFile generatedConfigFile(configFileName);
    generatedConfigFile.setContents(QLatin1String("// ADD PREDEFINED MACROS HERE!\n"));

    Core::GeneratedFiles files;
    files.append(generatedFilesFile);
    files.append(generatedIncludesFile);
    files.append(generatedConfigFile);
    files.append(generatedCreatorFile);

    return files;
}

bool GenericProjectWizard::postGenerateFiles(const QWizard *w, const Core::GeneratedFiles &l, QString *errorMessage)
{
    Q_UNUSED(w);
    return ProjectExplorer::CustomProjectWizard::postGenerateOpen(l, errorMessage);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrCCPathProcessor.h"

#include "GrGpuCommandBuffer.h"
#include "GrOnFlushResourceProvider.h"
#include "GrTexture.h"
#include "ccpr/GrCCPerFlushResources.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLGeometryProcessor.h"
#include "glsl/GrGLSLProgramBuilder.h"
#include "glsl/GrGLSLVarying.h"

// Slightly undershoot an AA bloat radius of 0.5 so vertices that fall on integer boundaries don't
// accidentally reach into neighboring path masks within the atlas.
constexpr float kAABloatRadius = 0.491111f;

// Paths are drawn as octagons. Each point on the octagon is the intersection of two lines: one edge
// from the path's bounding box and one edge from its 45-degree bounding box. The below inputs
// define a vertex by the two edges that need to be intersected. Normals point out of the octagon,
// and the bounding boxes are sent in as instance attribs.
static constexpr float kOctoEdgeNorms[8 * 4] = {
    // bbox   // bbox45
    -1, 0,    -1,+1,
    -1, 0,    -1,-1,
     0,-1,    -1,-1,
     0,-1,    +1,-1,
    +1, 0,    +1,-1,
    +1, 0,    +1,+1,
     0,+1,    +1,+1,
     0,+1,    -1,+1,
};

GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);

sk_sp<const GrBuffer> GrCCPathProcessor::FindVertexBuffer(GrOnFlushResourceProvider* onFlushRP) {
    GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
    return onFlushRP->findOrMakeStaticBuffer(kVertex_GrBufferType, sizeof(kOctoEdgeNorms),
                                             kOctoEdgeNorms, gVertexBufferKey);
}

static constexpr uint16_t kRestartStrip = 0xffff;

static constexpr uint16_t kOctoIndicesAsStrips[] = {
    1, 0, 2, 4, 3, kRestartStrip, // First half.
    5, 4, 6, 0, 7 // Second half.
};

static constexpr uint16_t kOctoIndicesAsTris[] = {
    // First half.
    1, 0, 2,
    0, 4, 2,
    2, 4, 3,

    // Second half.
    5, 4, 6,
    4, 0, 6,
    6, 0, 7,
};

GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);

constexpr GrPrimitiveProcessor::Attribute GrCCPathProcessor::kInstanceAttribs[];
constexpr GrPrimitiveProcessor::Attribute GrCCPathProcessor::kEdgeNormsAttrib;

sk_sp<const GrBuffer> GrCCPathProcessor::FindIndexBuffer(GrOnFlushResourceProvider* onFlushRP) {
    GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
    if (onFlushRP->caps()->usePrimitiveRestart()) {
        return onFlushRP->findOrMakeStaticBuffer(kIndex_GrBufferType, sizeof(kOctoIndicesAsStrips),
                                                 kOctoIndicesAsStrips, gIndexBufferKey);
    } else {
        return onFlushRP->findOrMakeStaticBuffer(kIndex_GrBufferType, sizeof(kOctoIndicesAsTris),
                                                 kOctoIndicesAsTris, gIndexBufferKey);
    }
}

GrCCPathProcessor::GrCCPathProcessor(const GrTextureProxy* atlas,
                                     const SkMatrix& viewMatrixIfUsingLocalCoords)
        : INHERITED(kGrCCPathProcessor_ClassID)
        , fAtlasAccess(atlas->textureType(), atlas->config(), GrSamplerState::Filter::kNearest,
                       GrSamplerState::WrapMode::kClamp)
        , fAtlasSize(atlas->isize())
        , fAtlasOrigin(atlas->origin()) {
    // TODO: Can we just assert that atlas has GrCCAtlas::kTextureOrigin and remove fAtlasOrigin?
    this->setInstanceAttributeCnt(kNumInstanceAttribs);
    // Check that instance attributes exactly match Instance struct layout.
    SkASSERT(!strcmp(this->instanceAttribute(0).name(), "devbounds"));
    SkASSERT(!strcmp(this->instanceAttribute(1).name(), "devbounds45"));
    SkASSERT(!strcmp(this->instanceAttribute(2).name(), "dev_to_atlas_offset"));
    SkASSERT(!strcmp(this->instanceAttribute(3).name(), "color"));
    SkASSERT(this->debugOnly_instanceAttributeOffset(0) == offsetof(Instance, fDevBounds));
    SkASSERT(this->debugOnly_instanceAttributeOffset(1) == offsetof(Instance, fDevBounds45));
    SkASSERT(this->debugOnly_instanceAttributeOffset(2) == offsetof(Instance, fDevToAtlasOffset));
    SkASSERT(this->debugOnly_instanceAttributeOffset(3) == offsetof(Instance, fColor));
    SkASSERT(this->debugOnly_instanceStride() == sizeof(Instance));

    this->setVertexAttributeCnt(1);
    this->setTextureSamplerCnt(1);

    if (!viewMatrixIfUsingLocalCoords.invert(&fLocalMatrix)) {
        fLocalMatrix.setIdentity();
    }
}

class GLSLPathProcessor : public GrGLSLGeometryProcessor {
public:
    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override;

private:
    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
                 FPCoordTransformIter&& transformIter) override {
        const GrCCPathProcessor& proc = primProc.cast<GrCCPathProcessor>();
        pdman.set2f(fAtlasAdjustUniform, 1.0f / proc.atlasSize().fWidth,
                    1.0f / proc.atlasSize().fHeight);
        this->setTransformDataHelper(proc.localMatrix(), pdman, &transformIter);
    }

    GrGLSLUniformHandler::UniformHandle fAtlasAdjustUniform;

    typedef GrGLSLGeometryProcessor INHERITED;
};

GrGLSLPrimitiveProcessor* GrCCPathProcessor::createGLSLInstance(const GrShaderCaps&) const {
    return new GLSLPathProcessor();
}

void GrCCPathProcessor::drawPaths(GrOpFlushState* flushState, const GrPipeline& pipeline,
                                  const GrPipeline::FixedDynamicState* fixedDynamicState,
                                  const GrCCPerFlushResources& resources, int baseInstance,
                                  int endInstance, const SkRect& bounds) const {
    const GrCaps& caps = flushState->caps();
    GrPrimitiveType primitiveType = caps.usePrimitiveRestart()
                                            ? GrPrimitiveType::kTriangleStrip
                                            : GrPrimitiveType::kTriangles;
    int numIndicesPerInstance = caps.usePrimitiveRestart()
                                        ? SK_ARRAY_COUNT(kOctoIndicesAsStrips)
                                        : SK_ARRAY_COUNT(kOctoIndicesAsTris);
    GrMesh mesh(primitiveType);
    auto enablePrimitiveRestart = GrPrimitiveRestart(flushState->caps().usePrimitiveRestart());

    mesh.setIndexedInstanced(resources.indexBuffer(), numIndicesPerInstance,
                             resources.instanceBuffer(), endInstance - baseInstance, baseInstance,
                             enablePrimitiveRestart);
    mesh.setVertexData(resources.vertexBuffer());

    flushState->rtCommandBuffer()->draw(*this, pipeline, fixedDynamicState, nullptr, &mesh, 1,
                                        bounds);
}

void GLSLPathProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
    using InstanceAttribs = GrCCPathProcessor::InstanceAttribs;
    using Interpolation = GrGLSLVaryingHandler::Interpolation;

    const GrCCPathProcessor& proc = args.fGP.cast<GrCCPathProcessor>();
    GrGLSLUniformHandler* uniHandler = args.fUniformHandler;
    GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;

    const char* atlasAdjust;
    fAtlasAdjustUniform = uniHandler->addUniform(
            kVertex_GrShaderFlag,
            kFloat2_GrSLType, "atlas_adjust", &atlasAdjust);

    varyingHandler->emitAttributes(proc);

    GrGLSLVarying texcoord(kFloat3_GrSLType);
    GrGLSLVarying color(kHalf4_GrSLType);
    varyingHandler->addVarying("texcoord", &texcoord);
    varyingHandler->addPassThroughAttribute(proc.getInstanceAttrib(InstanceAttribs::kColor),
                                            args.fOutputColor, Interpolation::kCanBeFlat);

    // The vertex shader bloats and intersects the devBounds and devBounds45 rectangles, in order to
    // find an octagon that circumscribes the (bloated) path.
    GrGLSLVertexBuilder* v = args.fVertBuilder;

    // Each vertex is the intersection of one edge from devBounds and one from devBounds45.
    // 'N' holds the normals to these edges as column vectors.
    //
    // NOTE: "float2x2(float4)" is valid and equivalent to "float2x2(float4.xy, float4.zw)",
    // however Intel compilers crash when we use the former syntax in this shader.
    v->codeAppendf("float2x2 N = float2x2(%s.xy, %s.zw);", proc.getEdgeNormsAttrib().name(),
                   proc.getEdgeNormsAttrib().name());

    // N[0] is the normal for the edge we are intersecting from the regular bounding box, pointing
    // out of the octagon.
    v->codeAppendf("float4 devbounds = %s;",
                   proc.getInstanceAttrib(InstanceAttribs::kDevBounds).name());
    v->codeAppend ("float2 refpt = (0 == sk_VertexID >> 2)"
                           "? float2(min(devbounds.x, devbounds.z), devbounds.y)"
                           ": float2(max(devbounds.x, devbounds.z), devbounds.w);");
    v->codeAppendf("refpt += N[0] * %f;", kAABloatRadius); // bloat for AA.

    // N[1] is the normal for the edge we are intersecting from the 45-degree bounding box, pointing
    // out of the octagon.
    v->codeAppendf("float2 refpt45 = (0 == ((sk_VertexID + 1) & (1 << 2))) ? %s.xy : %s.zw;",
                   proc.getInstanceAttrib(InstanceAttribs::kDevBounds45).name(),
                   proc.getInstanceAttrib(InstanceAttribs::kDevBounds45).name());
    v->codeAppendf("refpt45 *= float2x2(.5,.5,-.5,.5);"); // transform back to device space.
    v->codeAppendf("refpt45 += N[1] * %f;", kAABloatRadius); // bloat for AA.

    v->codeAppend ("float2 K = float2(dot(N[0], refpt), dot(N[1], refpt45));");
    v->codeAppendf("float2 octocoord = K * inverse(N);");

    gpArgs->fPositionVar.set(kFloat2_GrSLType, "octocoord");

    // Convert to atlas coordinates in order to do our texture lookup.
    v->codeAppendf("float2 atlascoord = octocoord + float2(%s);",
                   proc.getInstanceAttrib(InstanceAttribs::kDevToAtlasOffset).name());
    if (kTopLeft_GrSurfaceOrigin == proc.atlasOrigin()) {
        v->codeAppendf("%s.xy = atlascoord * %s;", texcoord.vsOut(), atlasAdjust);
    } else {
        SkASSERT(kBottomLeft_GrSurfaceOrigin == proc.atlasOrigin());
        v->codeAppendf("%s.xy = float2(atlascoord.x * %s.x, 1 - atlascoord.y * %s.y);",
                       texcoord.vsOut(), atlasAdjust, atlasAdjust);
    }
    // The third texture coordinate is -.5 for even-odd paths and +.5 for winding ones.
    // ("right < left" indicates even-odd fill type.)
    v->codeAppendf("%s.z = sign(devbounds.z - devbounds.x) * .5;", texcoord.vsOut());

    this->emitTransforms(v, varyingHandler, uniHandler, GrShaderVar("octocoord", kFloat2_GrSLType),
                         proc.localMatrix(), args.fFPCoordTransformHandler);

    // Fragment shader.
    GrGLSLFPFragmentBuilder* f = args.fFragBuilder;

    // Look up coverage count in the atlas.
    f->codeAppend ("half coverage = ");
    f->appendTextureLookup(args.fTexSamplers[0], SkStringPrintf("%s.xy", texcoord.fsIn()).c_str(),
                           kFloat2_GrSLType);
    f->codeAppend (".a;");

    // Scale coverage count by .5. Make it negative for even-odd paths and positive for winding
    // ones. Clamp winding coverage counts at 1.0 (i.e. min(coverage/2, .5)).
    f->codeAppendf("coverage = min(abs(coverage) * %s.z, .5);", texcoord.fsIn());

    // For negative values, this finishes the even-odd sawtooth function. Since positive (winding)
    // values were clamped at "coverage/2 = .5", this only undoes the previous multiply by .5.
    f->codeAppend ("coverage = 1 - abs(fract(coverage) * 2 - 1);");

    f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
}
<commit_msg>ccpr: Use ceil/floor to round out path cover octagons<commit_after>/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrCCPathProcessor.h"

#include "GrGpuCommandBuffer.h"
#include "GrOnFlushResourceProvider.h"
#include "GrTexture.h"
#include "ccpr/GrCCPerFlushResources.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLGeometryProcessor.h"
#include "glsl/GrGLSLProgramBuilder.h"
#include "glsl/GrGLSLVarying.h"

// Paths are drawn as octagons. Each point on the octagon is the intersection of two lines: one edge
// from the path's bounding box and one edge from its 45-degree bounding box. The below inputs
// define a vertex by the two edges that need to be intersected. Normals point out of the octagon,
// and the bounding boxes are sent in as instance attribs.
static constexpr float kOctoEdgeNorms[8 * 4] = {
    // bbox   // bbox45
    -1, 0,    -1,+1,
    -1, 0,    -1,-1,
     0,-1,    -1,-1,
     0,-1,    +1,-1,
    +1, 0,    +1,-1,
    +1, 0,    +1,+1,
     0,+1,    +1,+1,
     0,+1,    -1,+1,
};

GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);

sk_sp<const GrBuffer> GrCCPathProcessor::FindVertexBuffer(GrOnFlushResourceProvider* onFlushRP) {
    GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
    return onFlushRP->findOrMakeStaticBuffer(kVertex_GrBufferType, sizeof(kOctoEdgeNorms),
                                             kOctoEdgeNorms, gVertexBufferKey);
}

static constexpr uint16_t kRestartStrip = 0xffff;

static constexpr uint16_t kOctoIndicesAsStrips[] = {
    1, 0, 2, 4, 3, kRestartStrip, // First half.
    5, 4, 6, 0, 7 // Second half.
};

static constexpr uint16_t kOctoIndicesAsTris[] = {
    // First half.
    1, 0, 2,
    0, 4, 2,
    2, 4, 3,

    // Second half.
    5, 4, 6,
    4, 0, 6,
    6, 0, 7,
};

GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);

constexpr GrPrimitiveProcessor::Attribute GrCCPathProcessor::kInstanceAttribs[];
constexpr GrPrimitiveProcessor::Attribute GrCCPathProcessor::kEdgeNormsAttrib;

sk_sp<const GrBuffer> GrCCPathProcessor::FindIndexBuffer(GrOnFlushResourceProvider* onFlushRP) {
    GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
    if (onFlushRP->caps()->usePrimitiveRestart()) {
        return onFlushRP->findOrMakeStaticBuffer(kIndex_GrBufferType, sizeof(kOctoIndicesAsStrips),
                                                 kOctoIndicesAsStrips, gIndexBufferKey);
    } else {
        return onFlushRP->findOrMakeStaticBuffer(kIndex_GrBufferType, sizeof(kOctoIndicesAsTris),
                                                 kOctoIndicesAsTris, gIndexBufferKey);
    }
}

GrCCPathProcessor::GrCCPathProcessor(const GrTextureProxy* atlas,
                                     const SkMatrix& viewMatrixIfUsingLocalCoords)
        : INHERITED(kGrCCPathProcessor_ClassID)
        , fAtlasAccess(atlas->textureType(), atlas->config(), GrSamplerState::Filter::kNearest,
                       GrSamplerState::WrapMode::kClamp)
        , fAtlasSize(atlas->isize())
        , fAtlasOrigin(atlas->origin()) {
    // TODO: Can we just assert that atlas has GrCCAtlas::kTextureOrigin and remove fAtlasOrigin?
    this->setInstanceAttributeCnt(kNumInstanceAttribs);
    // Check that instance attributes exactly match Instance struct layout.
    SkASSERT(!strcmp(this->instanceAttribute(0).name(), "devbounds"));
    SkASSERT(!strcmp(this->instanceAttribute(1).name(), "devbounds45"));
    SkASSERT(!strcmp(this->instanceAttribute(2).name(), "dev_to_atlas_offset"));
    SkASSERT(!strcmp(this->instanceAttribute(3).name(), "color"));
    SkASSERT(this->debugOnly_instanceAttributeOffset(0) == offsetof(Instance, fDevBounds));
    SkASSERT(this->debugOnly_instanceAttributeOffset(1) == offsetof(Instance, fDevBounds45));
    SkASSERT(this->debugOnly_instanceAttributeOffset(2) == offsetof(Instance, fDevToAtlasOffset));
    SkASSERT(this->debugOnly_instanceAttributeOffset(3) == offsetof(Instance, fColor));
    SkASSERT(this->debugOnly_instanceStride() == sizeof(Instance));

    this->setVertexAttributeCnt(1);
    this->setTextureSamplerCnt(1);

    if (!viewMatrixIfUsingLocalCoords.invert(&fLocalMatrix)) {
        fLocalMatrix.setIdentity();
    }
}

class GLSLPathProcessor : public GrGLSLGeometryProcessor {
public:
    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override;

private:
    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
                 FPCoordTransformIter&& transformIter) override {
        const GrCCPathProcessor& proc = primProc.cast<GrCCPathProcessor>();
        pdman.set2f(fAtlasAdjustUniform, 1.0f / proc.atlasSize().fWidth,
                    1.0f / proc.atlasSize().fHeight);
        this->setTransformDataHelper(proc.localMatrix(), pdman, &transformIter);
    }

    GrGLSLUniformHandler::UniformHandle fAtlasAdjustUniform;

    typedef GrGLSLGeometryProcessor INHERITED;
};

GrGLSLPrimitiveProcessor* GrCCPathProcessor::createGLSLInstance(const GrShaderCaps&) const {
    return new GLSLPathProcessor();
}

void GrCCPathProcessor::drawPaths(GrOpFlushState* flushState, const GrPipeline& pipeline,
                                  const GrPipeline::FixedDynamicState* fixedDynamicState,
                                  const GrCCPerFlushResources& resources, int baseInstance,
                                  int endInstance, const SkRect& bounds) const {
    const GrCaps& caps = flushState->caps();
    GrPrimitiveType primitiveType = caps.usePrimitiveRestart()
                                            ? GrPrimitiveType::kTriangleStrip
                                            : GrPrimitiveType::kTriangles;
    int numIndicesPerInstance = caps.usePrimitiveRestart()
                                        ? SK_ARRAY_COUNT(kOctoIndicesAsStrips)
                                        : SK_ARRAY_COUNT(kOctoIndicesAsTris);
    GrMesh mesh(primitiveType);
    auto enablePrimitiveRestart = GrPrimitiveRestart(flushState->caps().usePrimitiveRestart());

    mesh.setIndexedInstanced(resources.indexBuffer(), numIndicesPerInstance,
                             resources.instanceBuffer(), endInstance - baseInstance, baseInstance,
                             enablePrimitiveRestart);
    mesh.setVertexData(resources.vertexBuffer());

    flushState->rtCommandBuffer()->draw(*this, pipeline, fixedDynamicState, nullptr, &mesh, 1,
                                        bounds);
}

void GLSLPathProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
    using InstanceAttribs = GrCCPathProcessor::InstanceAttribs;
    using Interpolation = GrGLSLVaryingHandler::Interpolation;

    const GrCCPathProcessor& proc = args.fGP.cast<GrCCPathProcessor>();
    GrGLSLUniformHandler* uniHandler = args.fUniformHandler;
    GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;

    const char* atlasAdjust;
    fAtlasAdjustUniform = uniHandler->addUniform(
            kVertex_GrShaderFlag,
            kFloat2_GrSLType, "atlas_adjust", &atlasAdjust);

    varyingHandler->emitAttributes(proc);

    GrGLSLVarying texcoord(kFloat3_GrSLType);
    GrGLSLVarying color(kHalf4_GrSLType);
    varyingHandler->addVarying("texcoord", &texcoord);
    varyingHandler->addPassThroughAttribute(proc.getInstanceAttrib(InstanceAttribs::kColor),
                                            args.fOutputColor, Interpolation::kCanBeFlat);

    // The vertex shader bloats and intersects the devBounds and devBounds45 rectangles, in order to
    // find an octagon that circumscribes the (bloated) path.
    GrGLSLVertexBuilder* v = args.fVertBuilder;

    // Each vertex is the intersection of one edge from devBounds and one from devBounds45.
    // 'N' holds the normals to these edges as column vectors.
    //
    // NOTE: "float2x2(float4)" is valid and equivalent to "float2x2(float4.xy, float4.zw)",
    // however Intel compilers crash when we use the former syntax in this shader.
    v->codeAppendf("float2x2 N = float2x2(%s.xy, %s.zw);", proc.getEdgeNormsAttrib().name(),
                   proc.getEdgeNormsAttrib().name());

    // N[0] is the normal for the edge we are intersecting from the regular bounding box, pointing
    // out of the octagon.
    v->codeAppendf("float4 devbounds = %s;",
                   proc.getInstanceAttrib(InstanceAttribs::kDevBounds).name());
    v->codeAppend ("float2 refpt = (0 == sk_VertexID >> 2)"
                           "? float2(min(devbounds.x, devbounds.z), devbounds.y)"
                           ": float2(max(devbounds.x, devbounds.z), devbounds.w);");

    // N[1] is the normal for the edge we are intersecting from the 45-degree bounding box, pointing
    // out of the octagon.
    v->codeAppendf("float2 refpt45 = (0 == ((sk_VertexID + 1) & (1 << 2))) ? %s.xy : %s.zw;",
                   proc.getInstanceAttrib(InstanceAttribs::kDevBounds45).name(),
                   proc.getInstanceAttrib(InstanceAttribs::kDevBounds45).name());
    v->codeAppendf("refpt45 *= float2x2(.5,.5,-.5,.5);"); // transform back to device space.

    v->codeAppend ("float2 K = float2(dot(N[0], refpt), dot(N[1], refpt45));");
    v->codeAppendf("float2 octocoord = K * inverse(N);");

    // Round the octagon out to ensure we rasterize every pixel the path might touch. (Positive
    // bloatdir means we should take the "ceil" and negative means to take the "floor".)
    //
    // NOTE: If we were just drawing a rect, ceil/floor would be enough. But since there are also
    // diagonals in the octagon that cross through pixel centers, we need to outset by another
    // quarter px to ensure those pixels get rasterized.
    v->codeAppend ("float2 bloatdir = (0 != N[0].x) "
                           "? half2(N[0].x, N[1].y) : half2(N[1].x, N[0].y);");
    v->codeAppend ("octocoord = (ceil(octocoord * bloatdir - 1e-4) + 0.25) * bloatdir;");

    gpArgs->fPositionVar.set(kFloat2_GrSLType, "octocoord");

    // Convert to atlas coordinates in order to do our texture lookup.
    v->codeAppendf("float2 atlascoord = octocoord + float2(%s);",
                   proc.getInstanceAttrib(InstanceAttribs::kDevToAtlasOffset).name());
    if (kTopLeft_GrSurfaceOrigin == proc.atlasOrigin()) {
        v->codeAppendf("%s.xy = atlascoord * %s;", texcoord.vsOut(), atlasAdjust);
    } else {
        SkASSERT(kBottomLeft_GrSurfaceOrigin == proc.atlasOrigin());
        v->codeAppendf("%s.xy = float2(atlascoord.x * %s.x, 1 - atlascoord.y * %s.y);",
                       texcoord.vsOut(), atlasAdjust, atlasAdjust);
    }
    // The third texture coordinate is -.5 for even-odd paths and +.5 for winding ones.
    // ("right < left" indicates even-odd fill type.)
    v->codeAppendf("%s.z = sign(devbounds.z - devbounds.x) * .5;", texcoord.vsOut());

    this->emitTransforms(v, varyingHandler, uniHandler, GrShaderVar("octocoord", kFloat2_GrSLType),
                         proc.localMatrix(), args.fFPCoordTransformHandler);

    // Fragment shader.
    GrGLSLFPFragmentBuilder* f = args.fFragBuilder;

    // Look up coverage count in the atlas.
    f->codeAppend ("half coverage = ");
    f->appendTextureLookup(args.fTexSamplers[0], SkStringPrintf("%s.xy", texcoord.fsIn()).c_str(),
                           kFloat2_GrSLType);
    f->codeAppend (".a;");

    // Scale coverage count by .5. Make it negative for even-odd paths and positive for winding
    // ones. Clamp winding coverage counts at 1.0 (i.e. min(coverage/2, .5)).
    f->codeAppendf("coverage = min(abs(coverage) * %s.z, .5);", texcoord.fsIn());

    // For negative values, this finishes the even-odd sawtooth function. Since positive (winding)
    // values were clamped at "coverage/2 = .5", this only undoes the previous multiply by .5.
    f->codeAppend ("coverage = 1 - abs(fract(coverage) * 2 - 1);");

    f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
}
<|endoftext|>


<commit_before>#include "webstack/SessionRequestHandler.h"

void Susi::SessionRequestHandler::handleRequest( Poco::Net::HTTPServerRequest& request,
        Poco::Net::HTTPServerResponse& response ) {
    std::string id = "";
    Susi::Logger::debug( "starting SessionRequestHandler" );
    try {
        Poco::Net::NameValueCollection cookies;
        request.getCookies( cookies );
        id = cookies["susisession"];
        Susi::Logger::debug( "sessionid: "+id );
        if( !_sessionManager->checkSession( id ) ) {
            Susi::Logger::debug( "No valid session" );
            auto oldCookie = Poco::Net::HTTPCookie {"susisession",id};
            oldCookie.setMaxAge( 0 );
            response.addCookie( oldCookie );
            Poco::Timestamp now;
            id = std::to_string( now.epochMicroseconds() );
            _sessionManager->updateSession( id );
            auto cookie = Poco::Net::HTTPCookie {"susisession",id};
            cookie.setPath( "/" );
            response.addCookie( cookie );
        }
    }
    catch( const std::exception & e ) {
        Susi::Logger::debug( "no session found, add new one" );
        Poco::Timestamp now;
        id = std::to_string( now.epochMicroseconds() );
        _sessionManager->updateSession( id );
        auto cookie = Poco::Net::HTTPCookie {"susisession",id};
        cookie.setPath( "/" );
        response.addCookie( cookie );
        Poco::Net::NameValueCollection cookies;
        request.getCookies( cookies );
        cookies.add( "susisession",id );
        request.setCookies( cookies );
    }
    try {
        defaultHandler->handleRequest( request,response );
    }
    catch( const std::exception & e ) {
        std::string msg = "error in http handler: ";
        msg += e.what();
        Susi::Logger::error( msg );
    }
    Susi::Logger::debug( "finished in SessionRequestHandler" );
}
<commit_msg>[http stack] added flush after processing;<commit_after>#include "webstack/SessionRequestHandler.h"

void Susi::SessionRequestHandler::handleRequest( Poco::Net::HTTPServerRequest& request,
        Poco::Net::HTTPServerResponse& response ) {
    std::string id = "";
    Susi::Logger::debug( "starting SessionRequestHandler" );
    try {
        Poco::Net::NameValueCollection cookies;
        request.getCookies( cookies );
        id = cookies["susisession"];
        Susi::Logger::debug( "sessionid: "+id );
        if( !_sessionManager->checkSession( id ) ) {
            Susi::Logger::debug( "No valid session" );
            auto oldCookie = Poco::Net::HTTPCookie {"susisession",id};
            oldCookie.setMaxAge( 0 );
            response.addCookie( oldCookie );
            Poco::Timestamp now;
            id = std::to_string( now.epochMicroseconds() );
            _sessionManager->updateSession( id );
            auto cookie = Poco::Net::HTTPCookie {"susisession",id};
            cookie.setPath( "/" );
            response.addCookie( cookie );
        }
    }
    catch( const std::exception & e ) {
        Susi::Logger::debug( "no session found, add new one" );
        Poco::Timestamp now;
        id = std::to_string( now.epochMicroseconds() );
        _sessionManager->updateSession( id );
        auto cookie = Poco::Net::HTTPCookie {"susisession",id};
        cookie.setPath( "/" );
        response.addCookie( cookie );
        Poco::Net::NameValueCollection cookies;
        request.getCookies( cookies );
        cookies.add( "susisession",id );
        request.setCookies( cookies );
    }
    try {
        defaultHandler->handleRequest( request,response );
        response.send().flush();
    }
    catch( const std::exception & e ) {
        std::string msg = "error in http handler: ";
        msg += e.what();
        Susi::Logger::error( msg );
    }

    Susi::Logger::debug( "finished in SessionRequestHandler" );
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016-2017, Loic Blot <loic.blot@unix-experience.fr>
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "elasticsearchclient.h"
#include <cassert>
#include <iostream>
#include <core/http/query.h>

namespace winterwind
{
using namespace http;
namespace extras
{
#define ES_URL_CLUSTER_STATE "/_cluster/state"
#define ES_URL_NODES "/_nodes"
#define ES_BULK "/_bulk"
#define ES_ANALYZE "/_analyze"

ElasticsearchClient::ElasticsearchClient(const std::string &url)
	: HTTPClient(100 * 1024), m_init_url(url)
{
	discover_cluster();
}

ElasticsearchClient::~ElasticsearchClient()
{
	while (!m_bulk_queue.empty()) {
		m_bulk_queue.pop();
	}
}

void ElasticsearchClient::discover_cluster()
{
	Json::Value res;
	Query query(m_init_url + ES_URL_CLUSTER_STATE);

	if (!_get_json(query, res) ||
		!res.isMember("cluster_name") ||
		!res["cluster_name"].isString()) {
		throw ElasticsearchException("Unable to parse Elasticsearch cluster state");
	}

	m_cluster_name = res["cluster_name"].asString();

	if (!_get_json(query, res) || !res.isMember("nodes") ||
		!res["nodes"].isObject()) {
		throw ElasticsearchException("Unable to parse Elasticsearch nodes");
	}

	Json::Value::Members cluster_members = res["nodes"].getMemberNames();
	for (const auto &member : cluster_members) {
		ElasticsearchNode node(member);
		const Json::Value &member_obj = res["nodes"][member];
		if (member_obj.isMember("http_address") &&
			member_obj["http_address"].isString()) {
			node.http_addr = "http://" + member_obj["http_address"].asString();
		} else if (member_obj.isMember("http") &&
			member_obj["http"].isObject() &&
			member_obj["http"].isMember("publish_address") &&
			member_obj["http"]["publish_address"].isString()) {
			node.http_addr =
				"http://" + member_obj["http"]["publish_address"].asString();
		}

		// If node HTTP_ADDR is empty, try nodes API
		if (node.http_addr.empty()) {
			Json::Value res_http;
			Query query_http(m_init_url + ES_URL_NODES + "/" + member + "/http");
			if (_get_json(query_http, res_http) &&
				res_http.isMember("cluster_name") &&
				res_http["cluster_name"].isString() &&
				res_http["cluster_name"].asString() == m_cluster_name &&
				res_http.isMember("nodes") &&
				res_http["nodes"].isObject() &&
				res_http["nodes"].isMember(member) &&
				res_http["nodes"][member].isObject() &&
				res_http["nodes"][member].isMember("http") &&
				res_http["nodes"][member]["http"].isObject() &&
				res_http["nodes"][member]["http"].isMember("publish_address") &&
				res_http["nodes"][member]["http"]["publish_address"].isString()) {
				const Json::Value &http_member_obj = res_http["nodes"][member];
				node.http_addr =
					"http://" + http_member_obj["http"]["publish_address"].asString();
			}
		}

		if (member_obj.isMember("version") && member_obj["version"].isString()) {
			node.version = member_obj["version"].asString();
		}

		if (member_obj.isMember("attributes") && member_obj["attributes"].isObject()) {
			Json::Value member_attrs = member_obj["attributes"];
			// Master attribute is a string, not a bool
			if (member_attrs.isMember("master") && member_attrs["master"].isString()) {
				node.is_master =
					member_attrs["master"].asString() == "true";
			}
		}

		m_nodes.push_back(node);
	}

	m_last_discovery_time = std::chrono::system_clock::now();
}

const ElasticsearchNode &ElasticsearchClient::get_fresh_node()
{
	// Rediscover cluster after 1 min
	const auto freshness = std::chrono::system_clock::now() - m_last_discovery_time;
	if (freshness.count() > 60000) {
		discover_cluster();
	}

	return m_nodes[0];
}

void ElasticsearchClient::create_doc(const std::string &index, const std::string &type,
	const Json::Value &doc)
{
	const ElasticsearchNode &node = get_fresh_node();
	std::string res;
	Json::FastWriter writer;
	std::string post_data = writer.write(doc);
	Query query(node.http_addr + "/" + index + "/" + type + "/", post_data);
	request(query, post_data);
}

void ElasticsearchClient::insert_doc(const std::string &index, const std::string &type,
	const std::string &doc_id, const Json::Value &doc)
{
	const ElasticsearchNode &node = get_fresh_node();
	std::string res;
	Json::FastWriter writer;

	std::string post_data = writer.write(doc);
	Query query(node.http_addr + "/" + index + "/" + type + "/" + doc_id, post_data, PUT);
	request(query, res);
}

void ElasticsearchClient::delete_doc(const std::string &index, const std::string &type,
	const std::string &doc_id)
{
	const ElasticsearchNode &node = get_fresh_node();
	std::string res;
	request(Query(node.http_addr + "/" + index + "/" + type + "/" + doc_id, DELETE), res);
}

// related to ElasticsearchBulkActionType
static const std::string bulkaction_str_mapping[ESBULK_AT_MAX] = {
	"create",
	"delete",
	"index",
	"update"};

void ElasticsearchBulkAction::toJson(Json::FastWriter &writer, std::string &res)
{
	// This should not happen
	assert(action < ESBULK_AT_MAX);

	Json::Value action_res;
	std::string action_type = bulkaction_str_mapping[action];
	action_res[action_type] = Json::Value();
	if (!index.empty()) {
		action_res[action_type]["_index"] = index;
	}

	if (!type.empty()) {
		action_res[action_type]["_type"] = type;
	}

	if (!doc_id.empty()) {
		action_res[action_type]["_id"] = doc_id;
	}

	res += writer.write(action_res);

	if (action == ESBULK_AT_CREATE || action == ESBULK_AT_INDEX) {
		res += writer.write(doc);
	} else if (action == ESBULK_AT_UPDATE) {
		Json::Value update;
		update["doc"] = doc;
		res += writer.write(update);
	}
}

void ElasticsearchClient::process_bulkaction_queue(std::string &res,
	uint32_t actions_limit)
{
	const ElasticsearchNode &node = get_fresh_node();
	std::string post_data;

	uint32_t processed_actions = 0;
	Json::FastWriter writer;
	while (!m_bulk_queue.empty() &&
		(actions_limit == 0 || processed_actions < actions_limit)) {
		processed_actions++;
		const ElasticsearchBulkActionPtr &action = m_bulk_queue.front();
		action->toJson(writer, post_data);
		m_bulk_queue.pop();
	}

	Query query(node.http_addr + ES_BULK, post_data, POST);
	request(query, res);
}

bool ElasticsearchClient::analyze(const std::string &index, const std::string &analyzer,
		const std::string &str, Json::Value &res)
{
	const ElasticsearchNode &node = get_fresh_node();
	Json::Value request;
	request["analyzer"] = analyzer;
	request["text"] = str;

	Query query(node.http_addr + "/" + index + ES_ANALYZE);
	return _get_json(query, request, res);
}

namespace elasticsearch {

Index::Index(const std::string &name, ElasticsearchClient *es_client):
	m_name(name), m_es_client(es_client)
{

}

bool Index::exists()
{
	Json::Value res;
	Query query(m_es_client->get_node_addr() + "/" + m_name);
	if (!m_es_client->_get_json(query, res)) {
		return false;
	}

	return !res.isMember("error") && res.isMember(m_name) ||
		!(res.isMember("status") && res["status"].isInt() &&
			res["status"].asInt() == 404);
}

bool Index::create()
{
	if (exists()) {
		return true;
	}

	Json::Value req, res;
	req["settings"] = Json::objectValue;
	if (m_shard_count > 0) {
		req["settings"]["number_of_shards"] = m_shard_count;
	}

	if (!m_analyzers.empty()) {
		req["settings"]["analysis"] = Json::objectValue;
		req["settings"]["analysis"]["analyzer"] = Json::objectValue;
		for (const auto &analyzer: m_analyzers) {
			req["settings"]["analysis"]["analyzer"][analyzer.first] =
				analyzer.second->to_json();
		}
	}

	Query query(m_es_client->get_node_addr() + "/" + m_name, PUT);
	if (!m_es_client->_put_json(query, req, res)) {
		return false;
	}

	if (res.isMember("error")) {
		if (res["error"].isObject() && res["error"].isMember("reason")) {
			std::cerr << "Elasticsearch index removal error: "
				<< res["error"]["reason"] << std::endl;
		}
		return false;
	}

	return res.isMember("acknowledged") && res["acknowledged"].isBool()
		&& res["acknowledged"].asBool();
}

bool Index::remove()
{
	if (!exists()) {
		return true;
	}

	Json::Value res;
	if (!m_es_client->_delete(m_es_client->get_node_addr() + "/" + m_name, res)) {
		return false;
	}

	if (res.isMember("error")) {
		if (res["error"].isObject() && res["error"].isMember("reason")) {
			std::cerr << "Elasticsearch index removal error: "
				<< res["error"]["reason"] << std::endl;
		}
		return false;
	}

	return res.isMember("acknowledged") && res["acknowledged"].asBool();

}

bool Index::set_shard_count(uint16_t count)
{
	if (exists()) {
		std::cerr << "Unable to set shard count on an existing index" << std::endl;
		return false;
	}

	m_shard_count = count;
	return true;
}

Json::Value Analyzer::to_json() const
{
	Json::Value result;
	switch (m_type) {
		case CUSTOM: result["type"] = "custom"; break;
		default: assert(false);
	}

	result["tokenizer"] = m_tokenizer;
	result["filter"] = Json::arrayValue;
	for (const auto &filter: m_filters) {
		result["filter"].append(filter);
	}

	return result;
}
}
}
}
<commit_msg>ES: fix create_doc<commit_after>/*
 * Copyright (c) 2016-2017, Loic Blot <loic.blot@unix-experience.fr>
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "elasticsearchclient.h"
#include <cassert>
#include <iostream>
#include <core/http/query.h>

namespace winterwind
{
using namespace http;
namespace extras
{
#define ES_URL_CLUSTER_STATE "/_cluster/state"
#define ES_URL_NODES "/_nodes"
#define ES_BULK "/_bulk"
#define ES_ANALYZE "/_analyze"

ElasticsearchClient::ElasticsearchClient(const std::string &url)
	: HTTPClient(100 * 1024), m_init_url(url)
{
	discover_cluster();
}

ElasticsearchClient::~ElasticsearchClient()
{
	while (!m_bulk_queue.empty()) {
		m_bulk_queue.pop();
	}
}

void ElasticsearchClient::discover_cluster()
{
	Json::Value res;
	Query query(m_init_url + ES_URL_CLUSTER_STATE);

	if (!_get_json(query, res) ||
		!res.isMember("cluster_name") ||
		!res["cluster_name"].isString()) {
		throw ElasticsearchException("Unable to parse Elasticsearch cluster state");
	}

	m_cluster_name = res["cluster_name"].asString();

	if (!_get_json(query, res) || !res.isMember("nodes") ||
		!res["nodes"].isObject()) {
		throw ElasticsearchException("Unable to parse Elasticsearch nodes");
	}

	Json::Value::Members cluster_members = res["nodes"].getMemberNames();
	for (const auto &member : cluster_members) {
		ElasticsearchNode node(member);
		const Json::Value &member_obj = res["nodes"][member];
		if (member_obj.isMember("http_address") &&
			member_obj["http_address"].isString()) {
			node.http_addr = "http://" + member_obj["http_address"].asString();
		} else if (member_obj.isMember("http") &&
			member_obj["http"].isObject() &&
			member_obj["http"].isMember("publish_address") &&
			member_obj["http"]["publish_address"].isString()) {
			node.http_addr =
				"http://" + member_obj["http"]["publish_address"].asString();
		}

		// If node HTTP_ADDR is empty, try nodes API
		if (node.http_addr.empty()) {
			Json::Value res_http;
			Query query_http(m_init_url + ES_URL_NODES + "/" + member + "/http");
			if (_get_json(query_http, res_http) &&
				res_http.isMember("cluster_name") &&
				res_http["cluster_name"].isString() &&
				res_http["cluster_name"].asString() == m_cluster_name &&
				res_http.isMember("nodes") &&
				res_http["nodes"].isObject() &&
				res_http["nodes"].isMember(member) &&
				res_http["nodes"][member].isObject() &&
				res_http["nodes"][member].isMember("http") &&
				res_http["nodes"][member]["http"].isObject() &&
				res_http["nodes"][member]["http"].isMember("publish_address") &&
				res_http["nodes"][member]["http"]["publish_address"].isString()) {
				const Json::Value &http_member_obj = res_http["nodes"][member];
				node.http_addr =
					"http://" + http_member_obj["http"]["publish_address"].asString();
			}
		}

		if (member_obj.isMember("version") && member_obj["version"].isString()) {
			node.version = member_obj["version"].asString();
		}

		if (member_obj.isMember("attributes") && member_obj["attributes"].isObject()) {
			Json::Value member_attrs = member_obj["attributes"];
			// Master attribute is a string, not a bool
			if (member_attrs.isMember("master") && member_attrs["master"].isString()) {
				node.is_master =
					member_attrs["master"].asString() == "true";
			}
		}

		m_nodes.push_back(node);
	}

	m_last_discovery_time = std::chrono::system_clock::now();
}

const ElasticsearchNode &ElasticsearchClient::get_fresh_node()
{
	// Rediscover cluster after 1 min
	const auto freshness = std::chrono::system_clock::now() - m_last_discovery_time;
	if (freshness.count() > 60000) {
		discover_cluster();
	}

	return m_nodes[0];
}

void ElasticsearchClient::create_doc(const std::string &index, const std::string &type,
	const Json::Value &doc)
{
	const ElasticsearchNode &node = get_fresh_node();
	std::string res;
	Json::FastWriter writer;
	std::string post_data = writer.write(doc);
	Query query(node.http_addr + "/" + index + "/" + type + "/", post_data);
	request(query, res);
}

void ElasticsearchClient::insert_doc(const std::string &index, const std::string &type,
	const std::string &doc_id, const Json::Value &doc)
{
	const ElasticsearchNode &node = get_fresh_node();
	std::string res;
	Json::FastWriter writer;

	std::string post_data = writer.write(doc);
	Query query(node.http_addr + "/" + index + "/" + type + "/" + doc_id, post_data, PUT);
	request(query, res);
}

void ElasticsearchClient::delete_doc(const std::string &index, const std::string &type,
	const std::string &doc_id)
{
	const ElasticsearchNode &node = get_fresh_node();
	std::string res;
	request(Query(node.http_addr + "/" + index + "/" + type + "/" + doc_id, DELETE), res);
}

// related to ElasticsearchBulkActionType
static const std::string bulkaction_str_mapping[ESBULK_AT_MAX] = {
	"create",
	"delete",
	"index",
	"update"};

void ElasticsearchBulkAction::toJson(Json::FastWriter &writer, std::string &res)
{
	// This should not happen
	assert(action < ESBULK_AT_MAX);

	Json::Value action_res;
	std::string action_type = bulkaction_str_mapping[action];
	action_res[action_type] = Json::Value();
	if (!index.empty()) {
		action_res[action_type]["_index"] = index;
	}

	if (!type.empty()) {
		action_res[action_type]["_type"] = type;
	}

	if (!doc_id.empty()) {
		action_res[action_type]["_id"] = doc_id;
	}

	res += writer.write(action_res);

	if (action == ESBULK_AT_CREATE || action == ESBULK_AT_INDEX) {
		res += writer.write(doc);
	} else if (action == ESBULK_AT_UPDATE) {
		Json::Value update;
		update["doc"] = doc;
		res += writer.write(update);
	}
}

void ElasticsearchClient::process_bulkaction_queue(std::string &res,
	uint32_t actions_limit)
{
	const ElasticsearchNode &node = get_fresh_node();
	std::string post_data;

	uint32_t processed_actions = 0;
	Json::FastWriter writer;
	while (!m_bulk_queue.empty() &&
		(actions_limit == 0 || processed_actions < actions_limit)) {
		processed_actions++;
		const ElasticsearchBulkActionPtr &action = m_bulk_queue.front();
		action->toJson(writer, post_data);
		m_bulk_queue.pop();
	}

	Query query(node.http_addr + ES_BULK, post_data, POST);
	request(query, res);
}

bool ElasticsearchClient::analyze(const std::string &index, const std::string &analyzer,
		const std::string &str, Json::Value &res)
{
	const ElasticsearchNode &node = get_fresh_node();
	Json::Value request;
	request["analyzer"] = analyzer;
	request["text"] = str;

	Query query(node.http_addr + "/" + index + ES_ANALYZE);
	return _get_json(query, request, res);
}

namespace elasticsearch {

Index::Index(const std::string &name, ElasticsearchClient *es_client):
	m_name(name), m_es_client(es_client)
{

}

bool Index::exists()
{
	Json::Value res;
	Query query(m_es_client->get_node_addr() + "/" + m_name);
	if (!m_es_client->_get_json(query, res)) {
		return false;
	}

	return !res.isMember("error") && res.isMember(m_name) ||
		!(res.isMember("status") && res["status"].isInt() &&
			res["status"].asInt() == 404);
}

bool Index::create()
{
	if (exists()) {
		return true;
	}

	Json::Value req, res;
	req["settings"] = Json::objectValue;
	if (m_shard_count > 0) {
		req["settings"]["number_of_shards"] = m_shard_count;
	}

	if (!m_analyzers.empty()) {
		req["settings"]["analysis"] = Json::objectValue;
		req["settings"]["analysis"]["analyzer"] = Json::objectValue;
		for (const auto &analyzer: m_analyzers) {
			req["settings"]["analysis"]["analyzer"][analyzer.first] =
				analyzer.second->to_json();
		}
	}

	Query query(m_es_client->get_node_addr() + "/" + m_name, PUT);
	if (!m_es_client->_put_json(query, req, res)) {
		return false;
	}

	if (res.isMember("error")) {
		if (res["error"].isObject() && res["error"].isMember("reason")) {
			std::cerr << "Elasticsearch index removal error: "
				<< res["error"]["reason"] << std::endl;
		}
		return false;
	}

	return res.isMember("acknowledged") && res["acknowledged"].isBool()
		&& res["acknowledged"].asBool();
}

bool Index::remove()
{
	if (!exists()) {
		return true;
	}

	Json::Value res;
	if (!m_es_client->_delete(m_es_client->get_node_addr() + "/" + m_name, res)) {
		return false;
	}

	if (res.isMember("error")) {
		if (res["error"].isObject() && res["error"].isMember("reason")) {
			std::cerr << "Elasticsearch index removal error: "
				<< res["error"]["reason"] << std::endl;
		}
		return false;
	}

	return res.isMember("acknowledged") && res["acknowledged"].asBool();

}

bool Index::set_shard_count(uint16_t count)
{
	if (exists()) {
		std::cerr << "Unable to set shard count on an existing index" << std::endl;
		return false;
	}

	m_shard_count = count;
	return true;
}

Json::Value Analyzer::to_json() const
{
	Json::Value result;
	switch (m_type) {
		case CUSTOM: result["type"] = "custom"; break;
		default: assert(false);
	}

	result["tokenizer"] = m_tokenizer;
	result["filter"] = Json::arrayValue;
	for (const auto &filter: m_filters) {
		result["filter"].append(filter);
	}

	return result;
}
}
}
}
<|endoftext|>


<commit_before>#include "util.h"
#include "camera.h"

static const float SPEED = 50.0f;
static const float MOUSE_SPEED = 0.025f;

static Camera* camera;

static void error_callback(int error, const char* description)
{
    std::cerr << description << std::endl;
}

GLFWwindow* init(const char* exampleName, int width, int height)
{
    GLFWwindow* window;

    glfwSetErrorCallback(error_callback);

    if(!glfwInit())
    {
        return 0;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // OS X
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    window = glfwCreateWindow(width, height, exampleName, NULL, NULL);
    if(!window)
    {
        glfwTerminate();
        return 0;
    }

    glfwMakeContextCurrent(window);

    std::cout << "Using OpenGL version " << glGetString(GL_VERSION) << std::endl;

    return window;
}

GLuint loadImage(const char* fileName, int* w, int* h, int index)
{
    GLuint tex;
    unsigned char* img = SOIL_load_image(fileName, w, h, NULL, SOIL_LOAD_RGB);
    if(!img)
    {
        std::cerr << "Error loading image " << fileName << ": " << SOIL_last_result() << std::endl;
        return 0;
    }

    glGenTextures(1, &tex);
    glActiveTexture(GL_TEXTURE0 + index);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, *w, *h, 0, GL_RGB, GL_UNSIGNED_BYTE, img);
    SOIL_free_image_data(img);

    return tex;
}

GLuint loadCubeMap(const char* posX, const char* negX, const char* posY,
        const char* negY, const char* posZ, const char* negZ)
{
    static const GLenum textureTypes[] =
    {
        GL_TEXTURE_CUBE_MAP_POSITIVE_X,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
    };
    const char* names[] =
    {
        posX,
        negX,
        posY,
        negY,
        posZ,
        negZ
    };

    GLuint tex;
    glGenTextures(1, &tex);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, tex);

    for(int i = 0; i < 6; ++i)
    {
        int w, h;
        unsigned char* img = SOIL_load_image(names[i], &w, &h, NULL, SOIL_LOAD_RGBA);
        if(!img)
        {
            std::cerr << "Could not load image " << names[i] << " for cubemap: " << SOIL_last_result() << std::endl;
            glDeleteTextures(1, &tex);
            return 0;
        }
        glTexImage2D(textureTypes[i], 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
        SOIL_free_image_data(img);
    }
    return tex;
}

void setCamera(Camera* cam)
{
    camera = cam;
}

void updateCamera(int width, int height, GLFWwindow* window)
{
    float deltaTime = (float)glfwGetTime();
    glfwSetTime(0.0);

    // Get mouse position
    double xpos, ypos;
    glfwGetCursorPos(window, &xpos, &ypos);
    glfwSetCursorPos(window, width / 2, height / 2);
    float horizontalAngle = camera->getHorizontalAngle();
    float verticalAngle = camera->getVerticalAngle();
    horizontalAngle += MOUSE_SPEED * deltaTime * (float)(width / 2 - xpos);
    verticalAngle   += MOUSE_SPEED * deltaTime * (float)(height / 2 - ypos);

    camera->setHorizontalAngle(horizontalAngle);
    camera->setVerticalAngle(verticalAngle);

    // Get key input
    glm::vec3 direction = camera->getDirectionVector();
    glm::vec3 position = camera->getPosition();
    glm::vec3 right = camera->getRightVector();
    if(glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS)
    {
        position += direction * deltaTime * SPEED;
    }
    if(glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS)
    {
        position -= direction * deltaTime * SPEED;
    }
    if(glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS)
    {
        position -= right * deltaTime * SPEED;
    }
    else if(glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS)
    {
        position += right * deltaTime * SPEED;
    }
    camera->setPosition(position.x, position.y, position.z);
}
<commit_msg>Skybox rendering<commit_after>#include "util.h"
#include "camera.h"

static const float SPEED = 50.0f;
static const float MOUSE_SPEED = 0.025f;

static Camera* camera;

static void error_callback(int error, const char* description)
{
    std::cerr << description << std::endl;
}

GLFWwindow* init(const char* exampleName, int width, int height)
{
    GLFWwindow* window;

    glfwSetErrorCallback(error_callback);

    if(!glfwInit())
    {
        return 0;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // OS X
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    window = glfwCreateWindow(width, height, exampleName, NULL, NULL);
    if(!window)
    {
        glfwTerminate();
        return 0;
    }

    glfwMakeContextCurrent(window);

    std::cout << "Using OpenGL version " << glGetString(GL_VERSION) << std::endl;

    return window;
}

GLuint loadImage(const char* fileName, int* w, int* h, int index)
{
    GLuint tex;
    unsigned char* img = SOIL_load_image(fileName, w, h, NULL, SOIL_LOAD_RGB);
    if(!img)
    {
        std::cerr << "Error loading image " << fileName << ": " << SOIL_last_result() << std::endl;
        return 0;
    }

    glGenTextures(1, &tex);
    glActiveTexture(GL_TEXTURE0 + index);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, *w, *h, 0, GL_RGB, GL_UNSIGNED_BYTE, img);
    SOIL_free_image_data(img);

    return tex;
}

GLuint loadCubeMap(const char* posX, const char* negX, const char* posY,
        const char* negY, const char* posZ, const char* negZ)
{
    static const GLenum textureTypes[] =
    {
        GL_TEXTURE_CUBE_MAP_POSITIVE_X,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
    };
    const char* names[] =
    {
        posX,
        negX,
        posY,
        negY,
        posZ,
        negZ
    };

    GLuint tex;
    glGenTextures(1, &tex);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, tex);

    for(int i = 0; i < 6; ++i)
    {
        int w, h;
        unsigned char* img = SOIL_load_image(names[i], &w, &h, NULL, SOIL_LOAD_RGBA);
        if(!img)
        {
            std::cerr << "Could not load image " << names[i] << " for cubemap: " << SOIL_last_result() << std::endl;
            glDeleteTextures(1, &tex);
            return 0;
        }
        glTexImage2D(textureTypes[i], 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
        SOIL_free_image_data(img);
    }
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

    return tex;
}

void setCamera(Camera* cam)
{
    camera = cam;
}

void updateCamera(int width, int height, GLFWwindow* window)
{
    float deltaTime = (float)glfwGetTime();
    glfwSetTime(0.0);

    // Get mouse position
    double xpos, ypos;
    glfwGetCursorPos(window, &xpos, &ypos);
    glfwSetCursorPos(window, width / 2, height / 2);
    float horizontalAngle = camera->getHorizontalAngle();
    float verticalAngle = camera->getVerticalAngle();
    horizontalAngle += MOUSE_SPEED * deltaTime * (float)(width / 2 - xpos);
    verticalAngle   += MOUSE_SPEED * deltaTime * (float)(height / 2 - ypos);

    camera->setHorizontalAngle(horizontalAngle);
    camera->setVerticalAngle(verticalAngle);

    // Get key input
    glm::vec3 direction = camera->getDirectionVector();
    glm::vec3 position = camera->getPosition();
    glm::vec3 right = camera->getRightVector();
    if(glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS)
    {
        position += direction * deltaTime * SPEED;
    }
    if(glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS)
    {
        position -= direction * deltaTime * SPEED;
    }
    if(glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS)
    {
        position -= right * deltaTime * SPEED;
    }
    else if(glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS)
    {
        position += right * deltaTime * SPEED;
    }
    camera->setPosition(position.x, position.y, position.z);
}
<|endoftext|>