<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include "dp_misc.h"
#include "dp_resource.h"
#include "osl/module.hxx"
#include "osl/mutex.hxx"
#include <tools/resmgr.hxx>
#include "rtl/ustring.h"
#include "cppuhelper/implbase1.hxx"
#include "unotools/configmgr.hxx"

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using ::rtl::OUString;

namespace dp_misc {
namespace {

struct OfficeLocale :
        public rtl::StaticWithInit<OUString, OfficeLocale> {
    const OUString operator () () {
        OUString slang(utl::ConfigManager::getLocale());
        //fallback, the locale is currently only set when the user starts the
        //office for the first time.
        if (slang.isEmpty())
            slang = "en-US";
        return slang;
    }
};

struct DeploymentResMgr : public rtl::StaticWithInit<
    ResMgr *, DeploymentResMgr> {
    ResMgr * operator () () {
        return ResMgr::CreateResMgr( "deployment", getOfficeLocale() );
    }
};

class theResourceMutex : public rtl::Static<osl::Mutex, theResourceMutex> {};

} // anon namespace

//==============================================================================
ResId getResId( sal_uInt16 id )
{
    const osl::MutexGuard guard( theResourceMutex::get() );
    return ResId( id, *DeploymentResMgr::get() );
}

//==============================================================================
String getResourceString( sal_uInt16 id )
{
    const osl::MutexGuard guard( theResourceMutex::get() );
    String ret( ResId( id, *DeploymentResMgr::get() ) );
    ret.SearchAndReplaceAllAscii(
        "%PRODUCTNAME", utl::ConfigManager::getProductName() );
    return ret;
}

//throws an Exception on failure
//primary subtag 2 or three letters(A-Z, a-z), i or x
void checkPrimarySubtag(::rtl::OUString const & tag)
{
    sal_Int32 len = tag.getLength();
    sal_Unicode const * arLang = tag.getStr();
    if (len < 1 || len > 3)
        throw Exception("Invalid language string.", 0);

    if (len == 1
        && (arLang[0] != 'i' && arLang[0] != 'x'))
        throw Exception("Invalid language string.", 0);

    if (len == 2 || len == 3)
    {
        for (sal_Int32 i = 0; i < len; i++)
        {
            if ( !((arLang[i] >= 'A' && arLang[i] <= 'Z')
                || (arLang[i] >= 'a' && arLang[i] <= 'z')))
            {
                throw Exception("Invalid language string.", 0);
            }
        }
    }
}

//throws an Exception on failure
//second subtag 2 letter country code or 3-8 letter other code(A-Z, a-z, 0-9)
void checkSecondSubtag(::rtl::OUString const & tag, bool & bIsCountry)
{
    sal_Int32 len = tag.getLength();
    sal_Unicode const * arLang = tag.getStr();
    if (len < 2 || len > 8)
        throw Exception("Invalid language string.", 0);
    //country code
    bIsCountry = false;
    if (len == 2)
    {
        for (sal_Int32 i = 0; i < 2; i++)
        {
            if (!( (arLang[i] >= 'A' && arLang[i] <= 'Z')
                || (arLang[i] >= 'a' && arLang[i] <= 'z')))
            {
                throw Exception("Invalid language string.", 0);
            }
        }
        bIsCountry = true;
    }

    if (len > 2)
    {
        for (sal_Int32 i = 0; i < len; i++)
        {
            if (!( (arLang[i] >= 'A' && arLang[i] <= 'Z')
                || (arLang[i] >= 'a' && arLang[i] <= 'z')
                || (arLang[i] >= '0' && arLang[i] <= '9') ))
            {
                throw Exception("Invalid language string.", 0);
            }
        }
    }
}

void checkThirdSubtag(::rtl::OUString const & tag)
{
    sal_Int32 len = tag.getLength();
    sal_Unicode const * arLang = tag.getStr();
    if (len < 1 || len > 8)
        throw Exception("Invalid language string.", 0);

    for (sal_Int32 i = 0; i < len; i++)
    {
        if (!( (arLang[i] >= 'A' && arLang[i] <= 'Z')
            || (arLang[i] >= 'a' && arLang[i] <= 'z')
            || (arLang[i] >= '0' && arLang[i] <= '9') ))
        {
            throw Exception("Invalid language string.", 0);
        }
    }
}

//=============================================================================

//We parse the string acording to RFC 3066
//We only use the primary sub-tag and two subtags. That is lang-country-variant
//We do some simple tests if the string is correct. Actually this should do a
//validating parser
//We may have the case that there is no country tag, for example en-welsh
::com::sun::star::lang::Locale toLocale( ::rtl::OUString const & slang )
{
    OUString _sLang = slang.trim();
    ::com::sun::star::lang::Locale locale;
    sal_Int32 nIndex = 0;
    OUString lang = _sLang.getToken( 0, '-', nIndex );
    checkPrimarySubtag(lang);
    locale.Language = lang;
    OUString country = _sLang.getToken( 0, '-', nIndex );
    if (!country.isEmpty())
    {
        bool bIsCountry = false;
        checkSecondSubtag(country, bIsCountry);
        if (bIsCountry)
        {
            locale.Country = country;
        }
        else
        {
             locale.Variant = country;
        }
    }
    if (locale.Variant.isEmpty())
    {
        OUString variant = _sLang.getToken( 0, '-', nIndex );
        if (!variant.isEmpty())
        {
            checkThirdSubtag(variant);
            locale.Variant = variant;
        }
    }

    return locale;
}

//==============================================================================
lang::Locale getOfficeLocale()
{
    return toLocale(OfficeLocale::get());
}

::rtl::OUString getOfficeLocaleString()
{
    return OfficeLocale::get();
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>ditch this overengineered nonsense<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include "dp_misc.h"
#include "dp_resource.h"
#include "osl/module.hxx"
#include "osl/mutex.hxx"
#include <tools/resmgr.hxx>
#include "rtl/ustring.h"
#include "cppuhelper/implbase1.hxx"
#include "unotools/configmgr.hxx"
#include <i18npool/languagetag.hxx>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using ::rtl::OUString;

namespace dp_misc {
namespace {

struct OfficeLocale :
        public rtl::StaticWithInit<OUString, OfficeLocale> {
    const OUString operator () () {
        OUString slang(utl::ConfigManager::getLocale());
        //fallback, the locale is currently only set when the user starts the
        //office for the first time.
        if (slang.isEmpty())
            slang = "en-US";
        return slang;
    }
};

struct DeploymentResMgr : public rtl::StaticWithInit<
    ResMgr *, DeploymentResMgr> {
    ResMgr * operator () () {
        return ResMgr::CreateResMgr( "deployment", getOfficeLocale() );
    }
};

class theResourceMutex : public rtl::Static<osl::Mutex, theResourceMutex> {};

} // anon namespace

//==============================================================================
ResId getResId( sal_uInt16 id )
{
    const osl::MutexGuard guard( theResourceMutex::get() );
    return ResId( id, *DeploymentResMgr::get() );
}

//==============================================================================
String getResourceString( sal_uInt16 id )
{
    const osl::MutexGuard guard( theResourceMutex::get() );
    String ret( ResId( id, *DeploymentResMgr::get() ) );
    ret.SearchAndReplaceAllAscii(
        "%PRODUCTNAME", utl::ConfigManager::getProductName() );
    return ret;
}

//=============================================================================
::com::sun::star::lang::Locale toLocale( ::rtl::OUString const & slang )
{
    return LanguageTag( slang).getLocale();
}

//==============================================================================
lang::Locale getOfficeLocale()
{
    return toLocale(OfficeLocale::get());
}

::rtl::OUString getOfficeLocaleString()
{
    return OfficeLocale::get();
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/*
  Copyright (C) 2013-2015 The Communi Project

  You may use this file under the terms of BSD license as follows:

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

  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 HOLDERS 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 "bufferproxymodel.h"
#include "simplecrypt.h"
#include "zncmanager.h"
#include <QCoreApplication>
#include <QDataStream>
#include <QTimer>
#include <IrcCommandQueue>
#include <IrcBufferModel>
#include <IrcConnection>
#include <IrcBuffer>

IRC_USE_NAMESPACE

class IrcServerBuffer : public IrcBuffer
{
    Q_OBJECT

public:
    explicit IrcServerBuffer(QObject* parent = 0) : IrcBuffer(parent) { }
    ~IrcServerBuffer() { quit(tr("%1 %2").arg(qApp->applicationName(), qApp->applicationVersion())); }

public slots:
    void close(const QString& reason)
    {
        quit(reason);
        IrcBuffer::close(reason);
    }

private slots:
    void quit(const QString& reason)
    {
        IrcConnection* connection = IrcBuffer::connection();
        if (connection && connection->isActive()) {
            connection->quit(reason);
            connection->close();
        }
    }
};

BufferProxyModel::BufferProxyModel(QObject* parent) : RowsJoinerProxy(parent), m_method(Irc::SortByTitle)
{
}

IrcBuffer* BufferProxyModel::get(int index) const
{
    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            int count = model->count();
            if (index < count)
                return model->get(index);
            index -= count;
        }
    }
    return 0;
}

int BufferProxyModel::indexOf(IrcBuffer* buffer) const
{
    int count = 0;
    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            int index = model->indexOf(buffer);
            if (index != -1)
                return count + index;
            count += model->count();
        }
    }
    return -1;
}

QList<QObject*> BufferProxyModel::models() const
{
    return m_models;
}

QList<QObject*> BufferProxyModel::servers() const
{
    return m_servers;
}

QList<QObject*> BufferProxyModel::connections() const
{
    return m_connections;
}

IrcBuffer* BufferProxyModel::currentBuffer() const
{
    return m_current;
}

void BufferProxyModel::setCurrentBuffer(IrcBuffer* buffer)
{
    if (m_current != buffer) {
        m_current = buffer;
        emit currentBufferChanged(buffer);
    }
}

QObject* BufferProxyModel::model(IrcConnection* connection) const
{
    if (connection)
        return connection->findChild<IrcBufferModel*>();
    return 0;
}

QObject* BufferProxyModel::server(IrcConnection* connection) const
{
    if (connection)
        return connection->findChild<IrcServerBuffer*>();
    return 0;
}

void BufferProxyModel::addConnection(IrcConnection* connection)
{
    insertConnection(m_models.size(), connection);
}

void BufferProxyModel::insertConnection(int index, IrcConnection* connection)
{
    IrcBufferModel* model = new IrcBufferModel(connection);
    model->setSortMethod(static_cast<Irc::SortMethod>(m_method));
    connect(model, SIGNAL(added(IrcBuffer*)), this, SIGNAL(bufferAdded(IrcBuffer*)));
    connect(model, SIGNAL(removed(IrcBuffer*)), this, SIGNAL(bufferRemoved(IrcBuffer*)));
    connect(model, SIGNAL(aboutToBeAdded(IrcBuffer*)), this, SIGNAL(bufferAboutToBeAdded(IrcBuffer*)));
    connect(model, SIGNAL(aboutToBeRemoved(IrcBuffer*)), this, SIGNAL(bufferAboutToBeRemoved(IrcBuffer*)));

    ZncManager* znc = new ZncManager(model);
    znc->setModel(model);

    IrcCommandQueue* queue = new IrcCommandQueue(connection);
    queue->setConnection(connection);

    IrcServerBuffer* buffer = new IrcServerBuffer(model);
    connect(buffer, SIGNAL(destroyed(IrcBuffer*)), this, SLOT(closeConnection(IrcBuffer*)));
    buffer->setName(connection->displayName());
    buffer->setSticky(true);
    model->add(buffer);

    connection->setReconnectDelay(15); // TODO: settings?
    // give bouncers 2 seconds to start joining channels, otherwise a
    // non-bouncer connection is assumed and model state is restored
    model->setJoinDelay(2);

    connect(connection, SIGNAL(displayNameChanged(QString)), buffer, SLOT(setName(QString)));
    connect(model, SIGNAL(messageIgnored(IrcMessage*)), this, SLOT(processMessage(IrcMessage*)));

    connect(connection, SIGNAL(connected()), this, SLOT(onConnected()));
    connect(connection, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
    connect(connection, SIGNAL(nickNameRequired(QString,QString*)), this, SLOT(onNickNameRequired(QString)));
    connect(connection, SIGNAL(channelKeyRequired(QString,QString*)), this, SLOT(onChannelKeyRequired(QString)));

    m_connections.insert(index, connection);
    m_servers.insert(index, buffer);
    m_models.insert(index, model);

    emit connectionAdded(connection);
    emit connectionsChanged();
    emit serversChanged();
    emit modelsChanged();

    insertSourceModel(model, index);
}

void BufferProxyModel::removeConnection(IrcConnection* connection)
{
    disconnect(connection, SIGNAL(connected()), this, SLOT(onConnected()));
    disconnect(connection, SIGNAL(disconnected()), this, SLOT(onDisconnected()));

    IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
    if (model) {
        disconnect(model, SIGNAL(added(IrcBuffer*)), this, SIGNAL(bufferAdded(IrcBuffer*)));
        disconnect(model, SIGNAL(removed(IrcBuffer*)), this, SIGNAL(bufferRemoved(IrcBuffer*)));
        disconnect(model, SIGNAL(aboutToBeAdded(IrcBuffer*)), this, SIGNAL(bufferAboutToBeAdded(IrcBuffer*)));
        disconnect(model, SIGNAL(aboutToBeRemoved(IrcBuffer*)), this, SIGNAL(bufferAboutToBeRemoved(IrcBuffer*)));

        int index = m_connections.indexOf(connection);
        if (index != -1) {
            if (QObject* server = m_servers.takeAt(index))
                server->disconnect(this);
            if (QObject* model = m_models.takeAt(index))
                model->deleteLater();
            if (QObject* connection = m_connections.takeAt(index))
                connection->deleteLater();

            emit connectionRemoved(connection);
            emit connectionsChanged();
            emit serversChanged();
            emit modelsChanged();

            removeSourceModel(model);

            if (m_models.isEmpty())
                emit reseted();
        }
    }
}

QHash<int, QByteArray> BufferProxyModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[Qt::DisplayRole] = "display";
    roles[Qt::UserRole] = "section";
    roles[Irc::BufferRole] = "buffer";
    roles[Irc::ChannelRole] = "channel";
    roles[Irc::NameRole] = "name";
    roles[Irc::PrefixRole] = "prefix";
    roles[Irc::TitleRole] = "title";
    return roles;
}

QVariant BufferProxyModel::data(const QModelIndex& index, int role) const
{
    if (role == Qt::UserRole) {
        IrcBuffer* buffer = data(index, Irc::BufferRole).value<IrcBuffer*>();
        if (buffer)
            return m_connections.indexOf(buffer->connection()); // TODO: optimize
    }
    return RowsJoinerProxy::data(index, role);
}

QByteArray BufferProxyModel::saveState() const
{
    QVariantList modelStates;
    QVariantList connectionStates;
    SimpleCrypt crypto(Q_UINT64_C(0xff610ed9de767b09));

    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            IrcConnection* connection = model->connection();
            connectionStates += crypto.encryptToByteArray(connection->saveState());
            // do not save the server buffer - let addConnection() handle it when restoring
            model->remove(model->get(0));
            modelStates += crypto.encryptToByteArray(model->saveState());
        }
    }

    QVariantMap state;
    state.insert("models", modelStates);
    state.insert("connections", connectionStates);

    QByteArray data;
    QDataStream out(&data, QIODevice::WriteOnly);
    out << state;
    return data;
}

bool BufferProxyModel::restoreState(const QByteArray& data)
{
    QVariantMap state;
    QDataStream in(data);
    in >> state;
    if (in.status() != QDataStream::Ok)
        return false;

    QVariantList modelStates = state.value("models").toList();
    QVariantList connectionStates = state.value("connections").toList();
    SimpleCrypt crypto(Q_UINT64_C(0xff610ed9de767b09));

    for (int i = 0; i < connectionStates.length(); ++i) {
        IrcConnection* connection = new IrcConnection(this);
        QByteArray cs = crypto.decryptToByteArray(connectionStates.at(i).toByteArray());
        connection->restoreState(!crypto.lastError() ? cs : connectionStates.at(i).toByteArray());
        addConnection(connection);
        IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
        QByteArray ms = crypto.decryptToByteArray(modelStates.value(i).toByteArray());
        model->restoreState(!crypto.lastError() ? ms : modelStates.value(i).toByteArray());
    }
    return true;
}

int BufferProxyModel::sortMethod() const
{
    return m_method;
}

void BufferProxyModel::setSortMethod(int method)
{
    if (m_method != method) {
        m_method = method;
        foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
            IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
            if (model)
                model->setSortMethod(static_cast<Irc::SortMethod>(method));
        }
    }
}

void BufferProxyModel::onConnected()
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit connected(connection);
}

void BufferProxyModel::onDisconnected()
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit disconnected(connection);
}

void BufferProxyModel::closeConnection(IrcBuffer* buffer)
{
    removeConnection(buffer->connection());
}

void BufferProxyModel::onChannelKeyRequired(const QString& channel)
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit channelKeyRequired(connection, channel);
}

void BufferProxyModel::onNickNameRequired(const QString& reserved)
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit nickNameRequired(connection, reserved);
}

void BufferProxyModel::processMessage(IrcMessage* message)
{
    IrcBufferModel* model = message->connection()->findChild<IrcBufferModel*>();
    if (model) {
        // deliver targeted ChanServ notices to the target channel
        // ":ChanServ!ChanServ@services. NOTICE myself :[#channel] foo bar..."
        if (message->type() == IrcMessage::Notice) {
            if (message->prefix() == "ChanServ!ChanServ@services.") {
                QString content = static_cast<IrcNoticeMessage*>(message)->content();
                if (content.startsWith("[")) {
                    int i = content.indexOf("]");
                    if (i != -1) {
                        QString title = content.mid(1, i - 1);
                        IrcBuffer* buffer = model->find(title);
                        if (buffer) {
                            message->setProperty("forwarded", true);
                            buffer->receiveMessage(message);
                            return;
                        }
                    }
                }
            }
            if (m_current && m_current->model() == model) {
                m_current->receiveMessage(message);
                return;
            }
        }
        if (IrcBuffer* buffer = model->get(0))
            buffer->receiveMessage(message);
    }
}

#include "bufferproxymodel.moc"
<commit_msg>BufferProxymodel: Convert to new style connect/disconnect syntax<commit_after>/*
  Copyright (C) 2013-2015 The Communi Project

  You may use this file under the terms of BSD license as follows:

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

  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 HOLDERS 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 "bufferproxymodel.h"
#include "simplecrypt.h"
#include "zncmanager.h"
#include <QCoreApplication>
#include <QDataStream>
#include <QTimer>
#include <IrcCommandQueue>
#include <IrcBufferModel>
#include <IrcConnection>
#include <IrcBuffer>

IRC_USE_NAMESPACE

class IrcServerBuffer : public IrcBuffer
{
    Q_OBJECT

public:
    explicit IrcServerBuffer(QObject* parent = 0) : IrcBuffer(parent) { }
    ~IrcServerBuffer() { quit(tr("%1 %2").arg(qApp->applicationName(), qApp->applicationVersion())); }

public slots:
    void close(const QString& reason)
    {
        quit(reason);
        IrcBuffer::close(reason);
    }

private slots:
    void quit(const QString& reason)
    {
        IrcConnection* connection = IrcBuffer::connection();
        if (connection && connection->isActive()) {
            connection->quit(reason);
            connection->close();
        }
    }
};

BufferProxyModel::BufferProxyModel(QObject* parent) : RowsJoinerProxy(parent), m_method(Irc::SortByTitle)
{
}

IrcBuffer* BufferProxyModel::get(int index) const
{
    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            int count = model->count();
            if (index < count)
                return model->get(index);
            index -= count;
        }
    }
    return 0;
}

int BufferProxyModel::indexOf(IrcBuffer* buffer) const
{
    int count = 0;
    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            int index = model->indexOf(buffer);
            if (index != -1)
                return count + index;
            count += model->count();
        }
    }
    return -1;
}

QList<QObject*> BufferProxyModel::models() const
{
    return m_models;
}

QList<QObject*> BufferProxyModel::servers() const
{
    return m_servers;
}

QList<QObject*> BufferProxyModel::connections() const
{
    return m_connections;
}

IrcBuffer* BufferProxyModel::currentBuffer() const
{
    return m_current;
}

void BufferProxyModel::setCurrentBuffer(IrcBuffer* buffer)
{
    if (m_current != buffer) {
        m_current = buffer;
        emit currentBufferChanged(buffer);
    }
}

QObject* BufferProxyModel::model(IrcConnection* connection) const
{
    if (connection)
        return connection->findChild<IrcBufferModel*>();
    return 0;
}

QObject* BufferProxyModel::server(IrcConnection* connection) const
{
    if (connection)
        return connection->findChild<IrcServerBuffer*>();
    return 0;
}

void BufferProxyModel::addConnection(IrcConnection* connection)
{
    insertConnection(m_models.size(), connection);
}

void BufferProxyModel::insertConnection(int index, IrcConnection* connection)
{
    IrcBufferModel* model = new IrcBufferModel(connection);
    model->setSortMethod(static_cast<Irc::SortMethod>(m_method));
    connect(model, &IrcBufferModel::added, this, &BufferProxyModel::bufferAdded);
    connect(model, &IrcBufferModel::removed, this, &BufferProxyModel::bufferRemoved);
    connect(model, &IrcBufferModel::aboutToBeAdded, this, &BufferProxyModel::bufferAboutToBeAdded);
    connect(model, &IrcBufferModel::aboutToBeRemoved, this, &BufferProxyModel::bufferAboutToBeRemoved);

    ZncManager* znc = new ZncManager(model);
    znc->setModel(model);

    IrcCommandQueue* queue = new IrcCommandQueue(connection);
    queue->setConnection(connection);

    IrcServerBuffer* buffer = new IrcServerBuffer(model);
    connect(buffer, &IrcServerBuffer::destroyed, this, &BufferProxyModel::closeConnection);
    buffer->setName(connection->displayName());
    buffer->setSticky(true);
    model->add(buffer);

    connection->setReconnectDelay(15); // TODO: settings?
    // give bouncers 2 seconds to start joining channels, otherwise a
    // non-bouncer connection is assumed and model state is restored
    model->setJoinDelay(2);

    connect(connection, &IrcConnection::displayNameChanged, buffer, &IrcServerBuffer::setName);
    connect(model, &IrcBufferModel::messageIgnored, this, &BufferProxyModel::processMessage);

    connect(connection, &IrcConnection::connected, this, &BufferProxyModel::onConnected);
    connect(connection, &IrcConnection::disconnected, this, &BufferProxyModel::onDisconnected);
    connect(connection, &IrcConnection::nickNameRequired, this, &BufferProxyModel::onNickNameRequired);
    connect(connection, &IrcConnection::channelKeyRequired, this, &BufferProxyModel::onChannelKeyRequired);

    m_connections.insert(index, connection);
    m_servers.insert(index, buffer);
    m_models.insert(index, model);

    emit connectionAdded(connection);
    emit connectionsChanged();
    emit serversChanged();
    emit modelsChanged();

    insertSourceModel(model, index);
}

void BufferProxyModel::removeConnection(IrcConnection* connection)
{
    disconnect(connection, &IrcConnection::connected, this, &BufferProxyModel::onConnected);
    disconnect(connection, &IrcConnection::disconnected, this, &BufferProxyModel::onDisconnected);

    IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
    if (model) {
        disconnect(model, &IrcBufferModel::added, this, &BufferProxyModel::bufferAdded);
        disconnect(model, &IrcBufferModel::removed, this, &BufferProxyModel::bufferRemoved);
        disconnect(model, &IrcBufferModel::aboutToBeAdded, this, &BufferProxyModel::bufferAboutToBeAdded);
        disconnect(model, &IrcBufferModel::aboutToBeRemoved, this, &BufferProxyModel::bufferAboutToBeRemoved);

        int index = m_connections.indexOf(connection);
        if (index != -1) {
            if (QObject* server = m_servers.takeAt(index))
                server->disconnect(this);
            if (QObject* model = m_models.takeAt(index))
                model->deleteLater();
            if (QObject* connection = m_connections.takeAt(index))
                connection->deleteLater();

            emit connectionRemoved(connection);
            emit connectionsChanged();
            emit serversChanged();
            emit modelsChanged();

            removeSourceModel(model);

            if (m_models.isEmpty())
                emit reseted();
        }
    }
}

QHash<int, QByteArray> BufferProxyModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[Qt::DisplayRole] = "display";
    roles[Qt::UserRole] = "section";
    roles[Irc::BufferRole] = "buffer";
    roles[Irc::ChannelRole] = "channel";
    roles[Irc::NameRole] = "name";
    roles[Irc::PrefixRole] = "prefix";
    roles[Irc::TitleRole] = "title";
    return roles;
}

QVariant BufferProxyModel::data(const QModelIndex& index, int role) const
{
    if (role == Qt::UserRole) {
        IrcBuffer* buffer = data(index, Irc::BufferRole).value<IrcBuffer*>();
        if (buffer)
            return m_connections.indexOf(buffer->connection()); // TODO: optimize
    }
    return RowsJoinerProxy::data(index, role);
}

QByteArray BufferProxyModel::saveState() const
{
    QVariantList modelStates;
    QVariantList connectionStates;
    SimpleCrypt crypto(Q_UINT64_C(0xff610ed9de767b09));

    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            IrcConnection* connection = model->connection();
            connectionStates += crypto.encryptToByteArray(connection->saveState());
            // do not save the server buffer - let addConnection() handle it when restoring
            model->remove(model->get(0));
            modelStates += crypto.encryptToByteArray(model->saveState());
        }
    }

    QVariantMap state;
    state.insert("models", modelStates);
    state.insert("connections", connectionStates);

    QByteArray data;
    QDataStream out(&data, QIODevice::WriteOnly);
    out << state;
    return data;
}

bool BufferProxyModel::restoreState(const QByteArray& data)
{
    QVariantMap state;
    QDataStream in(data);
    in >> state;
    if (in.status() != QDataStream::Ok)
        return false;

    QVariantList modelStates = state.value("models").toList();
    QVariantList connectionStates = state.value("connections").toList();
    SimpleCrypt crypto(Q_UINT64_C(0xff610ed9de767b09));

    for (int i = 0; i < connectionStates.length(); ++i) {
        IrcConnection* connection = new IrcConnection(this);
        QByteArray cs = crypto.decryptToByteArray(connectionStates.at(i).toByteArray());
        connection->restoreState(!crypto.lastError() ? cs : connectionStates.at(i).toByteArray());
        addConnection(connection);
        IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
        QByteArray ms = crypto.decryptToByteArray(modelStates.value(i).toByteArray());
        model->restoreState(!crypto.lastError() ? ms : modelStates.value(i).toByteArray());
    }
    return true;
}

int BufferProxyModel::sortMethod() const
{
    return m_method;
}

void BufferProxyModel::setSortMethod(int method)
{
    if (m_method != method) {
        m_method = method;
        foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
            IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
            if (model)
                model->setSortMethod(static_cast<Irc::SortMethod>(method));
        }
    }
}

void BufferProxyModel::onConnected()
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit connected(connection);
}

void BufferProxyModel::onDisconnected()
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit disconnected(connection);
}

void BufferProxyModel::closeConnection(IrcBuffer* buffer)
{
    removeConnection(buffer->connection());
}

void BufferProxyModel::onChannelKeyRequired(const QString& channel)
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit channelKeyRequired(connection, channel);
}

void BufferProxyModel::onNickNameRequired(const QString& reserved)
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit nickNameRequired(connection, reserved);
}

void BufferProxyModel::processMessage(IrcMessage* message)
{
    IrcBufferModel* model = message->connection()->findChild<IrcBufferModel*>();
    if (model) {
        // deliver targeted ChanServ notices to the target channel
        // ":ChanServ!ChanServ@services. NOTICE myself :[#channel] foo bar..."
        if (message->type() == IrcMessage::Notice) {
            if (message->prefix() == "ChanServ!ChanServ@services.") {
                QString content = static_cast<IrcNoticeMessage*>(message)->content();
                if (content.startsWith("[")) {
                    int i = content.indexOf("]");
                    if (i != -1) {
                        QString title = content.mid(1, i - 1);
                        IrcBuffer* buffer = model->find(title);
                        if (buffer) {
                            message->setProperty("forwarded", true);
                            buffer->receiveMessage(message);
                            return;
                        }
                    }
                }
            }
            if (m_current && m_current->model() == model) {
                m_current->receiveMessage(message);
                return;
            }
        }
        if (IrcBuffer* buffer = model->get(0))
            buffer->receiveMessage(message);
    }
}

#include "bufferproxymodel.moc"
<|endoftext|>


<commit_before><commit_msg>Remove debug from the runnin_monitoring_to_rrd live unit test.<commit_after><|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS vclcompact (1.8.262); FILE MERGED 2004/12/14 16:32:37 ssa 1.8.262.1: hide Window members (pImpl)<commit_after><|endoftext|>


<commit_before>// __BEGIN_LICENSE__
//  Copyright (c) 2009-2013, United States Government as represented by the
//  Administrator of the National Aeronautics and Space Administration. All
//  rights reserved.
//
//  The NGT platform is licensed under the Apache License, Version 2.0 (the
//  "License"); you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// __END_LICENSE__

/// \file stereo_fltr.cc
///
#include <asp/Tools/stereo.h>
#include <vw/Stereo/DisparityMap.h>

#include <asp/Core/BlobIndexThreaded.h>
#include <asp/Core/InpaintView.h>
#include <asp/Core/ErodeView.h>
#include <asp/Core/ThreadedEdgeMask.h>

using namespace vw;
using namespace asp;
using namespace std;

namespace vw {
  template<> struct PixelFormatID<PixelMask<Vector<float, 5> > >   { static const PixelFormatEnum value = VW_PIXEL_GENERIC_6_CHANNEL; };
}

// Erode blobs from given image by iterating through tiles, biasing
// each tile by a factor of blob size, removing blobs in the tile,
// then shrinking the tile back. The bias is necessary to help avoid
// fragmenting (and then unnecessarily removing) blobs.
template <class ImageT>
class PerTileErode: public ImageViewBase<PerTileErode<ImageT> >{
  ImageT m_img;
public:
  PerTileErode( ImageViewBase<ImageT>   const& img):
    m_img(img.impl()){}

  // Image View interface
  typedef typename ImageT::pixel_type pixel_type;
  typedef pixel_type result_type;
  typedef ProceduralPixelAccessor<PerTileErode> pixel_accessor;

  inline int32 cols  () const { return m_img.cols(); }
  inline int32 rows  () const { return m_img.rows(); }
  inline int32 planes() const { return 1; }

  inline pixel_accessor origin() const { return pixel_accessor( *this, 0, 0 ); }

  inline pixel_type operator()( double /*i*/, double /*j*/, int32 /*p*/ = 0 ) const {
    vw_throw(NoImplErr() << "PerTileErode::operator()(...) is not implemented");
    return pixel_type();
  }

  typedef CropView<ImageView<pixel_type> > prerasterize_type;
  inline prerasterize_type prerasterize(BBox2i const& bbox) const {

    int area = stereo_settings().erode_max_size;

    // We look a beyond the current tile, to avoid cutting blobs
    // if possible. Skinny blobs will be cut though.
    int bias = 2*int(ceil(sqrt(double(area))));

    BBox2i bbox2 = bbox;
    bbox2.expand(bias);
    bbox2.crop(bounding_box(m_img));
    ImageView<pixel_type> tile_img = crop(m_img, bbox2);

    int tile_size = max(bbox2.width(), bbox2.height()); // don't subsplit
    BlobIndexThreaded smallBlobIndex(tile_img, area, tile_size);
    ImageView<pixel_type> clean_tile_img = applyErodeView(tile_img,
                                                          smallBlobIndex);
    return prerasterize_type(clean_tile_img,
                             -bbox2.min().x(), -bbox2.min().y(),
                             cols(), rows() );
  }

  template <class DestT>
  inline void rasterize(DestT const& dest, BBox2i bbox) const {
    vw::rasterize(prerasterize(bbox), dest, bbox);
  }
};

template <class ImageT>
PerTileErode<ImageT>
per_tile_erode( ImageViewBase<ImageT> const& img) {
  typedef PerTileErode<ImageT> return_type;
  return return_type( img.impl() );
}

// Run several cleanup passes with desired cleanup mode.
template <class ViewT>
struct MultipleDisparityCleanUp {
  typedef ImageViewRef< typename ViewT::pixel_type > result_type;

  inline result_type operator()( ImageViewBase<ViewT> const& input, int N) {

    result_type out = input;
    for (int i = 0; i < N; i++){
      int mode = stereo_settings().filter_mode;
      if (mode == 1){
        out = stereo::disparity_cleanup_using_mean
          (out.impl(),
           stereo_settings().rm_half_kernel.x(),
           stereo_settings().rm_half_kernel.y(),
           stereo_settings().max_mean_diff);
      }else if (mode == 2){
        out = stereo::disparity_cleanup_using_thresh
          (out.impl(),
           stereo_settings().rm_half_kernel.x(),
           stereo_settings().rm_half_kernel.y(),
           stereo_settings().rm_threshold,
           stereo_settings().rm_min_matches/100.0);
      }else
        vw_throw( ArgumentErr() << "\nExpecting value of 1 or 2 for filter-mode. "
                  << "Got: " << mode << "\n" );
    }

    return out;
  }
};

template <class ImageT>
void write_good_pixel_and_filtered( ImageViewBase<ImageT> const& inputview,
                                    Options const& opt ) {
  // Write Good Pixel Map
  // Sub-sampling so that the user can actually view it.
  float sub_scale = float( min( inputview.impl().cols(),
                                inputview.impl().rows() ) ) / 2048.0;
  if (sub_scale < 1) // Don't use a sub_scale less than one.
    sub_scale = 1;

  // Determine if we can attach geo information to the output image
  bool has_georef = (  (opt.session->uses_map_projected_inputs()) &&
                       (opt.input_dem != ""));
  vw::cartography::GeoReference left_georef, good_pixel_georef;
  if (has_georef) {
    if (!read_georeference(left_georef, opt.in_file1))
      has_georef = false;
    // Account for sub_scale
    good_pixel_georef = resample(left_georef, sub_scale);
  }

  // Write out the good pixel map
  std::string goodPixelFile = opt.out_prefix + "-GoodPixelMap.tif";
  vw_out() << "Writing: " << goodPixelFile << std::endl;
  asp::block_write_gdal_image
    ( goodPixelFile,
      subsample
      (apply_mask
       (copy_mask
        (stereo::missing_pixel_image(inputview.impl()),
         create_mask(DiskImageView<vw::uint8>(opt.out_prefix+"-lMask.tif"), 0)
         )
        ),
       sub_scale
       ),
      has_georef, good_pixel_georef,
      false, 0, // Not using nodata
      opt, TerminalProgressCallback("asp", "\t--> Good pixel map: ") );

  bool removeSmallBlobs = (stereo_settings().erode_max_size > 0);

  string outF = opt.out_prefix + "-F.tif";

  // Fill holes
  if(stereo_settings().enable_fill_holes) {
    // Generate a list of blobs below a maximum size
    // - This requires the entire input image to be read in
    //    and produces a single blob list for the entire image.
    vw_out() << "\t--> Filling holes with inpainting method.\n";
    BlobIndexThreaded smallHoleIndex( invert_mask( inputview.impl() ),
                                      stereo_settings().fill_hole_max_size,
                                      vw::vw_settings().default_tile_size(),
                                      vw::vw_settings().default_num_threads()
                                      );
    vw_out() << "\t    * Identified " << smallHoleIndex.num_blobs() << " holes\n";
    bool use_grassfire = true;
    typename ImageT::pixel_type default_inpaint_val;

    if (!removeSmallBlobs) { // Skip small blob removal
      // Write out the image to disk, filling in the blobs in the process
      vw_out() << "Writing: " << outF << endl;
      asp::block_write_gdal_image( outF,
                                   inpaint(inputview.impl(), smallHoleIndex,
                                           use_grassfire, default_inpaint_val),
                                   opt, TerminalProgressCallback
                                   ("asp","\t--> Filtering: ") );
    }
    else { // Add small blob removal step
      // Write out the image to disk, filling in and removing blobs in the process
      // - Blob removal is done second to make sure inner-blob holes are removed.
      vw_out() << "Writing: " << outF << endl;
      asp::block_write_gdal_image( outF,
                                   per_tile_erode
                                   (inpaint(inputview.impl(),
                                            smallHoleIndex,
                                            use_grassfire,
                                            default_inpaint_val)
                                    ), opt, TerminalProgressCallback
                                   ("asp","\t--> Filtering: ") );
    }

  } else { // No hole filling
    if (!removeSmallBlobs) { // Skip small blob removal
      vw_out() << "Writing: " << outF << endl;
      asp::block_write_gdal_image( outF, inputview.impl(), opt,
                                   TerminalProgressCallback
                                   ("asp", "\t--> Filtering: ") );
    }
    else { // Add small blob removal step
      vw_out() << "\t--> Removing small blobs.\n";
      // Write out the image to disk, removing the blobs in the process
      vw_out() << "Writing: " << outF << endl;
      asp::block_write_gdal_image(outF, per_tile_erode(inputview.impl()),
                                  opt, TerminalProgressCallback
                                  ("asp","\t--> Filtering: ") );
    }

  } // End no hole filling case
}

void stereo_filtering( Options& opt ) {

  string post_correlation_fname;
  opt.session->pre_filtering_hook(opt.out_prefix+"-RD.tif",
                                  post_correlation_fname);

  try {

    // Rasterize the results so far to a temporary file on disk.
    // This file is deleted once we complete the second half of the
    // disparity map filtering process.

    // Apply filtering for high frequencies
    typedef DiskImageView<PixelMask<Vector2f> > input_type;
    input_type disparity_disk_image(post_correlation_fname);

    // Applying additional clipping from the edge. We make new
    // mask files to avoid a weird and tricky segfault due to
    // ownership issues.
    DiskImageView<vw::uint8> left_mask ( opt.out_prefix+"-lMask.tif" );
    DiskImageView<vw::uint8> right_mask( opt.out_prefix+"-rMask.tif" );
    int32 mask_buffer = max( stereo_settings().subpixel_kernel );

    vw_out() << "\t--> Cleaning up disparity map prior to filtering processes ("
             << stereo_settings().rm_cleanup_passes << " pass).\n";

    // If the user wants to do no filtering at all, that amounts
    // to doing no passes.
    if (stereo_settings().filter_mode == 0)
      stereo_settings().rm_cleanup_passes = 0;

    if ( stereo_settings().mask_flatfield ) {
      ImageViewRef<PixelMask<Vector2f> > filtered_disparity;
      if ( stereo_settings().rm_cleanup_passes >= 1 )
      {
        filtered_disparity =
          stereo::disparity_mask
          (MultipleDisparityCleanUp<input_type>()
           (disparity_disk_image, stereo_settings().rm_cleanup_passes),
           apply_mask(asp::threaded_edge_mask(left_mask, 0,mask_buffer,1024)),
           apply_mask(asp::threaded_edge_mask(right_mask,0,mask_buffer,1024)));
      }
      else { // No cleanup passes
        filtered_disparity =
          stereo::disparity_mask
          (disparity_disk_image,
           apply_mask(asp::threaded_edge_mask(left_mask, 0,mask_buffer,1024)),
           apply_mask(asp::threaded_edge_mask(right_mask,0,mask_buffer,1024)));
      }

      // This is only turned on for apollo. Blob detection doesn't
      // work too great when tracking a whole lot of spots. HiRISE
      // seems to keep breaking this so I've keep it turned off.
      //
      // The crash happens inside Boost Graph when dealing with
      // large number of blobs.
      BlobIndexThreaded bindex( filtered_disparity,
                                stereo_settings().erode_max_size,
                                vw::vw_settings().default_tile_size(),
                                vw::vw_settings().default_num_threads()
                                );
      vw_out() << "\t    * Eroding " << bindex.num_blobs() << " islands\n";
      write_good_pixel_and_filtered
        ( ErodeView<ImageViewRef<PixelMask<Vector2f> > >(filtered_disparity,
                                                         bindex ), opt );
    } else {
      // No Erosion step
      if ( stereo_settings().rm_cleanup_passes >= 1 ) {
        // Apply an outlier removal filter
        write_good_pixel_and_filtered
          (stereo::disparity_mask
            (MultipleDisparityCleanUp<input_type>()
              (disparity_disk_image, stereo_settings().rm_cleanup_passes),
               apply_mask(asp::threaded_edge_mask(left_mask, 0,mask_buffer,1024)),
               apply_mask(asp::threaded_edge_mask(right_mask,0,mask_buffer,1024))),
             opt);
      }
      else { // No cleanup passes
        write_good_pixel_and_filtered
          (stereo::disparity_mask
            (disparity_disk_image,
              apply_mask(asp::threaded_edge_mask(left_mask, 0,mask_buffer,1024)),
              apply_mask(asp::threaded_edge_mask(right_mask,0,mask_buffer,1024))),
            opt);
      } // End cleanup passes check
    } // End mask_flatfield check

  } catch (IOErr const& e) {
    vw_throw( ArgumentErr() << "\nUnable to start at filtering stage -- could not read input files.\n"
              << e.what() << "\nExiting.\n\n" );
  }
} // end write_good_pixel_and_filtered

int main(int argc, char* argv[]) {

  try {

    vw_out() << "\n[ " << current_posix_time_string()
             << " ] : Stage 3 --> FILTERING \n";

    // This is probably the right place in which to warn the user about
    // new hole filling behavior.
    vw_out(WarningMessage)
      << "Hole-filling is disabled by default in stereo_fltr. "
      << "It is suggested to use instead point2dem's analogous "
      << "functionality. It can be re-enabled using "
      << "--enable-fill-holes." << endl;

    stereo_register_sessions();

    bool verbose = false;
    vector<Options> opt_vec;
    string output_prefix;
    asp::parse_multiview(argc, argv, FilteringDescription(),
                         verbose, output_prefix, opt_vec);
    Options opt = opt_vec[0];

    // Internal Processes
    //---------------------------------------------------------
    stereo_filtering( opt );

    vw_out() << "\n[ " << current_posix_time_string()
             << " ] : FILTERING FINISHED \n";

  } ASP_STANDARD_CATCHES;

  return 0;
}
<commit_msg>stereo_fltr: Fix for good pixel map<commit_after>// __BEGIN_LICENSE__
//  Copyright (c) 2009-2013, United States Government as represented by the
//  Administrator of the National Aeronautics and Space Administration. All
//  rights reserved.
//
//  The NGT platform is licensed under the Apache License, Version 2.0 (the
//  "License"); you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// __END_LICENSE__

/// \file stereo_fltr.cc
///
#include <asp/Tools/stereo.h>
#include <vw/Stereo/DisparityMap.h>

#include <asp/Core/BlobIndexThreaded.h>
#include <asp/Core/InpaintView.h>
#include <asp/Core/ErodeView.h>
#include <asp/Core/ThreadedEdgeMask.h>

using namespace vw;
using namespace asp;
using namespace std;

namespace vw {
  template<> struct PixelFormatID<PixelMask<Vector<float, 5> > >   { static const PixelFormatEnum value = VW_PIXEL_GENERIC_6_CHANNEL; };
}

// Erode blobs from given image by iterating through tiles, biasing
// each tile by a factor of blob size, removing blobs in the tile,
// then shrinking the tile back. The bias is necessary to help avoid
// fragmenting (and then unnecessarily removing) blobs.
template <class ImageT>
class PerTileErode: public ImageViewBase<PerTileErode<ImageT> >{
  ImageT m_img;
public:
  PerTileErode( ImageViewBase<ImageT>   const& img):
    m_img(img.impl()){}

  // Image View interface
  typedef typename ImageT::pixel_type pixel_type;
  typedef pixel_type result_type;
  typedef ProceduralPixelAccessor<PerTileErode> pixel_accessor;

  inline int32 cols  () const { return m_img.cols(); }
  inline int32 rows  () const { return m_img.rows(); }
  inline int32 planes() const { return 1; }

  inline pixel_accessor origin() const { return pixel_accessor( *this, 0, 0 ); }

  inline pixel_type operator()( double /*i*/, double /*j*/, int32 /*p*/ = 0 ) const {
    vw_throw(NoImplErr() << "PerTileErode::operator()(...) is not implemented");
    return pixel_type();
  }

  typedef CropView<ImageView<pixel_type> > prerasterize_type;
  inline prerasterize_type prerasterize(BBox2i const& bbox) const {

    int area = stereo_settings().erode_max_size;

    // We look a beyond the current tile, to avoid cutting blobs
    // if possible. Skinny blobs will be cut though.
    int bias = 2*int(ceil(sqrt(double(area))));

    BBox2i bbox2 = bbox;
    bbox2.expand(bias);
    bbox2.crop(bounding_box(m_img));
    ImageView<pixel_type> tile_img = crop(m_img, bbox2);

    int tile_size = max(bbox2.width(), bbox2.height()); // don't subsplit
    BlobIndexThreaded smallBlobIndex(tile_img, area, tile_size);
    ImageView<pixel_type> clean_tile_img = applyErodeView(tile_img,
                                                          smallBlobIndex);
    return prerasterize_type(clean_tile_img,
                             -bbox2.min().x(), -bbox2.min().y(),
                             cols(), rows() );
  }

  template <class DestT>
  inline void rasterize(DestT const& dest, BBox2i bbox) const {
    vw::rasterize(prerasterize(bbox), dest, bbox);
  }
};

template <class ImageT>
PerTileErode<ImageT>
per_tile_erode( ImageViewBase<ImageT> const& img) {
  typedef PerTileErode<ImageT> return_type;
  return return_type( img.impl() );
}

// Run several cleanup passes with desired cleanup mode.
template <class ViewT>
struct MultipleDisparityCleanUp {
  typedef ImageViewRef< typename ViewT::pixel_type > result_type;

  inline result_type operator()( ImageViewBase<ViewT> const& input, int N) {

    result_type out = input;
    for (int i = 0; i < N; i++){
      int mode = stereo_settings().filter_mode;
      if (mode == 1){
        out = stereo::disparity_cleanup_using_mean
          (out.impl(),
           stereo_settings().rm_half_kernel.x(),
           stereo_settings().rm_half_kernel.y(),
           stereo_settings().max_mean_diff);
      }else if (mode == 2){
        out = stereo::disparity_cleanup_using_thresh
          (out.impl(),
           stereo_settings().rm_half_kernel.x(),
           stereo_settings().rm_half_kernel.y(),
           stereo_settings().rm_threshold,
           stereo_settings().rm_min_matches/100.0);
      }else
        vw_throw( ArgumentErr() << "\nExpecting value of 1 or 2 for filter-mode. "
                  << "Got: " << mode << "\n" );
    }

    return out;
  }
};

template <class ImageT>
void write_good_pixel_and_filtered( ImageViewBase<ImageT> const& inputview,
                                    Options const& opt ) {
  // Write Good Pixel Map
  // Sub-sampling so that the user can actually view it.
  double sub_scale = double( min( inputview.impl().cols(),
                                inputview.impl().rows() ) ) / 2048.0;
  if (sub_scale < 1) // Don't use a sub_scale less than one.
    sub_scale = 1;

  // Write out the good pixel map
  std::string goodPixelFile = opt.out_prefix + "-GoodPixelMap.tif";
  vw_out() << "Writing: " << goodPixelFile << std::endl;
  ImageViewRef<  PixelRGB<uint8> > goodPixelImage
    = subsample(apply_mask
                (copy_mask
                 (stereo::missing_pixel_image(inputview.impl()),
                  create_mask(DiskImageView<vw::uint8>(opt.out_prefix+"-lMask.tif"), 0)
                  )
                 ), sub_scale);

  // Determine if we can attach geo information to the output image
  bool has_georef = (  (opt.session->uses_map_projected_inputs()) &&
                       (opt.input_dem != ""));
  vw::cartography::GeoReference left_georef, good_pixel_georef;
  if (has_georef) {
    if (!read_georeference(left_georef, opt.in_file1))
      has_georef = false;
    // Account for scale. Note that goodPixelImage is not guaranteed to respect
    // the sub_scale factor above, hence this calculation.
    double good_pixel_scale = 0.5*( double(goodPixelImage.cols())/inputview.impl().cols()
                                    + double(goodPixelImage.rows())/inputview.impl().rows());
    good_pixel_georef = resample(left_georef, good_pixel_scale);
  }

  asp::block_write_gdal_image
    ( goodPixelFile, goodPixelImage, has_georef, good_pixel_georef,
      false, 0, // Not using nodata
      opt, TerminalProgressCallback("asp", "\t--> Good pixel map: ") );

  bool removeSmallBlobs = (stereo_settings().erode_max_size > 0);

  string outF = opt.out_prefix + "-F.tif";

  // Fill holes
  if(stereo_settings().enable_fill_holes) {
    // Generate a list of blobs below a maximum size
    // - This requires the entire input image to be read in
    //    and produces a single blob list for the entire image.
    vw_out() << "\t--> Filling holes with inpainting method.\n";
    BlobIndexThreaded smallHoleIndex( invert_mask( inputview.impl() ),
                                      stereo_settings().fill_hole_max_size,
                                      vw::vw_settings().default_tile_size(),
                                      vw::vw_settings().default_num_threads()
                                      );
    vw_out() << "\t    * Identified " << smallHoleIndex.num_blobs() << " holes\n";
    bool use_grassfire = true;
    typename ImageT::pixel_type default_inpaint_val;

    if (!removeSmallBlobs) { // Skip small blob removal
      // Write out the image to disk, filling in the blobs in the process
      vw_out() << "Writing: " << outF << endl;
      asp::block_write_gdal_image( outF,
                                   inpaint(inputview.impl(), smallHoleIndex,
                                           use_grassfire, default_inpaint_val),
                                   opt, TerminalProgressCallback
                                   ("asp","\t--> Filtering: ") );
    }
    else { // Add small blob removal step
      // Write out the image to disk, filling in and removing blobs in the process
      // - Blob removal is done second to make sure inner-blob holes are removed.
      vw_out() << "Writing: " << outF << endl;
      asp::block_write_gdal_image( outF,
                                   per_tile_erode
                                   (inpaint(inputview.impl(),
                                            smallHoleIndex,
                                            use_grassfire,
                                            default_inpaint_val)
                                    ), opt, TerminalProgressCallback
                                   ("asp","\t--> Filtering: ") );
    }

  } else { // No hole filling
    if (!removeSmallBlobs) { // Skip small blob removal
      vw_out() << "Writing: " << outF << endl;
      asp::block_write_gdal_image( outF, inputview.impl(), opt,
                                   TerminalProgressCallback
                                   ("asp", "\t--> Filtering: ") );
    }
    else { // Add small blob removal step
      vw_out() << "\t--> Removing small blobs.\n";
      // Write out the image to disk, removing the blobs in the process
      vw_out() << "Writing: " << outF << endl;
      asp::block_write_gdal_image(outF, per_tile_erode(inputview.impl()),
                                  opt, TerminalProgressCallback
                                  ("asp","\t--> Filtering: ") );
    }

  } // End no hole filling case
}

void stereo_filtering( Options& opt ) {

  string post_correlation_fname;
  opt.session->pre_filtering_hook(opt.out_prefix+"-RD.tif",
                                  post_correlation_fname);

  try {

    // Rasterize the results so far to a temporary file on disk.
    // This file is deleted once we complete the second half of the
    // disparity map filtering process.

    // Apply filtering for high frequencies
    typedef DiskImageView<PixelMask<Vector2f> > input_type;
    input_type disparity_disk_image(post_correlation_fname);

    // Applying additional clipping from the edge. We make new
    // mask files to avoid a weird and tricky segfault due to
    // ownership issues.
    DiskImageView<vw::uint8> left_mask ( opt.out_prefix+"-lMask.tif" );
    DiskImageView<vw::uint8> right_mask( opt.out_prefix+"-rMask.tif" );
    int32 mask_buffer = max( stereo_settings().subpixel_kernel );

    vw_out() << "\t--> Cleaning up disparity map prior to filtering processes ("
             << stereo_settings().rm_cleanup_passes << " pass).\n";

    // If the user wants to do no filtering at all, that amounts
    // to doing no passes.
    if (stereo_settings().filter_mode == 0)
      stereo_settings().rm_cleanup_passes = 0;

    if ( stereo_settings().mask_flatfield ) {
      ImageViewRef<PixelMask<Vector2f> > filtered_disparity;
      if ( stereo_settings().rm_cleanup_passes >= 1 )
      {
        filtered_disparity =
          stereo::disparity_mask
          (MultipleDisparityCleanUp<input_type>()
           (disparity_disk_image, stereo_settings().rm_cleanup_passes),
           apply_mask(asp::threaded_edge_mask(left_mask, 0,mask_buffer,1024)),
           apply_mask(asp::threaded_edge_mask(right_mask,0,mask_buffer,1024)));
      }
      else { // No cleanup passes
        filtered_disparity =
          stereo::disparity_mask
          (disparity_disk_image,
           apply_mask(asp::threaded_edge_mask(left_mask, 0,mask_buffer,1024)),
           apply_mask(asp::threaded_edge_mask(right_mask,0,mask_buffer,1024)));
      }

      // This is only turned on for apollo. Blob detection doesn't
      // work too great when tracking a whole lot of spots. HiRISE
      // seems to keep breaking this so I've keep it turned off.
      //
      // The crash happens inside Boost Graph when dealing with
      // large number of blobs.
      BlobIndexThreaded bindex( filtered_disparity,
                                stereo_settings().erode_max_size,
                                vw::vw_settings().default_tile_size(),
                                vw::vw_settings().default_num_threads()
                                );
      vw_out() << "\t    * Eroding " << bindex.num_blobs() << " islands\n";
      write_good_pixel_and_filtered
        ( ErodeView<ImageViewRef<PixelMask<Vector2f> > >(filtered_disparity,
                                                         bindex ), opt );
    } else {
      // No Erosion step
      if ( stereo_settings().rm_cleanup_passes >= 1 ) {
        // Apply an outlier removal filter
        write_good_pixel_and_filtered
          (stereo::disparity_mask
            (MultipleDisparityCleanUp<input_type>()
              (disparity_disk_image, stereo_settings().rm_cleanup_passes),
               apply_mask(asp::threaded_edge_mask(left_mask, 0,mask_buffer,1024)),
               apply_mask(asp::threaded_edge_mask(right_mask,0,mask_buffer,1024))),
             opt);
      }
      else { // No cleanup passes
        write_good_pixel_and_filtered
          (stereo::disparity_mask
            (disparity_disk_image,
              apply_mask(asp::threaded_edge_mask(left_mask, 0,mask_buffer,1024)),
              apply_mask(asp::threaded_edge_mask(right_mask,0,mask_buffer,1024))),
            opt);
      } // End cleanup passes check
    } // End mask_flatfield check

  } catch (IOErr const& e) {
    vw_throw( ArgumentErr() << "\nUnable to start at filtering stage -- could not read input files.\n"
              << e.what() << "\nExiting.\n\n" );
  }
} // end write_good_pixel_and_filtered

int main(int argc, char* argv[]) {

  try {

    vw_out() << "\n[ " << current_posix_time_string()
             << " ] : Stage 3 --> FILTERING \n";

    // This is probably the right place in which to warn the user about
    // new hole filling behavior.
    vw_out(WarningMessage)
      << "Hole-filling is disabled by default in stereo_fltr. "
      << "It is suggested to use instead point2dem's analogous "
      << "functionality. It can be re-enabled using "
      << "--enable-fill-holes." << endl;

    stereo_register_sessions();

    bool verbose = false;
    vector<Options> opt_vec;
    string output_prefix;
    asp::parse_multiview(argc, argv, FilteringDescription(),
                         verbose, output_prefix, opt_vec);
    Options opt = opt_vec[0];

    // Internal Processes
    //---------------------------------------------------------
    stereo_filtering( opt );

    vw_out() << "\n[ " << current_posix_time_string()
             << " ] : FILTERING FINISHED \n";

  } ASP_STANDARD_CATCHES;

  return 0;
}
<|endoftext|>


<commit_before>#include "rest_server.h"

#include <sstream>
#include <cstring>
#include <iomanip>
#include <vector>

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>

#include "json_stream.h"
#include "rest_exceptions.h"
#include "database.h"
#include "document.h"
#include "script_object_stream.h"

#include "libscriptobject_gason.h"

#define REGEX_DBNAME R"(_?[a-z][a-z0-9_\$\+\-\(\)]+)"
#define REGEX_DBNAME_GROUP "/(?<db>" REGEX_DBNAME ")"

#define REGEX_DOCID R"([a-zA-Z0-9\$\+\-\(\)\:\.\~][a-zA-Z0-9_\$\+\-\(\)\:\.\~]*)"
#define REGEX_DOCID_GROUP "/(?<id>" REGEX_DOCID ")"

RestServer::RestServer() {
    AddRoute("HEAD", REGEX_DBNAME_GROUP "/?", &RestServer::HeadDatabase);
    AddRoute("HEAD", REGEX_DBNAME_GROUP REGEX_DOCID_GROUP, &RestServer::HeadDocument);

    AddRoute("DELETE", REGEX_DBNAME_GROUP "/?/?", &RestServer::DeleteDatabase);
    AddRoute("DELETE", REGEX_DBNAME_GROUP REGEX_DOCID_GROUP, &RestServer::DeleteDocument);

    AddRoute("PUT", REGEX_DBNAME_GROUP REGEX_DOCID_GROUP, &RestServer::PutDocument);
    AddRoute("PUT", REGEX_DBNAME_GROUP "/?", &RestServer::PutDatabase);

    AddRoute("GET", "/_active_tasks", &RestServer::GetActiveTasks);
    AddRoute("GET", "/_uuids", &RestServer::GetUuids);
    AddRoute("GET", "/_session", &RestServer::GetSession);
    AddRoute("GET", "/_all_dbs", &RestServer::GetAllDbs);
    AddRoute("GET", REGEX_DBNAME_GROUP REGEX_DOCID_GROUP, &RestServer::GetDocument);
    AddRoute("GET", REGEX_DBNAME_GROUP "/_all_docs", &RestServer::GetDatabaseAllDocs);
    AddRoute("GET", REGEX_DBNAME_GROUP "/?", &RestServer::GetDatabase);
    AddRoute("GET", "/_config/query_servers/?", &RestServer::GetConfigQueryServers);
    AddRoute("GET", "/_config/native_query_servers/?", &RestServer::GetConfigNativeQueryServers);
    AddRoute("GET", "/", &RestServer::GetSignature);

    databases_.AddDatabase("_replicator");
    databases_.AddDatabase("_users");
}

void RestServer::AddRoute(const char* method, const char* re, Callback func) {
    router_.Add(method, re, boost::bind(func, this, _1, _2, _3));
}

void RestServer::RouteRequest(rs::httpserver::socket_ptr, rs::httpserver::request_ptr request, rs::httpserver::response_ptr response) {
    router_.Match(request, response);

    if (!response->HasResponded()) {
        response->setContentType("text/plain").setStatusCode(404).setStatusDescription("Not Found").Send(R"({"error":"not_found","reason":"no_db_file"})");
    }
}

bool RestServer::GetActiveTasks(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send("[]");
    return true;
}

bool RestServer::GetSession(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send(R"({"ok":true,"userCtx":{"name":null,"roles":["_admin"]},"info":{"authentication_db":"_users","authentication_handlers":["oauth","cookie","default"],"authenticated":"default"}})");
    return true;
}

bool RestServer::GetAllDbs(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    auto dbs = databases_.GetDatabases();

    std::stringstream stream;
    stream << "[";
    for (int i = 0; i < dbs.size(); ++i) {
        stream << (i > 0 ? "," : "") << "\"" << dbs[i] << "\"";
    }
    stream << "]";

    response->setContentType("application/json").Send(stream.str());
    return true;
}

bool RestServer::GetSignature(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send(R"({"couchdb":"Welcome","avancedb":"Welcome","uuid":"a2db86472466bcd02e84ac05a6c86185","version":"1.6.1","vendor":{"version":"0.0.1","name":"Ripcord Software"}})");
    return true;
}

bool RestServer::GetUuids(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    int count = 1;

    if (request->getQueryString().IsKey("count")) {
        auto countParam = request->getQueryString().getValue("count");
        if (countParam.length() > 0) {
            count = boost::lexical_cast<int>(countParam);
        }
    }

    if (count > 1000) {
        throw UuidCountLimit();
    }

    std::stringstream stream;
    stream << std::hex << std::setfill('0') << "{\"uuids\":[";

    boost::uuids::random_generator gen;
    for (int i = 0; i < count; ++i) {
        stream << (i > 0 ? "," : "") << "\"";

        auto uuid = gen();
        for (auto iter = uuid.begin(); iter != uuid.end(); ++iter) {
            stream << std::setw(2) << static_cast<unsigned>(*iter);
        }

        stream << "\"";
    }

    stream << "]}";

    response->setContentType("application/javascript").Send(stream.str());
    return true;
}

bool RestServer::HeadDatabase(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    auto iter = args.find("db");

    bool found = iter != args.cend() && databases_.IsDatabase(iter->second);
    if (found) {
        response->setContentType("application/json").Send();
    }

    return found;
}

bool RestServer::GetDatabase(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    auto argsIter = args.find("db");

    bool found = argsIter != args.cend();
    if (found) {
        auto db = databases_.GetDatabase(argsIter->second);

        found = !!db;
        if (found) {
            JsonStream stream;
            stream.Append("committed_update_seq", db->CommitedUpdateSequence());
            stream.Append("compact_running", false);
            stream.Append("data_size", db->DataSize());
            stream.Append("db_name", argsIter->second);
            stream.Append("disk_format_version", 6);
            stream.Append("disk_size", db->DiskSize());
            stream.Append("doc_count", db->DocCount());
            stream.Append("doc_del_count", db->DocDelCount());
            stream.Append("instance_start_time", db->InstanceStartTime());
            stream.Append("purge_seq", db->PurgeSequence());
            stream.Append("update_seq", db->UpdateSequence());

            response->setContentType("application/json").Send(stream.Flush());
        } else {
            throw MissingDatabase();
        }
    }

    return found;
}

bool RestServer::PutDatabase(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool created = false;
    auto argsIter = args.find("db");

    if (argsIter != args.cend()) {
        auto name = argsIter->second;

        if (std::strlen(name) <= 0 || name[0] == '_') {
            throw InvalidDatabaseName();
        }

        if (databases_.IsDatabase(name)) {
            throw DatabaseAlreadyExists();
        }

        created = databases_.AddDatabase(name);

        if (created) {
            response->setStatusCode(201).setContentType("application/json").Send(R"({"ok":true})");
        }
    }

    return created;
}

bool RestServer::GetDocument(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool gotDoc = false;
    auto db = GetDatabase(args);
    if (!!db) {
        auto id = GetParameter("id", args);

        auto doc = db->GetDocument(id);
        auto obj = doc->getObject();

        auto rev = doc->getRev();
        auto json = ScriptObjectStream::Serialize(obj);
        response->setStatusCode(200).setContentType("application/json").setETag(rev).Send(json);

        gotDoc = true;
    }

    return gotDoc;
}

bool RestServer::PutDocument(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool created = false;
    auto db = GetDatabase(args);
    if (!!db) {
        auto id = GetParameter("id", args);
        auto obj = GetJsonBody(request);

        if (!!obj) {
            auto doc = db->SetDocument(id, obj);

            auto rev = doc->getRev();

            JsonStream stream;
            stream.Append("ok", true);
            stream.Append("id", doc->getId());
            stream.Append("rev", rev);

            response->setStatusCode(201).setContentType("application/json").setETag(rev).Send(stream.Flush());

            created = true;
        }
    }
    return created;
}

bool RestServer::DeleteDocument(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool deleted = false;
    auto db = GetDatabase(args);
    if (!!db) {
        auto id = GetParameter("id", args);
        auto rev = GetParameter("rev", request->getQueryString(), false);

        auto doc = db->DeleteDocument(id, rev);

        rev = doc->getRev();

        JsonStream stream;
        stream.Append("ok", true);
        stream.Append("id", doc->getId());
        stream.Append("rev", rev);

        response->setStatusCode(200).setContentType("application/json").setETag(rev).Send(stream.Flush());

        deleted = true;
    }

    return deleted;
}

bool RestServer::HeadDocument(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool gotHead = false;
    auto db = GetDatabase(args);
    if (!!db) {
        auto id = GetParameter("id", args);

        auto doc = db->GetDocument(id);
        auto rev = doc->getRev();

        response->setStatusCode(200).setContentType("application/json").setETag(rev).Send();

        gotHead = true;
    }

    return gotHead;
}

bool RestServer::DeleteDatabase(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool deleted = false;
    auto argsIter = args.find("db");

    if (argsIter != args.cend()) {
        auto name = argsIter->second;

        if (std::strlen(name) <= 0 || name[0] == '_') {
            throw InvalidDatabaseName();
        }

        deleted = databases_.RemoveDatabase(argsIter->second);

        if (deleted) {
            response->setContentType("application/json").Send(R"({"ok":true})");
        } else {
            throw MissingDatabase();
        }
    }

    return deleted;
}

bool RestServer::GetDatabaseAllDocs(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send(R"({"offset":0,"rows":[],"total_rows":0})");
}

bool RestServer::GetConfigQueryServers(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send(R"({"javascript":"libjsapi"})");
}

bool RestServer::GetConfigNativeQueryServers(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send("{}");
}

database_ptr RestServer::GetDatabase(const rs::httpserver::RequestRouter::CallbackArgs& args) {
    database_ptr db;
    auto argsIter = args.find("db");
    if (argsIter != args.cend()) {
        auto dbName = argsIter->second;

        db = databases_.GetDatabase(dbName);
    }
    return db;
}

const char* RestServer::GetDatabaseName(const rs::httpserver::RequestRouter::CallbackArgs& args) {
    const char* dbName = nullptr;
    auto argsIter = args.find("db");
    if (argsIter != args.cend()) {
        dbName = argsIter->second;
    }
    return dbName;
}

const char* RestServer::GetParameter(const char* param, const rs::httpserver::RequestRouter::CallbackArgs& args) {
    auto argsIter = args.find(param);
    if (argsIter == args.cend()) {
        throw InvalidJson();
    }

    return argsIter->second;
}

const char* RestServer::GetParameter(const char* param, const rs::httpserver::QueryString& qs, bool throwIfMissing) {
    if (throwIfMissing && !qs.IsKey(param)) {
        // TODO: this isn't the best exception message to return, however there are no obvious alterates that CouchDB uses
        throw InvalidJson();
    }

    return qs.getValue(param).c_str();
}

rs::scriptobject::ScriptObjectPtr RestServer::GetJsonBody(rs::httpserver::request_ptr request) {
    if (request->HasBody()) {
        if (request->getContentType().find("application/json") == std::string::npos) {
            throw InvalidJson();
        }

        auto& requestStream = request->getRequestStream();
        auto requestLength = request->getContentLength();

        std::vector<rs::httpserver::RequestStream::byte> buffer(requestLength);

        decltype(requestLength) offset = 0;
        while (offset < requestLength) {
            auto remaining = requestLength - offset;
            auto bytesRead = requestStream.Read(&buffer[offset], 0, remaining, false);

            if (bytesRead <= 0) {
                throw InvalidJson();
            }

            offset += bytesRead;
        }

        auto json = reinterpret_cast<char*>(buffer.data());

        try {
            rs::scriptobject::ScriptObjectJsonSource source(json);
            return rs::scriptobject::ScriptObjectFactory::CreateObject(source);
        } catch (const std::exception&) {
            throw InvalidJson();
        }
    } else {
        return rs::scriptobject::ScriptObjectPtr(nullptr);
    }
}<commit_msg>Use GetDatabaseName() in rest_server instead of pulling it out of the args each time<commit_after>#include "rest_server.h"

#include <sstream>
#include <cstring>
#include <iomanip>
#include <vector>

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>

#include "json_stream.h"
#include "rest_exceptions.h"
#include "database.h"
#include "document.h"
#include "script_object_stream.h"

#include "libscriptobject_gason.h"

#define REGEX_DBNAME R"(_?[a-z][a-z0-9_\$\+\-\(\)]+)"
#define REGEX_DBNAME_GROUP "/(?<db>" REGEX_DBNAME ")"

#define REGEX_DOCID R"([a-zA-Z0-9\$\+\-\(\)\:\.\~][a-zA-Z0-9_\$\+\-\(\)\:\.\~]*)"
#define REGEX_DOCID_GROUP "/(?<id>" REGEX_DOCID ")"

RestServer::RestServer() {
    AddRoute("HEAD", REGEX_DBNAME_GROUP "/?", &RestServer::HeadDatabase);
    AddRoute("HEAD", REGEX_DBNAME_GROUP REGEX_DOCID_GROUP, &RestServer::HeadDocument);

    AddRoute("DELETE", REGEX_DBNAME_GROUP "/?/?", &RestServer::DeleteDatabase);
    AddRoute("DELETE", REGEX_DBNAME_GROUP REGEX_DOCID_GROUP, &RestServer::DeleteDocument);

    AddRoute("PUT", REGEX_DBNAME_GROUP REGEX_DOCID_GROUP, &RestServer::PutDocument);
    AddRoute("PUT", REGEX_DBNAME_GROUP "/?", &RestServer::PutDatabase);

    AddRoute("GET", "/_active_tasks", &RestServer::GetActiveTasks);
    AddRoute("GET", "/_uuids", &RestServer::GetUuids);
    AddRoute("GET", "/_session", &RestServer::GetSession);
    AddRoute("GET", "/_all_dbs", &RestServer::GetAllDbs);
    AddRoute("GET", REGEX_DBNAME_GROUP REGEX_DOCID_GROUP, &RestServer::GetDocument);
    AddRoute("GET", REGEX_DBNAME_GROUP "/_all_docs", &RestServer::GetDatabaseAllDocs);
    AddRoute("GET", REGEX_DBNAME_GROUP "/?", &RestServer::GetDatabase);
    AddRoute("GET", "/_config/query_servers/?", &RestServer::GetConfigQueryServers);
    AddRoute("GET", "/_config/native_query_servers/?", &RestServer::GetConfigNativeQueryServers);
    AddRoute("GET", "/", &RestServer::GetSignature);

    databases_.AddDatabase("_replicator");
    databases_.AddDatabase("_users");
}

void RestServer::AddRoute(const char* method, const char* re, Callback func) {
    router_.Add(method, re, boost::bind(func, this, _1, _2, _3));
}

void RestServer::RouteRequest(rs::httpserver::socket_ptr, rs::httpserver::request_ptr request, rs::httpserver::response_ptr response) {
    router_.Match(request, response);

    if (!response->HasResponded()) {
        response->setContentType("text/plain").setStatusCode(404).setStatusDescription("Not Found").Send(R"({"error":"not_found","reason":"no_db_file"})");
    }
}

bool RestServer::GetActiveTasks(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send("[]");
    return true;
}

bool RestServer::GetSession(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send(R"({"ok":true,"userCtx":{"name":null,"roles":["_admin"]},"info":{"authentication_db":"_users","authentication_handlers":["oauth","cookie","default"],"authenticated":"default"}})");
    return true;
}

bool RestServer::GetAllDbs(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    auto dbs = databases_.GetDatabases();

    std::stringstream stream;
    stream << "[";
    for (int i = 0; i < dbs.size(); ++i) {
        stream << (i > 0 ? "," : "") << "\"" << dbs[i] << "\"";
    }
    stream << "]";

    response->setContentType("application/json").Send(stream.str());
    return true;
}

bool RestServer::GetSignature(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send(R"({"couchdb":"Welcome","avancedb":"Welcome","uuid":"a2db86472466bcd02e84ac05a6c86185","version":"1.6.1","vendor":{"version":"0.0.1","name":"Ripcord Software"}})");
    return true;
}

bool RestServer::GetUuids(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    int count = 1;

    if (request->getQueryString().IsKey("count")) {
        auto countParam = request->getQueryString().getValue("count");
        if (countParam.length() > 0) {
            count = boost::lexical_cast<int>(countParam);
        }
    }

    if (count > 1000) {
        throw UuidCountLimit();
    }

    std::stringstream stream;
    stream << std::hex << std::setfill('0') << "{\"uuids\":[";

    boost::uuids::random_generator gen;
    for (int i = 0; i < count; ++i) {
        stream << (i > 0 ? "," : "") << "\"";

        auto uuid = gen();
        for (auto iter = uuid.begin(); iter != uuid.end(); ++iter) {
            stream << std::setw(2) << static_cast<unsigned>(*iter);
        }

        stream << "\"";
    }

    stream << "]}";

    response->setContentType("application/javascript").Send(stream.str());
    return true;
}

bool RestServer::HeadDatabase(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool found = false;

    auto name = GetDatabaseName(args);
    if (name != nullptr && databases_.IsDatabase(name)) {
        response->setContentType("application/json").Send();
        found = true;
    }

    return found;
}

bool RestServer::GetDatabase(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool found = false;

    auto name = GetDatabaseName(args);
    if (name != nullptr) {
        auto db = databases_.GetDatabase(name);

        found = !!db;
        if (found) {
            JsonStream stream;
            stream.Append("committed_update_seq", db->CommitedUpdateSequence());
            stream.Append("compact_running", false);
            stream.Append("data_size", db->DataSize());
            stream.Append("db_name", name);
            stream.Append("disk_format_version", 6);
            stream.Append("disk_size", db->DiskSize());
            stream.Append("doc_count", db->DocCount());
            stream.Append("doc_del_count", db->DocDelCount());
            stream.Append("instance_start_time", db->InstanceStartTime());
            stream.Append("purge_seq", db->PurgeSequence());
            stream.Append("update_seq", db->UpdateSequence());

            response->setContentType("application/json").Send(stream.Flush());
        } else {
            throw MissingDatabase();
        }
    }

    return found;
}

bool RestServer::PutDatabase(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool created = false;

    auto name = GetDatabaseName(args);
    if (name != nullptr) {
        if (std::strlen(name) <= 0 || name[0] == '_') {
            throw InvalidDatabaseName();
        }

        if (databases_.IsDatabase(name)) {
            throw DatabaseAlreadyExists();
        }

        created = databases_.AddDatabase(name);

        if (created) {
            response->setStatusCode(201).setContentType("application/json").Send(R"({"ok":true})");
        }
    }

    return created;
}

bool RestServer::DeleteDatabase(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool deleted = false;

    auto name = GetDatabaseName(args);
    if (name != nullptr) {
        if (std::strlen(name) <= 0 || name[0] == '_') {
            throw InvalidDatabaseName();
        }

        deleted = databases_.RemoveDatabase(name);

        if (deleted) {
            response->setContentType("application/json").Send(R"({"ok":true})");
        } else {
            throw MissingDatabase();
        }
    }

    return deleted;
}

bool RestServer::GetDocument(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool gotDoc = false;
    auto db = GetDatabase(args);
    if (!!db) {
        auto id = GetParameter("id", args);

        auto doc = db->GetDocument(id);
        auto obj = doc->getObject();

        auto rev = doc->getRev();
        auto json = ScriptObjectStream::Serialize(obj);
        response->setStatusCode(200).setContentType("application/json").setETag(rev).Send(json);

        gotDoc = true;
    }

    return gotDoc;
}

bool RestServer::PutDocument(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool created = false;
    auto db = GetDatabase(args);
    if (!!db) {
        auto id = GetParameter("id", args);
        auto obj = GetJsonBody(request);

        if (!!obj) {
            auto doc = db->SetDocument(id, obj);

            auto rev = doc->getRev();

            JsonStream stream;
            stream.Append("ok", true);
            stream.Append("id", doc->getId());
            stream.Append("rev", rev);

            response->setStatusCode(201).setContentType("application/json").setETag(rev).Send(stream.Flush());

            created = true;
        }
    }
    return created;
}

bool RestServer::DeleteDocument(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool deleted = false;
    auto db = GetDatabase(args);
    if (!!db) {
        auto id = GetParameter("id", args);
        auto rev = GetParameter("rev", request->getQueryString(), false);

        auto doc = db->DeleteDocument(id, rev);

        rev = doc->getRev();

        JsonStream stream;
        stream.Append("ok", true);
        stream.Append("id", doc->getId());
        stream.Append("rev", rev);

        response->setStatusCode(200).setContentType("application/json").setETag(rev).Send(stream.Flush());

        deleted = true;
    }

    return deleted;
}

bool RestServer::HeadDocument(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    bool gotHead = false;
    auto db = GetDatabase(args);
    if (!!db) {
        auto id = GetParameter("id", args);

        auto doc = db->GetDocument(id);
        auto rev = doc->getRev();

        response->setStatusCode(200).setContentType("application/json").setETag(rev).Send();

        gotHead = true;
    }

    return gotHead;
}

bool RestServer::GetDatabaseAllDocs(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs& args, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send(R"({"offset":0,"rows":[],"total_rows":0})");
}

bool RestServer::GetConfigQueryServers(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send(R"({"javascript":"libjsapi"})");
}

bool RestServer::GetConfigNativeQueryServers(rs::httpserver::request_ptr request, const rs::httpserver::RequestRouter::CallbackArgs&, rs::httpserver::response_ptr response) {
    response->setContentType("application/json").Send("{}");
}

database_ptr RestServer::GetDatabase(const rs::httpserver::RequestRouter::CallbackArgs& args) {
    database_ptr db;
    auto dbName = GetDatabaseName(args);
    if (dbName != nullptr) {
        db = databases_.GetDatabase(dbName);
    }
    return db;
}

const char* RestServer::GetDatabaseName(const rs::httpserver::RequestRouter::CallbackArgs& args) {
    const char* dbName = nullptr;
    auto argsIter = args.find("db");
    if (argsIter != args.cend()) {
        dbName = argsIter->second;
    }
    return dbName;
}

const char* RestServer::GetParameter(const char* param, const rs::httpserver::RequestRouter::CallbackArgs& args) {
    auto argsIter = args.find(param);
    if (argsIter == args.cend()) {
        throw InvalidJson();
    }

    return argsIter->second;
}

const char* RestServer::GetParameter(const char* param, const rs::httpserver::QueryString& qs, bool throwIfMissing) {
    if (throwIfMissing && !qs.IsKey(param)) {
        // TODO: this isn't the best exception message to return, however there are no obvious alterates that CouchDB uses
        throw InvalidJson();
    }

    return qs.getValue(param).c_str();
}

rs::scriptobject::ScriptObjectPtr RestServer::GetJsonBody(rs::httpserver::request_ptr request) {
    if (request->HasBody()) {
        if (request->getContentType().find("application/json") == std::string::npos) {
            throw InvalidJson();
        }

        auto& requestStream = request->getRequestStream();
        auto requestLength = request->getContentLength();

        std::vector<rs::httpserver::RequestStream::byte> buffer(requestLength);

        decltype(requestLength) offset = 0;
        while (offset < requestLength) {
            auto remaining = requestLength - offset;
            auto bytesRead = requestStream.Read(&buffer[offset], 0, remaining, false);

            if (bytesRead <= 0) {
                throw InvalidJson();
            }

            offset += bytesRead;
        }

        auto json = reinterpret_cast<char*>(buffer.data());

        try {
            rs::scriptobject::ScriptObjectJsonSource source(json);
            return rs::scriptobject::ScriptObjectFactory::CreateObject(source);
        } catch (const std::exception&) {
            throw InvalidJson();
        }
    } else {
        return rs::scriptobject::ScriptObjectPtr(nullptr);
    }
}<|endoftext|>


<commit_before>// Copyright 2014 MongoDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <type_traits>

#include <bsoncxx/config/prelude.hpp>

namespace bsoncxx {
BSONCXX_INLINE_NAMESPACE_BEGIN
namespace util {

// TODO(MSVC): VS2015U1 Completely falls apart trying to honor the
// simple definition of is_functor since is_convertible returns the
// wrong results for std::function, so we fall back to a bunch of
// other template metaprogramming there.

#if !defined(_MSC_VER)

template <typename FunctionLike, typename Signature>
using is_functor = std::is_convertible<FunctionLike, std::function<Signature>>;

#else

namespace functor {

template <typename, typename>
struct build_free_function;

template <typename F, typename R, typename... Args>
struct build_free_function<F, R(Args...)> {
    typedef R (*type)(Args...);
};

template <typename, typename>
struct build_class_function;

template <typename C, typename R, typename... Args>
struct build_class_function<C, R(Args...)> {
    typedef R (C::*type)(Args...);
};

template <typename>
struct strip_cv_from_class_function;

template <typename C, typename R, typename... Args>
struct strip_cv_from_class_function<R (C::*)(Args...)> {
    typedef R (C::*type)(Args...);
};

template <typename C, typename R, typename... Args>
struct strip_cv_from_class_function<R (C::*)(Args...) const> {
    typedef R (C::*type)(Args...);
};

template <typename C, typename R, typename... Args>
struct strip_cv_from_class_function<R (C::*)(Args...) volatile> {
    typedef R (C::*type)(Args...);
};

template <typename C, typename S>
struct is_class_method_with_signature {
    typedef int yes;
    typedef char no;

    // T stands for SFINAE
    template <typename T>
    static typename std::enable_if<std::is_convertible<typename build_class_function<C, S>::type,
                                                       typename strip_cv_from_class_function<
                                                           decltype(&T::operator())>::type>::value,
                                   yes>::type
    sfinae(void*);

    template <typename>
    static no sfinae(...);

    static bool constexpr value = sizeof(sfinae<C>(nullptr)) == sizeof(yes);
};

template <typename F, typename S>
struct is_function_with_signature
    : std::is_convertible<F, typename build_free_function<F, S>::type> {};

template <typename C, typename S, bool>
struct is_functor_impl : is_class_method_with_signature<C, S> {};

template <typename F, typename S>
struct is_functor_impl<F, S, false> : is_function_with_signature<F, S> {};

}  // namespace functor

template <typename C, typename S>
struct is_functor : functor::is_functor_impl<C, S, std::is_class<C>::value> {};

#endif

}  // namespace util
BSONCXX_INLINE_NAMESPACE_END
}  // namespace bsoncxx

#include <bsoncxx/config/postlude.hpp>
<commit_msg>CXX-1460 Add missing functional header<commit_after>// Copyright 2014 MongoDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <functional>
#include <type_traits>

#include <bsoncxx/config/prelude.hpp>

namespace bsoncxx {
BSONCXX_INLINE_NAMESPACE_BEGIN
namespace util {

// TODO(MSVC): VS2015U1 Completely falls apart trying to honor the
// simple definition of is_functor since is_convertible returns the
// wrong results for std::function, so we fall back to a bunch of
// other template metaprogramming there.

#if !defined(_MSC_VER)

template <typename FunctionLike, typename Signature>
using is_functor = std::is_convertible<FunctionLike, std::function<Signature>>;

#else

namespace functor {

template <typename, typename>
struct build_free_function;

template <typename F, typename R, typename... Args>
struct build_free_function<F, R(Args...)> {
    typedef R (*type)(Args...);
};

template <typename, typename>
struct build_class_function;

template <typename C, typename R, typename... Args>
struct build_class_function<C, R(Args...)> {
    typedef R (C::*type)(Args...);
};

template <typename>
struct strip_cv_from_class_function;

template <typename C, typename R, typename... Args>
struct strip_cv_from_class_function<R (C::*)(Args...)> {
    typedef R (C::*type)(Args...);
};

template <typename C, typename R, typename... Args>
struct strip_cv_from_class_function<R (C::*)(Args...) const> {
    typedef R (C::*type)(Args...);
};

template <typename C, typename R, typename... Args>
struct strip_cv_from_class_function<R (C::*)(Args...) volatile> {
    typedef R (C::*type)(Args...);
};

template <typename C, typename S>
struct is_class_method_with_signature {
    typedef int yes;
    typedef char no;

    // T stands for SFINAE
    template <typename T>
    static typename std::enable_if<std::is_convertible<typename build_class_function<C, S>::type,
                                                       typename strip_cv_from_class_function<
                                                           decltype(&T::operator())>::type>::value,
                                   yes>::type
    sfinae(void*);

    template <typename>
    static no sfinae(...);

    static bool constexpr value = sizeof(sfinae<C>(nullptr)) == sizeof(yes);
};

template <typename F, typename S>
struct is_function_with_signature
    : std::is_convertible<F, typename build_free_function<F, S>::type> {};

template <typename C, typename S, bool>
struct is_functor_impl : is_class_method_with_signature<C, S> {};

template <typename F, typename S>
struct is_functor_impl<F, S, false> : is_function_with_signature<F, S> {};

}  // namespace functor

template <typename C, typename S>
struct is_functor : functor::is_functor_impl<C, S, std::is_class<C>::value> {};

#endif

}  // namespace util
BSONCXX_INLINE_NAMESPACE_END
}  // namespace bsoncxx

#include <bsoncxx/config/postlude.hpp>
<|endoftext|>