<commit_before>#include "Test.h"
#include "DataStore/MemoryDataStore.h"
#include "DataStore/DataStoreWriteStream.h"
#include "DataStore/DataStoreReadStream.h"

#include <future>

using namespace Flourish;

class MemoryDataStoreTests : public ::testing::Test
{
protected:
    MemoryDataStore dataStore;

    void SetUp() override
    {
        SetupCallWait();
    }

    void SetupCallWait()
    {
        promise = std::promise<void>();
        future = promise.get_future();
    }

    void TriggerCallComplete()
    {
        promise.set_value();
    }

    void ExpectCallToCompleteInTime()
    {
        auto result = future.wait_for(std::chrono::seconds(1));
            EXPECT_EQUAL(result, std::future_status::ready) << "Callback was not called in time";
    }

    void WriteDataToPath(const DataStorePath& path)
    {
        SetupCallWait();
        dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam)
        {
            TriggerCallComplete();
        });
        ExpectCallToCompleteInTime();
        SetupCallWait();
    }

private:
    std::promise<void> promise;
    std::future<void> future;
};

TEST_F(MemoryDataStoreTests, ExistsReturnsFalseWithNoDataOrDirectory)
{
    EXPECT_FALSE(dataStore.Exists(DataStorePath("does/not/exist")));
}

TEST_F(MemoryDataStoreTests, ExistsReturnsTrueAfterDataWrite)
{
    DataStorePath path("will/be/written/to");

    dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam result)
    {
        EXPECT_FALSE(result.HasError());
        EXPECT_TRUE(dataStore.Exists(path));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, IsDirReturnsFalseForNonExistantPath)
{
    EXPECT_FALSE(dataStore.IsDir(DataStorePath("does/not/exist")));
}

TEST_F(MemoryDataStoreTests, IsDataReturnsFalseForNonExistantPath)
{
    EXPECT_FALSE(dataStore.IsData(DataStorePath("does/not/exist")));
}

TEST_F(MemoryDataStoreTests, IsDirReturnsFalseForData)
{
    DataStorePath path("will/be/written/to");

    dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam result)
    {
        EXPECT_FALSE(result.HasError());
        EXPECT_FALSE(dataStore.IsDir(path));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, IsDataReturnsTrueForData)
{
    DataStorePath path("will/be/written/to");

    dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam result)
    {
        EXPECT_FALSE(result.HasError());
        EXPECT_TRUE(dataStore.IsData(path));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, OpenForReadOnNonExistantPathCreatesError)
{
    DataStorePath path("does/not/exist");

    dataStore.OpenForRead(path, [&](DataStoreReadCallbackParam result)
    {
        EXPECT_TRUE(result.HasError());
            EXPECT_STRING_EQUAL(result.GetError(), "Path 'does/not/exist' does not exist in data store");
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, CanReadBackWrittenData)
{
    DataStorePath path("will/be/written/to");

    dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam openResult)
    {
        EXPECT_FALSE(openResult.HasError());
        auto dataToWrite = std::string("some data");
        openResult.Value()->Write(dataToWrite.c_str(), dataToWrite.length());
        openResult.Value()->Flush(
            [&](DataStoreWriteCallbackParam writeResult)
            {
                EXPECT_FALSE(writeResult.HasError());
                TriggerCallComplete();
            });
    });

    ExpectCallToCompleteInTime();
    SetupCallWait();

    dataStore.OpenForRead(path, [&](DataStoreReadCallbackParam readResult)
    {
            EXPECT_STRING_EQUAL(static_cast<const char*>(readResult.Value()->Data()), "some data");
        TriggerCallComplete();
    });
    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, WritingDataCreatesParentDirectories)
{
    DataStorePath path("will/be/written/to");

    dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam result)
    {
        EXPECT_TRUE(dataStore.IsDir(DataStorePath("")));
        EXPECT_TRUE(dataStore.IsDir(DataStorePath("will")));
        EXPECT_TRUE(dataStore.IsDir(DataStorePath("will/be")));
        EXPECT_TRUE(dataStore.IsDir(DataStorePath("will/be/written")));
        EXPECT_FALSE(result.HasError());
        EXPECT_FALSE(dataStore.IsDir(path));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, EnumerateFindsDataAndDirectories)
{
    WriteDataToPath(DataStorePath("some/path/to/file.txt"));
    WriteDataToPath(DataStorePath("some/path/to/another_file.txt"));
    WriteDataToPath(DataStorePath("some/path/to/subdir/with_file.txt"));

    std::vector<DataStorePath> result;
    dataStore.Enumerate(DataStorePath("some/path/to"), result);

    // We don't care what order things are in, so just assert that
    // all the expected elements (and only those) exist
        EXPECT_EQUAL(result.size(), 3);
        EXPECT_NOT_EQUAL(std::find(result.begin(), result.end(), DataStorePath("some/path/to/file.txt")), result.end());
        EXPECT_NOT_EQUAL(std::find(result.begin(), result.end(), DataStorePath("some/path/to/another_file.txt")), result.end());
        EXPECT_NOT_EQUAL(std::find(result.begin(), result.end(), DataStorePath("some/path/to/subdir")), result.end());
}

