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

#include <limits>

#include <QtTest/QtTest>

#include "person.h"
#include "parser.h"
#include "serializer.h"

#include <QtCore/QVariant>

class TestSerializer: public QObject
{
  Q_OBJECT
  private slots:
    void testReadWriteEmptyDocument();
    void testReadWrite();
    void testReadWrite_data();
    void testValueNull();
    void testValueString();
    void testValueString_data();
    void testValueInteger();
    void testValueInteger_data();
    void testValueDouble();
    void testValueDouble_data();
    void testValueBoolean();
    void testValueBoolean_data();
    void testValueQObject();

  private:
    void valueTest( const QVariant& value, const QString& expectedRegExp );
    void valueTest( const QObject* object, const QString& expectedRegExp );
};

Q_DECLARE_METATYPE(QVariant)

using namespace QJson;

void TestSerializer::testReadWriteEmptyDocument()
{
  QByteArray json = "";
  Parser parser;
  bool ok;
  QVariant result = parser.parse( json, &ok );
  QVERIFY(ok);
  QVERIFY( ! result.isValid() );
  Serializer serializer;
  const QByteArray serialized = serializer.serialize( result );
  QVERIFY( !serialized.isNull() );
  QByteArray expected = "null";
  QCOMPARE(expected, serialized);
}

void TestSerializer::testReadWrite()
{
  QFETCH( QByteArray, json );
  Parser parser;
  bool ok;
  QVariant result = parser.parse( json, &ok );
  QVERIFY(ok);
  Serializer serializer;
  const QByteArray serialized = serializer.serialize( result );
  QVariant writtenThenRead = parser.parse( serialized, &ok );
  QVERIFY(ok);
  QCOMPARE( result, writtenThenRead );
}

void TestSerializer::testReadWrite_data()
{
    QTest::addColumn<QByteArray>( "json" );

    // array tests
    QTest::newRow( "empty array" ) << QByteArray("[]");
    QTest::newRow( "basic array" ) << QByteArray("[\"person\",\"bar\"]");
    QTest::newRow( "single int array" ) << QByteArray("[6]");
    QTest::newRow( "int array" ) << QByteArray("[6,5,6,7]");
    const QByteArray json = "[1,2.4, -100, -3.4, -5e+, 2e,3e+,4.3E,5.4E-]";
    QTest::newRow( QByteArray("array of various numbers") ) << json;

    // document tests
    QTest::newRow( "empty object" ) << QByteArray("{}");
    QTest::newRow( "basic document" ) << QByteArray("{\"person\":\"bar\"}");
    QTest::newRow( "object with ints" ) << QByteArray("{\"person\":6}");
    const QByteArray json2 = "{ \"person\":\"bar\",\n\"number\" : 51.3 , \"array\":[\"item1\", 123]}";
    QTest::newRow( "complicated document" ) << json2;

    // more complex cases
    const QByteArray json3 = "[ {\"person\":\"bar\"},\n\"number\",51.3 , [\"item1\", 123]]";
    QTest::newRow( "complicated array" ) << json3;
}

void TestSerializer::valueTest( const QVariant& value, const QString& expectedRegExp )
{
  Serializer serializer;
  const QByteArray serialized = serializer.serialize( value );
  const QString serializedUnicode = QString::fromUtf8( serialized );
  QRegExp expected( expectedRegExp );
  QVERIFY( expected.isValid() );
  QVERIFY2( expected.exactMatch( serializedUnicode ),
    qPrintable( QString( QLatin1String( "Expected regexp \"%1\" but got \"%2\"." ) )
      .arg( expectedRegExp ).arg( serializedUnicode ) ) );
}

void TestSerializer::valueTest( const QObject* object, const QString& expectedRegExp )
{
  Serializer serializer;
  const QByteArray serialized = serializer.serialize( object );
  const QString serializedUnicode = QString::fromUtf8( serialized );
  QRegExp expected( expectedRegExp );
  QVERIFY( expected.isValid() );
  QVERIFY2( expected.exactMatch( serializedUnicode ),
    qPrintable( QString( QLatin1String( "Expected regexp \"%1\" but got \"%2\"." ) )
      .arg( expectedRegExp ).arg( serializedUnicode ) ) );
}

void TestSerializer::testValueNull()
{
  valueTest( QVariant(), QLatin1String( "\\s*null\\s*" ) );
  QVariantMap map;
  map[QLatin1String("value")] = QVariant();
  valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:\\s*null\\s*\\}\\s*" ) );
}

void TestSerializer::testValueString()
{
  QFETCH( QVariant, value );
  QFETCH( QString, expected );
  valueTest( value, expected );

  QVariantMap map;
  map[QLatin1String("value")] = value;
  valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) );
}

void TestSerializer::testValueString_data()
{
  QTest::addColumn<QVariant>( "value" );
  QTest::addColumn<QString>( "expected" );

  QTest::newRow( "null string" ) << QVariant( QString() ) << QString( QLatin1String( "\\s*\"\"\\s*" ) );
  QTest::newRow( "empty string" ) << QVariant( QString( QLatin1String( "" ) ) ) << QString( QLatin1String( "\\s*\"\"\\s*" ) );
  QTest::newRow( "Simple String" ) << QVariant( QString( QLatin1String( "simpleString" ) ) ) << QString( QLatin1String( "\\s*\"simpleString\"\\s*" ) );
  QTest::newRow( "string with tab" ) << QVariant( QString( QLatin1String( "string\tstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\tstring\"\\s*" ) );
  QTest::newRow( "string with newline" ) << QVariant( QString( QLatin1String( "string\nstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\nstring\"\\s*" ) );
  QTest::newRow( "string with bell" ) << QVariant( QString( QLatin1String( "string\bstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\bstring\"\\s*" ) );
  QTest::newRow( "string with return" ) << QVariant( QString( QLatin1String( "string\rstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\rstring\"\\s*" ) );
  QTest::newRow( "string with double quote" ) << QVariant( QString( QLatin1String( "string\"string" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\\"string\"\\s*" ) );
  QTest::newRow( "string with backslash" ) << QVariant( QString( QLatin1String( "string\\string" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\\\\\string\"\\s*" ) );
  QString testStringWithUnicode = QString( QLatin1String( "string" ) ) + QChar( 0x2665 ) + QLatin1String( "string" );
  QTest::newRow( "string with unicode" ) << QVariant( testStringWithUnicode ) << QLatin1String( "\\s*\"" ) + testStringWithUnicode + QLatin1String( "\"\\s*" );
}

void TestSerializer::testValueInteger()
{
  QFETCH( QVariant, value );
  QFETCH( QString, expected );
  valueTest( value, expected );

  QVariantMap map;
  map[QLatin1String("value")] = value;
  valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) );
}

void TestSerializer::testValueInteger_data()
{
  QTest::addColumn<QVariant>( "value" );
  QTest::addColumn<QString>( "expected" );

  QTest::newRow( "int 0" ) << QVariant( static_cast<int>( 0 ) ) << QString( QLatin1String( "\\s*0\\s*" ) );
  QTest::newRow( "uint 0" ) << QVariant( static_cast<uint>( 0 ) ) << QString( QLatin1String( "\\s*0\\s*" ) );
  QTest::newRow( "int -1" ) << QVariant( static_cast<int>( -1 ) ) << QString( QLatin1String( "\\s*-1\\s*" ) );
  QTest::newRow( "int 2133149800" ) << QVariant( static_cast<int>(2133149800) ) << QString( QLatin1String( "\\s*2133149800\\s*" ) );
  QTest::newRow( "uint 4133149800" ) << QVariant( static_cast<uint>(4133149800u) ) << QString( QLatin1String( "\\s*4133149800\\s*" ) );
  QTest::newRow( "uint64 932838457459459" ) << QVariant( Q_UINT64_C(932838457459459) ) << QString( QLatin1String( "\\s*932838457459459\\s*" ) );
  QTest::newRow( "max unsigned long long" ) << QVariant( std::numeric_limits<unsigned long long>::max() ) << QString( QLatin1String( "\\s*%1\\s*" ) ).arg(std::numeric_limits<unsigned long long>::max());
}

void TestSerializer::testValueDouble()
{
  QFETCH( QVariant, value );
  QFETCH( QString, expected );
  valueTest( value, expected );

  QVariantMap map;
  map[QLatin1String("value")] = value;
  valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) );
}

void TestSerializer::testValueDouble_data()
{
  QTest::addColumn<QVariant>( "value" );
  QTest::addColumn<QString>( "expected" );

  QTest::newRow( "double 0" ) << QVariant( 0.0 ) << QString( QLatin1String( "\\s*0.0\\s*" ) );
  QTest::newRow( "double -1" ) << QVariant( -1.0 ) << QString( QLatin1String( "\\s*-1.0\\s*" ) );
  QTest::newRow( "double 1.5E-20" ) << QVariant( 1.5e-20 ) << QString( QLatin1String( "\\s*1.5[Ee]-20\\s*" ) );
  QTest::newRow( "double -1.5E-20" ) << QVariant( -1.5e-20 ) << QString( QLatin1String( "\\s*-1.5[Ee]-20\\s*" ) );
  QTest::newRow( "double 2.0E-20" ) << QVariant( 2.0e-20 ) << QString( QLatin1String( "\\s*2(?:.0)?[Ee]-20\\s*" ) );
}

void TestSerializer::testValueBoolean()
{
  QFETCH( QVariant, value );
  QFETCH( QString, expected );
  valueTest( value, expected );

  QVariantMap map;
  map[QLatin1String("value")] = value;
  valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) );
}

void TestSerializer::testValueBoolean_data()
{
  QTest::addColumn<QVariant>( "value" );
  QTest::addColumn<QString>( "expected" );

  QTest::newRow( "bool false" ) << QVariant( false ) << QString( QLatin1String( "\\s*false\\s*" ) );
  QTest::newRow( "bool true" ) << QVariant( true ) << QString( QLatin1String( "\\s*true\\s*" ) );
}

void TestSerializer::testValueQObject()
{
  QString name = QLatin1String("Flavio Castelli");

  int phoneNumber = 26;
  QString phoneNumberString;
  phoneNumberString.setNum(phoneNumber);

  Person::Gender gender = Person::Male;
  QString genderString;
  genderString.setNum((int)gender);

  QDate date (1982, 7, 12);

  Person person;
  person.setName(name);
  person.setPhoneNumber(phoneNumber);
  person.setGender(gender);
  person.setDob(date);

  QString expected = QLatin1String( "\\s*\\{");
  expected += QLatin1String( "\\s*\"dob\"\\s*:\\s*\"") + date.toString(QLatin1String("yyyy-MM-dd")) + QLatin1String("\",");
  expected += QLatin1String( "\\s*\"gender\"\\s*:\\s*") + genderString + QLatin1String(",");
  expected += QLatin1String( "\\s*\"name\"\\s*:\\s*\"") + name + QLatin1String("\",");
  expected += QLatin1String( "\\s*\"phoneNumber\"\\s*:\\s*") + phoneNumberString;
  expected += QLatin1String( "\\s*\\}\\s*" );

  valueTest( &person, expected );
}

