<commit_before>#include "GEK\Math\Float4x4.h"
#include "GEK\Shapes\AlignedBox.h"
#include "GEK\Shapes\OrientedBox.h"
#include "GEK\Utility\FileSystem.h"
#include "GEK\Utility\String.h"
#include "GEK\Utility\XML.h"
#include "GEK\Utility\Allocator.h"
#include "GEK\Context\ContextUser.h"
#include "GEK\System\VideoDevice.h"
#include "GEK\Engine\Core.h"
#include "GEK\Engine\Processor.h"
#include "GEK\Engine\ComponentMixin.h"
#include "GEK\Engine\Population.h"
#include "GEK\Engine\Entity.h"
#include "GEK\Engine\Renderer.h"
#include "GEK\Engine\Resources.h"
#include "GEK\Components\Transform.h"
#include "GEK\Components\Color.h"
#include <concurrent_unordered_map.h>
#include <functional>
#include <random>
#include <ppl.h>

namespace Gek
{
    namespace Components
    {
        GEK_COMPONENT(Explosion)
        {
            float strength;

            void save(Plugin::Population::ComponentDefinition &componentData) const
            {
                saveParameter(componentData, L"strength", strength);
            }

            void load(const Plugin::Population::ComponentDefinition &componentData)
            {
                strength = loadParameter(componentData, L"strength", 10.0f);
            }
        };
    }; // namespace Components

    namespace Sprites
    {
        GEK_CONTEXT_USER(Explosion)
            , public Plugin::ComponentMixin<Components::Explosion>
        {
        public:
            Explosion(Context *context)
                : ContextRegistration(context)
            {
            }

            // Plugin::Component
            const wchar_t * const getName(void) const
            {
                return L"explosion";
            }
        };

        GEK_CONTEXT_USER(EmitterProcessor, Plugin::Core *)
            , public Plugin::PopulationListener
            , public Plugin::PopulationStep
            , public Plugin::RendererListener
            , public Plugin::Processor
        {
        public:
            static const uint32_t SpritesBufferCount = 1000000;
            static std::random_device randomDevice;
            static std::mt19937 mersineTwister;

            __declspec(align(16))
            struct Sprite
            {
                Math::Float3 position;
                Math::Float3 velocity;
                float angle;
                float torque;
                float halfSize;
                float age;
                float buffer[2];

                Sprite(void)
                    : halfSize(0.0f)
                {
                }
            };

            struct EmitterData
            {
                Shapes::AlignedBox box;
                MaterialHandle material;
                std::vector<Sprite> spritesList;

                uint32_t tail;
                std::function<void(const Plugin::Entity *, EmitterData &, float)> update;
            };

        private:
            Plugin::Population *population;
            Plugin::Resources *resources;
            Plugin::Renderer *renderer;

            VisualHandle visual;
            Video::BufferPtr spritesBuffer;

            using EntityEmitterMap = std::unordered_map<Plugin::Entity *, std::vector<EmitterData>>;
            EntityEmitterMap entityEmitterMap;

            using VisibleMap = concurrency::concurrent_unordered_multimap<MaterialHandle, const EmitterData *>;
            VisibleMap visibleMap;

        public:
            EmitterProcessor(Context *context, Plugin::Core *core)
                : ContextRegistration(context)
                , population(core->getPopulation())
                , resources(core->getResources())
                , renderer(core->getRenderer())
            {
                GEK_REQUIRE(population);
                GEK_REQUIRE(resources);
                GEK_REQUIRE(renderer);

                population->addListener(this);
                population->addStep(this, 60);
                renderer->addListener(this);

                visual = resources->loadVisual(L"Sprites");
                spritesBuffer = renderer->getDevice()->createBuffer(sizeof(Sprite), SpritesBufferCount, Video::BufferType::Structured, Video::BufferFlags::Mappable | Video::BufferFlags::Resource, false);
            }

            ~EmitterProcessor(void)
            {
                renderer->removeListener(this);
                population->removeStep(this);
                population->removeListener(this);
            }

            // Plugin::PopulationListener
            void onLoadBegin(void)
            {
                entityEmitterMap.clear();
            }

            void onLoadSucceeded(void)
            {
            }

            void onLoadFailed(void)
            {
            }

            struct combineMinimum
            {
                concurrency::combinable<float> combinable;

                combineMinimum(void)
                    : combinable([&]
                    {
                        return Math::Infinity;
                    })
                {
                }

                void set(float value)
                {
                    auto &localValue = combinable.local();
                    localValue = std::min<float>(value, localValue);
                }

                float get(void)
                {
                    return combinable.combine([](float left, float right)
                    {
                        return std::min<float>(left, right);
                    });
                }
            };

            struct combineMaximum
            {
                concurrency::combinable<float> combinable;

                combineMaximum(void)
                    : combinable([&]
                    {
                        return Math::NegativeInfinity;
                    })
                {
                }

                void set(float value)
                {
                    auto &localValue = combinable.local();
                    localValue = std::max<float>(value, localValue);
                }

                float get(void)
                {
                    return combinable.combine([](float left, float right)
                    {
                        return std::max<float>(left, right);
                    });
                }
            };

            void onEntityCreated(Plugin::Entity *entity)
            {
                GEK_REQUIRE(resources);
                GEK_REQUIRE(entity);

                if (entity->hasComponent<Components::Transform>())
                {
                    static const Math::Float3 gravity(0.0f, -32.174f, 0.0f);
                    static const auto smoke = [this](const Plugin::Entity *entity, EmitterData &emitter) -> void
                    {
                        static const auto update = [](const Plugin::Entity *entity, EmitterData &emitter, float frameTime) -> void
                        {
                            auto &sprite = emitter.spritesList.front();
                            emitter.spritesList[emitter.tail++] = sprite;
                            if (emitter.tail >= emitter.spritesList.size())
                            {
                                emitter.tail = 1;
                            }

                            sprite.position += (sprite.velocity * frameTime);

                            combineMinimum minimum[3];
                            combineMaximum maximum[3];
                            concurrency::parallel_for_each((emitter.spritesList.begin() + 1), emitter.spritesList.end(), [&emitter, frameTime, &minimum, &maximum](Sprite &sprite) -> void
                            {
                                sprite.age += frameTime;
                                sprite.halfSize += frameTime;
                                sprite.angle += (sprite.torque * frameTime);
                                //sprite.position -= (gravity * frameTime);
                                for (uint32_t axis = 0; axis < 3; axis++)
                                {
                                    minimum[axis].set(sprite.position[axis] - sprite.halfSize);
                                    maximum[axis].set(sprite.position[axis] + sprite.halfSize);
                                }
                            });

                            emitter.box.minimum.x = minimum[0].get();
                            emitter.box.minimum.y = minimum[1].get();
                            emitter.box.minimum.z = minimum[2].get();
                            emitter.box.maximum.x = maximum[0].get();
                            emitter.box.maximum.y = maximum[1].get();
                            emitter.box.maximum.z = maximum[2].get();
                        };

                        const auto &explosionComponent = entity->getComponent<Components::Explosion>();
                        const auto &transformComponent = entity->getComponent<Components::Transform>();

                        static const std::uniform_real_distribution<float> spawnDirection(-1.0f, 1.0f);
                        static const std::uniform_real_distribution<float> spawnAngle(0.0f, (Gek::Math::Pi * 2.0f));
                        static const std::uniform_real_distribution<float> spawnTorque(-Gek::Math::Pi + 0.25f, Gek::Math::Pi * 0.25f);
                        static const std::uniform_real_distribution<float> spawnStrength(0.5f, 1.0f);

                        emitter.tail = 1;
                        emitter.update = update;
                        emitter.material = resources->loadMaterial(L"Sprites\\Smoke");

                        emitter.spritesList.resize(1000);
                        auto &sprite = emitter.spritesList.front();
                        sprite.position = transformComponent.position;
                        sprite.velocity.x = spawnDirection(mersineTwister);
                        sprite.velocity.y = spawnDirection(mersineTwister);
                        sprite.velocity.z = spawnDirection(mersineTwister);
                        sprite.velocity *= ((1.0f / sprite.velocity.getLength()) * spawnStrength(mersineTwister) * explosionComponent.strength);
                        sprite.angle = spawnAngle(mersineTwister);
                        sprite.torque = spawnTorque(mersineTwister);
                        sprite.halfSize = 0.0f;
                        sprite.age = 0.0f;
                    };

                    static const auto spark = [this](const Plugin::Entity *entity, EmitterData &emitter) -> void
                    {
                        static const auto update = [](const Plugin::Entity *entity, EmitterData &emitter, float frameTime) -> void
                        {
                            auto &sprite = emitter.spritesList.front();
                            emitter.spritesList[emitter.tail++] = sprite;
                            if (emitter.tail >= emitter.spritesList.size())
                            {
                                emitter.tail = 1;
                            }

                            sprite.position += (sprite.velocity * frameTime);
                            sprite.velocity += (gravity * frameTime);

                            combineMinimum minimum[3];
                            combineMaximum maximum[3];
                            concurrency::parallel_for_each((emitter.spritesList.begin() + 1), emitter.spritesList.end(), [&emitter, frameTime, &minimum, &maximum](Sprite &sprite) -> void
                            {
                                sprite.age += frameTime;
                                sprite.angle += (sprite.torque * frameTime);
                                for (uint32_t axis = 0; axis < 3; axis++)
                                {
                                    minimum[axis].set(sprite.position[axis] - sprite.halfSize);
                                    maximum[axis].set(sprite.position[axis] + sprite.halfSize);
                                }
                            });

                            emitter.box.minimum.x = minimum[0].get();
                            emitter.box.minimum.y = minimum[1].get();
                            emitter.box.minimum.z = minimum[2].get();
                            emitter.box.maximum.x = maximum[0].get();
                            emitter.box.maximum.y = maximum[1].get();
                            emitter.box.maximum.z = maximum[2].get();
                        };

                        const auto &explosionComponent = entity->getComponent<Components::Explosion>();
                        const auto &transformComponent = entity->getComponent<Components::Transform>();

                        static const std::uniform_real_distribution<float> spawnDirection(-1.0f, 1.0f);
                        static const std::uniform_real_distribution<float> spawnAngle(0.0f, (Gek::Math::Pi * 2.0f));
                        static const std::uniform_real_distribution<float> spawnTorque(-Gek::Math::Pi, Gek::Math::Pi);
                        static const std::uniform_real_distribution<float> spawnStrength(1.0f, 2.0f);

                        emitter.tail = 1;
                        emitter.update = update;
                        emitter.material = resources->loadMaterial(L"Sprites\\Spark");

                        emitter.spritesList.resize(1000);
                        auto &sprite = emitter.spritesList.front();
                        sprite.position = transformComponent.position;
                        sprite.velocity.x = spawnDirection(mersineTwister);
                        sprite.velocity.y = spawnDirection(mersineTwister);
                        sprite.velocity.z = spawnDirection(mersineTwister);
                        sprite.velocity *= ((1.0f / sprite.velocity.getLength()) * spawnStrength(mersineTwister) * explosionComponent.strength);
                        sprite.angle = spawnAngle(mersineTwister);
                        sprite.torque = spawnTorque(mersineTwister);
                        sprite.halfSize = 0.1f;
                        sprite.age = 0.0f;
                    };

                    if (entity->hasComponent<Components::Explosion>())
                    {
                        auto &entityEmitterList = entityEmitterMap[entity];

                        entityEmitterList.resize(30);
                        for (uint32_t index = 0; index < 20; index++)
                        {
                            smoke(entity, entityEmitterList[index]);
                        }

                        for (uint32_t index = 20; index < 30; index++)
                        {
                            spark(entity, entityEmitterList[index]);
                        }
                    }
                }
            }

            void onEntityDestroyed(Plugin::Entity *entity)
            {
                GEK_REQUIRE(entity);

                auto entitySearch = entityEmitterMap.find(entity);
                if (entitySearch != entityEmitterMap.end())
                {
                    entityEmitterMap.erase(entitySearch);
                }
            }

            // Plugin::PopulationStep
            void onUpdate(uint32_t order, State state)
            {
                if (state == State::Active)
                {
                    const float frameTime = population->getFrameTime() * 0.1f;
                    concurrency::parallel_for_each(entityEmitterMap.begin(), entityEmitterMap.end(), [&](auto &entityEmitterPair) -> void
                    {
                        const Plugin::Entity *entity = entityEmitterPair.first;
                        concurrency::parallel_for_each(entityEmitterPair.second.begin(), entityEmitterPair.second.end(), [&](auto &emitter) -> void
                        {
                            emitter.update(entity, emitter, frameTime);
                        });
                    });
                }
            }

            // Plugin::RendererListener
            static void drawCall(Video::Device::Context *deviceContext, Plugin::Resources *resources, const VisibleMap::iterator visibleBegin, const VisibleMap::iterator visibleEnd, Video::Buffer *spritesBuffer)
            {
                GEK_REQUIRE(deviceContext);
                GEK_REQUIRE(resources);

                deviceContext->vertexPipeline()->setResource(spritesBuffer, 0);

                uint32_t bufferCopied = 0;
                Sprite *bufferData = nullptr;
                deviceContext->getDevice()->mapBuffer(spritesBuffer, (void **)&bufferData);
                for (auto emitterSearch = visibleBegin; emitterSearch != visibleEnd; ++emitterSearch)
                {
                    const auto &emitter = *emitterSearch->second;

                    uint32_t spritesCopied = 0;
                    uint32_t spritesCount = emitter.spritesList.size();
                    const Sprite *spriteData = emitter.spritesList.data();
                    while (spritesCopied < spritesCount)
                    {
                        uint32_t bufferRemaining = (SpritesBufferCount - bufferCopied);
                        uint32_t spritesRemaining = (spritesCount - spritesCopied);
                        uint32_t copyCount = std::min(bufferRemaining, spritesRemaining);
                        memcpy(&bufferData[bufferCopied], &spriteData[spritesCopied], (sizeof(Sprite) * copyCount));

                        bufferCopied += copyCount;
                        spritesCopied += copyCount;
                        if (bufferCopied >= SpritesBufferCount)
                        {
                            deviceContext->getDevice()->unmapBuffer(spritesBuffer);
                            deviceContext->drawPrimitive((SpritesBufferCount * 6), 0);
                            deviceContext->getDevice()->mapBuffer(spritesBuffer, (void **)&bufferData);
                            bufferCopied = 0;
                        }
                    };
                }

                deviceContext->getDevice()->unmapBuffer(spritesBuffer);
                if (bufferCopied > 0)
                {
                    deviceContext->drawPrimitive((bufferCopied * 6), 0);
                }
            }

            void onRenderScene(const Plugin::Entity *cameraEntity, const Math::Float4x4 &viewMatrix, const Shapes::Frustum &viewFrustum)
            {
                GEK_REQUIRE(renderer);
                GEK_REQUIRE(cameraEntity);

                visibleMap.clear();
                concurrency::parallel_for_each(entityEmitterMap.begin(), entityEmitterMap.end(), [&](auto &entityEmitterPair) -> void
                {
                    concurrency::parallel_for_each(entityEmitterPair.second.begin(), entityEmitterPair.second.end(), [&](auto &emitter) -> void
                    {
                        if (viewFrustum.isVisible(emitter.box))
                        {
                            visibleMap.insert(std::make_pair(emitter.material, &emitter));
                        }
                    });
                });

                for (auto propertiesSearch = visibleMap.begin(); propertiesSearch != visibleMap.end(); )
                {
                    const auto emittersRange = visibleMap.equal_range(propertiesSearch->first);
                    renderer->queueDrawCall(visual, propertiesSearch->first, std::bind(drawCall, std::placeholders::_1, resources, emittersRange.first, emittersRange.second, spritesBuffer.get()));
                    propertiesSearch = emittersRange.second;
                }
            }
        };