TEST_F(MemoryDataStoreTests, SequentialWritesAppend)
{
    DataStorePath path("will/be/written/to");

    dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam openResult)
    {
        std::string firstData("first data");
        openResult.Value()->Write(firstData.c_str(), firstData.length());
        openResult.Value()->Flush([&](DataStoreWriteCallbackParam writeResult)
                                  {
                                      std::string secondData(" second data");
                                      writeResult.Value()->Write(secondData.c_str(), secondData.length());
                                      writeResult.Value()->Flush([&](DataStoreWriteCallbackParam secondWriteResult)
                                                                 {
                                                                     TriggerCallComplete();
                                                                 });
                                  });
    });

    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore.OpenForRead(path, [&](DataStoreReadCallbackParam readResult)
    {
            EXPECT_STRING_EQUAL("first data second data", static_cast<const char*>(readResult.Value()->Data()));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, ConsecutiveWritesOverwrite)
{
    DataStorePath path("will/be/written/to");

    dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam openResult)
    {
        std::string firstData("first data");
        openResult.Value()->Write(firstData.c_str(), firstData.length());
        openResult.Value()->Flush([&](DataStoreWriteCallbackParam writeResult)
                                  {
                                      TriggerCallComplete();
                                  });
    });

    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam openResult)
    {
        std::string secondData("second data");
        openResult.Value()->Write(secondData.c_str(), secondData.length());
        openResult.Value()->Flush([&](DataStoreWriteCallbackParam writeResult)
                                  {
                                      TriggerCallComplete();
                                  });
    });

    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore.OpenForRead(path, [&](DataStoreReadCallbackParam readResult)
    {
            EXPECT_STRING_EQUAL("second data", static_cast<const char*>(readResult.Value()->Data()));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, ConsecutiveAppendsAppend)
{
    DataStorePath path("will/be/written/to");

    dataStore.OpenForAppend(path, [&](DataStoreWriteCallbackParam openResult)
    {
        std::string firstData("first data");
        openResult.Value()->Write(firstData.c_str(), firstData.length());
        openResult.Value()->Flush([&](DataStoreWriteCallbackParam writeResult)
                                  {
                                      TriggerCallComplete();
                                  });
    });

    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore.OpenForAppend(path, [&](DataStoreWriteCallbackParam openResult)
    {
        std::string firstData(" second data");
        openResult.Value()->Write(firstData.c_str(), firstData.length());
        openResult.Value()->Flush([&](DataStoreWriteCallbackParam writeResult)
                                  {
                                      TriggerCallComplete();
                                  });
    });

    SetupCallWait();
    dataStore.OpenForRead(path, [&](DataStoreReadCallbackParam readResult)
    {
            EXPECT_STRING_EQUAL("first data second data", static_cast<const char*>(readResult.Value()->Data()));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, ConsecutiveReadsReadSameData)
{
    // Note that this test will fail if a read buffer is smaller than
    // the write buffer
    DataStorePath path("will/be/written/to");

    dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam openResult)
    {
        // Write a buffer full of 1's
        const auto openStream = openResult.Value();
        auto data = new uint8_t[openStream->Available()];
        std::fill_n(data, openStream->Available(), 1);
        openStream->Write(data, openStream->Available());
        delete[] data;
        openStream->Flush([&](DataStoreWriteCallbackParam writeResult)
                          {
                              // Write a buffer full of 2's
                              const auto writeStream = writeResult.Value();
                              auto data = new uint8_t[writeStream->Available()];
                              std::fill_n(data, writeStream->Available(), 2);
                              writeStream->Write(data, writeStream->Available());
                              delete[] data;
                              writeStream->Flush([&](DataStoreWriteCallbackParam)
                                                 {
                                                     TriggerCallComplete();
                                                 });
                          });
    });

    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore.OpenForRead(path, [&](DataStoreReadCallbackParam openResult)
    {
        const auto openStream = openResult.Value();
        const auto openData = static_cast<const uint8_t*>(openStream->Data());
            EXPECT_EQUAL(openData[0], 1);
        TriggerCallComplete();
    });
    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore.OpenForRead(path, [&](DataStoreReadCallbackParam openResult)
    {
        const auto openStream = openResult.Value();
        const auto openData = static_cast<const uint8_t*>(openStream->Data());
            EXPECT_EQUAL(openData[0], 1);
        TriggerCallComplete();
    });
    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, ReadToEndOfDataSetsFlag)
{
    DataStorePath path("will/be/written/to");

    WriteDataToPath(path);

    dataStore.OpenForRead(path, [&](DataStoreReadCallbackParam openResult){
        EXPECT_TRUE(openResult.Value()->EndOfData());
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, RefreshOnEndOfDataStreamCreatesError)
{
    DataStorePath path("will/be/written/to");

    WriteDataToPath(path);

    dataStore.OpenForRead(path, [&](DataStoreReadCallbackParam openResult){
        EXPECT_TRUE(openResult.Value()->EndOfData());
        openResult.Value()->Refresh([&](DataStoreReadCallbackParam readResult){
            EXPECT_TRUE(readResult.HasError());
                EXPECT_STRING_EQUAL(readResult.GetError(), "Attempted to read past end of stream  (Path: 'will/be/written/to')");
            TriggerCallComplete();
        });
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, OpenMultipleStreamsToSamePathCreatesError)
{
    DataStorePath path("some/path");

    dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam){
       dataStore.OpenForRead(path, [&](DataStoreReadCallbackParam read)
        {
            EXPECT_TRUE(read.HasError());
                EXPECT_STRING_EQUAL(read.GetError(), "Path: 'some/path' is already open in another stream");

            dataStore.OpenForAppend(path, [&](DataStoreWriteCallbackParam append)
            {
                EXPECT_TRUE(append.HasError());
                    EXPECT_STRING_EQUAL(append.GetError(), "Path: 'some/path' is already open in another stream");

                dataStore.OpenForWrite(path, [&](DataStoreWriteCallbackParam secondWrite){
                    EXPECT_TRUE(secondWrite.HasError());
                        EXPECT_STRING_EQUAL(secondWrite.GetError(), "Path: 'some/path' is already open in another stream");
                    TriggerCallComplete();
                });
            });
        });
    });

    ExpectCallToCompleteInTime();
}<commit_msg>MemoryDataStoreTests use a ptr, starting preperations testing all data stores<commit_after>#include "Test.h"
#include "DataStore/MemoryDataStore.h"
#include "DataStore/DataStoreWriteStream.h"
#include "DataStore/DataStoreReadStream.h"

#include <future>

using namespace Flourish;

class MemoryDataStoreTests : public ::testing::Test
{
public:
    MemoryDataStoreTests()
        : dataStore(new MemoryDataStore())
    {
        SetupCallWait();
    }

    virtual ~MemoryDataStoreTests()
    {
        delete dataStore;
    }

protected:
    MemoryDataStore* dataStore;

    void SetupCallWait()
    {
        promise = std::promise<void>();
        future = promise.get_future();
    }

    void TriggerCallComplete()
    {
        promise.set_value();
    }

    void ExpectCallToCompleteInTime()
    {
        auto result = future.wait_for(std::chrono::seconds(1));
            EXPECT_EQUAL(result, std::future_status::ready) << "Callback was not called in time";
    }

    void WriteDataToPath(const DataStorePath& path)
    {
        SetupCallWait();
        dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam)
        {
            TriggerCallComplete();
        });
        ExpectCallToCompleteInTime();
        SetupCallWait();
    }

private:
    std::promise<void> promise;
    std::future<void> future;
};

TEST_F(MemoryDataStoreTests, ExistsReturnsFalseWithNoDataOrDirectory)
{
    EXPECT_FALSE(dataStore->Exists(DataStorePath("does/not/exist")));
}

TEST_F(MemoryDataStoreTests, ExistsReturnsTrueAfterDataWrite)
{
    DataStorePath path("will/be/written/to");

    dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam result)
    {
        EXPECT_FALSE(result.HasError());
        EXPECT_TRUE(dataStore->Exists(path));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, IsDirReturnsFalseForNonExistantPath)
{
    EXPECT_FALSE(dataStore->IsDir(DataStorePath("does/not/exist")));
}

TEST_F(MemoryDataStoreTests, IsDataReturnsFalseForNonExistantPath)
{
    EXPECT_FALSE(dataStore->IsData(DataStorePath("does/not/exist")));
}

TEST_F(MemoryDataStoreTests, IsDirReturnsFalseForData)
{
    DataStorePath path("will/be/written/to");

    dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam result)
    {
        EXPECT_FALSE(result.HasError());
        EXPECT_FALSE(dataStore->IsDir(path));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, IsDataReturnsTrueForData)
{
    DataStorePath path("will/be/written/to");

    dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam result)
    {
        EXPECT_FALSE(result.HasError());
        EXPECT_TRUE(dataStore->IsData(path));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, OpenForReadOnNonExistantPathCreatesError)
{
    DataStorePath path("does/not/exist");

    dataStore->OpenForRead(path, [&](DataStoreReadCallbackParam result)
    {
        EXPECT_TRUE(result.HasError());
            EXPECT_STRING_EQUAL(result.GetError(), "Path 'does/not/exist' does not exist in data store");
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, CanReadBackWrittenData)
{
    DataStorePath path("will/be/written/to");

    dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam openResult)
    {
        EXPECT_FALSE(openResult.HasError());
        auto dataToWrite = std::string("some data");
        openResult.Value()->Write(dataToWrite.c_str(), dataToWrite.length());
        openResult.Value()->Flush(
            [&](DataStoreWriteCallbackParam writeResult)
            {
                EXPECT_FALSE(writeResult.HasError());
                TriggerCallComplete();
            });
    });

    ExpectCallToCompleteInTime();
    SetupCallWait();

    dataStore->OpenForRead(path, [&](DataStoreReadCallbackParam readResult)
    {
            EXPECT_STRING_EQUAL(static_cast<const char*>(readResult.Value()->Data()), "some data");
        TriggerCallComplete();
    });
    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, WritingDataCreatesParentDirectories)
{
    DataStorePath path("will/be/written/to");

    dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam result)
    {
        EXPECT_TRUE(dataStore->IsDir(DataStorePath("")));
        EXPECT_TRUE(dataStore->IsDir(DataStorePath("will")));
        EXPECT_TRUE(dataStore->IsDir(DataStorePath("will/be")));
        EXPECT_TRUE(dataStore->IsDir(DataStorePath("will/be/written")));
        EXPECT_FALSE(result.HasError());
        EXPECT_FALSE(dataStore->IsDir(path));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, EnumerateFindsDataAndDirectories)
{
    WriteDataToPath(DataStorePath("some/path/to/file.txt"));
    WriteDataToPath(DataStorePath("some/path/to/another_file.txt"));
    WriteDataToPath(DataStorePath("some/path/to/subdir/with_file.txt"));

    std::vector<DataStorePath> result;
    dataStore->Enumerate(DataStorePath("some/path/to"), result);

    // We don't care what order things are in, so just assert that
    // all the expected elements (and only those) exist
        EXPECT_EQUAL(result.size(), 3);
        EXPECT_NOT_EQUAL(std::find(result.begin(), result.end(), DataStorePath("some/path/to/file.txt")), result.end());
        EXPECT_NOT_EQUAL(std::find(result.begin(), result.end(), DataStorePath("some/path/to/another_file.txt")), result.end());
        EXPECT_NOT_EQUAL(std::find(result.begin(), result.end(), DataStorePath("some/path/to/subdir")), result.end());
}

TEST_F(MemoryDataStoreTests, SequentialWritesAppend)
{
    DataStorePath path("will/be/written/to");

    dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam openResult)
    {
        std::string firstData("first data");
        openResult.Value()->Write(firstData.c_str(), firstData.length());
        openResult.Value()->Flush([&](DataStoreWriteCallbackParam writeResult)
                                  {
                                      std::string secondData(" second data");
                                      writeResult.Value()->Write(secondData.c_str(), secondData.length());
                                      writeResult.Value()->Flush([&](DataStoreWriteCallbackParam secondWriteResult)
                                                                 {
                                                                     TriggerCallComplete();
                                                                 });
                                  });
    });

    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore->OpenForRead(path, [&](DataStoreReadCallbackParam readResult)
    {
            EXPECT_STRING_EQUAL("first data second data", static_cast<const char*>(readResult.Value()->Data()));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, ConsecutiveWritesOverwrite)
{
    DataStorePath path("will/be/written/to");

    dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam openResult)
    {
        std::string firstData("first data");
        openResult.Value()->Write(firstData.c_str(), firstData.length());
        openResult.Value()->Flush([&](DataStoreWriteCallbackParam writeResult)
                                  {
                                      TriggerCallComplete();
                                  });
    });

    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam openResult)
    {
        std::string secondData("second data");
        openResult.Value()->Write(secondData.c_str(), secondData.length());
        openResult.Value()->Flush([&](DataStoreWriteCallbackParam writeResult)
                                  {
                                      TriggerCallComplete();
                                  });
    });

    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore->OpenForRead(path, [&](DataStoreReadCallbackParam readResult)
    {
            EXPECT_STRING_EQUAL("second data", static_cast<const char*>(readResult.Value()->Data()));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, ConsecutiveAppendsAppend)
{
    DataStorePath path("will/be/written/to");

    dataStore->OpenForAppend(path, [&](DataStoreWriteCallbackParam openResult)
    {
        std::string firstData("first data");
        openResult.Value()->Write(firstData.c_str(), firstData.length());
        openResult.Value()->Flush([&](DataStoreWriteCallbackParam writeResult)
                                  {
                                      TriggerCallComplete();
                                  });
    });

    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore->OpenForAppend(path, [&](DataStoreWriteCallbackParam openResult)
    {
        std::string firstData(" second data");
        openResult.Value()->Write(firstData.c_str(), firstData.length());
        openResult.Value()->Flush([&](DataStoreWriteCallbackParam writeResult)
                                  {
                                      TriggerCallComplete();
                                  });
    });

    SetupCallWait();
    dataStore->OpenForRead(path, [&](DataStoreReadCallbackParam readResult)
    {
            EXPECT_STRING_EQUAL("first data second data", static_cast<const char*>(readResult.Value()->Data()));
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, ConsecutiveReadsReadSameData)
{
    // Note that this test will fail if a read buffer is smaller than
    // the write buffer
    DataStorePath path("will/be/written/to");

    dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam openResult)
    {
        // Write a buffer full of 1's
        const auto openStream = openResult.Value();
        auto data = new uint8_t[openStream->Available()];
        std::fill_n(data, openStream->Available(), 1);
        openStream->Write(data, openStream->Available());
        delete[] data;
        openStream->Flush([&](DataStoreWriteCallbackParam writeResult)
                          {
                              // Write a buffer full of 2's
                              const auto writeStream = writeResult.Value();
                              auto data = new uint8_t[writeStream->Available()];
                              std::fill_n(data, writeStream->Available(), 2);
                              writeStream->Write(data, writeStream->Available());
                              delete[] data;
                              writeStream->Flush([&](DataStoreWriteCallbackParam)
                                                 {
                                                     TriggerCallComplete();
                                                 });
                          });
    });

    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore->OpenForRead(path, [&](DataStoreReadCallbackParam openResult)
    {
        const auto openStream = openResult.Value();
        const auto openData = static_cast<const uint8_t*>(openStream->Data());
            EXPECT_EQUAL(openData[0], 1);
        TriggerCallComplete();
    });
    ExpectCallToCompleteInTime();

    SetupCallWait();
    dataStore->OpenForRead(path, [&](DataStoreReadCallbackParam openResult)
    {
        const auto openStream = openResult.Value();
        const auto openData = static_cast<const uint8_t*>(openStream->Data());
            EXPECT_EQUAL(openData[0], 1);
        TriggerCallComplete();
    });
    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, ReadToEndOfDataSetsFlag)
{
    DataStorePath path("will/be/written/to");

    WriteDataToPath(path);

    dataStore->OpenForRead(path, [&](DataStoreReadCallbackParam openResult)
    {
        EXPECT_TRUE(openResult.Value()->EndOfData());
        TriggerCallComplete();
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, RefreshOnEndOfDataStreamCreatesError)
{
    DataStorePath path("will/be/written/to");

    WriteDataToPath(path);

    dataStore->OpenForRead(path, [&](DataStoreReadCallbackParam openResult)
    {
        EXPECT_TRUE(openResult.Value()->EndOfData());
        openResult.Value()->Refresh([&](DataStoreReadCallbackParam readResult)
                                    {
                                        EXPECT_TRUE(readResult.HasError());
                                            EXPECT_STRING_EQUAL(readResult.GetError(), "Attempted to read past end of stream  (Path: 'will/be/written/to')");
                                        TriggerCallComplete();
                                    });
    });

    ExpectCallToCompleteInTime();
}

TEST_F(MemoryDataStoreTests, OpenMultipleStreamsToSamePathCreatesError)
{
    DataStorePath path("some/path");

    dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam)
    {
        dataStore->OpenForRead(path, [&](DataStoreReadCallbackParam read)
        {
            EXPECT_TRUE(read.HasError());
                EXPECT_STRING_EQUAL(read.GetError(), "Path: 'some/path' is already open in another stream");

            dataStore->OpenForAppend(path, [&](DataStoreWriteCallbackParam append)
            {
                EXPECT_TRUE(append.HasError());
                    EXPECT_STRING_EQUAL(append.GetError(), "Path: 'some/path' is already open in another stream");

                dataStore->OpenForWrite(path, [&](DataStoreWriteCallbackParam secondWrite)
                {
                    EXPECT_TRUE(secondWrite.HasError());
                        EXPECT_STRING_EQUAL(secondWrite.GetError(), "Path: 'some/path' is already open in another stream");
                    TriggerCallComplete();
                });
            });
        });
    });

    ExpectCallToCompleteInTime();
}<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itkImageRegion.h>
#include <itkRGBPixel.h>
#include <itkTestingMacros.h>
#include <fstream>
#include <iostream>
#include <string>

namespace
{
bool
WriteSmallBmp(const std::string & filename)
{
  const char    bmp_raw[] = { 66, 77, 30, 0, 0, 0, 0, 0, 0, 0,  26, 0, 0, 0,  12,
                           0,  0,  0,  1, 0, 1, 0, 1, 0, 24, 0,  0, 0, -1, 0 };
  std::ofstream bmp_output(filename, std::ios::binary);
  if (!bmp_output)
  {
    std::cerr << "Cannot open file for writing (" << filename << ")\n";
    return false;
  }
  bmp_output.write(bmp_raw, sizeof(bmp_raw));
  bmp_output.close();
  if (!bmp_output)
  {
    std::cerr << "Cannot write to file (" << filename << ")\n";
    return false;
  }
  return true;
}
} // namespace

int
itkBMPImageIOTestExtension(int argc, char * argv[])
{
  if (argc < 2)
  {
    std::cerr << "Usage: " << itkNameOfTestExecutableMacro(argv) << " temporary_output_folder\n";
    return EXIT_FAILURE;
  }

  // filename shouldn't have bmp extension
  const std::string filename(std::string(argv[1]) + "/itkBMPImageIOTestExtension");

  ITK_TEST_EXPECT_TRUE(WriteSmallBmp(filename));

  using ComponentType = unsigned char;
  using PixelType = itk::RGBPixel<ComponentType>;
  auto reader = itk::ImageFileReader<itk::Image<PixelType, 2>>::New();
  reader->SetFileName(filename);
  ITK_TRY_EXPECT_NO_EXCEPTION(reader->Update());

  const itk::ImageRegion<2> expected_region{ itk::Size<2>{ 1, 1 } };
  const ComponentType       expected_rgb[] = { 255, 0, 0 };
  const PixelType           expected_pixel{ expected_rgb };
  // check that data has been actually read
  auto image = reader->GetOutput();
  ITK_TEST_EXPECT_EQUAL(image->GetLargestPossibleRegion(), expected_region);
  ITK_TEST_EXPECT_EQUAL(image->GetPixel({ 0, 0 }), expected_pixel);
  std::cout << "Test finished\n";
  return EXIT_SUCCESS;
}
<commit_msg>STYLE: Add additional braces around initialization of subobject<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itkImageRegion.h>
#include <itkRGBPixel.h>
#include <itkTestingMacros.h>
#include <fstream>
#include <iostream>
#include <string>

namespace
{
bool
WriteSmallBmp(const std::string & filename)
{
  const char    bmp_raw[] = { 66, 77, 30, 0, 0, 0, 0, 0, 0, 0,  26, 0, 0, 0,  12,
                           0,  0,  0,  1, 0, 1, 0, 1, 0, 24, 0,  0, 0, -1, 0 };
  std::ofstream bmp_output(filename, std::ios::binary);
  if (!bmp_output)
  {
    std::cerr << "Cannot open file for writing (" << filename << ")\n";
    return false;
  }
  bmp_output.write(bmp_raw, sizeof(bmp_raw));
  bmp_output.close();
  if (!bmp_output)
  {
    std::cerr << "Cannot write to file (" << filename << ")\n";
    return false;
  }
  return true;
}
} // namespace

int
itkBMPImageIOTestExtension(int argc, char * argv[])
{
  if (argc < 2)
  {
    std::cerr << "Usage: " << itkNameOfTestExecutableMacro(argv) << " temporary_output_folder\n";
    return EXIT_FAILURE;
  }

  // filename shouldn't have bmp extension
  const std::string filename(std::string(argv[1]) + "/itkBMPImageIOTestExtension");

  ITK_TEST_EXPECT_TRUE(WriteSmallBmp(filename));

  using ComponentType = unsigned char;
  using PixelType = itk::RGBPixel<ComponentType>;
  auto reader = itk::ImageFileReader<itk::Image<PixelType, 2>>::New();
  reader->SetFileName(filename);
  ITK_TRY_EXPECT_NO_EXCEPTION(reader->Update());

  const itk::ImageRegion<2> expected_region{ itk::Size<2>{ { 1, 1 } } };
  const ComponentType       expected_rgb[] = { 255, 0, 0 };
  const PixelType           expected_pixel{ expected_rgb };
  // check that data has been actually read
  auto image = reader->GetOutput();
  ITK_TEST_EXPECT_EQUAL(image->GetLargestPossibleRegion(), expected_region);
  ITK_TEST_EXPECT_EQUAL(image->GetPixel({ { 0, 0 } }), expected_pixel);
  std::cout << "Test finished\n";
  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include <vector>

#include <fblualib/LuaUtils.h>
#include <fblualib/Reactor.h>
#include <fblualib/UserData.h>
#include <folly/io/async/EventBase.h>
#include <folly/MoveWrapper.h>

namespace fblualib {

namespace {

class Reactor : public folly::Executor {
  enum : int {
    kImmediateCallbacksTable = 1,
    kDelayedCallbacksTable = 2,
  };
 public:
  enum LookupCallback : int {
    kNotFound = 0,
    kRunnable = 1,
    kDelayed = 2,
  };

  explicit Reactor(lua_State* L);
  ~Reactor();

  void add(folly::Func func) override;

  int luaLoop(lua_State* L);
  int luaAddCallback(lua_State* L);
  int luaAddCallbackDelayed(lua_State* L);
  int luaLookupCallback(lua_State* L);
  int luaRemoveCallback(lua_State* L);

  // Return a lightuserdata object that is a pointer to the corresponding
  // folly::Executor.
  int luaGetExecutor(lua_State* L);
  int luaGetEventBase(lua_State* L);
  int luaGC(lua_State* L);

 private:
  int doLoop(lua_State* L);

  void pushTable(lua_State* L, int table);
  int doAddCallback(lua_State* L);
  bool doLookupOrRemoveCallback(lua_State* L, int key, bool remove);
  int doLuaLookupOrRemoveCallback(lua_State* L, bool remove);
  void doAddDelayedCallback(lua_State* L, int key);

  int seq_;
  std::unique_ptr<folly::EventBase> eb_;
};

Reactor::Reactor(lua_State* L)
  : seq_(0),
    eb_(std::make_unique<folly::EventBase>()) {
  lua_pushlightuserdata(L, this);
  lua_createtable(L, 2, 0);
  lua_newtable(L);
  lua_rawseti(L, -2, kImmediateCallbacksTable);
  lua_newtable(L);
  lua_rawseti(L, -2, kDelayedCallbacksTable);
  lua_settable(L, LUA_REGISTRYINDEX);
}

Reactor::~Reactor() {
  DCHECK(!eb_);  // luaGC must have been called
}

int Reactor::luaGC(lua_State* L) {
  // This is fugly.
  //
  // The EventBase has pointers to this (via callbacks added using add()),
  // and it can run callbacks during destruction. So we must make sure to
  // drain it explicitly.
  //
  // Even more, we normally only execute callbacks once we get back to Lua
  // (via luaLoop), so even if we drained the EventBase here, those callbacks
  // would never get to run.
  //
  // So we have to drain it with a real Lua state.
  //
  // We'll first destroy the EventBase (running any callbacks that need to run
  // during destruction) then drain our callback tables.

  // First, make sure eb_ is nullptr, so that any attempt to use it (perhaps
  // by referring to this Reactor recursively) fails hard. Then, destroy the
  // EventBase.

  auto prevLoopingState = detail::gLoopingState;
  SCOPE_EXIT {
    detail::gLoopingState = prevLoopingState;
  };
  detail::gLoopingState = LoopingState(L, this);

  eb_.reset();

  // Now, run our callbacks.
  pushTable(L, kImmediateCallbacksTable);
  while (doLoop(L) != 0) { }
  lua_pop(L, 1);

  lua_pushlightuserdata(L, this);
  lua_pushnil(L);
  lua_settable(L, LUA_REGISTRYINDEX);

  return 0;
}

namespace {
int runFunc(lua_State* L) {
  auto fptr = static_cast<folly::Func*>(lua_touserdata(L, lua_upvalueindex(1)));
  SCOPE_EXIT {
    delete fptr;
  };
  (*fptr)();
  return 0;
}
}  // namespace

void Reactor::add(folly::Func func) {
  if (!eb_) {
    // We're trying to add callbacks recursively during destruction.
    // This all stems from the fact that EventBase lets you do such horrible
    // things. There's absolutely nothing good we can do here, so we'll
    // do exactly what EventBase does -- ignore.
    return;
  }

  // We can't run the callback directly from the EventBase, because we
  // want Reactor to be reentrant; func may call back into Lua, which
  // may call loop() again, and EventBase doesn't like that. So we'll
  // unwind the stack and call the callbacks from loop().
  auto fptr = new folly::Func(std::move(func));
  eb_->add(
      [this, fptr] () {
        auto& ls = loopingState();
        DCHECK(ls.L);
        DCHECK(ls.executor == this);
        auto L = ls.L;
        pushTable(L, kImmediateCallbacksTable);
        lua_pushlightuserdata(L, fptr);
        pushWrappedCClosure(L, &runFunc, 1);
        doAddCallback(L);
        lua_pop(L, 1);
      });
}

int Reactor::luaAddCallbackDelayed(lua_State* L) {
  if (!eb_) {
    luaL_error(
        L, "Reactor being destroyed, delayed callbacks no longer allowed");
  }
  pushTable(L, kDelayedCallbacksTable);
  lua_pushvalue(L, 3);
  int key = doAddCallback(L);
  lua_pop(L, 1);

  std::chrono::duration<double> seconds(luaGetChecked<double>(L, 2));
  auto millis =
    std::chrono::duration_cast<std::chrono::milliseconds>(seconds).count();

  eb_->runAfterDelay(
      [this, key] () {
        auto& ls = loopingState();
        DCHECK(ls.L);
        DCHECK(ls.executor == this);
        doAddDelayedCallback(ls.L, key);
      },
      millis);

  lua_pushinteger(L, key);
  return 1;
}

int Reactor::luaAddCallback(lua_State* L) {
  pushTable(L, kImmediateCallbacksTable);
  lua_pushvalue(L, 2);
  lua_pushinteger(L, doAddCallback(L));
  return 1;
}

bool Reactor::doLookupOrRemoveCallback(lua_State* L, int key, bool remove) {
  // table
  lua_rawgeti(L, -1, key);
  // table cb
  bool found = !lua_isnil(L, -1);
  lua_pop(L, 1);
  if (found && remove) {
    lua_pushnil(L);
    lua_rawseti(L, -2, key);
  }
  return found;
}

int Reactor::doLuaLookupOrRemoveCallback(lua_State* L, bool remove) {
  auto key = luaGetChecked<int>(L, 2);

  pushTable(L, kImmediateCallbacksTable);
  if (doLookupOrRemoveCallback(L, key, remove)) {
    lua_pushinteger(L, kRunnable);
    return 1;
  }
  lua_pop(L, 1);

  pushTable(L, kDelayedCallbacksTable);
  if (doLookupOrRemoveCallback(L, key, remove)) {
    lua_pushinteger(L, kDelayed);
    return 1;
  }
  lua_pop(L, 1);

  lua_pushinteger(L, kNotFound);
  return 1;
}

int Reactor::luaRemoveCallback(lua_State* L) {
  return doLuaLookupOrRemoveCallback(L, true);
}

int Reactor::luaLookupCallback(lua_State* L) {
  return doLuaLookupOrRemoveCallback(L, false);
}

void Reactor::pushTable(lua_State* L, int table) {
  lua_pushlightuserdata(L, this);
  lua_gettable(L, LUA_REGISTRYINDEX);
  lua_rawgeti(L, -1, table);
  lua_remove(L, -2);
}

int Reactor::doAddCallback(lua_State* L) {
  // run_table cb
  int key = ++seq_;
  lua_rawseti(L, -2, key);
  // run_table
  return key;
}

void Reactor::doAddDelayedCallback(lua_State* L, int key) {
  lua_pushlightuserdata(L, this);
  lua_gettable(L, LUA_REGISTRYINDEX);
  lua_rawgeti(L, -1, kImmediateCallbacksTable);
  lua_rawgeti(L, -2, kDelayedCallbacksTable);

  // move from delayed to immediate table

  // tables immediate delayed
  lua_rawgeti(L, -1, key);
  // tables immediate delayed cb
  lua_pushnil(L);
  // tables immediate delayed cb nil
  lua_rawseti(L, -3, key);
  // tables immediate delayed cb
  lua_rawseti(L, -3, key);
  // tables immediate delayed
  lua_pop(L, 3);
}

int Reactor::luaLoop(lua_State* L) {
  auto block = luaGetChecked<bool>(L, 2);
  int flags = block ? 0 : EVLOOP_NONBLOCK;

  pushTable(L, kImmediateCallbacksTable);
  int numCallbacks = 0;
  int top = lua_gettop(L);

  auto prevLoopingState = detail::gLoopingState;
  SCOPE_EXIT {
    detail::gLoopingState = prevLoopingState;
  };
  detail::gLoopingState = LoopingState(L, this);

  auto keepAlive = eb_->loopKeepAlive();
  do {
    if (!eb_->loopOnce(flags)) {
      luaL_error(L, "EventBase loop returned error!");
    }

    DCHECK_EQ(top, lua_gettop(L));
    numCallbacks += doLoop(L);
  } while (block && numCallbacks == 0);

  luaPush(L, numCallbacks);
  return 1;
}

int Reactor::doLoop(lua_State* L) {
  int numCallbacks = 0;
  for (;;) {
    // tab
    lua_pushnil(L);
    // tab nil
    if (!lua_next(L, -2)) {
      break;
    }
    // tab key value
    lua_insert(L, -2);
    // tab value key
    lua_pushnil(L);
    // tab value key nil
    lua_rawset(L, -4);
    // tab value
    lua_call(L, 0, 0);
    // tab
    ++numCallbacks;
  }

  return numCallbacks;
}

int Reactor::luaGetExecutor(lua_State* L) {
  lua_pushlightuserdata(L, static_cast<folly::Executor*>(this));
  return 1;
}

int Reactor::luaGetEventBase(lua_State* L) {
  lua_pushlightuserdata(L, eb_.get());
  return 1;
}

int luaNew(lua_State* L) {
  pushUserData<Reactor>(L, L);
  return 1;
}

const luaL_Reg gModuleFuncs[] = {
  {"new", luaNew},
  {nullptr, nullptr},
};

}  // namespace

template <>
const UserDataMethod<Reactor> Metatable<Reactor>::methods[] = {
  {"add_callback", &Reactor::luaAddCallback},
  {"add_callback_delayed", &Reactor::luaAddCallbackDelayed},
  {"lookup_callback", &Reactor::luaLookupCallback},
  {"remove_callback", &Reactor::luaRemoveCallback},
  {"loop", &Reactor::luaLoop},
  {"get_executor", &Reactor::luaGetExecutor},
  {"get_event_base", &Reactor::luaGetEventBase},
  {"__gc", &Reactor::luaGC},
  {nullptr, nullptr},
};

}  // namespace fblualib

using namespace fblualib;

extern "C" int LUAOPEN(lua_State* L) {
  lua_newtable(L);
  luaL_register(L, nullptr, gModuleFuncs);

  lua_pushinteger(L, Reactor::kNotFound);
  lua_setfield(L, -2, "NOT_FOUND");

  lua_pushinteger(L, Reactor::kRunnable);
  lua_setfield(L, -2, "RUNNABLE");

  lua_pushinteger(L, Reactor::kDelayed);
  lua_setfield(L, -2, "DELAYED");

  return 1;
}
<commit_msg>Add keepAlive() mechanism<commit_after>/*
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include <vector>

#include <fblualib/LuaUtils.h>
#include <fblualib/Reactor.h>
#include <fblualib/UserData.h>
#include <folly/io/async/EventBase.h>
#include <folly/MoveWrapper.h>

namespace fblualib {

namespace {

class Reactor : public folly::Executor {
  enum : int {
    kImmediateCallbacksTable = 1,
    kDelayedCallbacksTable = 2,
  };
 public:
  enum LookupCallback : int {
    kNotFound = 0,
    kRunnable = 1,
    kDelayed = 2,
  };

  explicit Reactor(lua_State* L);
  ~Reactor();

  void add(folly::Func func) override;

  int luaLoop(lua_State* L);
  int luaAddCallback(lua_State* L);
  int luaAddCallbackDelayed(lua_State* L);
  int luaLookupCallback(lua_State* L);
  int luaRemoveCallback(lua_State* L);

  // Return a lightuserdata object that is a pointer to the corresponding
  // folly::Executor.
  int luaGetExecutor(lua_State* L);
  int luaGetEventBase(lua_State* L);
  int luaGC(lua_State* L);

 private:
  int doLoop(lua_State* L);

  void pushTable(lua_State* L, int table);
  int doAddCallback(lua_State* L);
  bool doLookupOrRemoveCallback(lua_State* L, int key, bool remove);
  int doLuaLookupOrRemoveCallback(lua_State* L, bool remove);
  void doAddDelayedCallback(lua_State* L, int key);

  int seq_;
  std::unique_ptr<folly::EventBase> eb_;
};

Reactor::Reactor(lua_State* L)
  : seq_(0),
    eb_(std::make_unique<folly::EventBase>()) {
  lua_pushlightuserdata(L, this);
  lua_createtable(L, 2, 0);
  lua_newtable(L);
  lua_rawseti(L, -2, kImmediateCallbacksTable);
  lua_newtable(L);
  lua_rawseti(L, -2, kDelayedCallbacksTable);
  lua_settable(L, LUA_REGISTRYINDEX);
}

Reactor::~Reactor() {
  DCHECK(!eb_);  // luaGC must have been called
}

int Reactor::luaGC(lua_State* L) {
  // This is fugly.
  //
  // The EventBase has pointers to this (via callbacks added using add()),
  // and it can run callbacks during destruction. So we must make sure to
  // drain it explicitly.
  //
  // Even more, we normally only execute callbacks once we get back to Lua
  // (via luaLoop), so even if we drained the EventBase here, those callbacks
  // would never get to run.
  //
  // So we have to drain it with a real Lua state.
  //
  // We'll first destroy the EventBase (running any callbacks that need to run
  // during destruction) then drain our callback tables.

  // First, make sure eb_ is nullptr, so that any attempt to use it (perhaps
  // by referring to this Reactor recursively) fails hard. Then, destroy the
  // EventBase.

  auto prevLoopingState = detail::gLoopingState;
  SCOPE_EXIT {
    detail::gLoopingState = prevLoopingState;
  };
  detail::gLoopingState = LoopingState(L, this);

  eb_.reset();

  // Now, run our callbacks.
  pushTable(L, kImmediateCallbacksTable);
  while (doLoop(L) != 0) { }
  lua_pop(L, 1);

  lua_pushlightuserdata(L, this);
  lua_pushnil(L);
  lua_settable(L, LUA_REGISTRYINDEX);

  return 0;
}

namespace {
int runFunc(lua_State* L) {
  auto fptr = static_cast<folly::Func*>(lua_touserdata(L, lua_upvalueindex(1)));
  SCOPE_EXIT {
    delete fptr;
  };
  (*fptr)();
  return 0;
}
}  // namespace

void Reactor::add(folly::Func func) {
  if (!eb_) {
    // We're trying to add callbacks recursively during destruction.
    // This all stems from the fact that EventBase lets you do such horrible
    // things. There's absolutely nothing good we can do here, so we'll
    // do exactly what EventBase does -- ignore.
    return;
  }

  // We can't run the callback directly from the EventBase, because we
  // want Reactor to be reentrant; func may call back into Lua, which
  // may call loop() again, and EventBase doesn't like that. So we'll
  // unwind the stack and call the callbacks from loop().
  auto fptr = new folly::Func(std::move(func));
  eb_->add(
      [this, fptr] () {
        auto& ls = loopingState();
        DCHECK(ls.L);
        DCHECK(ls.executor == this);
        auto L = ls.L;
        pushTable(L, kImmediateCallbacksTable);
        lua_pushlightuserdata(L, fptr);
        pushWrappedCClosure(L, &runFunc, 1);
        doAddCallback(L);
        lua_pop(L, 1);
      });
}

int Reactor::luaAddCallbackDelayed(lua_State* L) {
  if (!eb_) {
    luaL_error(
        L, "Reactor being destroyed, delayed callbacks no longer allowed");
  }
  pushTable(L, kDelayedCallbacksTable);
  lua_pushvalue(L, 3);
  int key = doAddCallback(L);
  lua_pop(L, 1);

  std::chrono::duration<double> seconds(luaGetChecked<double>(L, 2));
  auto millis =
    std::chrono::duration_cast<std::chrono::milliseconds>(seconds).count();

  eb_->runAfterDelay(
      [this, key] () {
        auto& ls = loopingState();
        DCHECK(ls.L);
        DCHECK(ls.executor == this);
        doAddDelayedCallback(ls.L, key);
      },
      millis);

  lua_pushinteger(L, key);
  return 1;
}

int Reactor::luaAddCallback(lua_State* L) {
  pushTable(L, kImmediateCallbacksTable);
  lua_pushvalue(L, 2);
  lua_pushinteger(L, doAddCallback(L));
  return 1;
}

bool Reactor::doLookupOrRemoveCallback(lua_State* L, int key, bool remove) {
  // table
  lua_rawgeti(L, -1, key);
  // table cb
  bool found = !lua_isnil(L, -1);
  lua_pop(L, 1);
  if (found && remove) {
    lua_pushnil(L);
    lua_rawseti(L, -2, key);
  }
  return found;
}

int Reactor::doLuaLookupOrRemoveCallback(lua_State* L, bool remove) {
  auto key = luaGetChecked<int>(L, 2);

  pushTable(L, kImmediateCallbacksTable);
  if (doLookupOrRemoveCallback(L, key, remove)) {
    lua_pushinteger(L, kRunnable);
    return 1;
  }
  lua_pop(L, 1);

  pushTable(L, kDelayedCallbacksTable);
  if (doLookupOrRemoveCallback(L, key, remove)) {
    lua_pushinteger(L, kDelayed);
    return 1;
  }
  lua_pop(L, 1);

  lua_pushinteger(L, kNotFound);
  return 1;
}

int Reactor::luaRemoveCallback(lua_State* L) {
  return doLuaLookupOrRemoveCallback(L, true);
}

int Reactor::luaLookupCallback(lua_State* L) {
  return doLuaLookupOrRemoveCallback(L, false);
}

void Reactor::pushTable(lua_State* L, int table) {
  lua_pushlightuserdata(L, this);
  lua_gettable(L, LUA_REGISTRYINDEX);
  lua_rawgeti(L, -1, table);
  lua_remove(L, -2);
}

int Reactor::doAddCallback(lua_State* L) {
  // run_table cb
  int key = ++seq_;
  lua_rawseti(L, -2, key);
  // run_table
  return key;
}

void Reactor::doAddDelayedCallback(lua_State* L, int key) {
  lua_pushlightuserdata(L, this);
  lua_gettable(L, LUA_REGISTRYINDEX);
  lua_rawgeti(L, -1, kImmediateCallbacksTable);
  lua_rawgeti(L, -2, kDelayedCallbacksTable);

  // move from delayed to immediate table

  // tables immediate delayed
  lua_rawgeti(L, -1, key);
  // tables immediate delayed cb
  lua_pushnil(L);
  // tables immediate delayed cb nil
  lua_rawseti(L, -3, key);
  // tables immediate delayed cb
  lua_rawseti(L, -3, key);
  // tables immediate delayed
  lua_pop(L, 3);
}

int Reactor::luaLoop(lua_State* L) {
  auto block = luaGetChecked<bool>(L, 2);
  int flags = block ? 0 : EVLOOP_NONBLOCK;

  pushTable(L, kImmediateCallbacksTable);
  int numCallbacks = 0;
  int top = lua_gettop(L);

  auto prevLoopingState = detail::gLoopingState;
  SCOPE_EXIT {
    detail::gLoopingState = prevLoopingState;
  };
  detail::gLoopingState = LoopingState(L, this);

  auto keepAlive = eb_->getKeepAliveToken();
  do {
    if (!eb_->loopOnce(flags)) {
      luaL_error(L, "EventBase loop returned error!");
    }

    DCHECK_EQ(top, lua_gettop(L));
    numCallbacks += doLoop(L);
  } while (block && numCallbacks == 0);

  luaPush(L, numCallbacks);
  return 1;
}

int Reactor::doLoop(lua_State* L) {
  int numCallbacks = 0;
  for (;;) {
    // tab
    lua_pushnil(L);
    // tab nil
    if (!lua_next(L, -2)) {
      break;
    }
    // tab key value
    lua_insert(L, -2);
    // tab value key
    lua_pushnil(L);
    // tab value key nil
    lua_rawset(L, -4);
    // tab value
    lua_call(L, 0, 0);
    // tab
    ++numCallbacks;
  }

  return numCallbacks;
}

int Reactor::luaGetExecutor(lua_State* L) {
  lua_pushlightuserdata(L, static_cast<folly::Executor*>(this));
  return 1;
}

int Reactor::luaGetEventBase(lua_State* L) {
  lua_pushlightuserdata(L, eb_.get());
  return 1;
}

int luaNew(lua_State* L) {
  pushUserData<Reactor>(L, L);
  return 1;
}

const luaL_Reg gModuleFuncs[] = {
  {"new", luaNew},
  {nullptr, nullptr},
};

}  // namespace

template <>
const UserDataMethod<Reactor> Metatable<Reactor>::methods[] = {
  {"add_callback", &Reactor::luaAddCallback},
  {"add_callback_delayed", &Reactor::luaAddCallbackDelayed},
  {"lookup_callback", &Reactor::luaLookupCallback},
  {"remove_callback", &Reactor::luaRemoveCallback},
  {"loop", &Reactor::luaLoop},
  {"get_executor", &Reactor::luaGetExecutor},
  {"get_event_base", &Reactor::luaGetEventBase},
  {"__gc", &Reactor::luaGC},
  {nullptr, nullptr},
};

}  // namespace fblualib

using namespace fblualib;

extern "C" int LUAOPEN(lua_State* L) {
  lua_newtable(L);
  luaL_register(L, nullptr, gModuleFuncs);

  lua_pushinteger(L, Reactor::kNotFound);
  lua_setfield(L, -2, "NOT_FOUND");

  lua_pushinteger(L, Reactor::kRunnable);
  lua_setfield(L, -2, "RUNNABLE");

  lua_pushinteger(L, Reactor::kDelayed);
  lua_setfield(L, -2, "DELAYED");

  return 1;
}
<|endoftext|>


<commit_before>// @(#)root/eve7:$Id$
// Authors: Matevz Tadel & Alja Mrak-Tadel: 2006, 2007, 2018

/*************************************************************************
 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include <ROOT/REveDataCollection.hxx>
#include <ROOT/REveUtil.hxx>
#include <ROOT/REveSelection.hxx>
#include <ROOT/REveManager.hxx>

#include "TROOT.h"
#include "TMethod.h"
#include "TMethodArg.h"
#include "TColor.h"
#include "TClass.h"
#include "TList.h"
#include "TBaseClass.h"

#include <sstream>
#include <regex>

#include <nlohmann/json.hpp>

using namespace ROOT::Experimental;

Color_t  REveDataCollection::fgDefaultColor  = kBlue;

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

REveDataItemList::REveDataItemList(const std::string& n, const std::string& t):
   REveElement(n,t)
{
   fAlwaysSecSelect = true;

   SetItemsChangeDelegate([&](REveDataItemList *collection, const REveDataCollection::Ids_t &ids) {
      REveDataItemList::DummyItemsChange(collection, ids);
   });

   SetFillImpliedSelectedDelegate([&](REveDataItemList *collection, REveElement::Set_t &impSelSet) {
      REveDataItemList::DummyFillImpliedSelected(collection, impSelSet);
   });

   SetupDefaultColorAndTransparency(REveDataCollection::fgDefaultColor, true, true);
}
//______________________________________________________________________________

void REveDataItemList::SetItemVisible(Int_t idx, Bool_t visible)
{
   fItems[idx]->SetRnrSelf(visible);
   ItemChanged(idx);
   StampObjProps();
}

//______________________________________________________________________________

void REveDataItemList::SetItemColorRGB(Int_t idx, UChar_t r, UChar_t g, UChar_t b)
{
   Color_t c = TColor::GetColor(r, g, b);
   fItems[idx]->SetMainColor(c);
   ItemChanged(idx);
   StampObjProps();
}
//______________________________________________________________________________

void REveDataItemList::ItemChanged(REveDataItem* iItem)
{
   int idx = 0;
   std::vector<int> ids;
   for (auto & chld : fItems)
   {
      if (chld == iItem) {
         ids.push_back(idx);
          fHandlerItemsChange( this , ids);
         return;
      }
      idx++;
   }
}

//______________________________________________________________________________

void REveDataItemList::ItemChanged(Int_t idx)
{
   std::vector<int> ids;
   ids.push_back(idx);
   fHandlerItemsChange( this , ids);
}

//______________________________________________________________________________

void REveDataItemList::FillImpliedSelectedSet( Set_t& impSelSet)
{
   /*
   printf("REveDataItemList::FillImpliedSelectedSet colecction setsize %zu\n",   RefSelectedSet().size());
   for (auto x : RefSelectedSet())
      printf("%d \n", x);
   */
   fHandlerFillImplied( this ,  impSelSet);
}

//______________________________________________________________________________

Int_t REveDataItemList::WriteCoreJson(nlohmann::json &j, Int_t rnr_offset)
{
   Int_t ret = REveElement::WriteCoreJson(j, rnr_offset);
   j["items"] =  nlohmann::json::array();
   for (auto & chld : fItems)
   {
      nlohmann::json i;
      i["fFiltered"] = chld->GetFiltered();
      i["fRnrSelf"] = chld->GetRnrSelf();
      i["fColor"] = chld->GetMainColor();
      j["items"].push_back(i);
   }

   return ret;
}

//______________________________________________________________________________

Bool_t REveDataItemList::SetRnrState(Bool_t iRnrSelf)
{
   Bool_t ret = REveElement::SetRnrState(iRnrSelf);
   std::vector<int> ids;

   for (size_t i = 0; i < fItems.size(); ++i ) {
      ids.push_back(i);
      fItems[i]->SetRnrSelf(fRnrSelf);
   }

   fHandlerItemsChange( this , ids);
   StampVisibility();
   StampObjProps();

   return ret;
}

//______________________________________________________________________________
void REveDataItemList::ProcessSelectionStr(ElementId_t id, bool multi, bool secondary, const char* secondary_idcs)
{
   static const REveException eh("REveDataItemList::ProcessSelectionStr ");
   static const std::regex comma_re("\\s*,\\s*", std::regex::optimize);
   std::string   str(secondary_idcs);
   std::set<int> sis;
   std::sregex_token_iterator itr(str.begin(), str.end(), comma_re, -1);
   std::sregex_token_iterator end;

   try {
      while (itr != end) sis.insert(std::stoi(*itr++));
   }
   catch (const std::invalid_argument& ia) {
      throw eh + "invalid secondary index argument '" + *itr + "' - must be int.";
   }

   ProcessSelection(id, multi, secondary, sis);
}

//______________________________________________________________________________
void REveDataItemList::ProcessSelection(ElementId_t selectionId, bool multi, bool secondary, const std::set<int>& secondary_idcs)
{
   RefSelectedSet() = secondary_idcs;
   REveSelection* selection = (REveSelection*) ROOT::Experimental::gEve->FindElementById(selectionId);
   selection->NewElementPicked(GetElementId(), multi, secondary, secondary_idcs);
}

//______________________________________________________________________________
std::string REveDataItemList::GetHighlightTooltip(const std::set<int>& secondary_idcs) const
{
   if (secondary_idcs.empty()) {
    return GetName();
  }

   // print info for first selected index
   int idx = *secondary_idcs.begin();
   auto col = dynamic_cast<REveDataCollection*>(fMother);
   void* data = col->GetDataPtr(idx);
   std::string name = col->GetName();
   auto li = name.size();
   if (li && name[li-1] == 's')
   {
      name = name.substr(0, li-1);
   }

   std::string res;
   for (auto &z : secondary_idcs)
   {
      idx = z;
      data = col->GetDataPtr(idx);
      res +=  std::string(TString::Format("%s %d",  name.c_str(), idx));
      for (auto &t : fTooltipExpressions) {
         std::string eval = t->fTooltipFunction.EvalExpr(data);
         res +=  std::string(TString::Format("\n  %s = %s", t->fTooltipTitle.c_str(), eval.c_str()));
      }
      res += "\n";
   }
   return res;
}

//______________________________________________________________________________
void REveDataItemList::AddTooltipExpression(const std::string &title, const std::string &expr, bool init)
{
   fTooltipExpressions.push_back(std::unique_ptr<TTip>(new TTip()));
   TTip *tt = fTooltipExpressions.back().get();

   tt->fTooltipTitle = title;
   tt->fTooltipFunction.SetPrecision(2);
   auto col = dynamic_cast<REveDataCollection *>(fMother);
   auto icls = col->GetItemClass();
   tt->fTooltipFunction.SetExpressionAndType(expr, REveDataColumn::FT_Double, icls);

   if (init) {
      auto re = tt->fTooltipFunction.GetFunctionExpressionString();
      gROOT->ProcessLine(re.c_str());
   }
}

//______________________________________________________________________________
void REveDataItemList::SetItemsChangeDelegate (ItemsChangeFunc_t handler_func)
{
   fHandlerItemsChange = handler_func;
}

//______________________________________________________________________________
void REveDataItemList::SetFillImpliedSelectedDelegate (FillImpliedSelectedFunc_t handler_func)
{
   fHandlerFillImplied = handler_func;
}

//______________________________________________________________________________
void REveDataItemList::DummyItemsChange(REveDataItemList*, const std::vector<int>&)
{
   if (gDebug) {
      printf("REveDataItemList::DummyItemsCahngeDelegate not implemented\n");
   }
}

//______________________________________________________________________________
void REveDataItemList::DummyFillImpliedSelected(REveDataItemList*, REveElement::Set_t&)
{
   if (gDebug) {
      printf("REveDataItemList::DummyFillImpliedSelectedDelegate not implemented\n");
   }
}

//==============================================================================
// REveDataCollection
//==============================================================================

REveDataCollection::REveDataCollection(const std::string& n, const std::string& t) :
   REveElement(n, t)
{
   std::string lname = n + "Items";
   fItemList = new REveDataItemList(lname.c_str());
   AddElement(fItemList);

   SetupDefaultColorAndTransparency(fgDefaultColor, true, true);
}

void REveDataCollection::AddItem(void *data_ptr, const std::string& /*n*/, const std::string& /*t*/)
{
   auto el = new REveDataItem(data_ptr, GetMainColor());
   fItemList->fItems.emplace_back(el);
}

//------------------------------------------------------------------------------

void REveDataCollection::SetFilterExpr(const char* filter)
{
   static const REveException eh("REveDataCollection::SetFilterExpr ");

   if (!fItemClass)
      throw eh + "item class has to be set before the filter expression.";

   fFilterExpr = filter;
   if (fFilterExpr.Length())
   {
      std::stringstream s;
      s << "*((std::function<bool(" << fItemClass->GetName() << "*)>*)" << std::hex << std::showbase
        << (size_t)&fFilterFoo << ") = [](" << fItemClass->GetName() << "* p){" << fItemClass->GetName()
        << " &i=*p; return (" << fFilterExpr.Data() << "); };";

      // printf("%s\n", s.Data());
      try {
         gROOT->ProcessLine(s.str().c_str());
         // AMT I don't know why ApplyFilter call is separated
         ApplyFilter();
      }
      catch (const std::exception &exc)
      {
         R__LOG_ERROR(REveLog()) << "EveDataCollection::SetFilterExpr" << exc.what();
      }
   }
   else
   {
      // Remove filter
      fFilterFoo = nullptr;
      Ids_t ids;
      int idx = 0;
      for (auto &ii : fItemList->fItems) {
         if (ii->GetFiltered()) {
            ii->SetFiltered(false);
            ids.push_back(idx);
            idx++;
         }
      }

      StampObjProps();
      fItemList->StampObjProps();
      fItemList->fHandlerItemsChange(fItemList, ids);
   }
}

void REveDataCollection::ApplyFilter()
{
   if (!fFilterFoo)
      return;

   Ids_t ids;
   int idx = 0;
   for (auto &ii : fItemList->fItems)
   {
      bool res = fFilterFoo(ii->GetDataPtr());

      // printf("Item:%s -- filter result = %d\n", ii.fItemPtr->GetElementName(), res);

      ii->SetFiltered( ! res );

      ids.push_back(idx++);
   }
   StampObjProps();
   fItemList->StampObjProps();
   fItemList->fHandlerItemsChange( fItemList , ids);
}

//______________________________________________________________________________

void  REveDataCollection::StreamPublicMethods(nlohmann::json &j) const
{
   struct PubMethods
   {
      void FillJSON(TClass* c, nlohmann::json & arr)
      {
         TString  ctor = c->GetName(), dtor = "~";
         {
            int i = ctor.Last(':');
            if (i != kNPOS)
            {
               ctor.Replace(0, i + 1, "");
            }
            dtor += ctor;
         }

         TMethod *meth;
         TIter    next(c->GetListOfMethods());
         while ((meth = (TMethod*) next()))
         {
            // Filter out ctor, dtor, some ROOT stuff.
            {
               TString m(meth->GetName());
               if (m == ctor || m == dtor ||
                   m == "Class" || m == "Class_Name" || m == "Class_Version" || m == "Dictionary" || m == "IsA" ||
                   m == "DeclFileName" || m == "ImplFileName" || m == "DeclFileLine" || m == "ImplFileLine" ||
                   m == "Streamer" || m == "StreamerNVirtual" || m == "ShowMembers" ||
                   m == "CheckTObjectHashConsistency")
               {
                  continue;
               }
            }

            TString     ms;
            TMethodArg *ma;
            TIter       next_ma(meth->GetListOfMethodArgs());
            while ((ma = (TMethodArg*) next_ma()))
            {
               if ( ! ms.IsNull()) ms += ", ";

               ms += ma->GetTypeName();
               ms += " ";
               ms += ma->GetName();
            }
            std::string entry(TString::Format("i.%s(%s)",meth->GetName(),ms.Data()).Data());
            nlohmann::json jm ;
            jm["f"] = entry;
            jm["r"] = meth->GetReturnTypeName();
            jm["c"] = c->GetName();
            arr.push_back(jm);
         }
         {
            TBaseClass *base;
            TIter       blnext(c->GetListOfBases());
            while ((base = (TBaseClass*) blnext()))
            {
               FillJSON(base->GetClassPointer(), arr);
            }
         }
      }
   };
   j["fPublicFunctions"]  = nlohmann::json::array();
   PubMethods pm;
   pm.FillJSON(fItemClass, j["fPublicFunctions"]);
}

//______________________________________________________________________________

void REveDataCollection::SetMainColor(Color_t newv)
{
   int idx = 0;
   Ids_t ids;
   for (auto & chld : fItemList->fItems)
   {
      chld->SetMainColor(newv);
      ids.push_back(idx);
      idx++;
   }

   REveElement::SetMainColor(newv);
   for (auto & chld : fItemList->fItems)
   {
      chld->SetMainColor(newv);
   }
   fItemList->StampObjProps();
   fItemList->SetMainColor(newv);
   fItemList->fHandlerItemsChange( fItemList , ids);
}

//______________________________________________________________________________

Bool_t REveDataCollection::SetRnrState(Bool_t iRnrSelf)
{
   Bool_t ret = REveElement::SetRnrState(iRnrSelf);
   Ids_t ids;
   for (int i = 0; i < GetNItems(); ++i ) {
      ids.push_back(i);
      fItemList->fItems[i]->SetRnrSelf(fRnrSelf);
   }

   fItemList->StampObjProps();
   fItemList->fHandlerItemsChange( fItemList , ids);

   return ret;
}

//______________________________________________________________________________

Int_t REveDataCollection::WriteCoreJson(nlohmann::json &j, Int_t rnr_offset)
{
   Int_t ret = REveElement::WriteCoreJson(j, rnr_offset);
   j["fFilterExpr"] = fFilterExpr.Data();
   return ret;
}

<commit_msg>Corrections in remove filter<commit_after>// @(#)root/eve7:$Id$
// Authors: Matevz Tadel & Alja Mrak-Tadel: 2006, 2007, 2018

/*************************************************************************
 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include <ROOT/REveDataCollection.hxx>
#include <ROOT/REveUtil.hxx>
#include <ROOT/REveSelection.hxx>
#include <ROOT/REveManager.hxx>

#include "TROOT.h"
#include "TMethod.h"
#include "TMethodArg.h"
#include "TColor.h"
#include "TClass.h"
#include "TList.h"
#include "TBaseClass.h"

#include <sstream>
#include <regex>

#include <nlohmann/json.hpp>

using namespace ROOT::Experimental;

Color_t  REveDataCollection::fgDefaultColor  = kBlue;

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

REveDataItemList::REveDataItemList(const std::string& n, const std::string& t):
   REveElement(n,t)
{
   fAlwaysSecSelect = true;

   SetItemsChangeDelegate([&](REveDataItemList *collection, const REveDataCollection::Ids_t &ids) {
      REveDataItemList::DummyItemsChange(collection, ids);
   });

   SetFillImpliedSelectedDelegate([&](REveDataItemList *collection, REveElement::Set_t &impSelSet) {
      REveDataItemList::DummyFillImpliedSelected(collection, impSelSet);
   });

   SetupDefaultColorAndTransparency(REveDataCollection::fgDefaultColor, true, true);
}
//______________________________________________________________________________

void REveDataItemList::SetItemVisible(Int_t idx, Bool_t visible)
{
   fItems[idx]->SetRnrSelf(visible);
   ItemChanged(idx);
   StampObjProps();
}

//______________________________________________________________________________

void REveDataItemList::SetItemColorRGB(Int_t idx, UChar_t r, UChar_t g, UChar_t b)
{
   Color_t c = TColor::GetColor(r, g, b);
   fItems[idx]->SetMainColor(c);
   ItemChanged(idx);
   StampObjProps();
}
//______________________________________________________________________________

void REveDataItemList::ItemChanged(REveDataItem* iItem)
{
   int idx = 0;
   std::vector<int> ids;
   for (auto & chld : fItems)
   {
      if (chld == iItem) {
         ids.push_back(idx);
          fHandlerItemsChange( this , ids);
         return;
      }
      idx++;
   }
}

//______________________________________________________________________________

void REveDataItemList::ItemChanged(Int_t idx)
{
   std::vector<int> ids;
   ids.push_back(idx);
   fHandlerItemsChange( this , ids);
}

//______________________________________________________________________________

void REveDataItemList::FillImpliedSelectedSet( Set_t& impSelSet)
{
   /*
   printf("REveDataItemList::FillImpliedSelectedSet colecction setsize %zu\n",   RefSelectedSet().size());
   for (auto x : RefSelectedSet())
      printf("%d \n", x);
   */
   fHandlerFillImplied( this ,  impSelSet);
}