QTEST_MAIN(TestSerializer)
#include "moc_testserializer.cxx"
<commit_msg>updated test case<commit_after>/* This file is part of QJson
 *
 * Copyright (C) 2009 Flavio Castelli <flavio.castelli@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include <limits>

#include <QtTest/QtTest>

#include "person.h"
#include "parser.h"
#include "serializer.h"

#include <QtCore/QVariant>

class TestSerializer: public QObject
{
  Q_OBJECT
  private slots:
    void testReadWriteEmptyDocument();
    void testReadWrite();
    void testReadWrite_data();
    void testValueNull();
    void testValueString();
    void testValueString_data();
    void testValueInteger();
    void testValueInteger_data();
    void testValueDouble();
    void testValueDouble_data();
    void testValueBoolean();
    void testValueBoolean_data();
    void testValueQObject();

  private:
    void valueTest( const QVariant& value, const QString& expectedRegExp );
    void valueTest( const QObject* object, const QString& expectedRegExp );
};

Q_DECLARE_METATYPE(QVariant)

using namespace QJson;

void TestSerializer::testReadWriteEmptyDocument()
{
  QByteArray json = "";
  Parser parser;
  bool ok;
  QVariant result = parser.parse( json, &ok );
  QVERIFY(ok);
  QVERIFY( ! result.isValid() );
  Serializer serializer;
  const QByteArray serialized = serializer.serialize( result );
  QVERIFY( !serialized.isNull() );
  QByteArray expected = "null";
  QCOMPARE(expected, serialized);
}

void TestSerializer::testReadWrite()
{
  QFETCH( QByteArray, json );
  Parser parser;
  bool ok;
  QVariant result = parser.parse( json, &ok );
  QVERIFY(ok);
  Serializer serializer;
  const QByteArray serialized = serializer.serialize( result );
  QVariant writtenThenRead = parser.parse( serialized, &ok );
  QVERIFY(ok);
  QCOMPARE( result, writtenThenRead );
}

void TestSerializer::testReadWrite_data()
{
    QTest::addColumn<QByteArray>( "json" );

    // array tests
    QTest::newRow( "empty array" ) << QByteArray("[]");
    QTest::newRow( "basic array" ) << QByteArray("[\"person\",\"bar\"]");
    QTest::newRow( "single int array" ) << QByteArray("[6]");
    QTest::newRow( "int array" ) << QByteArray("[6,5,6,7]");
    const QByteArray json = "[1,2.4, -100, -3.4, -5e+, 2e,3e+,4.3E,5.4E-]";
    QTest::newRow( QByteArray("array of various numbers") ) << json;

    // document tests
    QTest::newRow( "empty object" ) << QByteArray("{}");
    QTest::newRow( "basic document" ) << QByteArray("{\"person\":\"bar\"}");
    QTest::newRow( "object with ints" ) << QByteArray("{\"person\":6}");
    const QByteArray json2 = "{ \"person\":\"bar\",\n\"number\" : 51.3 , \"array\":[\"item1\", 123]}";
    QTest::newRow( "complicated document" ) << json2;

    // more complex cases
    const QByteArray json3 = "[ {\"person\":\"bar\"},\n\"number\",51.3 , [\"item1\", 123]]";
    QTest::newRow( "complicated array" ) << json3;
}

void TestSerializer::valueTest( const QVariant& value, const QString& expectedRegExp )
{
  Serializer serializer;
  const QByteArray serialized = serializer.serialize( value );
  const QString serializedUnicode = QString::fromUtf8( serialized );
  QRegExp expected( expectedRegExp );
  QVERIFY( expected.isValid() );
  QVERIFY2( expected.exactMatch( serializedUnicode ),
    qPrintable( QString( QLatin1String( "Expected regexp \"%1\" but got \"%2\"." ) )
      .arg( expectedRegExp ).arg( serializedUnicode ) ) );
}

void TestSerializer::valueTest( const QObject* object, const QString& expectedRegExp )
{
  Serializer serializer;
  const QByteArray serialized = serializer.serialize( object );
  const QString serializedUnicode = QString::fromUtf8( serialized );
  QRegExp expected( expectedRegExp );
  QVERIFY( expected.isValid() );
  QVERIFY2( expected.exactMatch( serializedUnicode ),
    qPrintable( QString( QLatin1String( "Expected regexp \"%1\" but got \"%2\"." ) )
      .arg( expectedRegExp ).arg( serializedUnicode ) ) );
}

void TestSerializer::testValueNull()
{
  valueTest( QVariant(), QLatin1String( "\\s*null\\s*" ) );
  QVariantMap map;
  map[QLatin1String("value")] = QVariant();
  valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:\\s*null\\s*\\}\\s*" ) );
}

void TestSerializer::testValueString()
{
  QFETCH( QVariant, value );
  QFETCH( QString, expected );
  valueTest( value, expected );

  QVariantMap map;
  map[QLatin1String("value")] = value;
  valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) );
}

void TestSerializer::testValueString_data()
{
  QTest::addColumn<QVariant>( "value" );
  QTest::addColumn<QString>( "expected" );

  QTest::newRow( "null string" ) << QVariant( QString() ) << QString( QLatin1String( "\\s*\"\"\\s*" ) );
  QTest::newRow( "empty string" ) << QVariant( QString( QLatin1String( "" ) ) ) << QString( QLatin1String( "\\s*\"\"\\s*" ) );
  QTest::newRow( "Simple String" ) << QVariant( QString( QLatin1String( "simpleString" ) ) ) << QString( QLatin1String( "\\s*\"simpleString\"\\s*" ) );
  QTest::newRow( "string with tab" ) << QVariant( QString( QLatin1String( "string\tstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\tstring\"\\s*" ) );
  QTest::newRow( "string with newline" ) << QVariant( QString( QLatin1String( "string\nstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\nstring\"\\s*" ) );
  QTest::newRow( "string with bell" ) << QVariant( QString( QLatin1String( "string\bstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\bstring\"\\s*" ) );
  QTest::newRow( "string with return" ) << QVariant( QString( QLatin1String( "string\rstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\rstring\"\\s*" ) );
  QTest::newRow( "string with double quote" ) << QVariant( QString( QLatin1String( "string\"string" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\\"string\"\\s*" ) );
  QTest::newRow( "string with backslash" ) << QVariant( QString( QLatin1String( "string\\string" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\\\\\string\"\\s*" ) );
  QString testStringWithUnicode = QString( QLatin1String( "string" ) ) + QChar( 0x2665 ) + QLatin1String( "string" );
  QTest::newRow( "string with unicode" ) << QVariant( testStringWithUnicode ) << QLatin1String( "\\s*\"" ) + testStringWithUnicode + QLatin1String( "\"\\s*" );
}

void TestSerializer::testValueInteger()
{
  QFETCH( QVariant, value );
  QFETCH( QString, expected );
  valueTest( value, expected );

  QVariantMap map;
  map[QLatin1String("value")] = value;
  valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) );
}

void TestSerializer::testValueInteger_data()
{
  QTest::addColumn<QVariant>( "value" );
  QTest::addColumn<QString>( "expected" );

  QTest::newRow( "int 0" ) << QVariant( static_cast<int>( 0 ) ) << QString( QLatin1String( "\\s*0\\s*" ) );
  QTest::newRow( "uint 0" ) << QVariant( static_cast<uint>( 0 ) ) << QString( QLatin1String( "\\s*0\\s*" ) );
  QTest::newRow( "int -1" ) << QVariant( static_cast<int>( -1 ) ) << QString( QLatin1String( "\\s*-1\\s*" ) );
  QTest::newRow( "int 2133149800" ) << QVariant( static_cast<int>(2133149800) ) << QString( QLatin1String( "\\s*2133149800\\s*" ) );
  QTest::newRow( "uint 4133149800" ) << QVariant( static_cast<uint>(4133149800u) ) << QString( QLatin1String( "\\s*4133149800\\s*" ) );
  QTest::newRow( "uint64 932838457459459" ) << QVariant( Q_UINT64_C(932838457459459) ) << QString( QLatin1String( "\\s*932838457459459\\s*" ) );
  QTest::newRow( "max unsigned long long" ) << QVariant( std::numeric_limits<unsigned long long>::max() ) << QString( QLatin1String( "\\s*%1\\s*" ) ).arg(std::numeric_limits<unsigned long long>::max());
}

void TestSerializer::testValueDouble()
{
  QFETCH( QVariant, value );
  QFETCH( QString, expected );
  valueTest( value, expected );

  QVariantMap map;
  map[QLatin1String("value")] = value;
  valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) );
}

void TestSerializer::testValueDouble_data()
{
  QTest::addColumn<QVariant>( "value" );
  QTest::addColumn<QString>( "expected" );

  QTest::newRow( "double 0" ) << QVariant( 0.0 ) << QString( QLatin1String( "\\s*0.0\\s*" ) );
  QTest::newRow( "double -1" ) << QVariant( -1.0 ) << QString( QLatin1String( "\\s*-1.0\\s*" ) );
  QTest::newRow( "double 1.5E-20" ) << QVariant( 1.5e-20 ) << QString( QLatin1String( "\\s*1.5[Ee]-20\\s*" ) );
  QTest::newRow( "double -1.5E-20" ) << QVariant( -1.5e-20 ) << QString( QLatin1String( "\\s*-1.5[Ee]-20\\s*" ) );
  QTest::newRow( "double 2.0E-20" ) << QVariant( 2.0e-20 ) << QString( QLatin1String( "\\s*2(?:.0)?[Ee]-20\\s*" ) );
}

void TestSerializer::testValueBoolean()
{
  QFETCH( QVariant, value );
  QFETCH( QString, expected );
  valueTest( value, expected );

  QVariantMap map;
  map[QLatin1String("value")] = value;
  valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) );
}

void TestSerializer::testValueBoolean_data()
{
  QTest::addColumn<QVariant>( "value" );
  QTest::addColumn<QString>( "expected" );

  QTest::newRow( "bool false" ) << QVariant( false ) << QString( QLatin1String( "\\s*false\\s*" ) );
  QTest::newRow( "bool true" ) << QVariant( true ) << QString( QLatin1String( "\\s*true\\s*" ) );
}

void TestSerializer::testValueQObject()
{
  QString name = QLatin1String("Flavio Castelli");

  int phoneNumber = 123456;
  QString phoneNumberString;
  phoneNumberString.setNum(phoneNumber);

  Person::Gender gender = Person::Male;
  QString genderString;
  genderString.setNum((int)gender);

  QDate date (1982, 7, 12);

  Person person;
  person.setName(name);
  person.setPhoneNumber(phoneNumber);
  person.setGender(gender);
  person.setDob(date);

  QString expected = QLatin1String( "\\s*\\{");
  expected += QLatin1String( "\\s*\"dob\"\\s*:\\s*\"") + date.toString(QLatin1String("yyyy-MM-dd")) + QLatin1String("\",");
  expected += QLatin1String( "\\s*\"gender\"\\s*:\\s*") + genderString + QLatin1String(",");
  expected += QLatin1String( "\\s*\"name\"\\s*:\\s*\"") + name + QLatin1String("\",");
  expected += QLatin1String( "\\s*\"phoneNumber\"\\s*:\\s*") + phoneNumberString;
  expected += QLatin1String( "\\s*\\}\\s*" );

  valueTest( &person, expected );
}

QTEST_MAIN(TestSerializer)
#include "moc_testserializer.cxx"
<|endoftext|>


<commit_before>/*
* TLS Server Proxy
* (C) 2014,2015 Jack Lloyd
* (C) 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "cli.h"

#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_BOOST_ASIO) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)

#include <iostream>
#include <string>
#include <vector>
#include <thread>

#define _GLIBCXX_HAVE_GTHR_DEFAULT
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <botan/tls_server.h>
#include <botan/x509cert.h>
#include <botan/pkcs8.h>
#include <botan/auto_rng.h>
#include <botan/hex.h>

#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
   #include <botan/tls_session_manager_sqlite.h>
#endif

#include "credentials.h"

#if BOOST_VERSION >= 107000
#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context())
#else
#define GET_IO_SERVICE(s) ((s).get_io_service())
#endif

namespace Botan_CLI {

namespace {

using boost::asio::ip::tcp;

inline void log_exception(const char* where, const std::exception& e)
   {
   std::cout << where << ' ' << e.what() << std::endl;
   }

inline void log_error(const char* where, const boost::system::error_code& error)
   {
   std::cout << where << ' ' << error.message() << std::endl;
   }

inline void log_binary_message(const char* where, const uint8_t buf[], size_t buf_len)
   {
   //std::cout << where << ' ' << Botan::hex_encode(buf, buf_len) << std::endl;
   }

void log_text_message(const char* where,  const uint8_t buf[], size_t buf_len)
   {
   //const char* c = reinterpret_cast<const char*>(buf);
   //std::cout << where << ' ' << std::string(c, c + buf_len)  << std::endl;
   }

class tls_proxy_session final : public boost::enable_shared_from_this<tls_proxy_session>,
                                public Botan::TLS::Callbacks
   {
   public:
      enum { readbuf_size = 4 * 1024 };

      typedef boost::shared_ptr<tls_proxy_session> pointer;

      static pointer create(
         boost::asio::io_service& io,
         Botan::TLS::Session_Manager& session_manager,
         Botan::Credentials_Manager& credentials,
         Botan::TLS::Policy& policy,
         tcp::resolver::iterator endpoints)
         {
         return pointer(
                   new tls_proxy_session(
                      io,
                      session_manager,
                      credentials,
                      policy,
                      endpoints)
                );
         }

      tcp::socket& client_socket()
         {
         return m_client_socket;
         }

      void start()
         {
         m_c2p.resize(readbuf_size);
         client_read(boost::system::error_code(), 0); // start read loop
         }

      void stop()
         {
         /*
         Don't need to talk to the server anymore
         Client socket is closed during write callback
         */
         m_server_socket.close();
         m_tls.close();
         }

   private:
      tls_proxy_session(
         boost::asio::io_service& io,
         Botan::TLS::Session_Manager& session_manager,
         Botan::Credentials_Manager& credentials,
         Botan::TLS::Policy& policy,
         tcp::resolver::iterator endpoints)
         : m_strand(io)
         , m_server_endpoints(endpoints)
         , m_client_socket(io)
         , m_server_socket(io)
         , m_tls(*this,
                 session_manager,
                 credentials,
                 policy,
                 m_rng) {}

      void client_read(const boost::system::error_code& error,
                       size_t bytes_transferred)
         {
         if(error)
            {
            log_error("Read failed", error);
            stop();
            return;
            }

         try
            {
            if(!m_tls.is_active())
               {
               log_binary_message("From client", &m_c2p[0], bytes_transferred);
               }
            m_tls.received_data(&m_c2p[0], bytes_transferred);
            }
         catch(Botan::Exception& e)
            {
            log_exception("TLS connection failed", e);
            stop();
            return;
            }

         m_client_socket.async_read_some(
            boost::asio::buffer(&m_c2p[0], m_c2p.size()),
            m_strand.wrap(
               boost::bind(
                  &tls_proxy_session::client_read, shared_from_this(),
                  boost::asio::placeholders::error,
                  boost::asio::placeholders::bytes_transferred)));
         }

      void handle_client_write_completion(const boost::system::error_code& error)
         {
         if(error)
            {
            log_error("Client write", error);
            stop();
            return;
            }

         m_p2c.clear();

         if(m_p2c_pending.empty() && m_tls.is_closed())
            {
            m_client_socket.close();
            }
         tls_emit_data(nullptr, 0); // initiate another write if needed
         }

      void handle_server_write_completion(const boost::system::error_code& error)
         {
         if(error)
            {
            log_error("Server write", error);
            stop();
            return;
            }

         m_p2s.clear();
         proxy_write_to_server(nullptr, 0); // initiate another write if needed
         }

      void tls_record_received(uint64_t /*rec_no*/, const uint8_t buf[], size_t buf_len) override
         {
         // Immediately bounce message to server
         proxy_write_to_server(buf, buf_len);
         }

      void tls_emit_data(const uint8_t buf[], size_t buf_len) override
         {
         if(buf_len > 0)
            {
            m_p2c_pending.insert(m_p2c_pending.end(), buf, buf + buf_len);
            }

         // no write now active and we still have output pending
         if(m_p2c.empty() && !m_p2c_pending.empty())
            {
            std::swap(m_p2c_pending, m_p2c);

            log_binary_message("To Client", &m_p2c[0], m_p2c.size());

            boost::asio::async_write(
               m_client_socket,
               boost::asio::buffer(&m_p2c[0], m_p2c.size()),
               m_strand.wrap(
                  boost::bind(
                     &tls_proxy_session::handle_client_write_completion,
                     shared_from_this(),
                     boost::asio::placeholders::error)));
            }
         }

      void proxy_write_to_server(const uint8_t buf[], size_t buf_len)
         {
         if(buf_len > 0)
            {
            m_p2s_pending.insert(m_p2s_pending.end(), buf, buf + buf_len);
            }

         // no write now active and we still have output pending
         if(m_p2s.empty() && !m_p2s_pending.empty())
            {
            std::swap(m_p2s_pending, m_p2s);

            log_text_message("To Server", &m_p2s[0], m_p2s.size());

            boost::asio::async_write(
               m_server_socket,
               boost::asio::buffer(&m_p2s[0], m_p2s.size()),
               m_strand.wrap(
                  boost::bind(
                     &tls_proxy_session::handle_server_write_completion,
                     shared_from_this(),
                     boost::asio::placeholders::error)));
            }
         }

      void server_read(const boost::system::error_code& error,
                       size_t bytes_transferred)
         {
         if(error)
            {
            log_error("Server read failed", error);
            stop();
            return;
            }

         try
            {
            if(bytes_transferred)
               {
               log_text_message("Server to client", &m_s2p[0], m_s2p.size());
               log_binary_message("Server to client", &m_s2p[0], m_s2p.size());
               m_tls.send(&m_s2p[0], bytes_transferred);
               }
            }
         catch(Botan::Exception& e)
            {
            log_exception("TLS connection failed", e);
            stop();
            return;
            }

         m_s2p.resize(readbuf_size);

         m_server_socket.async_read_some(
            boost::asio::buffer(&m_s2p[0], m_s2p.size()),
            m_strand.wrap(
               boost::bind(&tls_proxy_session::server_read, shared_from_this(),
                           boost::asio::placeholders::error,
                           boost::asio::placeholders::bytes_transferred)));
         }

      bool tls_session_established(const Botan::TLS::Session& session) override
         {
         //std::cout << "Handshake from client complete" << std::endl;

         m_hostname = session.server_info().hostname();

         if(m_hostname != "")
            {
            std::cout << "Client requested hostname '" << m_hostname << "'" << std::endl;
            }

         auto onConnect = [this](boost::system::error_code ec, tcp::resolver::iterator /*endpoint*/)
            {
            if(ec)
               {
               log_error("Server connection", ec);
               return;
               }
            server_read(boost::system::error_code(), 0); // start read loop
            proxy_write_to_server(nullptr, 0);
            };
         async_connect(m_server_socket, m_server_endpoints, onConnect);
         return true;
         }

      void tls_alert(Botan::TLS::Alert alert) override
         {
         if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
            {
            m_tls.close();
            return;
            }
         else
            {
            std::cout << "Alert " << alert.type_string() << std::endl;
            }
         }

      boost::asio::io_service::strand m_strand;

      tcp::resolver::iterator m_server_endpoints;

      tcp::socket m_client_socket;
      tcp::socket m_server_socket;

      Botan::AutoSeeded_RNG m_rng; // RNG per connection
      Botan::TLS::Server m_tls;
      std::string m_hostname;

      std::vector<uint8_t> m_c2p;
      std::vector<uint8_t> m_p2c;
      std::vector<uint8_t> m_p2c_pending;

      std::vector<uint8_t> m_s2p;
      std::vector<uint8_t> m_p2s;
      std::vector<uint8_t> m_p2s_pending;
   };