        std::random_device EmitterProcessor::randomDevice;
        std::mt19937 EmitterProcessor::mersineTwister(randomDevice());

        GEK_REGISTER_CONTEXT_USER(Explosion)
        GEK_REGISTER_CONTEXT_USER(EmitterProcessor)
    }; // namespace Sprites
}; // namespace Gek<commit_msg>Improving spherical point sampling, but slower.  Not a major issue since only the initial point is calculated.<commit_after>#include "GEK\Math\Float4x4.h"
#include "GEK\Shapes\AlignedBox.h"
#include "GEK\Shapes\OrientedBox.h"
#include "GEK\Utility\FileSystem.h"
#include "GEK\Utility\String.h"
#include "GEK\Utility\XML.h"
#include "GEK\Utility\Allocator.h"
#include "GEK\Context\ContextUser.h"
#include "GEK\System\VideoDevice.h"
#include "GEK\Engine\Core.h"
#include "GEK\Engine\Processor.h"
#include "GEK\Engine\ComponentMixin.h"
#include "GEK\Engine\Population.h"
#include "GEK\Engine\Entity.h"
#include "GEK\Engine\Renderer.h"
#include "GEK\Engine\Resources.h"
#include "GEK\Components\Transform.h"
#include "GEK\Components\Color.h"
#include <concurrent_unordered_map.h>
#include <functional>
#include <random>
#include <ppl.h>

namespace Gek
{
    namespace Components
    {
        GEK_COMPONENT(Explosion)
        {
            float strength;

            void save(Plugin::Population::ComponentDefinition &componentData) const
            {
                saveParameter(componentData, L"strength", strength);
            }

            void load(const Plugin::Population::ComponentDefinition &componentData)
            {
                strength = loadParameter(componentData, L"strength", 10.0f);
            }
        };
    }; // namespace Components

    namespace Sprites
    {
        GEK_CONTEXT_USER(Explosion)
            , public Plugin::ComponentMixin<Components::Explosion>
        {
        public:
            Explosion(Context *context)
                : ContextRegistration(context)
            {
            }

            // Plugin::Component
            const wchar_t * const getName(void) const
            {
                return L"explosion";
            }
        };