//______________________________________________________________________________

Int_t REveDataItemList::WriteCoreJson(nlohmann::json &j, Int_t rnr_offset)
{
   Int_t ret = REveElement::WriteCoreJson(j, rnr_offset);
   j["items"] =  nlohmann::json::array();
   for (auto & chld : fItems)
   {
      nlohmann::json i;
      i["fFiltered"] = chld->GetFiltered();
      i["fRnrSelf"] = chld->GetRnrSelf();
      i["fColor"] = chld->GetMainColor();
      j["items"].push_back(i);
   }

   return ret;
}

//______________________________________________________________________________

Bool_t REveDataItemList::SetRnrState(Bool_t iRnrSelf)
{
   Bool_t ret = REveElement::SetRnrState(iRnrSelf);
   std::vector<int> ids;

   for (size_t i = 0; i < fItems.size(); ++i ) {
      ids.push_back(i);
      fItems[i]->SetRnrSelf(fRnrSelf);
   }

   fHandlerItemsChange( this , ids);
   StampVisibility();
   StampObjProps();

   return ret;
}

//______________________________________________________________________________
void REveDataItemList::ProcessSelectionStr(ElementId_t id, bool multi, bool secondary, const char* secondary_idcs)
{
   static const REveException eh("REveDataItemList::ProcessSelectionStr ");
   static const std::regex comma_re("\\s*,\\s*", std::regex::optimize);
   std::string   str(secondary_idcs);
   std::set<int> sis;
   std::sregex_token_iterator itr(str.begin(), str.end(), comma_re, -1);
   std::sregex_token_iterator end;

   try {
      while (itr != end) sis.insert(std::stoi(*itr++));
   }
   catch (const std::invalid_argument& ia) {
      throw eh + "invalid secondary index argument '" + *itr + "' - must be int.";
   }

   ProcessSelection(id, multi, secondary, sis);
}