class tls_proxy_server final
   {
   public:
      typedef tls_proxy_session session;

      tls_proxy_server(
         boost::asio::io_service& io, unsigned short port,
         tcp::resolver::iterator endpoints,
         Botan::Credentials_Manager& creds,
         Botan::TLS::Policy& policy,
         Botan::TLS::Session_Manager& session_mgr)
         : m_acceptor(io, tcp::endpoint(tcp::v4(), port))
         , m_server_endpoints(endpoints)
         , m_creds(creds)
         , m_policy(policy)
         , m_session_manager(session_mgr)
         {
         session::pointer new_session = make_session();

         m_acceptor.async_accept(
            new_session->client_socket(),
            boost::bind(
               &tls_proxy_server::handle_accept,
               this,
               new_session,
               boost::asio::placeholders::error));
         }

   private:
      session::pointer make_session()
         {
         return session::create(
                   GET_IO_SERVICE(m_acceptor),
                   m_session_manager,
                   m_creds,
                   m_policy,
                   m_server_endpoints);
         }

      void handle_accept(session::pointer new_session,
                         const boost::system::error_code& error)
         {
         if(!error)
            {
            new_session->start();
            new_session = make_session();

            m_acceptor.async_accept(
               new_session->client_socket(),
               boost::bind(
                  &tls_proxy_server::handle_accept,
                  this,
                  new_session,
                  boost::asio::placeholders::error));
            }
         }

      tcp::acceptor m_acceptor;
      tcp::resolver::iterator m_server_endpoints;

      Botan::Credentials_Manager& m_creds;
      Botan::TLS::Policy& m_policy;
      Botan::TLS::Session_Manager& m_session_manager;
   };

}

class TLS_Proxy final : public Command
   {
   public:
      TLS_Proxy() : Command("tls_proxy listen_port target_host target_port server_cert server_key "
                               "--threads=0 --session-db= --session-db-pass=") {}

      std::string group() const override
         {
         return "tls";
         }

      std::string description() const override
         {
         return "Proxies requests between a TLS client and a TLS server";
         }

      size_t thread_count() const
         {
         if(size_t t = get_arg_sz("threads"))
            return t;
         if(size_t t = std::thread::hardware_concurrency())
            return t;
         return 2;
         }

      void go() override
         {
         const size_t listen_port = get_arg_sz("listen_port");
         const std::string target = get_arg("target_host");
         const std::string target_port = get_arg("target_port");

         const std::string server_crt = get_arg("server_cert");
         const std::string server_key = get_arg("server_key");

         const size_t num_threads = thread_count();

         Basic_Credentials_Manager creds(rng(), server_crt, server_key);

         Botan::TLS::Policy policy; // TODO: Read policy from text file

         boost::asio::io_service io;

         tcp::resolver resolver(io);
         auto server_endpoint_iterator = resolver.resolve({ target, target_port });

         std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;

#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
         const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass");
         const std::string sessions_db = get_arg("session-db");

         if(!sessions_db.empty())
            {
            session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db));
            }
#endif
         if(!session_mgr)
            {
            session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng()));
            }

         tls_proxy_server server(io, listen_port, server_endpoint_iterator, creds, policy, *session_mgr);

         std::vector<std::shared_ptr<std::thread>> threads;

         // run forever... first thread is main calling io.run below
         for(size_t i = 2; i <= num_threads; ++i)
            {
            threads.push_back(std::make_shared<std::thread>([&io]() { io.run(); }));
            }

         io.run();

         for(size_t i = 0; i < threads.size(); ++i)
            {
            threads[i]->join();
            }
         }
   };

BOTAN_REGISTER_COMMAND("tls_proxy", TLS_Proxy);

}