        GEK_CONTEXT_USER(EmitterProcessor, Plugin::Core *)
            , public Plugin::PopulationListener
            , public Plugin::PopulationStep
            , public Plugin::RendererListener
            , public Plugin::Processor
        {
        public:
            static const uint32_t SpritesBufferCount = 1000000;
            static std::random_device randomDevice;
            static std::mt19937 mersineTwister;

            __declspec(align(16))
            struct Sprite
            {
                Math::Float3 position;
                Math::Float3 velocity;
                float angle;
                float torque;
                float halfSize;
                float age;
                float buffer[2];

                Sprite(void)
                    : halfSize(0.0f)
                {
                }
            };

            struct EmitterData
            {
                Shapes::AlignedBox box;
                MaterialHandle material;
                std::vector<Sprite> spritesList;

                uint32_t tail;
                std::function<void(const Plugin::Entity *, EmitterData &, float)> update;
            };

        private:
            Plugin::Population *population;
            Plugin::Resources *resources;
            Plugin::Renderer *renderer;

            VisualHandle visual;
            Video::BufferPtr spritesBuffer;

            using EntityEmitterMap = std::unordered_map<Plugin::Entity *, std::vector<EmitterData>>;
            EntityEmitterMap entityEmitterMap;

            using VisibleMap = concurrency::concurrent_unordered_multimap<MaterialHandle, const EmitterData *>;
            VisibleMap visibleMap;

        public:
            EmitterProcessor(Context *context, Plugin::Core *core)
                : ContextRegistration(context)
                , population(core->getPopulation())
                , resources(core->getResources())
                , renderer(core->getRenderer())
            {
                GEK_REQUIRE(population);
                GEK_REQUIRE(resources);
                GEK_REQUIRE(renderer);

                population->addListener(this);
                population->addStep(this, 60);
                renderer->addListener(this);

                visual = resources->loadVisual(L"Sprites");
                spritesBuffer = renderer->getDevice()->createBuffer(sizeof(Sprite), SpritesBufferCount, Video::BufferType::Structured, Video::BufferFlags::Mappable | Video::BufferFlags::Resource, false);
            }

            ~EmitterProcessor(void)
            {
                renderer->removeListener(this);
                population->removeStep(this);
                population->removeListener(this);
            }

            // Plugin::PopulationListener
            void onLoadBegin(void)
            {
                entityEmitterMap.clear();
            }

            void onLoadSucceeded(void)
            {
            }

            void onLoadFailed(void)
            {
            }

            struct combineMinimum
            {
                concurrency::combinable<float> combinable;

                combineMinimum(void)
                    : combinable([&]
                    {
                        return Math::Infinity;
                    })
                {
                }

                void set(float value)
                {
                    auto &localValue = combinable.local();
                    localValue = std::min<float>(value, localValue);
                }

                float get(void)
                {
                    return combinable.combine([](float left, float right)
                    {
                        return std::min<float>(left, right);
                    });
                }
            };

            struct combineMaximum
            {
                concurrency::combinable<float> combinable;

                combineMaximum(void)
                    : combinable([&]
                    {
                        return Math::NegativeInfinity;
                    })
                {
                }

                void set(float value)
                {
                    auto &localValue = combinable.local();
                    localValue = std::max<float>(value, localValue);
                }

                float get(void)
                {
                    return combinable.combine([](float left, float right)
                    {
                        return std::max<float>(left, right);
                    });
                }
            };

            void onEntityCreated(Plugin::Entity *entity)
            {
                GEK_REQUIRE(resources);
                GEK_REQUIRE(entity);

                if (entity->hasComponent<Components::Transform>())
                {
                    static const Math::Float3 gravity(0.0f, -32.174f, 0.0f);
                    static const auto smoke = [this](const Plugin::Entity *entity, EmitterData &emitter) -> void
                    {
                        static const auto update = [](const Plugin::Entity *entity, EmitterData &emitter, float frameTime) -> void
                        {
                            auto &sprite = emitter.spritesList.front();
                            emitter.spritesList[emitter.tail++] = sprite;
                            if (emitter.tail >= emitter.spritesList.size())
                            {
                                emitter.tail = 1;
                            }

                            sprite.position += (sprite.velocity * frameTime);

                            combineMinimum minimum[3];
                            combineMaximum maximum[3];
                            concurrency::parallel_for_each((emitter.spritesList.begin() + 1), emitter.spritesList.end(), [&emitter, frameTime, &minimum, &maximum](Sprite &sprite) -> void
                            {
                                sprite.age += frameTime;
                                sprite.halfSize += frameTime;
                                sprite.angle += (sprite.torque * frameTime);
                                //sprite.position -= (gravity * frameTime);
                                for (uint32_t axis = 0; axis < 3; axis++)
                                {
                                    minimum[axis].set(sprite.position[axis] - sprite.halfSize);
                                    maximum[axis].set(sprite.position[axis] + sprite.halfSize);
                                }
                            });

                            emitter.box.minimum.x = minimum[0].get();
                            emitter.box.minimum.y = minimum[1].get();
                            emitter.box.minimum.z = minimum[2].get();
                            emitter.box.maximum.x = maximum[0].get();
                            emitter.box.maximum.y = maximum[1].get();
                            emitter.box.maximum.z = maximum[2].get();
                        };

                        const auto &explosionComponent = entity->getComponent<Components::Explosion>();
                        const auto &transformComponent = entity->getComponent<Components::Transform>();

                        static const std::uniform_real_distribution<float> spawnDirection(-1.0f, 1.0f);
                        static const std::uniform_real_distribution<float> spawnAzimuthal(0.0f, (Gek::Math::Pi * 2.0f));
                        static const std::uniform_real_distribution<float> spawnZenith(0.0f, 1.0f);
                        static const std::uniform_real_distribution<float> spawnCircle(-Gek::Math::Pi, Gek::Math::Pi);
                        static const std::uniform_real_distribution<float> spawnStrength(0.5f, 1.0f);

                        emitter.tail = 1;
                        emitter.update = update;
                        emitter.material = resources->loadMaterial(L"Sprites\\Smoke");
                        emitter.spritesList.resize(1000);

                        float theta = spawnAzimuthal(mersineTwister);
                        float phi = acos(spawnDirection(mersineTwister));

                        auto &sprite = emitter.spritesList.front();
                        sprite.position = transformComponent.position;
                        sprite.velocity.x = std::sin(phi) * std::sin(theta);
                        sprite.velocity.y = -std::sin(phi) * std::cos(theta);
                        sprite.velocity.z = std::cos(phi);
                        sprite.velocity *= (spawnStrength(mersineTwister) * explosionComponent.strength);
                        sprite.angle = spawnCircle(mersineTwister);
                        sprite.torque = spawnCircle(mersineTwister);
                        sprite.halfSize = 0.1f;
                        sprite.age = 0.0f;
                    };

                    static const auto spark = [this](const Plugin::Entity *entity, EmitterData &emitter) -> void
                    {
                        static const auto update = [](const Plugin::Entity *entity, EmitterData &emitter, float frameTime) -> void
                        {
                            auto &sprite = emitter.spritesList.front();
                            emitter.spritesList[emitter.tail++] = sprite;
                            if (emitter.tail >= emitter.spritesList.size())
                            {
                                emitter.tail = 1;
                            }

                            sprite.position += (sprite.velocity * frameTime);
                            sprite.velocity += (gravity * frameTime);

                            combineMinimum minimum[3];
                            combineMaximum maximum[3];
                            concurrency::parallel_for_each((emitter.spritesList.begin() + 1), emitter.spritesList.end(), [&emitter, frameTime, &minimum, &maximum](Sprite &sprite) -> void
                            {
                                sprite.age += frameTime;
                                sprite.angle += (sprite.torque * frameTime);
                                for (uint32_t axis = 0; axis < 3; axis++)
                                {
                                    minimum[axis].set(sprite.position[axis] - sprite.halfSize);
                                    maximum[axis].set(sprite.position[axis] + sprite.halfSize);
                                }
                            });

                            emitter.box.minimum.x = minimum[0].get();
                            emitter.box.minimum.y = minimum[1].get();
                            emitter.box.minimum.z = minimum[2].get();
                            emitter.box.maximum.x = maximum[0].get();
                            emitter.box.maximum.y = maximum[1].get();
                            emitter.box.maximum.z = maximum[2].get();
                        };

                        const auto &explosionComponent = entity->getComponent<Components::Explosion>();
                        const auto &transformComponent = entity->getComponent<Components::Transform>();

                        static const std::uniform_real_distribution<float> spawnDirection(-1.0f, 1.0f);
                        static const std::uniform_real_distribution<float> spawnAzimuthal(0.0f, (Gek::Math::Pi * 2.0f));
                        static const std::uniform_real_distribution<float> spawnZenith(0.0f, 1.0f);
                        static const std::uniform_real_distribution<float> spawnCircle(-Gek::Math::Pi, Gek::Math::Pi);
                        static const std::uniform_real_distribution<float> spawnStrength(1.0f, 2.0f);

                        emitter.tail = 1;
                        emitter.update = update;
                        emitter.material = resources->loadMaterial(L"Sprites\\Spark");
                        emitter.spritesList.resize(1000);

                        float theta = spawnAzimuthal(mersineTwister);
                        float phi = acos(spawnDirection(mersineTwister));

                        auto &sprite = emitter.spritesList.front();
                        sprite.position = transformComponent.position;
                        sprite.velocity.x = std::sin(phi) * std::sin(theta);
                        sprite.velocity.y = -std::sin(phi) * std::cos(theta);
                        sprite.velocity.z = std::cos(phi);
                        sprite.velocity *= (spawnStrength(mersineTwister) * explosionComponent.strength);
                        sprite.angle = spawnCircle(mersineTwister);
                        sprite.torque = spawnCircle(mersineTwister);
                        sprite.halfSize = 0.1f;
                        sprite.age = 0.0f;
                    };

                    if (entity->hasComponent<Components::Explosion>())
                    {
                        auto &entityEmitterList = entityEmitterMap[entity];

                        entityEmitterList.resize(30);
                        for (uint32_t index = 0; index < 20; index++)
                        {
                            smoke(entity, entityEmitterList[index]);
                        }

                        for (uint32_t index = 20; index < 30; index++)
                        {
                            spark(entity, entityEmitterList[index]);
                        }
                    }
                }
            }

            void onEntityDestroyed(Plugin::Entity *entity)
            {
                GEK_REQUIRE(entity);

                auto entitySearch = entityEmitterMap.find(entity);
                if (entitySearch != entityEmitterMap.end())
                {
                    entityEmitterMap.erase(entitySearch);
                }
            }

            // Plugin::PopulationStep
            void onUpdate(uint32_t order, State state)
            {
                if (state == State::Active)
                {
                    const float frameTime = population->getFrameTime() * 0.1f;
                    concurrency::parallel_for_each(entityEmitterMap.begin(), entityEmitterMap.end(), [&](auto &entityEmitterPair) -> void
                    {
                        const Plugin::Entity *entity = entityEmitterPair.first;
                        concurrency::parallel_for_each(entityEmitterPair.second.begin(), entityEmitterPair.second.end(), [&](auto &emitter) -> void
                        {
                            emitter.update(entity, emitter, frameTime);
                        });
                    });
                }
            }

            // Plugin::RendererListener
            static void drawCall(Video::Device::Context *deviceContext, Plugin::Resources *resources, const VisibleMap::iterator visibleBegin, const VisibleMap::iterator visibleEnd, Video::Buffer *spritesBuffer)
            {
                GEK_REQUIRE(deviceContext);
                GEK_REQUIRE(resources);

                deviceContext->vertexPipeline()->setResource(spritesBuffer, 0);

                uint32_t bufferCopied = 0;
                Sprite *bufferData = nullptr;
                deviceContext->getDevice()->mapBuffer(spritesBuffer, (void **)&bufferData);
                for (auto emitterSearch = visibleBegin; emitterSearch != visibleEnd; ++emitterSearch)
                {
                    const auto &emitter = *emitterSearch->second;

                    uint32_t spritesCopied = 0;
                    uint32_t spritesCount = emitter.spritesList.size();
                    const Sprite *spriteData = emitter.spritesList.data();
                    while (spritesCopied < spritesCount)
                    {
                        uint32_t bufferRemaining = (SpritesBufferCount - bufferCopied);
                        uint32_t spritesRemaining = (spritesCount - spritesCopied);
                        uint32_t copyCount = std::min(bufferRemaining, spritesRemaining);
                        memcpy(&bufferData[bufferCopied], &spriteData[spritesCopied], (sizeof(Sprite) * copyCount));

                        bufferCopied += copyCount;
                        spritesCopied += copyCount;
                        if (bufferCopied >= SpritesBufferCount)
                        {
                            deviceContext->getDevice()->unmapBuffer(spritesBuffer);
                            deviceContext->drawPrimitive((SpritesBufferCount * 6), 0);
                            deviceContext->getDevice()->mapBuffer(spritesBuffer, (void **)&bufferData);
                            bufferCopied = 0;
                        }
                    };
                }

                deviceContext->getDevice()->unmapBuffer(spritesBuffer);
                if (bufferCopied > 0)
                {
                    deviceContext->drawPrimitive((bufferCopied * 6), 0);
                }
            }

            void onRenderScene(const Plugin::Entity *cameraEntity, const Math::Float4x4 &viewMatrix, const Shapes::Frustum &viewFrustum)
            {
                GEK_REQUIRE(renderer);
                GEK_REQUIRE(cameraEntity);

                visibleMap.clear();
                concurrency::parallel_for_each(entityEmitterMap.begin(), entityEmitterMap.end(), [&](auto &entityEmitterPair) -> void
                {
                    concurrency::parallel_for_each(entityEmitterPair.second.begin(), entityEmitterPair.second.end(), [&](auto &emitter) -> void
                    {
                        if (viewFrustum.isVisible(emitter.box))
                        {
                            visibleMap.insert(std::make_pair(emitter.material, &emitter));
                        }
                    });
                });

                for (auto propertiesSearch = visibleMap.begin(); propertiesSearch != visibleMap.end(); )
                {
                    const auto emittersRange = visibleMap.equal_range(propertiesSearch->first);
                    renderer->queueDrawCall(visual, propertiesSearch->first, std::bind(drawCall, std::placeholders::_1, resources, emittersRange.first, emittersRange.second, spritesBuffer.get()));
                    propertiesSearch = emittersRange.second;
                }
            }
        };