//______________________________________________________________________________
void REveDataItemList::ProcessSelection(ElementId_t selectionId, bool multi, bool secondary, const std::set<int>& secondary_idcs)
{
   RefSelectedSet() = secondary_idcs;
   REveSelection* selection = (REveSelection*) ROOT::Experimental::gEve->FindElementById(selectionId);
   selection->NewElementPicked(GetElementId(), multi, secondary, secondary_idcs);
}

//______________________________________________________________________________
std::string REveDataItemList::GetHighlightTooltip(const std::set<int>& secondary_idcs) const
{
   if (secondary_idcs.empty()) {
    return GetName();
  }

   // print info for first selected index
   int idx = *secondary_idcs.begin();
   auto col = dynamic_cast<REveDataCollection*>(fMother);
   void* data = col->GetDataPtr(idx);
   std::string name = col->GetName();
   auto li = name.size();
   if (li && name[li-1] == 's')
   {
      name = name.substr(0, li-1);
   }

   std::string res;
   for (auto &z : secondary_idcs)
   {
      idx = z;
      data = col->GetDataPtr(idx);
      res +=  std::string(TString::Format("%s %d",  name.c_str(), idx));
      for (auto &t : fTooltipExpressions) {
         std::string eval = t->fTooltipFunction.EvalExpr(data);
         res +=  std::string(TString::Format("\n  %s = %s", t->fTooltipTitle.c_str(), eval.c_str()));
      }
      res += "\n";
   }
   return res;
}