#endif
<commit_msg>Avoid warnings<commit_after>/*
* TLS Server Proxy
* (C) 2014,2015 Jack Lloyd
* (C) 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "cli.h"

#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_BOOST_ASIO) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)

#include <iostream>
#include <string>
#include <vector>
#include <thread>

#define _GLIBCXX_HAVE_GTHR_DEFAULT
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <botan/tls_server.h>
#include <botan/x509cert.h>
#include <botan/pkcs8.h>
#include <botan/auto_rng.h>
#include <botan/hex.h>

#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
   #include <botan/tls_session_manager_sqlite.h>
#endif

#include "credentials.h"

#if BOOST_VERSION >= 107000
#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context())
#else
#define GET_IO_SERVICE(s) ((s).get_io_service())
#endif

namespace Botan_CLI {

namespace {

using boost::asio::ip::tcp;

inline void log_exception(const char* where, const std::exception& e)
   {
   std::cout << where << ' ' << e.what() << std::endl;
   }

inline void log_error(const char* where, const boost::system::error_code& error)
   {
   std::cout << where << ' ' << error.message() << std::endl;
   }

inline void log_binary_message(const char* where, const uint8_t buf[], size_t buf_len)
   {
   BOTAN_UNUSED(where, buf, buf_len);
   //std::cout << where << ' ' << Botan::hex_encode(buf, buf_len) << std::endl;
   }

void log_text_message(const char* where,  const uint8_t buf[], size_t buf_len)
   {
   BOTAN_UNUSED(where, buf, buf_len);
   //const char* c = reinterpret_cast<const char*>(buf);
   //std::cout << where << ' ' << std::string(c, c + buf_len)  << std::endl;
   }

class tls_proxy_session final : public boost::enable_shared_from_this<tls_proxy_session>,
                                public Botan::TLS::Callbacks
   {
   public:
      enum { readbuf_size = 4 * 1024 };

      typedef boost::shared_ptr<tls_proxy_session> pointer;

      static pointer create(
         boost::asio::io_service& io,
         Botan::TLS::Session_Manager& session_manager,
         Botan::Credentials_Manager& credentials,
         Botan::TLS::Policy& policy,
         tcp::resolver::iterator endpoints)
         {
         return pointer(
                   new tls_proxy_session(
                      io,
                      session_manager,
                      credentials,
                      policy,
                      endpoints)
                );
         }

      tcp::socket& client_socket()
         {
         return m_client_socket;
         }

      void start()
         {
         m_c2p.resize(readbuf_size);
         client_read(boost::system::error_code(), 0); // start read loop
         }

      void stop()
         {
         /*
         Don't need to talk to the server anymore
         Client socket is closed during write callback
         */
         m_server_socket.close();
         m_tls.close();
         }

   private:
      tls_proxy_session(
         boost::asio::io_service& io,
         Botan::TLS::Session_Manager& session_manager,
         Botan::Credentials_Manager& credentials,
         Botan::TLS::Policy& policy,
         tcp::resolver::iterator endpoints)
         : m_strand(io)
         , m_server_endpoints(endpoints)
         , m_client_socket(io)
         , m_server_socket(io)
         , m_tls(*this,
                 session_manager,
                 credentials,
                 policy,
                 m_rng) {}

      void client_read(const boost::system::error_code& error,
                       size_t bytes_transferred)
         {
         if(error)
            {
            log_error("Read failed", error);
            stop();
            return;
            }

         try
            {
            if(!m_tls.is_active())
               {
               log_binary_message("From client", &m_c2p[0], bytes_transferred);
               }
            m_tls.received_data(&m_c2p[0], bytes_transferred);
            }
         catch(Botan::Exception& e)
            {
            log_exception("TLS connection failed", e);
            stop();
            return;
            }

         m_client_socket.async_read_some(
            boost::asio::buffer(&m_c2p[0], m_c2p.size()),
            m_strand.wrap(
               boost::bind(
                  &tls_proxy_session::client_read, shared_from_this(),
                  boost::asio::placeholders::error,
                  boost::asio::placeholders::bytes_transferred)));
         }

      void handle_client_write_completion(const boost::system::error_code& error)
         {
         if(error)
            {
            log_error("Client write", error);
            stop();
            return;
            }

         m_p2c.clear();

         if(m_p2c_pending.empty() && m_tls.is_closed())
            {
            m_client_socket.close();
            }
         tls_emit_data(nullptr, 0); // initiate another write if needed
         }

      void handle_server_write_completion(const boost::system::error_code& error)
         {
         if(error)
            {
            log_error("Server write", error);
            stop();
            return;
            }

         m_p2s.clear();
         proxy_write_to_server(nullptr, 0); // initiate another write if needed
         }

      void tls_record_received(uint64_t /*rec_no*/, const uint8_t buf[], size_t buf_len) override
         {
         // Immediately bounce message to server
         proxy_write_to_server(buf, buf_len);
         }

      void tls_emit_data(const uint8_t buf[], size_t buf_len) override
         {
         if(buf_len > 0)
            {
            m_p2c_pending.insert(m_p2c_pending.end(), buf, buf + buf_len);
            }

         // no write now active and we still have output pending
         if(m_p2c.empty() && !m_p2c_pending.empty())
            {
            std::swap(m_p2c_pending, m_p2c);

            log_binary_message("To Client", &m_p2c[0], m_p2c.size());

            boost::asio::async_write(
               m_client_socket,
               boost::asio::buffer(&m_p2c[0], m_p2c.size()),
               m_strand.wrap(
                  boost::bind(
                     &tls_proxy_session::handle_client_write_completion,
                     shared_from_this(),
                     boost::asio::placeholders::error)));
            }
         }

      void proxy_write_to_server(const uint8_t buf[], size_t buf_len)
         {
         if(buf_len > 0)
            {
            m_p2s_pending.insert(m_p2s_pending.end(), buf, buf + buf_len);
            }

         // no write now active and we still have output pending
         if(m_p2s.empty() && !m_p2s_pending.empty())
            {
            std::swap(m_p2s_pending, m_p2s);

            log_text_message("To Server", &m_p2s[0], m_p2s.size());

            boost::asio::async_write(
               m_server_socket,
               boost::asio::buffer(&m_p2s[0], m_p2s.size()),
               m_strand.wrap(
                  boost::bind(
                     &tls_proxy_session::handle_server_write_completion,
                     shared_from_this(),
                     boost::asio::placeholders::error)));
            }
         }

      void server_read(const boost::system::error_code& error,
                       size_t bytes_transferred)
         {
         if(error)
            {
            log_error("Server read failed", error);
            stop();
            return;
            }

         try
            {
            if(bytes_transferred)
               {
               log_text_message("Server to client", &m_s2p[0], m_s2p.size());
               log_binary_message("Server to client", &m_s2p[0], m_s2p.size());
               m_tls.send(&m_s2p[0], bytes_transferred);
               }
            }
         catch(Botan::Exception& e)
            {
            log_exception("TLS connection failed", e);
            stop();
            return;
            }

         m_s2p.resize(readbuf_size);

         m_server_socket.async_read_some(
            boost::asio::buffer(&m_s2p[0], m_s2p.size()),
            m_strand.wrap(
               boost::bind(&tls_proxy_session::server_read, shared_from_this(),
                           boost::asio::placeholders::error,
                           boost::asio::placeholders::bytes_transferred)));
         }

      bool tls_session_established(const Botan::TLS::Session& session) override
         {
         //std::cout << "Handshake from client complete" << std::endl;

         m_hostname = session.server_info().hostname();

         if(m_hostname != "")
            {
            std::cout << "Client requested hostname '" << m_hostname << "'" << std::endl;
            }

         auto onConnect = [this](boost::system::error_code ec, tcp::resolver::iterator /*endpoint*/)
            {
            if(ec)
               {
               log_error("Server connection", ec);
               return;
               }
            server_read(boost::system::error_code(), 0); // start read loop
            proxy_write_to_server(nullptr, 0);
            };
         async_connect(m_server_socket, m_server_endpoints, onConnect);
         return true;
         }

      void tls_alert(Botan::TLS::Alert alert) override
         {
         if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
            {
            m_tls.close();
            return;
            }
         else
            {
            std::cout << "Alert " << alert.type_string() << std::endl;
            }
         }

      boost::asio::io_service::strand m_strand;

      tcp::resolver::iterator m_server_endpoints;

      tcp::socket m_client_socket;
      tcp::socket m_server_socket;

      Botan::AutoSeeded_RNG m_rng; // RNG per connection
      Botan::TLS::Server m_tls;
      std::string m_hostname;

      std::vector<uint8_t> m_c2p;
      std::vector<uint8_t> m_p2c;
      std::vector<uint8_t> m_p2c_pending;

      std::vector<uint8_t> m_s2p;
      std::vector<uint8_t> m_p2s;
      std::vector<uint8_t> m_p2s_pending;
   };

class tls_proxy_server final
   {
   public:
      typedef tls_proxy_session session;

      tls_proxy_server(
         boost::asio::io_service& io, unsigned short port,
         tcp::resolver::iterator endpoints,
         Botan::Credentials_Manager& creds,
         Botan::TLS::Policy& policy,
         Botan::TLS::Session_Manager& session_mgr)
         : m_acceptor(io, tcp::endpoint(tcp::v4(), port))
         , m_server_endpoints(endpoints)
         , m_creds(creds)
         , m_policy(policy)
         , m_session_manager(session_mgr)
         {
         session::pointer new_session = make_session();

         m_acceptor.async_accept(
            new_session->client_socket(),
            boost::bind(
               &tls_proxy_server::handle_accept,
               this,
               new_session,
               boost::asio::placeholders::error));
         }

   private:
      session::pointer make_session()
         {
         return session::create(
                   GET_IO_SERVICE(m_acceptor),
                   m_session_manager,
                   m_creds,
                   m_policy,
                   m_server_endpoints);
         }

      void handle_accept(session::pointer new_session,
                         const boost::system::error_code& error)
         {
         if(!error)
            {
            new_session->start();
            new_session = make_session();

            m_acceptor.async_accept(
               new_session->client_socket(),
               boost::bind(
                  &tls_proxy_server::handle_accept,
                  this,
                  new_session,
                  boost::asio::placeholders::error));
            }
         }

      tcp::acceptor m_acceptor;
      tcp::resolver::iterator m_server_endpoints;

      Botan::Credentials_Manager& m_creds;
      Botan::TLS::Policy& m_policy;
      Botan::TLS::Session_Manager& m_session_manager;
   };

}

class TLS_Proxy final : public Command
   {
   public:
      TLS_Proxy() : Command("tls_proxy listen_port target_host target_port server_cert server_key "
                               "--threads=0 --session-db= --session-db-pass=") {}

      std::string group() const override
         {
         return "tls";
         }

      std::string description() const override
         {
         return "Proxies requests between a TLS client and a TLS server";
         }

      size_t thread_count() const
         {
         if(size_t t = get_arg_sz("threads"))
            return t;
         if(size_t t = std::thread::hardware_concurrency())
            return t;
         return 2;
         }

      void go() override
         {
         const size_t listen_port = get_arg_sz("listen_port");
         const std::string target = get_arg("target_host");
         const std::string target_port = get_arg("target_port");

         const std::string server_crt = get_arg("server_cert");
         const std::string server_key = get_arg("server_key");

         const size_t num_threads = thread_count();

         Basic_Credentials_Manager creds(rng(), server_crt, server_key);

         Botan::TLS::Policy policy; // TODO: Read policy from text file

         boost::asio::io_service io;

         tcp::resolver resolver(io);
         auto server_endpoint_iterator = resolver.resolve({ target, target_port });

         std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;

#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
         const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass");
         const std::string sessions_db = get_arg("session-db");

         if(!sessions_db.empty())
            {
            session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db));
            }