        std::random_device EmitterProcessor::randomDevice;
        std::mt19937 EmitterProcessor::mersineTwister(randomDevice());

        GEK_REGISTER_CONTEXT_USER(Explosion)
        GEK_REGISTER_CONTEXT_USER(EmitterProcessor)
    }; // namespace Sprites
}; // namespace Gek<|endoftext|>


<commit_before>/****************** <VPR heading BEGIN do not edit this line> *****************
 *
 * VR Juggler Portable Runtime
 *
 * Original Authors:
 *   Allen Bierbaum, Patrick Hartling, Kevin Meinert, Carolina Cruz-Neira
 *
 * -----------------------------------------------------------------
 * File:          $RCSfile$
 * Date modified: $Date$
 * Version:       $Revision$
 * -----------------------------------------------------------------
 *
 ****************** <VPR heading END do not edit this line> ******************/

/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998-2003 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

#include <vpr/IO/XMLObjectReader.h>

#include <vector>

#include <boost/static_assert.hpp>
#include <vpr/IO/ObjectReader.h>

#include <vpr/Util/Assert.h>
#include <vpr/Util/Debug.h>

#include <cppdom/cppdom.h>
#include <string>
#include <sstream>
#include <iterator>

#define XML_OR_LEVEL vprDBG_HEX_LVL

namespace
{
/** Helper to read the data from the current string */
 template<class T>
 vpr::ReturnStatus readValueStringRep(T& val, std::stringstream* inStream)
 {
    vprASSERT(inStream != NULL);

    // Just to be safe do this again for the heck of it. :)
    //in_stream->exceptions( std::ios::eofbit | std::ios::failbit | std::ios::badbit );
    vprASSERT(!inStream->bad() && "Bad stream, BAAADDD stream.");
    vprASSERT(!inStream->fail() && "Stream failed.");
    vprASSERT(!inStream->eof() && "Stream EOF'd.");
    vprASSERT(inStream->good());

    //stream_content = (*in_stream).str();

    (*inStream) >> val;

    return vpr::ReturnStatus::Succeed;
 }

}

namespace vpr
{

XMLObjectReader::XMLObjectReader(std::vector<vpr::Uint8> data)
   : mCurSource(CdataSource)
{
   initCppDomTree(data);
   //mAttribSource.exceptions( std::ios::eofbit | std::ios::failbit | std::ios::badbit );
}

XMLObjectReader::XMLObjectReader(cppdom::NodePtr rootNode)
   : mCurSource(CdataSource)
{
   mRootNode = rootNode;
   //mAttribSource.exceptions( std::ios::eofbit | std::ios::failbit | std::ios::badbit );
}

XMLObjectReader::NodeState::NodeState(cppdom::Node* cur_node)
   : node(cur_node)
{
   nextChild_i = node->getChildren().begin();     // Initialize to the first child
   endChild_i = node->getChildren().end();
   //cdataSource.exceptions( std::ios::eofbit | std::ios::failbit | std::ios::badbit );
   cdataSource.str(node->getFullCdata());
}

XMLObjectReader::NodeState::NodeState(const NodeState& rhs)
{
   node = rhs.node;
   nextChild_i = rhs.nextChild_i;
   endChild_i = rhs.endChild_i;
   //cdataSource.exceptions( std::ios::eofbit | std::ios::failbit | std::ios::badbit );
   cdataSource.str(rhs.cdataSource.str());
}

XMLObjectReader::NodeState& XMLObjectReader::NodeState::operator=(const NodeState& rhs)
{
   node = rhs.node;
   nextChild_i = rhs.nextChild_i;
   endChild_i = rhs.endChild_i;
   cdataSource.str(rhs.cdataSource.str());
   return *this;
}

void XMLObjectReader::NodeState::debugDump(int debug_level)
{
   vprDEBUG(vprDBG_ALL, debug_level) << "Node:" << node->getName() << "  num children:" << node->getChildren().size()
                                     << "  distance: nextChild-->endChild:" << std::distance(nextChild_i,endChild_i) << std::endl << vprDEBUG_FLUSH;
}

/** Get the current string source we are reading from.
*/
std::stringstream* XMLObjectReader::getCurSource()
{
   std::stringstream* in_stream(NULL);

   if(AttribSource == mCurSource)
   {
      in_stream = &mAttribSource;
   }
   else
   {
      in_stream = &(mCurNodeStack.back().cdataSource);
   }

   return in_stream;
}

/** Initialize the members based on a serialized version of something in the data buffer */
void XMLObjectReader::initCppDomTree(std::vector<vpr::Uint8> data)
{
   std::string xml_data(data.begin(), data.end());
   std::stringstream xml_stream(xml_data);
   cppdom::ContextPtr cxt(new cppdom::Context);
   cppdom::NodePtr new_node = cppdom::NodePtr(new cppdom::Node(cxt));
   new_node->load(xml_stream, cxt);

   mRootNode = new_node;

   std::string root_name = mRootNode->getName();   // Debugging

   mCurNodeStack.clear();
}

void XMLObjectReader::debugDumpStack(int debug_level)
{
   vprDEBUG_OutputGuard(vprDBG_ALL, debug_level, "mCurNodeStack:\n", "end node stack\n");

   for(unsigned i=0; i<mCurNodeStack.size(); ++i)
   {
      mCurNodeStack[i].debugDump(debug_level);
   }
}

/** Starting a new tag
* Get the local cdata into the current data source
*/
vpr::ReturnStatus XMLObjectReader::beginTag(std::string tagName)
{
   vprDEBUG_OutputGuard(vprDBG_ALL, XML_OR_LEVEL, std::string("beginTag:[") + tagName + std::string("]\n"), "endTag");

   vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "mCurNodeStack: size: " << mCurNodeStack.size() << std::endl << vprDEBUG_FLUSH;
   debugDumpStack(XML_OR_LEVEL);

   // Find the correct child
   if(mCurNodeStack.empty())  // We should enter the root
   {
      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "mCurNodeStack is empty. Push on root.\n" << vprDEBUG_FLUSH;
      mCurNodeStack.push_back(NodeState(mRootNode.get()));
   }
   else                      // Find the next child and push it on
   {
      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "Attempting to find next child: "
                             << " cur node (back): " << mCurNodeStack.back().node->getName()
                             << "  num children:" << mCurNodeStack.back().node->getChildren().size() << std::endl << vprDEBUG_FLUSH;

      vprASSERT(mCurNodeStack.back().nextChild_i != mCurNodeStack.back().endChild_i && "Past last child. No remaining child tags.");

      // Make sure that we get to child of type node.
      // While next child is not of type node skip over cdata...
      while( (*(mCurNodeStack.back().nextChild_i))->getType() != cppdom::xml_nt_node)
      {
         vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "Skip node: " << (*(mCurNodeStack.back().nextChild_i))->getName() << " -- non nt_node type.\n" << vprDEBUG_FLUSH;
         mCurNodeStack.back().nextChild_i++;
         vprASSERT(mCurNodeStack.back().nextChild_i != mCurNodeStack.back().endChild_i && "Skipped past last child. No remaining child tags.");
      }

      cppdom::NodePtr tag_node = *(mCurNodeStack.back().nextChild_i);   // Get the node with the given tag
      mCurNodeStack.back().nextChild_i++;                               // Step to the next child for next time

      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "Incrementing next child:  at end:" << (mCurNodeStack.back().nextChild_i == mCurNodeStack.back().endChild_i) << std::endl << vprDEBUG_FLUSH;

      mCurNodeStack.push_back( NodeState(tag_node.get()));              // Add the child to the stack
      vprASSERT(tag_node.get() == mCurNodeStack.back().node);

      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "Found node:" << tag_node->getName() << std::endl << vprDEBUG_FLUSH;
      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "  Node stack size:" << mCurNodeStack.size() << std::endl << vprDEBUG_FLUSH;
      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "  Num children:" << tag_node->getChildren().size() << std::endl << vprDEBUG_FLUSH;
      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "  New node child at end:" << (mCurNodeStack.back().nextChild_i == mCurNodeStack.back().endChild_i)
                             << std::endl << vprDEBUG_FLUSH;
   }

   std::string cur_node_name = mCurNodeStack.back().node->getName();
   vprASSERT(tagName == cur_node_name && "Reading back in incorrect order");
   return vpr::ReturnStatus::Succeed;
}

/** Ends the most recently named tag.
* Just pop of the cur node state information
*/
vpr::ReturnStatus XMLObjectReader::endTag()
{
   vprASSERT(!mCurNodeStack.empty());
   mCurNodeStack.pop_back();
   return vpr::ReturnStatus::Succeed;
}

/** Starts an attribute of the name attributeName
* Get the attribute content and set the attribute source with that content
*/
vpr::ReturnStatus XMLObjectReader::beginAttribute(std::string attributeName)
{
   //std::cout << "beginAttribute: " << attributeName << std::endl;
   std::string attrib_content = mCurNodeStack.back().node->getAttribute(attributeName).getString();
   //std::cout << "  attrib content: [" << attrib_content << "]\n";
   mAttribSource.clear();                       // Clear error states
   mAttribSource.str(attrib_content);
   mAttribSource.seekg(0,std::ios::beg);        // Seek to beginning
   //std::cout << "  mAttribSource.str():[" << mAttribSource.str() << "]" << std::endl;
   mCurSource = AttribSource;             // Get content from attribute now
   return vpr::ReturnStatus::Succeed;
}

/** Ends the most recently named attribute */
vpr::ReturnStatus XMLObjectReader::endAttribute()
{
   mAttribSource.clear();
   mAttribSource.seekg(0,std::ios::beg);
   mAttribSource.str("");     // Clear the attrib content (not required, but helps point out bugs)
   mCurSource = CdataSource;     // Set back to getting data from cdata
   return vpr::ReturnStatus::Succeed;
}
//@}

void XMLObjectReader::resetReading()
{
   mCurSource = CdataSource;
   mCurNodeStack.clear();
}

void XMLObjectReader::pushState()
{
   ReadState cur_state;
   cur_state.mNodeStack = mCurNodeStack;
   mReadStateStack.push_back(cur_state);
}

void XMLObjectReader::popState()
{
   ReadState new_state = mReadStateStack.back();
   mReadStateStack.pop_back();
   mCurNodeStack = new_state.mNodeStack;
}

vpr::Uint8 XMLObjectReader::readUint8()
{
   // Read a uint16 so that it does not treat it as a single char
   vpr::Uint16 temp_data;
   readValueStringRep(temp_data, getCurSource());
   return vpr::Uint8(temp_data);
}

vpr::Uint16 XMLObjectReader::readUint16()
{
   vpr::Uint16 val;
   readValueStringRep(val, getCurSource());
   return val;
}

vpr::Uint32 XMLObjectReader::readUint32()
{
   vpr::Uint32 val;
   readValueStringRep(val, getCurSource());
   return val;
}

vpr::Uint64 XMLObjectReader::readUint64()
{
   vpr::Uint64 val;
   readValueStringRep(val, getCurSource());
   return val;
}

float XMLObjectReader::readFloat()
{
   float val;
   readValueStringRep(val, getCurSource());
   return val;
}

double XMLObjectReader::readDouble()
{
   double val;
   readValueStringRep(val, getCurSource());
   return val;
}

std::string XMLObjectReader::readString()
{
   std::string ret_val("");

   if(AttribSource == mCurSource)   // Just return a copy of the attribute source
   {
      ret_val = mAttribSource.str();
   }
   else  // Cdata, extract the "'s around the string
   {
      char char_buffer[1024];    // Buffer for writing the final value
      char temp_char('0');

      // Get the first "
      while('"' != temp_char)
      {
         mCurNodeStack.back().cdataSource.get(temp_char);
         vprASSERT(('>' != temp_char) && ('<' != temp_char) && "String delimiter not found");
      }

      // Get the contents of the string
      char next_char = mCurNodeStack.back().cdataSource.peek();
      bool done_reading(next_char == '"');                           // If next char is already ", then we are done reading empty string

      while(!done_reading)
      {
         mCurNodeStack.back().cdataSource.get(char_buffer,1024, '"');      // Copy over to target until we have second "
         ret_val += char_buffer;                                           // append onto end
         next_char = mCurNodeStack.back().cdataSource.peek();
         done_reading = (next_char == '"');     // Done reading if got to a "
      }
      mCurNodeStack.back().cdataSource.ignore(1);                    // Ignore that " as well
   }

   return ret_val;
}

bool XMLObjectReader::readBool()
{
   bool val;
   readValueStringRep(val, getCurSource());
   return val;
}

} // namespace vpr