//______________________________________________________________________________
void REveDataItemList::AddTooltipExpression(const std::string &title, const std::string &expr, bool init)
{
   fTooltipExpressions.push_back(std::unique_ptr<TTip>(new TTip()));
   TTip *tt = fTooltipExpressions.back().get();

   tt->fTooltipTitle = title;
   tt->fTooltipFunction.SetPrecision(2);
   auto col = dynamic_cast<REveDataCollection *>(fMother);
   auto icls = col->GetItemClass();
   tt->fTooltipFunction.SetExpressionAndType(expr, REveDataColumn::FT_Double, icls);

   if (init) {
      auto re = tt->fTooltipFunction.GetFunctionExpressionString();
      gROOT->ProcessLine(re.c_str());
   }
}

//______________________________________________________________________________
void REveDataItemList::SetItemsChangeDelegate (ItemsChangeFunc_t handler_func)
{
   fHandlerItemsChange = handler_func;
}

//______________________________________________________________________________
void REveDataItemList::SetFillImpliedSelectedDelegate (FillImpliedSelectedFunc_t handler_func)
{
   fHandlerFillImplied = handler_func;
}

//______________________________________________________________________________
void REveDataItemList::DummyItemsChange(REveDataItemList*, const std::vector<int>&)
{
   if (gDebug) {
      printf("REveDataItemList::DummyItemsCahngeDelegate not implemented\n");
   }
}