#endif
         if(!session_mgr)
            {
            session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng()));
            }

         tls_proxy_server server(io, listen_port, server_endpoint_iterator, creds, policy, *session_mgr);

         std::vector<std::shared_ptr<std::thread>> threads;

         // run forever... first thread is main calling io.run below
         for(size_t i = 2; i <= num_threads; ++i)
            {
            threads.push_back(std::make_shared<std::thread>([&io]() { io.run(); }));
            }

         io.run();

         for(size_t i = 0; i < threads.size(); ++i)
            {
            threads[i]->join();
            }
         }
   };

BOTAN_REGISTER_COMMAND("tls_proxy", TLS_Proxy);

}

#endif
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/filerepository.h"
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/random.h"
#include "fnord-base/thread/eventloop.h"
#include "fnord-base/thread/threadpool.h"
#include "fnord-base/thread/FixedSizeThreadPool.h"
#include "fnord-base/wallclock.h"
#include "fnord-base/VFS.h"
#include "fnord-rpc/ServerGroup.h"
#include "fnord-rpc/RPC.h"
#include "fnord-rpc/RPCClient.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-json/json.h"
#include "fnord-json/jsonrpc.h"
#include "fnord-http/httprouter.h"
#include "fnord-http/httpserver.h"
#include "fnord-http/VFSFileServlet.h"
#include "fnord-feeds/FeedService.h"
#include "fnord-feeds/RemoteFeedFactory.h"
#include "fnord-feeds/RemoteFeedReader.h"
#include "fnord-base/stats/statsdagent.h"
#include "fnord-sstable/SSTableServlet.h"
#include "fnord-logtable/LogTableServlet.h"
#include "fnord-logtable/TableRepository.h"
#include "fnord-logtable/TableJanitor.h"
#include "fnord-logtable/TableReplication.h"
#include "fnord-logtable/ArtifactReplication.h"
#include "fnord-logtable/ArtifactIndexReplication.h"
#include "fnord-logtable/NumericBoundsSummary.h"
#include "fnord-mdb/MDB.h"
#include "fnord-mdb/MDBUtil.h"
#include "fnord-tsdb/TSDBNode.h"
#include "fnord-tsdb/TSDBServlet.h"
#include "common.h"
#include "schemas.h"
#include "ModelReplication.h"

using namespace fnord;

std::atomic<bool> shutdown_sig;
fnord::thread::EventLoop ev;

void quit(int n) {
  shutdown_sig = true;
  fnord::logInfo("cm.chunkserver", "Shutting down...");
  // FIXPAUL: wait for http server stop...
  ev.shutdown();
}

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

  /* shutdown hook */
  shutdown_sig = false;
  struct sigaction sa;
  memset(&sa, 0, sizeof(struct sigaction));
  sa.sa_handler = quit;
  sigaction(SIGTERM, &sa, NULL);
  sigaction(SIGQUIT, &sa, NULL);
  sigaction(SIGINT, &sa, NULL);

  fnord::cli::FlagParser flags;

  flags.defineFlag(
      "http_port",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "8000",
      "Start the public http server on this port",
      "<port>");

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

  flags.defineFlag(
      "replicate_to",
      cli::FlagParser::T_STRING,
      false,
      NULL,
      NULL,
      "url",
      "<url>");

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

  flags.parseArgv(argc, argv);

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

  /* args */
  auto dir = flags.getString("datadir");
  auto repl_targets = flags.getStrings("replicate_to");

  /* start http server and worker pools */
  fnord::thread::ThreadPool tpool;
  http::HTTPConnectionPool http(&ev);
  fnord::http::HTTPRouter http_router;
  fnord::http::HTTPServer http_server(&http_router, &ev);
  http_server.listen(flags.getInt("http_port"));

  auto repl_scheme = mkRef(new dht::FixedReplicationScheme());
  for (const auto& r : repl_targets) {
    repl_scheme->addHost(r);
  }

  tsdb::TSDBNode tsdb_node(dir + "/tsdb", repl_scheme.get(), &http);

  tsdb::StreamProperties config(new msg::MessageSchema(joinedSessionsSchema()));
  config.max_datafile_size = 1024 * 1024 * 512;
  config.chunk_size = Duration(3600 * 4 * kMicrosPerSecond);
  config.compaction_interval = Duration(30 * kMicrosPerSecond);
  tsdb_node.configurePrefix("joined_sessions.", config);

  tsdb::TSDBServlet tsdb_servlet(&tsdb_node);
  http_router.addRouteByPrefixMatch("/tsdb", &tsdb_servlet, &tpool);

  tsdb_node.start();
  ev.run();

  tsdb_node.stop();
  fnord::logInfo("cm.chunkserver", "Exiting...");

  exit(0);
}

<commit_msg>build cstable index<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/filerepository.h"
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/random.h"
#include "fnord-base/thread/eventloop.h"
#include "fnord-base/thread/threadpool.h"
#include "fnord-base/thread/FixedSizeThreadPool.h"
#include "fnord-base/wallclock.h"
#include "fnord-base/VFS.h"
#include "fnord-rpc/ServerGroup.h"
#include "fnord-rpc/RPC.h"
#include "fnord-rpc/RPCClient.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-json/json.h"
#include "fnord-json/jsonrpc.h"
#include "fnord-http/httprouter.h"
#include "fnord-http/httpserver.h"
#include "fnord-http/VFSFileServlet.h"
#include "fnord-feeds/FeedService.h"
#include "fnord-feeds/RemoteFeedFactory.h"
#include "fnord-feeds/RemoteFeedReader.h"
#include "fnord-base/stats/statsdagent.h"
#include "fnord-sstable/SSTableServlet.h"
#include "fnord-logtable/LogTableServlet.h"
#include "fnord-logtable/TableRepository.h"
#include "fnord-logtable/TableJanitor.h"
#include "fnord-logtable/TableReplication.h"
#include "fnord-logtable/ArtifactReplication.h"
#include "fnord-logtable/ArtifactIndexReplication.h"
#include "fnord-logtable/NumericBoundsSummary.h"
#include "fnord-mdb/MDB.h"
#include "fnord-mdb/MDBUtil.h"
#include "fnord-tsdb/TSDBNode.h"
#include "fnord-tsdb/TSDBServlet.h"
#include "fnord-tsdb/CSTableIndex.h"
#include "common.h"
#include "schemas.h"
#include "ModelReplication.h"

using namespace fnord;

std::atomic<bool> shutdown_sig;
fnord::thread::EventLoop ev;

void quit(int n) {
  shutdown_sig = true;
  fnord::logInfo("cm.chunkserver", "Shutting down...");
  // FIXPAUL: wait for http server stop...
  ev.shutdown();
}

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

  /* shutdown hook */
  shutdown_sig = false;
  struct sigaction sa;
  memset(&sa, 0, sizeof(struct sigaction));
  sa.sa_handler = quit;
  sigaction(SIGTERM, &sa, NULL);
  sigaction(SIGQUIT, &sa, NULL);
  sigaction(SIGINT, &sa, NULL);

  fnord::cli::FlagParser flags;

  flags.defineFlag(
      "http_port",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "8000",
      "Start the public http server on this port",
      "<port>");

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

  flags.defineFlag(
      "replicate_to",
      cli::FlagParser::T_STRING,
      false,
      NULL,
      NULL,
      "url",
      "<url>");

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

  flags.parseArgv(argc, argv);

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

  /* args */
  auto dir = flags.getString("datadir");
  auto repl_targets = flags.getStrings("replicate_to");

  /* start http server and worker pools */
  fnord::thread::ThreadPool tpool;
  http::HTTPConnectionPool http(&ev);
  fnord::http::HTTPRouter http_router;
  fnord::http::HTTPServer http_server(&http_router, &ev);
  http_server.listen(flags.getInt("http_port"));

  auto repl_scheme = mkRef(new dht::FixedReplicationScheme());
  for (const auto& r : repl_targets) {
    repl_scheme->addHost(r);
  }

  tsdb::TSDBNode tsdb_node(dir + "/tsdb", repl_scheme.get(), &http);

  tsdb::StreamProperties config(new msg::MessageSchema(joinedSessionsSchema()));
  config.max_datafile_size = 1024 * 1024 * 512;
  config.chunk_size = Duration(3600 * 4 * kMicrosPerSecond);
  config.compaction_interval = Duration(30 * kMicrosPerSecond);
  config.derived_datasets.emplace_back(new tsdb::CSTableIndex(config.schema));
  tsdb_node.configurePrefix("joined_sessions.", config);

  tsdb::TSDBServlet tsdb_servlet(&tsdb_node);
  http_router.addRouteByPrefixMatch("/tsdb", &tsdb_servlet, &tpool);

  tsdb_node.start();
  ev.run();

  tsdb_node.stop();
  fnord::logInfo("cm.chunkserver", "Exiting...");

  exit(0);
}

<|endoftext|>


<commit_before>#include "module_pressuresensor.h"
#include "pressure_form.h"
#include <Module_UID/module_uid.h>

#define REGISTER_CALIB 00 // 8 bytes
#define REGISTER_PRESSURE_RAW 8
#define REGISTER_TEMP_RAW 10
#define REGISTER_PRESSURE 12
#define REGISTER_TEMP 14
#define REGISTER_STATUS 17
#define REGISTER_COUNTER 20

#define REQUEST_NEW_CALIB_VALUES 18

// indicates problem between i2c-spi bridge and pressure sensor
#define STATUS_MAGIC_VALUE 0x55
#define CALIB_MAGIC_VALUE 224

// pressure range. everything outside this range will be regarded as
// a meassurement error
#define PRESSURE_MIN 900
#define PRESSURE_MAX 3000

Module_PressureSensor::Module_PressureSensor(QString id, Module_UID *uid)
    : RobotModule(id)
{
    thread.start();

    this->uid=uid;

    setDefaultValue("i2cAddress", 0x50);
    setDefaultValue("frequency", 1);

    thread.start();
    //timer.moveToThread(&thread);

    connect(&timer,SIGNAL(timeout()), this, SLOT(refreshData()),
            Qt::DirectConnection);

    reset();
}

Module_PressureSensor::~Module_PressureSensor()
{
}

void Module_PressureSensor::terminate()
{
    RobotModule::terminate();
    QTimer::singleShot(0, &timer, SLOT(stop()));
}

void Module_PressureSensor::reset()
{
    RobotModule::reset();

    int freq = 1000/getSettings().value("frequency").toInt();
    if (freq>0) {
        timer.setInterval(freq);
        QTimer::singleShot(0, &timer, SLOT(start()));
    } else {
        QTimer::singleShot(0, &timer, SLOT(stop()));
    }

    if (!getSettings().value("enabled").toBool())
        return;

    unsigned char address = getSettings().value("i2cAddress").toInt();
    char reg = REQUEST_NEW_CALIB_VALUES;
    if (!uid->I2C_Write(address, &reg, 1)) {
        setHealthToSick(uid->getLastError());
    }
    msleep(100);

}

void Module_PressureSensor::refreshData()
{
    if (!getSettings().value("enabled").toBool())
        return;

    readPressure();
    readTemperature();

    if (getHealthStatus().isHealthOk()) {
        emit dataChanged(this);
        emit newDepthData(getDepth());
    }
}

void Module_PressureSensor::readPressure()
{
    unsigned char readBuffer[2];

    if (!readRegister(REGISTER_PRESSURE, 2, (char*)readBuffer)) {
        setHealthToSick(uid->getLastError());
        return;
    }

    // this is the pressure in mBar
    uint16_t pressure = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    QMutexLocker l(&this->moduleMutex);

    data["pressure"] =  pressure;

    // 100 mBar == ca. 1m wassersäule - druck an der luft
    data["depth"] =  ((float)pressure-getSettings().value("airPressure").toFloat())/100;

    if (pressure < PRESSURE_MIN || pressure > PRESSURE_MAX) {
        setHealthToSick("Pressure of "+QString::number(pressure) + " doesn't make sense.");
    }
}