<commit_msg>Update to work with latest version of cppdom.<commit_after>/****************** <VPR heading BEGIN do not edit this line> *****************
 *
 * VR Juggler Portable Runtime
 *
 * Original Authors:
 *   Allen Bierbaum, Patrick Hartling, Kevin Meinert, Carolina Cruz-Neira
 *
 * -----------------------------------------------------------------
 * File:          $RCSfile$
 * Date modified: $Date$
 * Version:       $Revision$
 * -----------------------------------------------------------------
 *
 ****************** <VPR heading END do not edit this line> ******************/

/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998-2003 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

#include <vpr/IO/XMLObjectReader.h>

#include <vector>

#include <boost/static_assert.hpp>
#include <vpr/IO/ObjectReader.h>

#include <vpr/Util/Assert.h>
#include <vpr/Util/Debug.h>

#include <cppdom/cppdom.h>
#include <string>
#include <sstream>
#include <iterator>

#define XML_OR_LEVEL vprDBG_HEX_LVL

namespace
{
/** Helper to read the data from the current string */
 template<class T>
 vpr::ReturnStatus readValueStringRep(T& val, std::stringstream* inStream)
 {
    vprASSERT(inStream != NULL);

    // Just to be safe do this again for the heck of it. :)
    //in_stream->exceptions( std::ios::eofbit | std::ios::failbit | std::ios::badbit );
    vprASSERT(!inStream->bad() && "Bad stream, BAAADDD stream.");
    vprASSERT(!inStream->fail() && "Stream failed.");
    vprASSERT(!inStream->eof() && "Stream EOF'd.");
    vprASSERT(inStream->good());

    //stream_content = (*in_stream).str();

    (*inStream) >> val;

    return vpr::ReturnStatus::Succeed;
 }

}

namespace vpr
{

XMLObjectReader::XMLObjectReader(std::vector<vpr::Uint8> data)
   : mCurSource(CdataSource)
{
   initCppDomTree(data);
   //mAttribSource.exceptions( std::ios::eofbit | std::ios::failbit | std::ios::badbit );
}

XMLObjectReader::XMLObjectReader(cppdom::NodePtr rootNode)
   : mCurSource(CdataSource)
{
   mRootNode = rootNode;
   //mAttribSource.exceptions( std::ios::eofbit | std::ios::failbit | std::ios::badbit );
}

XMLObjectReader::NodeState::NodeState(cppdom::Node* cur_node)
   : node(cur_node)
{
   nextChild_i = node->getChildren().begin();     // Initialize to the first child
   endChild_i = node->getChildren().end();
   //cdataSource.exceptions( std::ios::eofbit | std::ios::failbit | std::ios::badbit );
   cdataSource.str(node->getFullCdata());
}

XMLObjectReader::NodeState::NodeState(const NodeState& rhs)
{
   node = rhs.node;
   nextChild_i = rhs.nextChild_i;
   endChild_i = rhs.endChild_i;
   //cdataSource.exceptions( std::ios::eofbit | std::ios::failbit | std::ios::badbit );
   cdataSource.str(rhs.cdataSource.str());
}

XMLObjectReader::NodeState& XMLObjectReader::NodeState::operator=(const NodeState& rhs)
{
   node = rhs.node;
   nextChild_i = rhs.nextChild_i;
   endChild_i = rhs.endChild_i;
   cdataSource.str(rhs.cdataSource.str());
   return *this;
}

void XMLObjectReader::NodeState::debugDump(int debug_level)
{
   vprDEBUG(vprDBG_ALL, debug_level) << "Node:" << node->getName() << "  num children:" << node->getChildren().size()
                                     << "  distance: nextChild-->endChild:" << std::distance(nextChild_i,endChild_i) << std::endl << vprDEBUG_FLUSH;
}

/** Get the current string source we are reading from.
*/
std::stringstream* XMLObjectReader::getCurSource()
{
   std::stringstream* in_stream(NULL);

   if(AttribSource == mCurSource)
   {
      in_stream = &mAttribSource;
   }
   else
   {
      in_stream = &(mCurNodeStack.back().cdataSource);
   }

   return in_stream;
}

/** Initialize the members based on a serialized version of something in the data buffer */
void XMLObjectReader::initCppDomTree(std::vector<vpr::Uint8> data)
{
   std::string xml_data(data.begin(), data.end());
   std::stringstream xml_stream(xml_data);
   cppdom::ContextPtr cxt(new cppdom::Context);
   cppdom::NodePtr new_node = cppdom::NodePtr(new cppdom::Node(cxt));
   new_node->load(xml_stream, cxt);

   mRootNode = new_node;

   std::string root_name = mRootNode->getName();   // Debugging

   mCurNodeStack.clear();
}

void XMLObjectReader::debugDumpStack(int debug_level)
{
   vprDEBUG_OutputGuard(vprDBG_ALL, debug_level, "mCurNodeStack:\n", "end node stack\n");

   for(unsigned i=0; i<mCurNodeStack.size(); ++i)
   {
      mCurNodeStack[i].debugDump(debug_level);
   }
}

/** Starting a new tag
* Get the local cdata into the current data source
*/
vpr::ReturnStatus XMLObjectReader::beginTag(std::string tagName)
{
   vprDEBUG_OutputGuard(vprDBG_ALL, XML_OR_LEVEL, std::string("beginTag:[") + tagName + std::string("]\n"), "endTag");

   vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "mCurNodeStack: size: " << mCurNodeStack.size() << std::endl << vprDEBUG_FLUSH;
   debugDumpStack(XML_OR_LEVEL);

   // Find the correct child
   if(mCurNodeStack.empty())  // We should enter the root
   {
      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "mCurNodeStack is empty. Push on root.\n" << vprDEBUG_FLUSH;
      mCurNodeStack.push_back(NodeState(mRootNode.get()));
   }
   else                      // Find the next child and push it on
   {
      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "Attempting to find next child: "
                             << " cur node (back): " << mCurNodeStack.back().node->getName()
                             << "  num children:" << mCurNodeStack.back().node->getChildren().size() << std::endl << vprDEBUG_FLUSH;

      vprASSERT(mCurNodeStack.back().nextChild_i != mCurNodeStack.back().endChild_i && "Past last child. No remaining child tags.");

      // Make sure that we get to child of type node.
      // While next child is not of type node skip over cdata...
      while( (*(mCurNodeStack.back().nextChild_i))->getType() != cppdom::Node::xml_nt_node)
      {
         vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "Skip node: " << (*(mCurNodeStack.back().nextChild_i))->getName() << " -- non nt_node type.\n" << vprDEBUG_FLUSH;
         mCurNodeStack.back().nextChild_i++;
         vprASSERT(mCurNodeStack.back().nextChild_i != mCurNodeStack.back().endChild_i && "Skipped past last child. No remaining child tags.");
      }

      cppdom::NodePtr tag_node = *(mCurNodeStack.back().nextChild_i);   // Get the node with the given tag
      mCurNodeStack.back().nextChild_i++;                               // Step to the next child for next time

      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "Incrementing next child:  at end:" << (mCurNodeStack.back().nextChild_i == mCurNodeStack.back().endChild_i) << std::endl << vprDEBUG_FLUSH;

      mCurNodeStack.push_back( NodeState(tag_node.get()));              // Add the child to the stack
      vprASSERT(tag_node.get() == mCurNodeStack.back().node);

      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "Found node:" << tag_node->getName() << std::endl << vprDEBUG_FLUSH;
      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "  Node stack size:" << mCurNodeStack.size() << std::endl << vprDEBUG_FLUSH;
      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "  Num children:" << tag_node->getChildren().size() << std::endl << vprDEBUG_FLUSH;
      vprDEBUG(vprDBG_ALL,XML_OR_LEVEL) << "  New node child at end:" << (mCurNodeStack.back().nextChild_i == mCurNodeStack.back().endChild_i)
                             << std::endl << vprDEBUG_FLUSH;
   }

   std::string cur_node_name = mCurNodeStack.back().node->getName();
   vprASSERT(tagName == cur_node_name && "Reading back in incorrect order");
   return vpr::ReturnStatus::Succeed;
}

/** Ends the most recently named tag.
* Just pop of the cur node state information
*/
vpr::ReturnStatus XMLObjectReader::endTag()
{
   vprASSERT(!mCurNodeStack.empty());
   mCurNodeStack.pop_back();
   return vpr::ReturnStatus::Succeed;
}

/** Starts an attribute of the name attributeName
* Get the attribute content and set the attribute source with that content
*/
vpr::ReturnStatus XMLObjectReader::beginAttribute(std::string attributeName)
{
   //std::cout << "beginAttribute: " << attributeName << std::endl;
   std::string attrib_content = mCurNodeStack.back().node->getAttribute(attributeName).getString();
   //std::cout << "  attrib content: [" << attrib_content << "]\n";
   mAttribSource.clear();                       // Clear error states
   mAttribSource.str(attrib_content);
   mAttribSource.seekg(0,std::ios::beg);        // Seek to beginning
   //std::cout << "  mAttribSource.str():[" << mAttribSource.str() << "]" << std::endl;
   mCurSource = AttribSource;             // Get content from attribute now
   return vpr::ReturnStatus::Succeed;
}

/** Ends the most recently named attribute */
vpr::ReturnStatus XMLObjectReader::endAttribute()
{
   mAttribSource.clear();
   mAttribSource.seekg(0,std::ios::beg);
   mAttribSource.str("");     // Clear the attrib content (not required, but helps point out bugs)
   mCurSource = CdataSource;     // Set back to getting data from cdata
   return vpr::ReturnStatus::Succeed;
}
//@}

void XMLObjectReader::resetReading()
{
   mCurSource = CdataSource;
   mCurNodeStack.clear();
}

void XMLObjectReader::pushState()
{
   ReadState cur_state;
   cur_state.mNodeStack = mCurNodeStack;
   mReadStateStack.push_back(cur_state);
}

void XMLObjectReader::popState()
{
   ReadState new_state = mReadStateStack.back();
   mReadStateStack.pop_back();
   mCurNodeStack = new_state.mNodeStack;
}

vpr::Uint8 XMLObjectReader::readUint8()
{
   // Read a uint16 so that it does not treat it as a single char
   vpr::Uint16 temp_data;
   readValueStringRep(temp_data, getCurSource());
   return vpr::Uint8(temp_data);
}

vpr::Uint16 XMLObjectReader::readUint16()
{
   vpr::Uint16 val;
   readValueStringRep(val, getCurSource());
   return val;
}

vpr::Uint32 XMLObjectReader::readUint32()
{
   vpr::Uint32 val;
   readValueStringRep(val, getCurSource());
   return val;
}

vpr::Uint64 XMLObjectReader::readUint64()
{
   vpr::Uint64 val;
   readValueStringRep(val, getCurSource());
   return val;
}

float XMLObjectReader::readFloat()
{
   float val;
   readValueStringRep(val, getCurSource());
   return val;
}

double XMLObjectReader::readDouble()
{
   double val;
   readValueStringRep(val, getCurSource());
   return val;
}

std::string XMLObjectReader::readString()
{
   std::string ret_val("");

   if(AttribSource == mCurSource)   // Just return a copy of the attribute source
   {
      ret_val = mAttribSource.str();
   }
   else  // Cdata, extract the "'s around the string
   {
      char char_buffer[1024];    // Buffer for writing the final value
      char temp_char('0');

      // Get the first "
      while('"' != temp_char)
      {
         mCurNodeStack.back().cdataSource.get(temp_char);
         vprASSERT(('>' != temp_char) && ('<' != temp_char) && "String delimiter not found");
      }

      // Get the contents of the string
      char next_char = mCurNodeStack.back().cdataSource.peek();
      bool done_reading(next_char == '"');                           // If next char is already ", then we are done reading empty string

      while(!done_reading)
      {
         mCurNodeStack.back().cdataSource.get(char_buffer,1024, '"');      // Copy over to target until we have second "
         ret_val += char_buffer;                                           // append onto end
         next_char = mCurNodeStack.back().cdataSource.peek();
         done_reading = (next_char == '"');     // Done reading if got to a "
      }
      mCurNodeStack.back().cdataSource.ignore(1);                    // Ignore that " as well
   }

   return ret_val;
}

bool XMLObjectReader::readBool()
{
   bool val;
   readValueStringRep(val, getCurSource());
   return val;
}

} // namespace vpr