//______________________________________________________________________________
void REveDataItemList::DummyFillImpliedSelected(REveDataItemList*, REveElement::Set_t&)
{
   if (gDebug) {
      printf("REveDataItemList::DummyFillImpliedSelectedDelegate not implemented\n");
   }
}

//==============================================================================
// REveDataCollection
//==============================================================================

REveDataCollection::REveDataCollection(const std::string& n, const std::string& t) :
   REveElement(n, t)
{
   std::string lname = n + "Items";
   fItemList = new REveDataItemList(lname.c_str());
   AddElement(fItemList);

   SetupDefaultColorAndTransparency(fgDefaultColor, true, true);
}

void REveDataCollection::AddItem(void *data_ptr, const std::string& /*n*/, const std::string& /*t*/)
{
   auto el = new REveDataItem(data_ptr, GetMainColor());
   fItemList->fItems.emplace_back(el);
}

//------------------------------------------------------------------------------

void REveDataCollection::SetFilterExpr(const char* filter)
{
   static const REveException eh("REveDataCollection::SetFilterExpr ");

   if (!fItemClass)
      throw eh + "item class has to be set before the filter expression.";

   if (filter) {
      // printf("filter '%s'\n", filter);
      int ibeg = 0, iend = strlen(filter);
      while (ibeg < iend && isspace(filter[ibeg])) ++ibeg;
      while (iend > ibeg && isspace(filter[iend-1])) --iend;
      // printf("cleaned up beg=%d end=%d len =%d\n", ibeg, iend, (int)strlen(filter));
      fFilterExpr = TString(filter + ibeg, iend - ibeg);
   } else {
      fFilterExpr = "";
   }

   if (fFilterExpr.Length())
   {
      std::stringstream s;
      s << "*((std::function<bool(" << fItemClass->GetName() << "*)>*)" << std::hex << std::showbase
        << (size_t)&fFilterFoo << ") = [](" << fItemClass->GetName() << "* p){" << fItemClass->GetName()
        << " &i=*p; return (" << fFilterExpr.Data() << "); };";

      // printf("%s\n", s.Data());
      try {
         gROOT->ProcessLine(s.str().c_str());
         // AMT I don't know why ApplyFilter call is separated
         ApplyFilter();
      }
      catch (const std::exception &exc)
      {
         R__LOG_ERROR(REveLog()) << "EveDataCollection::SetFilterExpr" << exc.what();
      }
   }
   else
   {
      // Remove filter
      fFilterFoo = nullptr;
      Ids_t ids;
      int idx = 0;
      for (auto &ii : fItemList->fItems) {
         if (ii->GetFiltered()) {
            ii->SetFiltered(false);
            ids.push_back(idx);
         }
         idx++;
      }

      StampObjProps();
      fItemList->StampObjProps();
      fItemList->fHandlerItemsChange(fItemList, ids);
   }
}