void Module_PressureSensor::readTemperature()
{

    unsigned char readBuffer[2];
    if (!readRegister(REGISTER_TEMP, 2, (char*)readBuffer)) {
        setHealthToSick(uid->getLastError());
        return;
    }

    // this is the temperature in 10/degree celsius
    uint16_t temp = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    QMutexLocker l(&this->moduleMutex);

    data["temperatureHW"] = ((float)temp)/10;
}

float Module_PressureSensor::getDepth()
{
    QMutexLocker l(&this->moduleMutex);
    return data["depth"].toFloat();
}

float Module_PressureSensor::getTemperature()
{
    QMutexLocker l(&this->moduleMutex);
    return data["temperature"].toFloat()/10.0;
}

QList<RobotModule*> Module_PressureSensor::getDependencies()
{
    QList<RobotModule*> ret;
    ret.append(uid);
    return ret;
}

QWidget* Module_PressureSensor::createView(QWidget* parent)
{
    return new Pressure_Form(this, parent);
}

void Module_PressureSensor::doHealthCheck()
{
    if (!isEnabled())
        return;

    char readBuffer[1];

    if (!readRegister(REGISTER_STATUS, 1, readBuffer)) {
        setHealthToSick(uid->getLastError());
        return;
    }

    if (readBuffer[0] != STATUS_MAGIC_VALUE) {
        setHealthToSick("Status register doesn't match magic value: is="+QString::number(readBuffer[0]));
        return;
    }

    // check if the counter is increasing.

    if (!readRegister(REGISTER_COUNTER, 1, readBuffer)) {
        setHealthToSick(uid->getLastError());
        return;
    }

    if (readBuffer[0] == counter) {
        setHealthToSick("read the same counter value twice!");
        return;
    }

    QMutexLocker l(&this->moduleMutex);

    counter = (unsigned char)readBuffer[0];
    data["counter"] = counter;

    setHealthToOk();
}

bool Module_PressureSensor::readRegister(unsigned char reg, int size, char *ret_buf)
{
    unsigned char address = getSettings().value("i2cAddress").toInt();

    if (!uid->I2C_ReadRegisters(address, reg, size, ret_buf)) {
        setHealthToSick(uid->getLastError());
        return false;
    }
    return true;
}
<commit_msg>cntr fix<commit_after>#include "module_pressuresensor.h"
#include "pressure_form.h"
#include <Module_UID/module_uid.h>

#define REGISTER_CALIB 00 // 8 bytes
#define REGISTER_PRESSURE_RAW 8
#define REGISTER_TEMP_RAW 10
#define REGISTER_PRESSURE 12
#define REGISTER_TEMP 14
#define REGISTER_STATUS 17
#define REGISTER_COUNTER 20

#define REQUEST_NEW_CALIB_VALUES 18

// indicates problem between i2c-spi bridge and pressure sensor
#define STATUS_MAGIC_VALUE 0x55
#define CALIB_MAGIC_VALUE 224

// pressure range. everything outside this range will be regarded as
// a meassurement error
#define PRESSURE_MIN 900
#define PRESSURE_MAX 3000

Module_PressureSensor::Module_PressureSensor(QString id, Module_UID *uid)
    : RobotModule(id)
{
    thread.start();

    this->uid=uid;

    setDefaultValue("i2cAddress", 0x50);
    setDefaultValue("frequency", 1);

    thread.start();
    //timer.moveToThread(&thread);

    connect(&timer,SIGNAL(timeout()), this, SLOT(refreshData()),
            Qt::DirectConnection);

    reset();
}

Module_PressureSensor::~Module_PressureSensor()
{
}

void Module_PressureSensor::terminate()
{
    RobotModule::terminate();
    QTimer::singleShot(0, &timer, SLOT(stop()));
}

void Module_PressureSensor::reset()
{
    RobotModule::reset();

    int freq = 1000/getSettings().value("frequency").toInt();
    if (freq>0) {
        timer.setInterval(freq);
        QTimer::singleShot(0, &timer, SLOT(start()));
    } else {
        QTimer::singleShot(0, &timer, SLOT(stop()));
    }

    if (!getSettings().value("enabled").toBool())
        return;

    unsigned char address = getSettings().value("i2cAddress").toInt();
    char reg = REQUEST_NEW_CALIB_VALUES;
    if (!uid->I2C_Write(address, &reg, 1)) {
        setHealthToSick(uid->getLastError());
    }
    msleep(100);

}

void Module_PressureSensor::refreshData()
{
    if (!getSettings().value("enabled").toBool())
        return;

    readPressure();
    readTemperature();

    if (getHealthStatus().isHealthOk()) {
        emit dataChanged(this);
        emit newDepthData(getDepth());
    }
}

void Module_PressureSensor::readPressure()
{
    unsigned char readBuffer[2];

    if (!readRegister(REGISTER_PRESSURE, 2, (char*)readBuffer)) {
        setHealthToSick(uid->getLastError());
        return;
    }

    // this is the pressure in mBar
    uint16_t pressure = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    QMutexLocker l(&this->moduleMutex);

    data["pressure"] =  pressure;

    // 100 mBar == ca. 1m wassersäule - druck an der luft
    data["depth"] =  ((float)pressure-getSettings().value("airPressure").toFloat())/100;

    if (pressure < PRESSURE_MIN || pressure > PRESSURE_MAX) {
        setHealthToSick("Pressure of "+QString::number(pressure) + " doesn't make sense.");
    }
}

void Module_PressureSensor::readTemperature()
{

    unsigned char readBuffer[2];
    if (!readRegister(REGISTER_TEMP, 2, (char*)readBuffer)) {
        setHealthToSick(uid->getLastError());
        return;
    }

    // this is the temperature in 10/degree celsius
    uint16_t temp = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    QMutexLocker l(&this->moduleMutex);

    data["temperatureHW"] = ((float)temp)/10;
}

float Module_PressureSensor::getDepth()
{
    QMutexLocker l(&this->moduleMutex);
    return data["depth"].toFloat();
}

float Module_PressureSensor::getTemperature()
{
    QMutexLocker l(&this->moduleMutex);
    return data["temperature"].toFloat()/10.0;
}

QList<RobotModule*> Module_PressureSensor::getDependencies()
{
    QList<RobotModule*> ret;
    ret.append(uid);
    return ret;
}

QWidget* Module_PressureSensor::createView(QWidget* parent)
{
    return new Pressure_Form(this, parent);
}

void Module_PressureSensor::doHealthCheck()
{
    if (!isEnabled())
        return;

    char readBuffer[1];

    if (!readRegister(REGISTER_STATUS, 1, readBuffer)) {
        setHealthToSick(uid->getLastError());
        return;
    }

    if (readBuffer[0] != STATUS_MAGIC_VALUE) {
        setHealthToSick("Status register doesn't match magic value: is="+QString::number(readBuffer[0]));
        return;
    }

    // check if the counter is increasing.

    if (!readRegister(REGISTER_COUNTER, 1, readBuffer)) {
        setHealthToSick(uid->getLastError());
        return;
    }

    QMutexLocker l(&this->moduleMutex);

    data["counter"] = (unsigned char)readBuffer[0];

    if (readBuffer[0] == counter) {
        setHealthToSick("read the same counter value twice!");
        return;
    }

    counter = (unsigned char)readBuffer[0];

    setHealthToOk();
}

bool Module_PressureSensor::readRegister(unsigned char reg, int size, char *ret_buf)
{
    unsigned char address = getSettings().value("i2cAddress").toInt();

    if (!uid->I2C_ReadRegisters(address, reg, size, ret_buf)) {
        setHealthToSick(uid->getLastError());
        return false;
    }
    return true;
}
<|endoftext|>


<commit_before>// This code is based on Sabberstone project.
// Copyright (c) 2017-2019 SabberStone Team, darkfriend77 & rnilva
// RosettaStone is hearthstone simulator using C++ with reinforcement learning.
// Copyright (c) 2019 Chris Ohk, Youngjoong Kim, SeungHyun Jeon

#ifndef ROSETTASTONE_MODIFY_MANA_TASK_HPP
#define ROSETTASTONE_MODIFY_MANA_TASK_HPP

#include <Rosetta/Tasks/Tasks.hpp>

namespace RosettaStone
{
//! The operator to change mana.
enum class ManaOperator
{
    ADD = 0,  //!< Add mana.
    SUB = 1,  //!< Subtract mana.
    SET = 2,  //!< Set mana.
};

//! The type to change mana.
enum class ManaType
{
    AVAILABLE = 0,  //!< Available mana.
    MAXIMUM = 1,    //!< Maximum mana.
};

namespace SimpleTasks
{
//!
//! \brief ModifyManaTask class.
//!
//! This class represents the task for modifying mana of player.
//!
class ModifyManaTask : public ITask
{
 public:
    //! Constructs task with given \p numMode, \p manaMode and \p num.
    //! \param numMode A value that specifies the operation of \p num.
    //! \param manaMode A value that specifies whether the modified mana is
    //! one-time or permanent.
    //! \param num A value indicating how much to modify mana.
    ModifyManaTask(ManaOperator numMode, ManaType manaMode, std::size_t num);

    //! Returns task ID.
    //! \return Task ID.
    TaskID GetTaskID() const override;

 private:
    //! Processes task logic internally and returns meta data.
    //! \param player The player to run task.
    //! \return The result of task processing.
    TaskStatus Impl(Player& player) override;

    ManaOperator m_numMode;
    ManaType m_manaMode;
    std::size_t m_num;
};
}  // namespace SimpleTasks
}  // namespace RosettaStone

#endif  // ROSETTASTONE_MODIFY_MANA_TASK_HPP
<commit_msg>style: Integrate namespaces and remove enum value<commit_after>// This code is based on Sabberstone project.
// Copyright (c) 2017-2019 SabberStone Team, darkfriend77 & rnilva
// RosettaStone is hearthstone simulator using C++ with reinforcement learning.
// Copyright (c) 2019 Chris Ohk, Youngjoong Kim, SeungHyun Jeon

#ifndef ROSETTASTONE_MODIFY_MANA_TASK_HPP
#define ROSETTASTONE_MODIFY_MANA_TASK_HPP

#include <Rosetta/Tasks/Tasks.hpp>

namespace RosettaStone::SimpleTasks
{
//! The operator to change mana.
enum class ManaOperator
{
    ADD,  //!< Add mana.
    SUB,  //!< Subtract mana.
    SET   //!< Set mana.
};

//! The type to change mana.
enum class ManaType
{
    AVAILABLE,  //!< Available mana.
    MAXIMUM     //!< Maximum mana.
};

//!
//! \brief ModifyManaTask class.
//!
//! This class represents the task for modifying mana of player.
//!
class ModifyManaTask : public ITask
{
 public:
    //! Constructs task with given \p numMode, \p manaMode and \p num.
    //! \param numMode A value that specifies the operation of \p num.
    //! \param manaMode A value that specifies whether the modified mana is
    //! one-time or permanent.
    //! \param num A value indicating how much to modify mana.
    ModifyManaTask(ManaOperator numMode, ManaType manaMode, std::size_t num);

    //! Returns task ID.
    //! \return Task ID.
    TaskID GetTaskID() const override;

 private:
    //! Processes task logic internally and returns meta data.
    //! \param player The player to run task.
    //! \return The result of task processing.
    TaskStatus Impl(Player& player) override;