<|endoftext|>


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

#include <map>
#include <set>
#include <sstream>

#include "src/compiler/config.h"
#include "src/compiler/objective_c_generator.h"
#include "src/compiler/objective_c_generator_helpers.h"

#include <google/protobuf/compiler/objectivec/objectivec_helpers.h>

using ::google::protobuf::compiler::objectivec::ClassName;
using ::grpc::protobuf::FileDescriptor;
using ::grpc::protobuf::FileDescriptor;
using ::grpc::protobuf::MethodDescriptor;
using ::grpc::protobuf::ServiceDescriptor;
using ::grpc::protobuf::io::Printer;
using ::std::map;
using ::std::set;

namespace grpc_objective_c_generator {
namespace {

void PrintProtoRpcDeclarationAsPragma(
    Printer* printer, const MethodDescriptor* method,
    map< ::grpc::string, ::grpc::string> vars) {
  vars["client_stream"] = method->client_streaming() ? "stream " : "";
  vars["server_stream"] = method->server_streaming() ? "stream " : "";

  printer->Print(vars,
                 "#pragma mark $method_name$($client_stream$$request_type$)"
                 " returns ($server_stream$$response_type$)\n\n");
}

template <typename DescriptorType>
static void PrintAllComments(const DescriptorType* desc, Printer* printer) {
  std::vector<grpc::string> comments;
  grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED,
                             &comments);
  grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING,
                             &comments);
  grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING,
                             &comments);
  if (comments.empty()) {
    return;
  }
  printer->Print("/**\n");
  for (auto it = comments.begin(); it != comments.end(); ++it) {
    printer->Print(" * ");
    size_t start_pos = it->find_first_not_of(' ');
    if (start_pos != grpc::string::npos) {
      printer->PrintRaw(it->c_str() + start_pos);
    }
    printer->Print("\n");
  }
  printer->Print(" */\n");
}

void PrintMethodSignature(Printer* printer, const MethodDescriptor* method,
                          const map< ::grpc::string, ::grpc::string>& vars) {
  // Print comment
  PrintAllComments(method, printer);

  printer->Print(vars, "- ($return_type$)$method_name$With");
  if (method->client_streaming()) {
    printer->Print("RequestsWriter:(GRXWriter *)requestWriter");
  } else {
    printer->Print(vars, "Request:($request_class$ *)request");
  }

  // TODO(jcanizales): Put this on a new line and align colons.
  if (method->server_streaming()) {
    printer->Print(vars,
                   " eventHandler:(void(^)(BOOL done, "
                   "$response_class$ *_Nullable response, NSError *_Nullable "
                   "error))eventHandler");
  } else {
    printer->Print(vars,
                   " handler:(void(^)($response_class$ *_Nullable response, "
                   "NSError *_Nullable error))handler");
  }
}

void PrintSimpleSignature(Printer* printer, const MethodDescriptor* method,
                          map< ::grpc::string, ::grpc::string> vars) {
  vars["method_name"] =
      grpc_generator::LowercaseFirstLetter(vars["method_name"]);
  vars["return_type"] = "void";
  PrintMethodSignature(printer, method, vars);
}

void PrintAdvancedSignature(Printer* printer, const MethodDescriptor* method,
                            map< ::grpc::string, ::grpc::string> vars) {
  vars["method_name"] = "RPCTo" + vars["method_name"];
  vars["return_type"] = "GRPCProtoCall *";
  PrintMethodSignature(printer, method, vars);
}

void PrintV2Signature(Printer* printer, const MethodDescriptor* method,
                      map< ::grpc::string, ::grpc::string> vars) {
  if (method->client_streaming()) {
    vars["return_type"] = "GRPCStreamingProtoCall *";
  } else {
    vars["return_type"] = "GRPCUnaryProtoCall *";
  }
  vars["method_name"] =
      grpc_generator::LowercaseFirstLetter(vars["method_name"]);

  PrintAllComments(method, printer);

  printer->Print(vars, "- ($return_type$)$method_name$With");
  if (method->client_streaming()) {
    printer->Print("ResponseHandler:(id<GRPCProtoResponseHandler>)handler");
  } else {
    printer->Print(vars,
                   "Message:($request_class$ *)message "
                   "responseHandler:(id<GRPCProtoResponseHandler>)handler");
  }
  printer->Print(" callOptions:(GRPCCallOptions *_Nullable)callOptions");
}

inline map< ::grpc::string, ::grpc::string> GetMethodVars(
    const MethodDescriptor* method) {
  map< ::grpc::string, ::grpc::string> res;
  res["method_name"] = method->name();
  res["request_type"] = method->input_type()->name();
  res["response_type"] = method->output_type()->name();
  res["request_class"] = ClassName(method->input_type());
  res["response_class"] = ClassName(method->output_type());
  return res;
}

void PrintMethodDeclarations(Printer* printer, const MethodDescriptor* method) {
  map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);

  PrintProtoRpcDeclarationAsPragma(printer, method, vars);

  PrintSimpleSignature(printer, method, vars);
  printer->Print(";\n\n");
  PrintAdvancedSignature(printer, method, vars);
  printer->Print(";\n\n\n");
}

void PrintV2MethodDeclarations(Printer* printer,
                               const MethodDescriptor* method) {
  map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);

  PrintProtoRpcDeclarationAsPragma(printer, method, vars);

  PrintV2Signature(printer, method, vars);
  printer->Print(";\n\n");
}

void PrintSimpleImplementation(Printer* printer, const MethodDescriptor* method,
                               map< ::grpc::string, ::grpc::string> vars) {
  printer->Print("{\n");
  printer->Print(vars, "  [[self RPCTo$method_name$With");
  if (method->client_streaming()) {
    printer->Print("RequestsWriter:requestWriter");
  } else {
    printer->Print("Request:request");
  }
  if (method->server_streaming()) {
    printer->Print(" eventHandler:eventHandler] start];\n");
  } else {
    printer->Print(" handler:handler] start];\n");
  }
  printer->Print("}\n");
}

void PrintAdvancedImplementation(Printer* printer,
                                 const MethodDescriptor* method,
                                 map< ::grpc::string, ::grpc::string> vars) {
  printer->Print("{\n");
  printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");

  printer->Print("            requestsWriter:");
  if (method->client_streaming()) {
    printer->Print("requestWriter\n");
  } else {
    printer->Print("[GRXWriter writerWithValue:request]\n");
  }

  printer->Print(vars, "             responseClass:[$response_class$ class]\n");

  printer->Print("        responsesWriteable:[GRXWriteable ");
  if (method->server_streaming()) {
    printer->Print("writeableWithEventHandler:eventHandler]];\n");
  } else {
    printer->Print("writeableWithSingleHandler:handler]];\n");
  }

  printer->Print("}\n");
}