void REveDataCollection::ApplyFilter()
{
   if (!fFilterFoo)
      return;

   Ids_t ids;
   int idx = 0;
   for (auto &ii : fItemList->fItems)
   {
      bool res = fFilterFoo(ii->GetDataPtr());

      // printf("Item:%s -- filter result = %d\n", ii.fItemPtr->GetElementName(), res);

      ii->SetFiltered( ! res );

      ids.push_back(idx++);
   }
   StampObjProps();
   fItemList->StampObjProps();
   fItemList->fHandlerItemsChange( fItemList , ids);
}

//______________________________________________________________________________

void  REveDataCollection::StreamPublicMethods(nlohmann::json &j) const
{
   struct PubMethods
   {
      void FillJSON(TClass* c, nlohmann::json & arr)
      {
         TString  ctor = c->GetName(), dtor = "~";
         {
            int i = ctor.Last(':');
            if (i != kNPOS)
            {
               ctor.Replace(0, i + 1, "");
            }
            dtor += ctor;
         }

         TMethod *meth;
         TIter    next(c->GetListOfMethods());
         while ((meth = (TMethod*) next()))
         {
            // Filter out ctor, dtor, some ROOT stuff.
            {
               TString m(meth->GetName());
               if (m == ctor || m == dtor ||
                   m == "Class" || m == "Class_Name" || m == "Class_Version" || m == "Dictionary" || m == "IsA" ||
                   m == "DeclFileName" || m == "ImplFileName" || m == "DeclFileLine" || m == "ImplFileLine" ||
                   m == "Streamer" || m == "StreamerNVirtual" || m == "ShowMembers" ||
                   m == "CheckTObjectHashConsistency")
               {
                  continue;
               }
            }

            TString     ms;
            TMethodArg *ma;
            TIter       next_ma(meth->GetListOfMethodArgs());
            while ((ma = (TMethodArg*) next_ma()))
            {
               if ( ! ms.IsNull()) ms += ", ";

               ms += ma->GetTypeName();
               ms += " ";
               ms += ma->GetName();
            }
            std::string entry(TString::Format("i.%s(%s)",meth->GetName(),ms.Data()).Data());
            nlohmann::json jm ;
            jm["f"] = entry;
            jm["r"] = meth->GetReturnTypeName();
            jm["c"] = c->GetName();
            arr.push_back(jm);
         }
         {
            TBaseClass *base;
            TIter       blnext(c->GetListOfBases());
            while ((base = (TBaseClass*) blnext()))
            {
               FillJSON(base->GetClassPointer(), arr);
            }
         }
      }
   };
   j["fPublicFunctions"]  = nlohmann::json::array();
   PubMethods pm;
   pm.FillJSON(fItemClass, j["fPublicFunctions"]);
}

//______________________________________________________________________________

void REveDataCollection::SetMainColor(Color_t newv)
{
   int idx = 0;
   Ids_t ids;
   for (auto & chld : fItemList->fItems)
   {
      chld->SetMainColor(newv);
      ids.push_back(idx);
      idx++;
   }

   REveElement::SetMainColor(newv);
   for (auto & chld : fItemList->fItems)
   {
      chld->SetMainColor(newv);
   }
   fItemList->StampObjProps();
   fItemList->SetMainColor(newv);
   fItemList->fHandlerItemsChange( fItemList , ids);
}

//______________________________________________________________________________

Bool_t REveDataCollection::SetRnrState(Bool_t iRnrSelf)
{
   Bool_t ret = REveElement::SetRnrState(iRnrSelf);
   Ids_t ids;
   for (int i = 0; i < GetNItems(); ++i ) {
      ids.push_back(i);
      fItemList->fItems[i]->SetRnrSelf(fRnrSelf);
   }

   fItemList->StampObjProps();
   fItemList->fHandlerItemsChange( fItemList , ids);

   return ret;
}

//______________________________________________________________________________

Int_t REveDataCollection::WriteCoreJson(nlohmann::json &j, Int_t rnr_offset)
{
   Int_t ret = REveElement::WriteCoreJson(j, rnr_offset);
   j["fFilterExpr"] = fFilterExpr.Data();
   return ret;
}

<|endoftext|>


<commit_before>/* OpenSceneGraph example, osgtext.
*
*  Permission is hereby granted, free of charge, to any person obtaining a copy
*  of this software and associated documentation files (the "Software"), to deal
*  in the Software without restriction, including without limitation the rights
*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the Software is
*  furnished to do so, subject to the following conditions:
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
*  THE SOFTWARE.
*/

#include <osg/ArgumentParser>
#include <osg/Material>
#include <osg/PositionAttitudeTransform>
#include <osg/io_utils>

#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgGA/StateSetManipulator>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>

#include <osgText/Text3D>

#include "TextNode.h"

class Text3DAttributeHandler : public osgGA::GUIEventHandler
{
public:
    Text3DAttributeHandler(osgText::Text3D* aText3D)
    :    m_Text3D(aText3D)
    {
    }

    ~Text3DAttributeHandler()
    {
    }

    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&)
    {
        if (ea.getEventType() == osgGA::GUIEventAdapter::KEYUP)
        {
            if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Up)
            {
                m_Text3D->setCharacterSize(m_Text3D->getCharacterHeight() + 0.1);        // failed
                OSG_NOTICE<<"m_Text3D->getCharacterHeight()="<<m_Text3D->getCharacterHeight()<<std::endl;
            }
            else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Down)
            {
                m_Text3D->setCharacterDepth(m_Text3D->getCharacterDepth() + 0.1);        // ok
                OSG_NOTICE<<"m_Text3D->getCharacterDepth()="<<m_Text3D->getCharacterDepth()<<std::endl;
            }
            else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left)
            {
                m_Text3D->setText("setText\nworks!", osgText::String::ENCODING_UTF8);    // ok
                OSG_NOTICE<<"m_Text3D->getText()="<<m_Text3D->getText().size()<<std::endl;
            }
            else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
            {
                m_Text3D->setLineSpacing(m_Text3D->getLineSpacing() + 0.1);
                OSG_NOTICE<<"m_Text3D->getLineSpacing()="<<m_Text3D->getLineSpacing()<<std::endl;
            }
        }

        return false;
    }

private:
    osgText::Text3D*        m_Text3D;
};

int main(int argc, char** argv)
{
    osg::ArgumentParser arguments(&argc, argv);

    osgViewer::Viewer viewer(arguments);

    std::string fontFile("arial.ttf");
    while(arguments.read("-f",fontFile)) {}

    osg::ref_ptr<osgText::Font> font = osgText::readRefFontFile(fontFile);
    if (!font) return 1;
    OSG_NOTICE<<"Read font "<<fontFile<<" font="<<font.get()<<std::endl;

    std::string word("This is a new test.");
    while (arguments.read("-w",word)) {}

    osg::ref_ptr<osgText::Style> style = new osgText::Style;

    float thickness = 0.1f;
    while(arguments.read("--thickness",thickness)) {}
    style->setThicknessRatio(thickness);

    // set up any bevel if required
    float r;
    osg::ref_ptr<osgText::Bevel> bevel;
    while(arguments.read("--rounded",r)) { bevel = new osgText::Bevel; bevel->roundedBevel2(r); }
    while(arguments.read("--rounded")) { bevel = new osgText::Bevel; bevel->roundedBevel2(0.25); }
    while(arguments.read("--flat",r)) { bevel = new osgText::Bevel; bevel->flatBevel(r); }
    while(arguments.read("--flat")) { bevel = new osgText::Bevel; bevel->flatBevel(0.25); }
    while(arguments.read("--bevel-thickness",r)) { if (bevel.valid()) bevel->setBevelThickness(r); }

    if (bevel.valid())
    {
        while(arguments.read("--smooth-concave-Junctions") || arguments.read("--scj"))
        {
            bevel->setSmoothConcaveJunctions(true);
        }
    }

    style->setBevel(bevel.get());

    // set up outline.
    while(arguments.read("--outline",r)) { style->setOutlineRatio(r); }

    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
    viewer.addEventHandler(new osgViewer::StatsHandler);

#if 1
    osg::ref_ptr<osg::Group> group = new osg::Group;

    float characterSize = 1.0f;
    while(arguments.read("--size",characterSize)) {}

    if (arguments.read("--2d"))
    {
        osgText::Text* text2D = new osgText::Text;
        text2D->setFont(font.get());
        text2D->setCharacterSize(characterSize);
        text2D->setFontResolution(256,256);
        text2D->setDrawMode(osgText::Text::TEXT | osgText::Text::BOUNDINGBOX);
        text2D->setAxisAlignment(osgText::Text::XZ_PLANE);
        text2D->setText(word);
        osg::Geode* geode = new osg::Geode;
        geode->addDrawable(text2D);
        group->addChild(geode);
    }

    if (arguments.read("--TextNode"))
    {
        // experimental text node
        osgText::TextNode* text = new osgText::TextNode;
        text->setFont(font.get());
        text->setStyle(style.get());
        text->setTextTechnique(new osgText::TextTechnique);
        text->setText(word);
        text->update();

        group->addChild(text);
    }
    else if (!arguments.read("--no-3d"))
    {
        osgText::Text3D* text3D = new osgText::Text3D;

        // Does not help
        text3D->setDataVariance(osg::Object::DYNAMIC);

        text3D->setFont(font.get());
        text3D->setStyle(style.get());
        text3D->setCharacterSize(characterSize);
        text3D->setDrawMode(osgText::Text3D::TEXT | osgText::Text3D::BOUNDINGBOX);
        text3D->setAxisAlignment(osgText::Text3D::XZ_PLANE);
        text3D->setText(word);

        osg::Geode* geode = new osg::Geode;
        geode->addDrawable(text3D);
        group->addChild(geode);

        osg::Vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
        while(arguments.read("--color",color.r(),color.g(),color.b(),color.a()))
        {
            OSG_NOTICE<<"--color "<<color<<std::endl;
            text3D->setColor(color);
        }

        std::string imageFilename;
        while(arguments.read("--image",imageFilename))
        {
            OSG_NOTICE<<"--image "<<imageFilename<<std::endl;
            osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(imageFilename);
            if (image.valid())
            {
                OSG_NOTICE<<"  loaded image "<<imageFilename<<std::endl;
                osg::StateSet* stateset = text3D->getOrCreateStateSet();
                stateset->setTextureAttributeAndModes(0, new osg::Texture2D(image), osg::StateAttribute::ON);
            }
        }

        while(arguments.read("--wall-color",color.r(),color.g(),color.b(),color.a()))
        {
            osg::StateSet* stateset = text3D->getOrCreateWallStateSet();
            osg::Material* material = new osg::Material;
            material->setDiffuse(osg::Material::FRONT_AND_BACK, color);
            stateset->setAttribute(material);
        }

        while(arguments.read("--wall-image",imageFilename))
        {
            osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(imageFilename);
            if (image.valid())
            {
                osg::StateSet* stateset = text3D->getOrCreateWallStateSet();
                stateset->setTextureAttributeAndModes(0, new osg::Texture2D(image), osg::StateAttribute::ON);
            }
        }

        while(arguments.read("--back-color",color.r(),color.g(),color.b(),color.a()))
        {
            osg::StateSet* stateset = text3D->getOrCreateBackStateSet();
            osg::Material* material = new osg::Material;
            material->setDiffuse(osg::Material::FRONT_AND_BACK, color);
            stateset->setAttribute(material);
        }

        while(arguments.read("--back-image",imageFilename))
        {
            osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(imageFilename);
            if (image)
            {
                osg::StateSet* stateset = text3D->getOrCreateBackStateSet();
                stateset->setTextureAttributeAndModes(0, new osg::Texture2D(image), osg::StateAttribute::ON);
            }
        }

        if (arguments.read("--size-quad"))
        {
            geode->addDrawable( osg::createTexturedQuadGeometry(osg::Vec3(0.0f,characterSize*thickness,0.0f),osg::Vec3(characterSize,0.0,0.0),osg::Vec3(0.0f,0.0,characterSize), 0.0, 0.0, 1.0, 1.0) );
        }

		if (arguments.read("--add-axes"))
			group->addChild(osgDB::readNodeFile("axes.osgt"));

		std::string mode;
		if (arguments.read("--character-size-mode", mode))
		{
			if (mode == "screen_coords")
			{
				text3D->setCharacterSizeMode(osgText::TextBase::SCREEN_COORDS);
				text3D->setCharacterSize(1080/4);
			}
		}

        viewer.addEventHandler(new Text3DAttributeHandler(text3D));
    }

    viewer.setSceneData(group);

#endif

    return viewer.run();
}
<commit_msg>example_osgtext3d: more options for testing<commit_after>/* OpenSceneGraph example, osgtext.
*
*  Permission is hereby granted, free of charge, to any person obtaining a copy
*  of this software and associated documentation files (the "Software"), to deal
*  in the Software without restriction, including without limitation the rights
*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the Software is
*  furnished to do so, subject to the following conditions:
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
*  THE SOFTWARE.
*/