    ManaOperator m_numMode;
    ManaType m_manaMode;
    std::size_t m_num;
};
}  // namespace RosettaStone::SimpleTasks

#endif  // ROSETTASTONE_MODIFY_MANA_TASK_HPP
<|endoftext|>


<commit_before>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkCameraController.h"
#include "mitkVtkPropRenderer.h"
#include "mitkRenderingManager.h"
#include <vtkRenderWindowInteractor.h>
#include "vtkCommand.h"

#include "vtkCamera.h"
#include "vtkRenderer.h"
#include <vtkTransform.h>
#include <vtkSmartPointer.h>

mitk::CameraController::CameraController() :
  BaseController(),
  m_Renderer(NULL)
{
}

mitk::CameraController::~CameraController()
{}

mitk::ScalarType mitk::CameraController::ComputeMaxParallelScale()
{
  double widthInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0);
  double heightInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1);

  double dispHeight = this->GetRenderer()->GetViewportSize()[1]; //in pixel!
  double dispWidth = this->GetRenderer()->GetViewportSize()[0];

  //To get the right zooming factor, we need to set the (half) height to the vtk camera using SetParallelScale.
  //However, it could be, that our picture is so wide or the display so small, that we cannot take the height of the picture.
  //For a wide picture, we have to take the width and adapt the width so that our image fits to the screen.
  //But we can only set the height. Therefore, if the width is the limiting factor, we need to get the ratio of scaling
  //for the width and multiply it with the height, so that we have a modified height and set this one. Believe us, we figured it out...
  if ((dispWidth / widthInMM) < (dispHeight / heightInMM))
  {
    heightInMM = widthInMM / dispWidth * dispHeight;
  }

  return heightInMM *0.5;
}

void mitk::CameraController::AdjustConstrainedCameraPosition(mitk::Point2D &planePoint)
{
  //TODO: GetExtentInMM is calculated wrong for rotated planes, e.g. crosshair rotation (bug 19105)
  double widthInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0);
  double heightInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1);

  mitk::Point2D dispSizeInMM = this->GetRenderer()->GetViewportSizeInMM();

  double xMin, xMax, yMin, yMax;

  //different calculation of min/max if display is lager/smaller than image.
  //note, that the plane Position defines the middle of the display but is in image coordinates
  //([0,0] is defined by the image, so planePosition can sometimes be negative).
  if (dispSizeInMM[0] > widthInMM)
  {
    xMin = widthInMM - 0.5 * dispSizeInMM[0];
    xMax = 0.5 * dispSizeInMM[0] ;
  }
  else
  {
    xMin = 0.5 * dispSizeInMM[0];
    xMax = widthInMM - 0.5 * dispSizeInMM[0];
  }

  if (dispSizeInMM[1] > heightInMM)
  {
    yMin = heightInMM - 0.5 * dispSizeInMM[1];
    yMax = 0.5 * dispSizeInMM[1] ;
  }
  else
  {
    yMin = 0.5 * dispSizeInMM[1];
    yMax = heightInMM - 0.5 * dispSizeInMM[1];
  }

  if (planePoint[0] < xMin) { planePoint[0] = xMin; }
  if (planePoint[1] < yMin) { planePoint[1] = yMin; }
  if (planePoint[0] > xMax) { planePoint[0] = xMax; }
  if (planePoint[1] > yMax) { planePoint[1] = yMax; }
}

void mitk::CameraController::SetViewToAnterior()
{
  this->SetStandardView(ANTERIOR);
}

void mitk::CameraController::SetViewToPosterior()
{
  this->SetStandardView(POSTERIOR);
}

void mitk::CameraController::SetViewToSinister()
{
  this->SetStandardView(SINISTER);
}

void mitk::CameraController::SetViewToDexter()
{
  this->SetStandardView(DEXTER);
}

void mitk::CameraController::SetViewToCranial()
{
  this->SetStandardView(CRANIAL);
}

void mitk::CameraController::SetViewToCaudal()
{
  this->SetStandardView(CAUDAL);
}

void mitk::CameraController::SetStandardView(mitk::CameraController::StandardView view)
{
  const mitk::VtkPropRenderer* glRenderer = dynamic_cast<const mitk::VtkPropRenderer*>(m_Renderer);
  if (glRenderer == NULL)
    return;
  vtkRenderer* vtkRenderer = glRenderer->GetVtkRenderer();
  assert(vtkRenderer);

  mitk::BoundingBox::Pointer bb;
  mitk::DataStorage* ds = m_Renderer->GetDataStorage();
  if (ds != NULL)
    bb = ds->ComputeBoundingBox();
  else
    return;

  if (m_Renderer->GetMapperID() == mitk::BaseRenderer::Standard3D)
  {
    //set up the view for the 3D render window. The views for 2D are set up in the mitkVtkPropRenderer
    mitk::Point3D middle = bb->GetCenter();
    vtkRenderer->GetActiveCamera()->SetFocalPoint(middle[0], middle[1], middle[2]);
    switch (view)
    {
    case ANTERIOR:
    case POSTERIOR:
    case SINISTER:
    case DEXTER:
      vtkRenderer->GetActiveCamera()->SetViewUp(0, 0, 1);
      break;
    case CRANIAL:
    case CAUDAL:
      vtkRenderer->GetActiveCamera()->SetViewUp(0, -1, 0);
      break;
    }
    switch (view)
    {
    case ANTERIOR:
      vtkRenderer->GetActiveCamera()->SetPosition(middle[0], -100000, middle[2]);
      break;
    case POSTERIOR:
      vtkRenderer->GetActiveCamera()->SetPosition(middle[0], +100000, middle[2]);
      break;
    case SINISTER:
      vtkRenderer->GetActiveCamera()->SetPosition(+100000, middle[1], middle[2]);
      break;
    case DEXTER:
      vtkRenderer->GetActiveCamera()->SetPosition(-100000, middle[1], middle[2]);
      break;
    case CRANIAL:
      vtkRenderer->GetActiveCamera()->SetPosition(middle[0], middle[1], 100000);
      break;
    case CAUDAL:
      vtkRenderer->GetActiveCamera()->SetPosition(middle[0], middle[1], -100000);
      break;
    }
    vtkRenderer->ResetCamera();

    vtkRenderer->ResetCameraClippingRange();
  }

  mitk::RenderingManager* rm = m_Renderer->GetRenderingManager();
  rm->RequestUpdateAll();
}

void mitk::CameraController::MoveCameraToPoint(const mitk::Point2D& planePoint)
{
  Point2D moveToPoint = planePoint;
  AdjustCameraToPlane(moveToPoint);
}

void mitk::CameraController::MoveBy(const mitk::Vector2D &moveVectorInMM)
{
  MoveCameraToPoint(GetCameraPositionOnPlane() + moveVectorInMM);
}

void mitk::CameraController::Zoom(ScalarType factor, const Point2D& zoomPointInMM)
{
  if (factor <= 0.0) return;
  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
  {
    double parallelScale = this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->GetParallelScale()/factor;
    if (this->GetRenderer()->GetConstrainZoomingAndPanning() && factor < 1.0)
    {
      double maxParallelScale = ComputeMaxParallelScale();
      if (maxParallelScale - parallelScale * factor  < mitk::eps)//this is not the famous 05-bug... Return if already near max zooming
        return;

      if (parallelScale > maxParallelScale)
      {
        parallelScale = maxParallelScale;
      }
    }
    this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(parallelScale);
    //Move camera in a way that the clicked point stays visible on the display where it was.
    Point2D planePoint = GetCameraPositionOnPlane();
    MoveCameraToPoint(planePoint + ((zoomPointInMM - planePoint) * (factor - 1)));
  }
}

mitk::Point2D mitk::CameraController::GetCameraPositionOnPlane()
{
  Point2D CamPosOnPlane;
  CamPosOnPlane[0] = this->GetRenderer()->GetVtkRenderer()->GetCenter()[0];
  CamPosOnPlane[1] = this->GetRenderer()->GetVtkRenderer()->GetCenter()[1];
  this->GetRenderer()->DisplayToPlane(CamPosOnPlane, CamPosOnPlane);
  return CamPosOnPlane;
}

void mitk::CameraController::AdjustCameraToPlane()
{
  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
  {
    AdjustCameraToPlane(GetCameraPositionOnPlane());
  }
}

void mitk::CameraController::AdjustCameraToPlane(const Point2D& PlanePoint)
{
  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
  {
    Point2D _planePoint = PlanePoint;//PlanePoint is const...
    if (this->GetRenderer()->GetConstrainZoomingAndPanning())
    {
      double parallelScale = this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->GetParallelScale();
      double maxParallelScale = ComputeMaxParallelScale();
      if (parallelScale > maxParallelScale)
      {
        this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(maxParallelScale);
      }
      AdjustConstrainedCameraPosition(_planePoint);
    }
    const PlaneGeometry *planeGeometry = this->GetRenderer()->GetCurrentWorldPlaneGeometry();
    if (planeGeometry != NULL)
    {
      this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(0, 1, 0); //set the view-up for the camera
      this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(_planePoint[0], _planePoint[1], 900000);
      this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(_planePoint[0], _planePoint[1], 0);
      //Transform the camera to the current position (transversal, coronal and sagittal plane).
      //This is necessary, because the SetUserTransform() method does not manipulate the vtkCamera.
      //(Without not all three planes would be visible).
      vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
      vtkSmartPointer<vtkMatrix4x4> matrix = vtkSmartPointer<vtkMatrix4x4>::New();
      Point3D origin;
      Vector3D right, bottom, normal;

      origin = planeGeometry->GetOrigin();
      right = planeGeometry->GetAxisVector(0); // right = Extent of Image in mm (worldspace)
      bottom = planeGeometry->GetAxisVector(1);
      normal = planeGeometry->GetNormal();

      right.Normalize();
      bottom.Normalize();
      normal.Normalize();

      matrix->SetElement(0, 0, right[0]);
      matrix->SetElement(1, 0, right[1]);
      matrix->SetElement(2, 0, right[2]);
      matrix->SetElement(0, 1, bottom[0]);
      matrix->SetElement(1, 1, bottom[1]);
      matrix->SetElement(2, 1, bottom[2]);
      matrix->SetElement(0, 2, normal[0]);
      matrix->SetElement(1, 2, normal[1]);
      matrix->SetElement(2, 2, normal[2]);
      matrix->SetElement(0, 3, origin[0]);
      matrix->SetElement(1, 3, origin[1]);
      matrix->SetElement(2, 3, origin[2]);
      matrix->SetElement(3, 0, 0.0);
      matrix->SetElement(3, 1, 0.0);
      matrix->SetElement(3, 2, 0.0);
      matrix->SetElement(3, 3, 1.0);

      trans->SetMatrix(matrix);
      //Transform the camera to the current position (transversal, coronal and sagittal plane).
      this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->ApplyTransform(trans);
    }

    this->Modified();

  }
}

void mitk::CameraController::Fit()
{
  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
  {
    this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(ComputeMaxParallelScale());

    this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()
      ->SetClippingRange(0.1, 1000000);
    //Reason for huge range: VTK seems to calculate the clipping planes wrong for small values. See VTK bug (id #7823) in VTK bugtracker.

    Point2D planePoint;
    planePoint[0] = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0)*0.5;
    planePoint[1] = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1)*0.5;
    MoveCameraToPoint(planePoint);
  }
}