void PrintV2Implementation(Printer* printer, const MethodDescriptor* method,
                           map< ::grpc::string, ::grpc::string> vars) {
  printer->Print(" {\n");
  if (method->client_streaming()) {
    printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
    printer->Print("           responseHandler:handler\n");
    printer->Print("               callOptions:callOptions\n");
    printer->Print(
        vars, "             responseClass:[$response_class$ class]];\n}\n\n");
  } else {
    printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
    printer->Print("                   message:message\n");
    printer->Print("           responseHandler:handler\n");
    printer->Print("               callOptions:callOptions\n");
    printer->Print(
        vars, "             responseClass:[$response_class$ class]];\n}\n\n");
  }
}

void PrintMethodImplementations(Printer* printer,
                                const MethodDescriptor* method) {
  map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);

  PrintProtoRpcDeclarationAsPragma(printer, method, vars);

  // TODO(jcanizales): Print documentation from the method.
  printer->Print("// Deprecated methods.\n");
  PrintSimpleSignature(printer, method, vars);
  PrintSimpleImplementation(printer, method, vars);

  printer->Print("// Returns a not-yet-started RPC object.\n");
  PrintAdvancedSignature(printer, method, vars);
  PrintAdvancedImplementation(printer, method, vars);

  PrintV2Signature(printer, method, vars);
  PrintV2Implementation(printer, method, vars);
}

}  // namespace

::grpc::string GetAllMessageClasses(const FileDescriptor* file) {
  ::grpc::string output;
  set< ::grpc::string> classes;
  for (int i = 0; i < file->service_count(); i++) {
    const auto service = file->service(i);
    for (int i = 0; i < service->method_count(); i++) {
      const auto method = service->method(i);
      classes.insert(ClassName(method->input_type()));
      classes.insert(ClassName(method->output_type()));
    }
  }
  for (auto one_class : classes) {
    output += "@class " + one_class + ";\n";
  }

  return output;
}

::grpc::string GetProtocol(const ServiceDescriptor* service) {
  ::grpc::string output;

  // Scope the output stream so it closes and finalizes output to the string.
  grpc::protobuf::io::StringOutputStream output_stream(&output);
  Printer printer(&output_stream, '$');

  map< ::grpc::string, ::grpc::string> vars = {
      {"service_class", ServiceClassName(service)}};

  printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
  for (int i = 0; i < service->method_count(); i++) {
    PrintMethodDeclarations(&printer, service->method(i));
  }
  printer.Print("@end\n\n");

  return output;
}

::grpc::string GetV2Protocol(const ServiceDescriptor* service) {
  ::grpc::string output;

  // Scope the output stream so it closes and finalizes output to the string.
  grpc::protobuf::io::StringOutputStream output_stream(&output);
  Printer printer(&output_stream, '$');

  map< ::grpc::string, ::grpc::string> vars = {
      {"service_class", ServiceClassName(service) + "2"}};

  printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
  for (int i = 0; i < service->method_count(); i++) {
    PrintV2MethodDeclarations(&printer, service->method(i));
  }
  printer.Print("@end\n\n");

  return output;
}

::grpc::string GetInterface(const ServiceDescriptor* service) {
  ::grpc::string output;

  // Scope the output stream so it closes and finalizes output to the string.
  grpc::protobuf::io::StringOutputStream output_stream(&output);
  Printer printer(&output_stream, '$');

  map< ::grpc::string, ::grpc::string> vars = {
      {"service_class", ServiceClassName(service)}};

  printer.Print(vars,
                "/**\n"
                " * Basic service implementation, over gRPC, that only does\n"
                " * marshalling and parsing.\n"
                " */\n");
  printer.Print(vars,
                "@interface $service_class$ :"
                " GRPCProtoService<$service_class$, $service_class$2>\n");
  printer.Print(
      "- (instancetype)initWithHost:(NSString *)host "
      "callOptions:(GRPCCallOptions "
      "*_Nullable)callOptions"
      " NS_DESIGNATED_INITIALIZER;\n");
  printer.Print("- (instancetype)initWithHost:(NSString *)host;\n");
  printer.Print(
      "+ (instancetype)serviceWithHost:(NSString *)host "
      "callOptions:(GRPCCallOptions *_Nullable)callOptions;\n");
  printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n");
  printer.Print("@end\n");

  return output;
}

::grpc::string GetSource(const ServiceDescriptor* service) {
  ::grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    grpc::protobuf::io::StringOutputStream output_stream(&output);
    Printer printer(&output_stream, '$');

    map< ::grpc::string, ::grpc::string> vars = {
        {"service_name", service->name()},
        {"service_class", ServiceClassName(service)},
        {"package", service->file()->package()}};

    printer.Print(vars,
                  "@implementation $service_class$\n\n"
                  "#pragma clang diagnostic push\n"
                  "#pragma clang diagnostic ignored "
                  "\"-Wobjc-designated-initializers\"\n\n"
                  "// Designated initializer\n"
                  "- (instancetype)initWithHost:(NSString *)host "
                  "callOptions:(GRPCCallOptions *_Nullable)callOptions {\n"
                  "  return [super initWithHost:host\n"
                  "                 packageName:@\"$package$\"\n"
                  "                 serviceName:@\"$service_name$\"\n"
                  "                 callOptions:callOptions];\n"
                  "}\n\n"
                  "- (instancetype)initWithHost:(NSString *)host {\n"
                  "  return [super initWithHost:host\n"
                  "                 packageName:@\"$package$\"\n"
                  "                 serviceName:@\"$service_name$\"];\n"
                  "}\n\n"
                  "#pragma clang diagnostic pop\n\n");

    printer.Print(
        "// Override superclass initializer to disallow different"
        " package and service names.\n"
        "- (instancetype)initWithHost:(NSString *)host\n"
        "                 packageName:(NSString *)packageName\n"
        "                 serviceName:(NSString *)serviceName {\n"
        "  return [self initWithHost:host];\n"
        "}\n\n"
        "- (instancetype)initWithHost:(NSString *)host\n"
        "                 packageName:(NSString *)packageName\n"
        "                 serviceName:(NSString *)serviceName\n"
        "                 callOptions:(GRPCCallOptions *)callOptions {\n"
        "  return [self initWithHost:host callOptions:callOptions];\n"
        "}\n\n");

    printer.Print(
        "#pragma mark - Class Methods\n\n"
        "+ (instancetype)serviceWithHost:(NSString *)host {\n"
        "  return [[self alloc] initWithHost:host];\n"
        "}\n\n"
        "+ (instancetype)serviceWithHost:(NSString *)host "
        "callOptions:(GRPCCallOptions *_Nullable)callOptions {\n"
        "  return [[self alloc] initWithHost:host callOptions:callOptions];\n"
        "}\n\n");

    printer.Print("#pragma mark - Method Implementations\n\n");

    for (int i = 0; i < service->method_count(); i++) {
      PrintMethodImplementations(&printer, service->method(i));
    }

    printer.Print("@end\n");
  }
  return output;
}

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

#include <map>
#include <set>
#include <sstream>

#include "src/compiler/config.h"
#include "src/compiler/objective_c_generator.h"
#include "src/compiler/objective_c_generator_helpers.h"

#include <google/protobuf/compiler/objectivec/objectivec_helpers.h>

using ::google::protobuf::compiler::objectivec::ClassName;
using ::grpc::protobuf::FileDescriptor;
using ::grpc::protobuf::FileDescriptor;
using ::grpc::protobuf::MethodDescriptor;
using ::grpc::protobuf::ServiceDescriptor;
using ::grpc::protobuf::io::Printer;
using ::std::map;
using ::std::set;

namespace grpc_objective_c_generator {
namespace {

void PrintProtoRpcDeclarationAsPragma(
    Printer* printer, const MethodDescriptor* method,
    map< ::grpc::string, ::grpc::string> vars) {
  vars["client_stream"] = method->client_streaming() ? "stream " : "";
  vars["server_stream"] = method->server_streaming() ? "stream " : "";

  printer->Print(vars,
                 "#pragma mark $method_name$($client_stream$$request_type$)"
                 " returns ($server_stream$$response_type$)\n\n");
}

template <typename DescriptorType>
static void PrintAllComments(const DescriptorType* desc, Printer* printer) {
  std::vector<grpc::string> comments;
  grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED,
                             &comments);
  grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING,
                             &comments);
  grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING,
                             &comments);
  if (comments.empty()) {
    return;
  }
  printer->Print("/**\n");
  for (auto it = comments.begin(); it != comments.end(); ++it) {
    printer->Print(" * ");
    size_t start_pos = it->find_first_not_of(' ');
    if (start_pos != grpc::string::npos) {
      printer->PrintRaw(it->c_str() + start_pos);
    }
    printer->Print("\n");
  }
  printer->Print(" *\n");
  printer->Print(
      " * This method belongs to a set of APIs that have been deprecated. Using"
      " the v2 API is recommended.\n");
  printer->Print(" */\n");
}

void PrintMethodSignature(Printer* printer, const MethodDescriptor* method,
                          const map< ::grpc::string, ::grpc::string>& vars) {
  // Print comment
  PrintAllComments(method, printer);

  printer->Print(vars, "- ($return_type$)$method_name$With");
  if (method->client_streaming()) {
    printer->Print("RequestsWriter:(GRXWriter *)requestWriter");
  } else {
    printer->Print(vars, "Request:($request_class$ *)request");
  }

  // TODO(jcanizales): Put this on a new line and align colons.
  if (method->server_streaming()) {
    printer->Print(vars,
                   " eventHandler:(void(^)(BOOL done, "
                   "$response_class$ *_Nullable response, NSError *_Nullable "
                   "error))eventHandler");
  } else {
    printer->Print(vars,
                   " handler:(void(^)($response_class$ *_Nullable response, "
                   "NSError *_Nullable error))handler");
  }
}

void PrintSimpleSignature(Printer* printer, const MethodDescriptor* method,
                          map< ::grpc::string, ::grpc::string> vars) {
  vars["method_name"] =
      grpc_generator::LowercaseFirstLetter(vars["method_name"]);
  vars["return_type"] = "void";
  PrintMethodSignature(printer, method, vars);
}

void PrintAdvancedSignature(Printer* printer, const MethodDescriptor* method,
                            map< ::grpc::string, ::grpc::string> vars) {
  vars["method_name"] = "RPCTo" + vars["method_name"];
  vars["return_type"] = "GRPCProtoCall *";
  PrintMethodSignature(printer, method, vars);
}