#include <osg/ArgumentParser>
#include <osg/Material>
#include <osg/PositionAttitudeTransform>
#include <osg/io_utils>

#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgGA/StateSetManipulator>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>

#include <osgText/Text3D>

#include "TextNode.h"

class Text3DAttributeHandler : public osgGA::GUIEventHandler
{
public:
    Text3DAttributeHandler(osgText::Text3D* aText3D)
    :    m_Text3D(aText3D)
    {
    }

    ~Text3DAttributeHandler()
    {
    }

    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&)
    {
        if (ea.getEventType() == osgGA::GUIEventAdapter::KEYUP)
        {
            if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Up)
            {
                m_Text3D->setCharacterSize(m_Text3D->getCharacterHeight() + 0.1);
                OSG_NOTICE<<"m_Text3D->getCharacterHeight() = " << m_Text3D->getCharacterHeight() << std::endl;
            }
            else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Down)
            {
                m_Text3D->setCharacterDepth(m_Text3D->getCharacterDepth() + 0.1);
                OSG_NOTICE<<"m_Text3D->getCharacterDepth() = " << m_Text3D->getCharacterDepth() << std::endl;
            }
            else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left)
            {
                static int counter = 1;
                if (counter%3 == 0)
                   m_Text3D->setText("Press arrow keys.", osgText::String::ENCODING_UTF8);
                else if (counter%3 == 1)
                   m_Text3D->setText("setText\nworks!", osgText::String::ENCODING_UTF8);
                else if (counter%3 == 2)
                   m_Text3D->setText("setText really works?", osgText::String::ENCODING_UTF8);
                else if (counter%3 == 3)
                   m_Text3D->setText("setText works, really!", osgText::String::ENCODING_UTF8);

                ++counter;

                OSG_NOTICE<<"m_Text3D->getText().size() = " << m_Text3D->getText().size() << std::endl;
            }
            else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
            {
                m_Text3D->setLineSpacing(m_Text3D->getLineSpacing() + 0.1);
                OSG_NOTICE<<"m_Text3D->getLineSpacing() = " << m_Text3D->getLineSpacing() << std::endl;
            }
        }

        return false;
    }

private:
    osgText::Text3D*        m_Text3D;
};

int main(int argc, char** argv)
{
    osg::ArgumentParser arguments(&argc, argv);

    osgViewer::Viewer viewer(arguments);

    std::string fontFile("arial.ttf");
    while(arguments.read("-f",fontFile)) {}

    osg::ref_ptr<osgText::Font> font = osgText::readRefFontFile(fontFile);
    if (!font) return 1;
    OSG_NOTICE<<"Read font "<<fontFile<<" font="<<font.get()<<std::endl;

    std::string word("Press arrow keys.");
    while (arguments.read("-w",word)) {}

    osg::ref_ptr<osgText::Style> style = new osgText::Style;

    float thickness = 0.1f;
    while(arguments.read("--thickness",thickness)) {}
    style->setThicknessRatio(thickness);

    // set up any bevel if required
    float r;
    osg::ref_ptr<osgText::Bevel> bevel;
    while(arguments.read("--rounded",r)) { bevel = new osgText::Bevel; bevel->roundedBevel2(r); }
    while(arguments.read("--rounded")) { bevel = new osgText::Bevel; bevel->roundedBevel2(0.25); }
    while(arguments.read("--flat",r)) { bevel = new osgText::Bevel; bevel->flatBevel(r); }
    while(arguments.read("--flat")) { bevel = new osgText::Bevel; bevel->flatBevel(0.25); }
    while(arguments.read("--bevel-thickness",r)) { if (bevel.valid()) bevel->setBevelThickness(r); }

    if (bevel.valid())
    {
        while(arguments.read("--smooth-concave-Junctions") || arguments.read("--scj"))
        {
            bevel->setSmoothConcaveJunctions(true);
        }
    }

    style->setBevel(bevel.get());

    // set up outline.
    while(arguments.read("--outline",r)) { style->setOutlineRatio(r); }

    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
    viewer.addEventHandler(new osgViewer::StatsHandler);

#if 1
    osg::ref_ptr<osg::Group> group = new osg::Group;

    float characterSize = 1.0f;
    while(arguments.read("--size",characterSize)) {}

    if (arguments.read("--2d"))
    {
        osgText::Text* text2D = new osgText::Text;
        text2D->setFont(font.get());
        text2D->setCharacterSize(characterSize);
        text2D->setFontResolution(256,256);
        text2D->setDrawMode(osgText::Text::TEXT | osgText::Text::BOUNDINGBOX);
        text2D->setAxisAlignment(osgText::Text::XZ_PLANE);
        text2D->setText(word);
        osg::Geode* geode = new osg::Geode;
        geode->addDrawable(text2D);
        group->addChild(geode);
    }

    if (arguments.read("--TextNode"))
    {
        // experimental text node
        osgText::TextNode* text = new osgText::TextNode;
        text->setFont(font.get());
        text->setStyle(style.get());
        text->setTextTechnique(new osgText::TextTechnique);
        text->setText(word);
        text->update();

        group->addChild(text);
    }
    else if (!arguments.read("--no-3d"))
    {
        osgText::Text3D* text3D = new osgText::Text3D;

        // Does not help
        text3D->setDataVariance(osg::Object::DYNAMIC);

        text3D->setFont(font.get());
        text3D->setStyle(style.get());
        text3D->setCharacterSize(characterSize);
        text3D->setDrawMode(osgText::Text3D::TEXT | osgText::Text3D::BOUNDINGBOX);
        text3D->setAxisAlignment(osgText::Text3D::XZ_PLANE);
        text3D->setText(word);

        osg::Geode* geode = new osg::Geode;
        geode->addDrawable(text3D);
        group->addChild(geode);

        osg::Vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
        while(arguments.read("--color",color.r(),color.g(),color.b(),color.a()))
        {
            OSG_NOTICE<<"--color "<<color<<std::endl;
            text3D->setColor(color);
        }

        std::string imageFilename;
        while(arguments.read("--image",imageFilename))
        {
            OSG_NOTICE<<"--image "<<imageFilename<<std::endl;
            osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(imageFilename);
            if (image.valid())
            {
                OSG_NOTICE<<"  loaded image "<<imageFilename<<std::endl;
                osg::StateSet* stateset = text3D->getOrCreateStateSet();
                stateset->setTextureAttributeAndModes(0, new osg::Texture2D(image), osg::StateAttribute::ON);
            }
        }

        while(arguments.read("--wall-color",color.r(),color.g(),color.b(),color.a()))
        {
            osg::StateSet* stateset = text3D->getOrCreateWallStateSet();
            osg::Material* material = new osg::Material;
            material->setDiffuse(osg::Material::FRONT_AND_BACK, color);
            stateset->setAttribute(material);
        }

        while(arguments.read("--wall-image",imageFilename))
        {
            osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(imageFilename);
            if (image.valid())
            {
                osg::StateSet* stateset = text3D->getOrCreateWallStateSet();
                stateset->setTextureAttributeAndModes(0, new osg::Texture2D(image), osg::StateAttribute::ON);
            }
        }

        while(arguments.read("--back-color",color.r(),color.g(),color.b(),color.a()))
        {
            osg::StateSet* stateset = text3D->getOrCreateBackStateSet();
            osg::Material* material = new osg::Material;
            material->setDiffuse(osg::Material::FRONT_AND_BACK, color);
            stateset->setAttribute(material);
        }

        while(arguments.read("--back-image",imageFilename))
        {
            osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(imageFilename);
            if (image)
            {
                osg::StateSet* stateset = text3D->getOrCreateBackStateSet();
                stateset->setTextureAttributeAndModes(0, new osg::Texture2D(image), osg::StateAttribute::ON);
            }
        }

        if (arguments.read("--size-quad"))
        {
            geode->addDrawable( osg::createTexturedQuadGeometry(osg::Vec3(0.0f,characterSize*thickness,0.0f),osg::Vec3(characterSize,0.0,0.0),osg::Vec3(0.0f,0.0,characterSize), 0.0, 0.0, 1.0, 1.0) );
        }

        if (arguments.read("--add-axes"))
            group->addChild(osgDB::readNodeFile("axes.osgt"));

        std::string mode;
        if (arguments.read("--character-size-mode", mode))
        {
            if (mode == "screen_coords")
            {
                text3D->setCharacterSizeMode(osgText::TextBase::SCREEN_COORDS);
                text3D->setCharacterSize(1080/4);
            }
        }

        viewer.addEventHandler(new Text3DAttributeHandler(text3D));
    }

    viewer.setSceneData(group);

#endif

    return viewer.run();
}
<|endoftext|>