void mitk::CameraController::SetScaleFactorInMMPerDisplayUnit( ScalarType scale )
{
  if ( this->GetRenderer()->GetMapperID() != BaseRenderer::Standard2D )
  {
    return;
  }

  this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(
    this->GetRenderer()->GetViewportSize()[1] * scale * 0.5 );
}
<commit_msg>moved itk::Modified to methods where it's not triggered on each rendering<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkCameraController.h"
#include "mitkVtkPropRenderer.h"
#include "mitkRenderingManager.h"
#include <vtkRenderWindowInteractor.h>
#include "vtkCommand.h"

#include "vtkCamera.h"
#include "vtkRenderer.h"
#include <vtkTransform.h>
#include <vtkSmartPointer.h>

mitk::CameraController::CameraController() :
  BaseController(),
  m_Renderer(NULL)
{
}

mitk::CameraController::~CameraController()
{}

mitk::ScalarType mitk::CameraController::ComputeMaxParallelScale()
{
  double widthInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0);
  double heightInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1);

  double dispHeight = this->GetRenderer()->GetViewportSize()[1]; //in pixel!
  double dispWidth = this->GetRenderer()->GetViewportSize()[0];

  //To get the right zooming factor, we need to set the (half) height to the vtk camera using SetParallelScale.
  //However, it could be, that our picture is so wide or the display so small, that we cannot take the height of the picture.
  //For a wide picture, we have to take the width and adapt the width so that our image fits to the screen.
  //But we can only set the height. Therefore, if the width is the limiting factor, we need to get the ratio of scaling
  //for the width and multiply it with the height, so that we have a modified height and set this one. Believe us, we figured it out...
  if ((dispWidth / widthInMM) < (dispHeight / heightInMM))
  {
    heightInMM = widthInMM / dispWidth * dispHeight;
  }

  return heightInMM *0.5;
}

void mitk::CameraController::AdjustConstrainedCameraPosition(mitk::Point2D &planePoint)
{
  //TODO: GetExtentInMM is calculated wrong for rotated planes, e.g. crosshair rotation (bug 19105)
  double widthInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0);
  double heightInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1);

  mitk::Point2D dispSizeInMM = this->GetRenderer()->GetViewportSizeInMM();

  double xMin, xMax, yMin, yMax;

  //different calculation of min/max if display is lager/smaller than image.
  //note, that the plane Position defines the middle of the display but is in image coordinates
  //([0,0] is defined by the image, so planePosition can sometimes be negative).
  if (dispSizeInMM[0] > widthInMM)
  {
    xMin = widthInMM - 0.5 * dispSizeInMM[0];
    xMax = 0.5 * dispSizeInMM[0] ;
  }
  else
  {
    xMin = 0.5 * dispSizeInMM[0];
    xMax = widthInMM - 0.5 * dispSizeInMM[0];
  }

  if (dispSizeInMM[1] > heightInMM)
  {
    yMin = heightInMM - 0.5 * dispSizeInMM[1];
    yMax = 0.5 * dispSizeInMM[1] ;
  }
  else
  {
    yMin = 0.5 * dispSizeInMM[1];
    yMax = heightInMM - 0.5 * dispSizeInMM[1];
  }

  if (planePoint[0] < xMin) { planePoint[0] = xMin; }
  if (planePoint[1] < yMin) { planePoint[1] = yMin; }
  if (planePoint[0] > xMax) { planePoint[0] = xMax; }
  if (planePoint[1] > yMax) { planePoint[1] = yMax; }
}

void mitk::CameraController::SetViewToAnterior()
{
  this->SetStandardView(ANTERIOR);
}

void mitk::CameraController::SetViewToPosterior()
{
  this->SetStandardView(POSTERIOR);
}

void mitk::CameraController::SetViewToSinister()
{
  this->SetStandardView(SINISTER);
}

void mitk::CameraController::SetViewToDexter()
{
  this->SetStandardView(DEXTER);
}

void mitk::CameraController::SetViewToCranial()
{
  this->SetStandardView(CRANIAL);
}

void mitk::CameraController::SetViewToCaudal()
{
  this->SetStandardView(CAUDAL);
}

void mitk::CameraController::SetStandardView(mitk::CameraController::StandardView view)
{
  const mitk::VtkPropRenderer* glRenderer = dynamic_cast<const mitk::VtkPropRenderer*>(m_Renderer);
  if (glRenderer == NULL)
    return;
  vtkRenderer* vtkRenderer = glRenderer->GetVtkRenderer();
  assert(vtkRenderer);

  mitk::BoundingBox::Pointer bb;
  mitk::DataStorage* ds = m_Renderer->GetDataStorage();
  if (ds != NULL)
    bb = ds->ComputeBoundingBox();
  else
    return;

  if (m_Renderer->GetMapperID() == mitk::BaseRenderer::Standard3D)
  {
    //set up the view for the 3D render window. The views for 2D are set up in the mitkVtkPropRenderer
    mitk::Point3D middle = bb->GetCenter();
    vtkRenderer->GetActiveCamera()->SetFocalPoint(middle[0], middle[1], middle[2]);
    switch (view)
    {
    case ANTERIOR:
    case POSTERIOR:
    case SINISTER:
    case DEXTER:
      vtkRenderer->GetActiveCamera()->SetViewUp(0, 0, 1);
      break;
    case CRANIAL:
    case CAUDAL:
      vtkRenderer->GetActiveCamera()->SetViewUp(0, -1, 0);
      break;
    }
    switch (view)
    {
    case ANTERIOR:
      vtkRenderer->GetActiveCamera()->SetPosition(middle[0], -100000, middle[2]);
      break;
    case POSTERIOR:
      vtkRenderer->GetActiveCamera()->SetPosition(middle[0], +100000, middle[2]);
      break;
    case SINISTER:
      vtkRenderer->GetActiveCamera()->SetPosition(+100000, middle[1], middle[2]);
      break;
    case DEXTER:
      vtkRenderer->GetActiveCamera()->SetPosition(-100000, middle[1], middle[2]);
      break;
    case CRANIAL:
      vtkRenderer->GetActiveCamera()->SetPosition(middle[0], middle[1], 100000);
      break;
    case CAUDAL:
      vtkRenderer->GetActiveCamera()->SetPosition(middle[0], middle[1], -100000);
      break;
    }
    vtkRenderer->ResetCamera();

    vtkRenderer->ResetCameraClippingRange();
  }

  mitk::RenderingManager* rm = m_Renderer->GetRenderingManager();
  rm->RequestUpdateAll();
}

void mitk::CameraController::MoveCameraToPoint(const mitk::Point2D& planePoint)
{
  Point2D moveToPoint = planePoint;
  AdjustCameraToPlane(moveToPoint);

  this->Modified();
}

void mitk::CameraController::MoveBy(const mitk::Vector2D &moveVectorInMM)
{
  MoveCameraToPoint(GetCameraPositionOnPlane() + moveVectorInMM);
}

void mitk::CameraController::Zoom(ScalarType factor, const Point2D& zoomPointInMM)
{
  if (factor <= 0.0) return;
  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
  {
    double parallelScale = this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->GetParallelScale()/factor;
    if (this->GetRenderer()->GetConstrainZoomingAndPanning() && factor < 1.0)
    {
      double maxParallelScale = ComputeMaxParallelScale();
      if (maxParallelScale - parallelScale * factor  < mitk::eps)//this is not the famous 05-bug... Return if already near max zooming
        return;

      if (parallelScale > maxParallelScale)
      {
        parallelScale = maxParallelScale;
      }
    }
    this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(parallelScale);
    //Move camera in a way that the clicked point stays visible on the display where it was.
    Point2D planePoint = GetCameraPositionOnPlane();
    MoveCameraToPoint(planePoint + ((zoomPointInMM - planePoint) * (factor - 1)));
  }
}

mitk::Point2D mitk::CameraController::GetCameraPositionOnPlane()
{
  Point2D CamPosOnPlane;
  CamPosOnPlane[0] = this->GetRenderer()->GetVtkRenderer()->GetCenter()[0];
  CamPosOnPlane[1] = this->GetRenderer()->GetVtkRenderer()->GetCenter()[1];
  this->GetRenderer()->DisplayToPlane(CamPosOnPlane, CamPosOnPlane);
  return CamPosOnPlane;
}

void mitk::CameraController::AdjustCameraToPlane()
{
  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
  {
    AdjustCameraToPlane(GetCameraPositionOnPlane());
  }
}

void mitk::CameraController::AdjustCameraToPlane(const Point2D& PlanePoint)
{
  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
  {
    Point2D _planePoint = PlanePoint;//PlanePoint is const...
    if (this->GetRenderer()->GetConstrainZoomingAndPanning())
    {
      double parallelScale = this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->GetParallelScale();
      double maxParallelScale = ComputeMaxParallelScale();
      if (parallelScale > maxParallelScale)
      {
        this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(maxParallelScale);
      }
      AdjustConstrainedCameraPosition(_planePoint);
    }
    const PlaneGeometry *planeGeometry = this->GetRenderer()->GetCurrentWorldPlaneGeometry();
    if (planeGeometry != NULL)
    {
      this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(0, 1, 0); //set the view-up for the camera
      this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(_planePoint[0], _planePoint[1], 900000);
      this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(_planePoint[0], _planePoint[1], 0);
      //Transform the camera to the current position (transversal, coronal and sagittal plane).
      //This is necessary, because the SetUserTransform() method does not manipulate the vtkCamera.
      //(Without not all three planes would be visible).
      vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
      vtkSmartPointer<vtkMatrix4x4> matrix = vtkSmartPointer<vtkMatrix4x4>::New();
      Point3D origin;
      Vector3D right, bottom, normal;

      origin = planeGeometry->GetOrigin();
      right = planeGeometry->GetAxisVector(0); // right = Extent of Image in mm (worldspace)
      bottom = planeGeometry->GetAxisVector(1);
      normal = planeGeometry->GetNormal();

      right.Normalize();
      bottom.Normalize();
      normal.Normalize();

      matrix->SetElement(0, 0, right[0]);
      matrix->SetElement(1, 0, right[1]);
      matrix->SetElement(2, 0, right[2]);
      matrix->SetElement(0, 1, bottom[0]);
      matrix->SetElement(1, 1, bottom[1]);
      matrix->SetElement(2, 1, bottom[2]);
      matrix->SetElement(0, 2, normal[0]);
      matrix->SetElement(1, 2, normal[1]);
      matrix->SetElement(2, 2, normal[2]);
      matrix->SetElement(0, 3, origin[0]);
      matrix->SetElement(1, 3, origin[1]);
      matrix->SetElement(2, 3, origin[2]);
      matrix->SetElement(3, 0, 0.0);
      matrix->SetElement(3, 1, 0.0);
      matrix->SetElement(3, 2, 0.0);
      matrix->SetElement(3, 3, 1.0);

      trans->SetMatrix(matrix);
      //Transform the camera to the current position (transversal, coronal and sagittal plane).
      this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->ApplyTransform(trans);
    }
  }
}

void mitk::CameraController::Fit()
{
  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
  {
    this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(ComputeMaxParallelScale());

    this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()
      ->SetClippingRange(0.1, 1000000);
    //Reason for huge range: VTK seems to calculate the clipping planes wrong for small values. See VTK bug (id #7823) in VTK bugtracker.

    Point2D planePoint;
    planePoint[0] = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0)*0.5;
    planePoint[1] = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1)*0.5;
    MoveCameraToPoint(planePoint);
  }
}

void mitk::CameraController::SetScaleFactorInMMPerDisplayUnit( ScalarType scale )
{
  if ( this->GetRenderer()->GetMapperID() != BaseRenderer::Standard2D )
  {
    return;
  }

  this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(
    this->GetRenderer()->GetViewportSize()[1] * scale * 0.5 );

  this->Modified();

}
<|endoftext|>