void PrintV2Signature(Printer* printer, const MethodDescriptor* method,
                      map< ::grpc::string, ::grpc::string> vars) {
  if (method->client_streaming()) {
    vars["return_type"] = "GRPCStreamingProtoCall *";
  } else {
    vars["return_type"] = "GRPCUnaryProtoCall *";
  }
  vars["method_name"] =
      grpc_generator::LowercaseFirstLetter(vars["method_name"]);

  PrintAllComments(method, printer);

  printer->Print(vars, "- ($return_type$)$method_name$With");
  if (method->client_streaming()) {
    printer->Print("ResponseHandler:(id<GRPCProtoResponseHandler>)handler");
  } else {
    printer->Print(vars,
                   "Message:($request_class$ *)message "
                   "responseHandler:(id<GRPCProtoResponseHandler>)handler");
  }
  printer->Print(" callOptions:(GRPCCallOptions *_Nullable)callOptions");
}

inline map< ::grpc::string, ::grpc::string> GetMethodVars(
    const MethodDescriptor* method) {
  map< ::grpc::string, ::grpc::string> res;
  res["method_name"] = method->name();
  res["request_type"] = method->input_type()->name();
  res["response_type"] = method->output_type()->name();
  res["request_class"] = ClassName(method->input_type());
  res["response_class"] = ClassName(method->output_type());
  return res;
}

void PrintMethodDeclarations(Printer* printer, const MethodDescriptor* method) {
  map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);

  PrintProtoRpcDeclarationAsPragma(printer, method, vars);

  PrintSimpleSignature(printer, method, vars);
  printer->Print(";\n\n");
  PrintAdvancedSignature(printer, method, vars);
  printer->Print(";\n\n\n");
}

void PrintV2MethodDeclarations(Printer* printer,
                               const MethodDescriptor* method) {
  map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);

  PrintProtoRpcDeclarationAsPragma(printer, method, vars);

  PrintV2Signature(printer, method, vars);
  printer->Print(";\n\n");
}

void PrintSimpleImplementation(Printer* printer, const MethodDescriptor* method,
                               map< ::grpc::string, ::grpc::string> vars) {
  printer->Print("{\n");
  printer->Print(vars, "  [[self RPCTo$method_name$With");
  if (method->client_streaming()) {
    printer->Print("RequestsWriter:requestWriter");
  } else {
    printer->Print("Request:request");
  }
  if (method->server_streaming()) {
    printer->Print(" eventHandler:eventHandler] start];\n");
  } else {
    printer->Print(" handler:handler] start];\n");
  }
  printer->Print("}\n");
}

void PrintAdvancedImplementation(Printer* printer,
                                 const MethodDescriptor* method,
                                 map< ::grpc::string, ::grpc::string> vars) {
  printer->Print("{\n");
  printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");

  printer->Print("            requestsWriter:");
  if (method->client_streaming()) {
    printer->Print("requestWriter\n");
  } else {
    printer->Print("[GRXWriter writerWithValue:request]\n");
  }

  printer->Print(vars, "             responseClass:[$response_class$ class]\n");

  printer->Print("        responsesWriteable:[GRXWriteable ");
  if (method->server_streaming()) {
    printer->Print("writeableWithEventHandler:eventHandler]];\n");
  } else {
    printer->Print("writeableWithSingleHandler:handler]];\n");
  }

  printer->Print("}\n");
}

void PrintV2Implementation(Printer* printer, const MethodDescriptor* method,
                           map< ::grpc::string, ::grpc::string> vars) {
  printer->Print(" {\n");
  if (method->client_streaming()) {
    printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
    printer->Print("           responseHandler:handler\n");
    printer->Print("               callOptions:callOptions\n");
    printer->Print(
        vars, "             responseClass:[$response_class$ class]];\n}\n\n");
  } else {
    printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
    printer->Print("                   message:message\n");
    printer->Print("           responseHandler:handler\n");
    printer->Print("               callOptions:callOptions\n");
    printer->Print(
        vars, "             responseClass:[$response_class$ class]];\n}\n\n");
  }
}

void PrintMethodImplementations(Printer* printer,
                                const MethodDescriptor* method) {
  map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);

  PrintProtoRpcDeclarationAsPragma(printer, method, vars);

  // TODO(jcanizales): Print documentation from the method.
  printer->Print("// Deprecated methods.\n");
  PrintSimpleSignature(printer, method, vars);
  PrintSimpleImplementation(printer, method, vars);

  printer->Print("// Returns a not-yet-started RPC object.\n");
  PrintAdvancedSignature(printer, method, vars);
  PrintAdvancedImplementation(printer, method, vars);

  PrintV2Signature(printer, method, vars);
  PrintV2Implementation(printer, method, vars);
}

}  // namespace

::grpc::string GetAllMessageClasses(const FileDescriptor* file) {
  ::grpc::string output;
  set< ::grpc::string> classes;
  for (int i = 0; i < file->service_count(); i++) {
    const auto service = file->service(i);
    for (int i = 0; i < service->method_count(); i++) {
      const auto method = service->method(i);
      classes.insert(ClassName(method->input_type()));
      classes.insert(ClassName(method->output_type()));
    }
  }
  for (auto one_class : classes) {
    output += "@class " + one_class + ";\n";
  }

  return output;
}

::grpc::string GetProtocol(const ServiceDescriptor* service) {
  ::grpc::string output;

  // Scope the output stream so it closes and finalizes output to the string.
  grpc::protobuf::io::StringOutputStream output_stream(&output);
  Printer printer(&output_stream, '$');

  map< ::grpc::string, ::grpc::string> vars = {
      {"service_class", ServiceClassName(service)}};

  printer.Print(vars,
                "/**\n"
                " * The methods in this protocol belong to a set of old APIs "
                "that have been deprecated. They do not\n"
                " * recognize call options provided in the initializer. Using "
                "the v2 protocol is recommended.\n"
                " */\n");
  printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
  for (int i = 0; i < service->method_count(); i++) {
    PrintMethodDeclarations(&printer, service->method(i));
  }
  printer.Print("@end\n\n");

  return output;
}

::grpc::string GetV2Protocol(const ServiceDescriptor* service) {
  ::grpc::string output;

  // Scope the output stream so it closes and finalizes output to the string.
  grpc::protobuf::io::StringOutputStream output_stream(&output);
  Printer printer(&output_stream, '$');

  map< ::grpc::string, ::grpc::string> vars = {
      {"service_class", ServiceClassName(service) + "2"}};

  printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
  for (int i = 0; i < service->method_count(); i++) {
    PrintV2MethodDeclarations(&printer, service->method(i));
  }
  printer.Print("@end\n\n");

  return output;
}

::grpc::string GetInterface(const ServiceDescriptor* service) {
  ::grpc::string output;

  // Scope the output stream so it closes and finalizes output to the string.
  grpc::protobuf::io::StringOutputStream output_stream(&output);
  Printer printer(&output_stream, '$');

  map< ::grpc::string, ::grpc::string> vars = {
      {"service_class", ServiceClassName(service)}};

  printer.Print(vars,
                "/**\n"
                " * Basic service implementation, over gRPC, that only does\n"
                " * marshalling and parsing.\n"
                " */\n");
  printer.Print(vars,
                "@interface $service_class$ :"
                " GRPCProtoService<$service_class$, $service_class$2>\n");
  printer.Print(
      "- (instancetype)initWithHost:(NSString *)host "
      "callOptions:(GRPCCallOptions "
      "*_Nullable)callOptions"
      " NS_DESIGNATED_INITIALIZER;\n");
  printer.Print(
      "+ (instancetype)serviceWithHost:(NSString *)host "
      "callOptions:(GRPCCallOptions *_Nullable)callOptions;\n");
  printer.Print(
      "// The following methods belong to a set of old APIs that have been "
      "deprecated.\n");
  printer.Print("- (instancetype)initWithHost:(NSString *)host;\n");
  printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n");
  printer.Print("@end\n");

  return output;
}

::grpc::string GetSource(const ServiceDescriptor* service) {
  ::grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    grpc::protobuf::io::StringOutputStream output_stream(&output);
    Printer printer(&output_stream, '$');

    map< ::grpc::string, ::grpc::string> vars = {
        {"service_name", service->name()},
        {"service_class", ServiceClassName(service)},
        {"package", service->file()->package()}};

    printer.Print(vars,
                  "@implementation $service_class$\n\n"
                  "#pragma clang diagnostic push\n"
                  "#pragma clang diagnostic ignored "
                  "\"-Wobjc-designated-initializers\"\n\n"
                  "// Designated initializer\n"
                  "- (instancetype)initWithHost:(NSString *)host "
                  "callOptions:(GRPCCallOptions *_Nullable)callOptions {\n"
                  "  return [super initWithHost:host\n"
                  "                 packageName:@\"$package$\"\n"
                  "                 serviceName:@\"$service_name$\"\n"
                  "                 callOptions:callOptions];\n"
                  "}\n\n"
                  "- (instancetype)initWithHost:(NSString *)host {\n"
                  "  return [super initWithHost:host\n"
                  "                 packageName:@\"$package$\"\n"
                  "                 serviceName:@\"$service_name$\"];\n"
                  "}\n\n"
                  "#pragma clang diagnostic pop\n\n");

    printer.Print(
        "// Override superclass initializer to disallow different"
        " package and service names.\n"
        "- (instancetype)initWithHost:(NSString *)host\n"
        "                 packageName:(NSString *)packageName\n"
        "                 serviceName:(NSString *)serviceName {\n"
        "  return [self initWithHost:host];\n"
        "}\n\n"
        "- (instancetype)initWithHost:(NSString *)host\n"
        "                 packageName:(NSString *)packageName\n"
        "                 serviceName:(NSString *)serviceName\n"
        "                 callOptions:(GRPCCallOptions *)callOptions {\n"
        "  return [self initWithHost:host callOptions:callOptions];\n"
        "}\n\n");

    printer.Print(
        "#pragma mark - Class Methods\n\n"
        "+ (instancetype)serviceWithHost:(NSString *)host {\n"
        "  return [[self alloc] initWithHost:host];\n"
        "}\n\n"
        "+ (instancetype)serviceWithHost:(NSString *)host "
        "callOptions:(GRPCCallOptions *_Nullable)callOptions {\n"
        "  return [[self alloc] initWithHost:host callOptions:callOptions];\n"
        "}\n\n");

    printer.Print("#pragma mark - Method Implementations\n\n");

    for (int i = 0; i < service->method_count(); i++) {
      PrintMethodImplementations(&printer, service->method(i));
    }

    printer.Print("@end\n");
  }
  return output;
}

}  // namespace grpc_objective_c_generator
<|endoftext|>