<commit_before>/*
 * Copyright (c) 2011 Matthew Iselin
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <Widget.h>

#include <ipc/Ipc.h>

/// \todo GTFO libc!
#include <unistd.h>

/// \todo GTFO libc!
#include <string.h>
#include <stdlib.h>
#include <syslog.h>

#include <map>
#include <queue>

#include "protocol.h"

using namespace LibUiProtocol;

#define PEDIGREE_WINMAN_ENDPOINT "pedigree-winman"

/// Default event handler for new widgets.
static bool defaultEventHandler(WidgetMessages message, size_t dataSize, void *dataBuffer)
{
    return false;
}

static Widget *g_pWidget = 0;

static PedigreeIpc::IpcEndpoint *g_pWinmanEndpoint = 0;

std::map<uint64_t, widgetCallback_t> Widget::m_CallbackMap;
std::queue<PedigreeIpc::IpcMessage *> g_PendingMessages;

Widget::Widget() :
    m_bConstructed(false), m_pFramebuffer(0), m_Handle(0),
    m_EventCallback(defaultEventHandler), m_Endpoint(0),
    m_SharedFramebuffer(0)
{
}

Widget::~Widget()
{
    if(m_Endpoint) {
        free((void *) m_Endpoint);
    }
}

bool Widget::construct(const char *endpoint, const char *title, widgetCallback_t cb, PedigreeGraphics::Rect &dimensions)
{
    if(m_Handle)
    {
        // Already constructed!
        return false;
    }

    /// \todo Maybe we can get a decent way of having a default handler?
    if(!cb)
        return false;

    // Create endpoint, if not already created.
    /// \todo fail if endpoint already exists.
    PedigreeIpc::createEndpoint(endpoint);
    m_Endpoint = PedigreeIpc::getEndpoint(endpoint);
    if(!g_pWinmanEndpoint)
    {
        g_pWinmanEndpoint = PedigreeIpc::getEndpoint(PEDIGREE_WINMAN_ENDPOINT);
    }

    // Construct the handle first.
    uint64_t pid = getpid();
    uintptr_t widgetPointer = reinterpret_cast<uintptr_t>(this);
#ifdef BITS_64
    m_Handle = (pid << 32) | (((widgetPointer >> 32) | (widgetPointer & 0xFFFFFFFF)) & 0xFFFFFFFF);
#else
    m_Handle = (pid << 32) | (widgetPointer);
#endif

    PedigreeIpc::IpcMessage *pCreateMessage = new PedigreeIpc::IpcMessage();
    if(!pCreateMessage->initialise())
    {
        m_Handle = 0;
        return false;
    }

    // Prepare a message to send.
    size_t totalSize = sizeof(WindowManagerMessage) + sizeof(CreateMessage);
    char *messageData = static_cast<char *>(pCreateMessage->getBuffer());
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    CreateMessage *pCreate = reinterpret_cast<CreateMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill.
    pWinMan->messageCode = Create;
    pWinMan->messageSize = sizeof(CreateMessage);
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;
    strlcpy(pCreate->endpoint, endpoint, 256);
    strlcpy(pCreate->title, title, 256);
    pCreate->minWidth = dimensions.getW();
    pCreate->minHeight = dimensions.getH();
    pCreate->rigid = true;

    // Send the message off to the window manager and wait for a response. The
    // response will contain a GraphicsProvider that we can use to create our
    // Framebuffer for drawing on.
    if(!PedigreeIpc::send(g_pWinmanEndpoint, pCreateMessage, false))
    {
        m_Handle = 0;
        delete pCreateMessage;
        return false;
    }

    PedigreeIpc::IpcMessage *pResponse = 0;

    while(1)
    {
        if(!PedigreeIpc::recv(m_Endpoint, &pResponse, false))
        {
            m_Handle = 0;
            return false;
        }

        if(!(pResponse && pResponse->getBuffer()))
        {
            continue;
        }

        pWinMan = reinterpret_cast<WindowManagerMessage*>(pResponse->getBuffer());
        if(!(pWinMan->isResponse && (pWinMan->messageCode == Create)))
        {
            g_PendingMessages.push(pResponse);
        }
        else
        {
            messageData = static_cast<char *>(pResponse->getBuffer());
            break;
        }
    }

    // Grab the results and use them.
    CreateMessageResponse *pCreateResp = reinterpret_cast<CreateMessageResponse*>(messageData + sizeof(WindowManagerMessage));
    m_EventCallback = cb;
    m_CallbackMap[m_Handle] = cb;

    delete pResponse;

    g_pWidget = this;

    // Asynchronously check for any events in the pipeline. This should be an
    // initial reposition event that we really need to get out to the client
    // ASAP.
    checkForEvents(true);

    return true;
}

bool Widget::setProperty(std::string propName, void *propVal, size_t maxSize)
{
    // Constructed yet?
    if(!m_Handle)
        return false;

    // Are the arguments sane?
    if(!propVal)
        return false;
    if(maxSize > 0x1000)
        return false;

    // Allocate the message.
    /// \todo Sanitise maxSize once large messages can be sent.
    size_t headerSize = sizeof(WindowManagerMessage) + sizeof(SetPropertyMessage);
    size_t totalSize = headerSize + maxSize;
    char *messageData = new char[totalSize];
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    SetPropertyMessage *pMessage = reinterpret_cast<SetPropertyMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill the message.
    pWinMan->messageCode = SetProperty;
    pWinMan->messageSize = sizeof(SetPropertyMessage) + maxSize;
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;

    propName.copy(pMessage->propertyName, sizeof pMessage->propertyName);
    pMessage->valueLength = maxSize;

    // Copy in the property data.
    memcpy(messageData + headerSize, propVal, maxSize);

    // Transmit.
    bool result = sendMessage(messageData, totalSize);

    // Clean up.
    delete [] messageData;

    return result;
}

bool Widget::getProperty(std::string propName, char **buffer, size_t maxSize)
{
    return false;
}

void Widget::setParent(Widget *pWidget)
{
}

Widget *Widget::getParent()
{
    return 0;
}

bool Widget::redraw(PedigreeGraphics::Rect &rt)
{
    // Constructed yet?
    if(!m_Handle)
        return false;

    // Allocate the message.
    PedigreeIpc::IpcMessage *pRedrawMessage = new PedigreeIpc::IpcMessage();
    if(!pRedrawMessage->initialise())
    {
        m_Handle = 0;
        return false;
    }

    size_t totalSize = sizeof(WindowManagerMessage) + sizeof(RequestRedrawMessage);
    char *messageData = static_cast<char *>(pRedrawMessage->getBuffer());
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    RequestRedrawMessage *pMessage = reinterpret_cast<RequestRedrawMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill the message.
    pWinMan->messageCode = RequestRedraw;
    pWinMan->messageSize = sizeof(RequestRedrawMessage);
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;
    pMessage->x = rt.getX();
    pMessage->y = rt.getY();
    pMessage->width = rt.getW();
    pMessage->height = rt.getH();

    // Transmit.
    if(!PedigreeIpc::send(g_pWinmanEndpoint, pRedrawMessage))
    {
        delete pRedrawMessage;
        return false;
    }

    return true;
}

bool Widget::visibility(bool vis)
{
    // Constructed yet?
    if(!m_Handle)
        return false;

    // Allocate the message.
    size_t totalSize = sizeof(WindowManagerMessage) + sizeof(SetVisibilityMessage);
    char *messageData = new char[totalSize];
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    SetVisibilityMessage *pMessage = reinterpret_cast<SetVisibilityMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill the message.
    pWinMan->messageCode = SetVisibility;
    pWinMan->messageSize = sizeof(SetVisibilityMessage);
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;
    pMessage->bVisible = vis;

    // Transmit.
    bool result = sendMessage(messageData, totalSize);

    // Clean up.
    delete [] messageData;

    return result;
}

void Widget::destroy()
{
    // Constructed yet?
    if(!m_Handle)
        return;

    // Allocate the message.
    size_t totalSize = sizeof(WindowManagerMessage) + sizeof(DestroyMessage);
    char *messageData = new char[totalSize];
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    DestroyMessage *pMessage = reinterpret_cast<DestroyMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill the message.
    pWinMan->messageCode = Destroy;
    pWinMan->messageSize = sizeof(DestroyMessage);
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;

    // Transmit.
    sendMessage(messageData, totalSize);

    // Clean up.
    delete [] messageData;

    // Invalidate this widget now.
    delete m_pFramebuffer;
    m_Handle = 0;
}

PedigreeGraphics::Framebuffer *Widget::getFramebuffer()
{
    return 0;

    /*
    // Constructed yet?
    if(!m_Handle)
        return 0;

    // No framebuffer yet?
    if(!m_pFramebuffer)
        return 0;

    // Allocate the message.
    size_t totalSize = sizeof(WindowManagerMessage) + sizeof(SyncMessage);
    char *messageData = new char[totalSize];
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    SyncMessage *pMessage = reinterpret_cast<SyncMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill the message.
    pWinMan->messageCode = Sync;
    pWinMan->messageSize = sizeof(SyncMessage);
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;

    // Transmit, synchronously.
    syslog(LOG_INFO, "TX Sync [%d]", getpid());
    sendMessage(messageData, totalSize);
    syslog(LOG_INFO, "TX complete");

    delete [] messageData;

    // Get the response.
    totalSize = 1024;
    messageData = new char[totalSize];

    while(1)
    {
        if(!recvMessage(m_Endpoint, messageData, totalSize))
        {
            m_Handle = 0;
            delete [] messageData;
            return 0;
        }

        pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
        if(!(pWinMan->isResponse && (pWinMan->messageCode == Sync)))
        {
            g_PendingMessages.push(messageData);
            messageData = new char[totalSize];
        }
        else
        {
            break;
        }
    }

    SyncMessageResponse *pSyncResp = reinterpret_cast<SyncMessageResponse*>(messageData + sizeof(WindowManagerMessage));
    if(m_pFramebuffer->getProvider().contextId !=
        pSyncResp->provider.contextId)
    {
        delete m_pFramebuffer;
        m_pFramebuffer = new PedigreeGraphics::Framebuffer(pSyncResp->provider);
    }

    return m_pFramebuffer;
    */
}

void Widget::checkForEvents(bool bAsync)
{
    /// \todo ALL created widgets, not just one.
    if(g_pWidget)
    {
        char *buffer = 0;
        PedigreeIpc::IpcMessage *pMessage = 0;
        if(g_PendingMessages.empty())
        {
            PedigreeIpc::recv(g_pWidget->m_Endpoint, &pMessage, bAsync);
        }
        else
        {
            pMessage = g_PendingMessages.front();
            g_PendingMessages.pop();
        }

        if(pMessage)
        {
            buffer = static_cast<char *>(pMessage->getBuffer());

            LibUiProtocol::WindowManagerMessage *pHeader =
                reinterpret_cast<LibUiProtocol::WindowManagerMessage*>(buffer);

            widgetCallback_t cb = m_CallbackMap[pHeader->widgetHandle];

            switch(pHeader->messageCode)
            {
                case LibUiProtocol::Reposition:
                {
                    LibUiProtocol::RepositionMessage *pReposition =
                        reinterpret_cast<LibUiProtocol::RepositionMessage*>(buffer + sizeof(LibUiProtocol::WindowManagerMessage));
                    delete g_pWidget->m_SharedFramebuffer;
                    g_pWidget->m_SharedFramebuffer =
                            new PedigreeIpc::SharedIpcMessage(pReposition->shmem_size, pReposition->shmem_handle);
                    g_pWidget->m_SharedFramebuffer->initialise();

                    // Run the callback now that the framebuffer is re-created.
                    cb(::Reposition, sizeof(pReposition->rt), &pReposition->rt);
                    break;
                }
                case LibUiProtocol::KeyEvent:
                {
                    LibUiProtocol::KeyEventMessage *pKeyEvent =
                        reinterpret_cast<LibUiProtocol::KeyEventMessage*>(buffer + sizeof(LibUiProtocol::WindowManagerMessage));

                    cb(::KeyUp, sizeof(pKeyEvent->key), &pKeyEvent->key);
                    break;
                }
                default:
                    syslog(LOG_INFO, "** unknown event");
            }
        }

        if(pMessage)
        {
            delete pMessage;
        }
    }
}
<commit_msg>libui: avoid being too greedy with freeing framebuffers<commit_after>/*
 * Copyright (c) 2011 Matthew Iselin
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <Widget.h>

#include <ipc/Ipc.h>

/// \todo GTFO libc!
#include <unistd.h>

/// \todo GTFO libc!
#include <string.h>
#include <stdlib.h>
#include <syslog.h>

#include <map>
#include <queue>

#include "protocol.h"

using namespace LibUiProtocol;

#define PEDIGREE_WINMAN_ENDPOINT "pedigree-winman"

/// Default event handler for new widgets.
static bool defaultEventHandler(WidgetMessages message, size_t dataSize, void *dataBuffer)
{
    return false;
}

static Widget *g_pWidget = 0;

static PedigreeIpc::IpcEndpoint *g_pWinmanEndpoint = 0;

std::map<uint64_t, widgetCallback_t> Widget::m_CallbackMap;
std::queue<PedigreeIpc::IpcMessage *> g_PendingMessages;

Widget::Widget() :
    m_bConstructed(false), m_pFramebuffer(0), m_Handle(0),
    m_EventCallback(defaultEventHandler), m_Endpoint(0),
    m_SharedFramebuffer(0)
{
}

Widget::~Widget()
{
    if(m_Endpoint) {
        free((void *) m_Endpoint);
    }
}

bool Widget::construct(const char *endpoint, const char *title, widgetCallback_t cb, PedigreeGraphics::Rect &dimensions)
{
    if(m_Handle)
    {
        // Already constructed!
        return false;
    }

    /// \todo Maybe we can get a decent way of having a default handler?
    if(!cb)
        return false;

    // Create endpoint, if not already created.
    /// \todo fail if endpoint already exists.
    PedigreeIpc::createEndpoint(endpoint);
    m_Endpoint = PedigreeIpc::getEndpoint(endpoint);
    if(!g_pWinmanEndpoint)
    {
        g_pWinmanEndpoint = PedigreeIpc::getEndpoint(PEDIGREE_WINMAN_ENDPOINT);
    }

    // Construct the handle first.
    uint64_t pid = getpid();
    uintptr_t widgetPointer = reinterpret_cast<uintptr_t>(this);
#ifdef BITS_64
    m_Handle = (pid << 32) | (((widgetPointer >> 32) | (widgetPointer & 0xFFFFFFFF)) & 0xFFFFFFFF);
#else
    m_Handle = (pid << 32) | (widgetPointer);
#endif

    PedigreeIpc::IpcMessage *pCreateMessage = new PedigreeIpc::IpcMessage();
    if(!pCreateMessage->initialise())
    {
        m_Handle = 0;
        return false;
    }

    // Prepare a message to send.
    size_t totalSize = sizeof(WindowManagerMessage) + sizeof(CreateMessage);
    char *messageData = static_cast<char *>(pCreateMessage->getBuffer());
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    CreateMessage *pCreate = reinterpret_cast<CreateMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill.
    pWinMan->messageCode = Create;
    pWinMan->messageSize = sizeof(CreateMessage);
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;
    strlcpy(pCreate->endpoint, endpoint, 256);
    strlcpy(pCreate->title, title, 256);
    pCreate->minWidth = dimensions.getW();
    pCreate->minHeight = dimensions.getH();
    pCreate->rigid = true;

    // Send the message off to the window manager and wait for a response. The
    // response will contain a GraphicsProvider that we can use to create our
    // Framebuffer for drawing on.
    if(!PedigreeIpc::send(g_pWinmanEndpoint, pCreateMessage, false))
    {
        m_Handle = 0;
        delete pCreateMessage;
        return false;
    }

    PedigreeIpc::IpcMessage *pResponse = 0;

    while(1)
    {
        if(!PedigreeIpc::recv(m_Endpoint, &pResponse, false))
        {
            m_Handle = 0;
            return false;
        }

        if(!(pResponse && pResponse->getBuffer()))
        {
            continue;
        }

        pWinMan = reinterpret_cast<WindowManagerMessage*>(pResponse->getBuffer());
        if(!(pWinMan->isResponse && (pWinMan->messageCode == Create)))
        {
            g_PendingMessages.push(pResponse);
        }
        else
        {
            messageData = static_cast<char *>(pResponse->getBuffer());
            break;
        }
    }

    // Grab the results and use them.
    CreateMessageResponse *pCreateResp = reinterpret_cast<CreateMessageResponse*>(messageData + sizeof(WindowManagerMessage));
    m_EventCallback = cb;
    m_CallbackMap[m_Handle] = cb;

    delete pResponse;

    g_pWidget = this;

    // Asynchronously check for any events in the pipeline. This should be an
    // initial reposition event that we really need to get out to the client
    // ASAP.
    checkForEvents(true);

    return true;
}

bool Widget::setProperty(std::string propName, void *propVal, size_t maxSize)
{
    // Constructed yet?
    if(!m_Handle)
        return false;

    // Are the arguments sane?
    if(!propVal)
        return false;
    if(maxSize > 0x1000)
        return false;

    // Allocate the message.
    /// \todo Sanitise maxSize once large messages can be sent.
    size_t headerSize = sizeof(WindowManagerMessage) + sizeof(SetPropertyMessage);
    size_t totalSize = headerSize + maxSize;
    char *messageData = new char[totalSize];
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    SetPropertyMessage *pMessage = reinterpret_cast<SetPropertyMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill the message.
    pWinMan->messageCode = SetProperty;
    pWinMan->messageSize = sizeof(SetPropertyMessage) + maxSize;
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;

    propName.copy(pMessage->propertyName, sizeof pMessage->propertyName);
    pMessage->valueLength = maxSize;

    // Copy in the property data.
    memcpy(messageData + headerSize, propVal, maxSize);

    // Transmit.
    bool result = sendMessage(messageData, totalSize);

    // Clean up.
    delete [] messageData;

    return result;
}

bool Widget::getProperty(std::string propName, char **buffer, size_t maxSize)
{
    return false;
}

void Widget::setParent(Widget *pWidget)
{
}

Widget *Widget::getParent()
{
    return 0;
}

bool Widget::redraw(PedigreeGraphics::Rect &rt)
{
    // Constructed yet?
    if(!m_Handle)
        return false;

    // Allocate the message.
    PedigreeIpc::IpcMessage *pRedrawMessage = new PedigreeIpc::IpcMessage();
    if(!pRedrawMessage->initialise())
    {
        m_Handle = 0;
        return false;
    }

    size_t totalSize = sizeof(WindowManagerMessage) + sizeof(RequestRedrawMessage);
    char *messageData = static_cast<char *>(pRedrawMessage->getBuffer());
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    RequestRedrawMessage *pMessage = reinterpret_cast<RequestRedrawMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill the message.
    pWinMan->messageCode = RequestRedraw;
    pWinMan->messageSize = sizeof(RequestRedrawMessage);
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;
    pMessage->x = rt.getX();
    pMessage->y = rt.getY();
    pMessage->width = rt.getW();
    pMessage->height = rt.getH();

    // Transmit.
    if(!PedigreeIpc::send(g_pWinmanEndpoint, pRedrawMessage))
    {
        delete pRedrawMessage;
        return false;
    }

    return true;
}

bool Widget::visibility(bool vis)
{
    // Constructed yet?
    if(!m_Handle)
        return false;

    // Allocate the message.
    size_t totalSize = sizeof(WindowManagerMessage) + sizeof(SetVisibilityMessage);
    char *messageData = new char[totalSize];
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    SetVisibilityMessage *pMessage = reinterpret_cast<SetVisibilityMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill the message.
    pWinMan->messageCode = SetVisibility;
    pWinMan->messageSize = sizeof(SetVisibilityMessage);
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;
    pMessage->bVisible = vis;

    // Transmit.
    bool result = sendMessage(messageData, totalSize);

    // Clean up.
    delete [] messageData;

    return result;
}

void Widget::destroy()
{
    // Constructed yet?
    if(!m_Handle)
        return;

    // Allocate the message.
    size_t totalSize = sizeof(WindowManagerMessage) + sizeof(DestroyMessage);
    char *messageData = new char[totalSize];
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    DestroyMessage *pMessage = reinterpret_cast<DestroyMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill the message.
    pWinMan->messageCode = Destroy;
    pWinMan->messageSize = sizeof(DestroyMessage);
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;

    // Transmit.
    sendMessage(messageData, totalSize);

    // Clean up.
    delete [] messageData;

    // Invalidate this widget now.
    delete m_pFramebuffer;
    m_Handle = 0;
}

PedigreeGraphics::Framebuffer *Widget::getFramebuffer()
{
    return 0;

    /*
    // Constructed yet?
    if(!m_Handle)
        return 0;

    // No framebuffer yet?
    if(!m_pFramebuffer)
        return 0;

    // Allocate the message.
    size_t totalSize = sizeof(WindowManagerMessage) + sizeof(SyncMessage);
    char *messageData = new char[totalSize];
    WindowManagerMessage *pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
    SyncMessage *pMessage = reinterpret_cast<SyncMessage*>(messageData + sizeof(WindowManagerMessage));

    // Fill the message.
    pWinMan->messageCode = Sync;
    pWinMan->messageSize = sizeof(SyncMessage);
    pWinMan->widgetHandle = m_Handle;
    pWinMan->isResponse = false;

    // Transmit, synchronously.
    syslog(LOG_INFO, "TX Sync [%d]", getpid());
    sendMessage(messageData, totalSize);
    syslog(LOG_INFO, "TX complete");

    delete [] messageData;

    // Get the response.
    totalSize = 1024;
    messageData = new char[totalSize];

    while(1)
    {
        if(!recvMessage(m_Endpoint, messageData, totalSize))
        {
            m_Handle = 0;
            delete [] messageData;
            return 0;
        }

        pWinMan = reinterpret_cast<WindowManagerMessage*>(messageData);
        if(!(pWinMan->isResponse && (pWinMan->messageCode == Sync)))
        {
            g_PendingMessages.push(messageData);
            messageData = new char[totalSize];
        }
        else
        {
            break;
        }
    }

    SyncMessageResponse *pSyncResp = reinterpret_cast<SyncMessageResponse*>(messageData + sizeof(WindowManagerMessage));
    if(m_pFramebuffer->getProvider().contextId !=
        pSyncResp->provider.contextId)
    {
        delete m_pFramebuffer;
        m_pFramebuffer = new PedigreeGraphics::Framebuffer(pSyncResp->provider);
    }

    return m_pFramebuffer;
    */
}

void Widget::checkForEvents(bool bAsync)
{
    /// \todo ALL created widgets, not just one.
    if(g_pWidget)
    {
        char *buffer = 0;
        PedigreeIpc::IpcMessage *pMessage = 0;
        if(g_PendingMessages.empty())
        {
            PedigreeIpc::recv(g_pWidget->m_Endpoint, &pMessage, bAsync);
        }
        else
        {
            pMessage = g_PendingMessages.front();
            g_PendingMessages.pop();
        }

        if(pMessage)
        {
            buffer = static_cast<char *>(pMessage->getBuffer());

            LibUiProtocol::WindowManagerMessage *pHeader =
                reinterpret_cast<LibUiProtocol::WindowManagerMessage*>(buffer);

            widgetCallback_t cb = m_CallbackMap[pHeader->widgetHandle];

            switch(pHeader->messageCode)
            {
                case LibUiProtocol::Reposition:
                {
                    LibUiProtocol::RepositionMessage *pReposition =
                        reinterpret_cast<LibUiProtocol::RepositionMessage*>(buffer + sizeof(LibUiProtocol::WindowManagerMessage));
                    /// \todo If we delete this pointer, we free an extra memory region
                    ///       that we REALLY didn't mean to free. Fix that on the kernel
                    ///       side...
                    //delete g_pWidget->m_SharedFramebuffer;
                    g_pWidget->m_SharedFramebuffer =
                            new PedigreeIpc::SharedIpcMessage(pReposition->shmem_size, pReposition->shmem_handle);
                    g_pWidget->m_SharedFramebuffer->initialise();

                    // Run the callback now that the framebuffer is re-created.
                    cb(::Reposition, sizeof(pReposition->rt), &pReposition->rt);
                    break;
                }
                case LibUiProtocol::KeyEvent:
                {
                    LibUiProtocol::KeyEventMessage *pKeyEvent =
                        reinterpret_cast<LibUiProtocol::KeyEventMessage*>(buffer + sizeof(LibUiProtocol::WindowManagerMessage));

                    cb(::KeyUp, sizeof(pKeyEvent->key), &pKeyEvent->key);
                    break;
                }
                default:
                    syslog(LOG_INFO, "** unknown event");
            }
        }

        if(pMessage)
        {
            delete pMessage;
        }
    }
}
<|endoftext|>


<commit_before>#ifndef BUW_SPHERE_HPP
#define BUW_SPHERE_HPP

#include "shape.hpp"
#include <glm/vec3.hpp>
#include "color.hpp"
#include <string>

class Sphere : public Shape {
public:
	Sphere(std::string const& name, glm::vec3 const& center, double const radius, Color const& color);
	Sphere(glm::vec3 const& center, double const radius);
	Sphere();
	glm::vec3 center() const;
	double radius() const;
	double area() const override;
	double volume() const override;
private:
	glm::vec3 center_;
	double radius_;
};

#endif
<commit_msg>Add ostream<commit_after>#ifndef BUW_SPHERE_HPP
#define BUW_SPHERE_HPP

#include "shape.hpp"
#include <glm/vec3.hpp>
#include "color.hpp"
#include <string>

class Sphere : public Shape {
public:
	Sphere(std::string const& name, glm::vec3 const& center, double const radius, Color const& color);
	Sphere(glm::vec3 const& center, double const radius);
	Sphere();
	glm::vec3 center() const;
	double radius() const;
	double area() const override;
	double volume() const override;
	std::ostream& print(std::ostream& os) const override;
private:
	glm::vec3 center_;
	double radius_;
};

std::ostream& operator<<(std::ostream& os, Sphere const& s);

#endif
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// simple_query_test.cpp
//
// Identification: test/network/simple_query_test.cpp
//
// Copyright (c) 2016-17, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "common/harness.h"
#include "gtest/gtest.h"
#include "common/logger.h"
#include "network/network_manager.h"
#include "network/protocol_handler_factory.h"
#include "util/string_util.h"
#include <pqxx/pqxx> /* libpqxx is used to instantiate C++ client */
#include <include/network/postgres_protocol_handler.h>

#define NUM_THREADS 1

namespace peloton {
namespace test {

//===--------------------------------------------------------------------===//
// Simple Query Tests
//===--------------------------------------------------------------------===//

class SimpleQueryTests : public PelotonTest {};

static void *LaunchServer(peloton::network::NetworkManager network_manager,
                          int port) {
  try {
    network_manager.SetPort(port);
    network_manager.StartServer();
  } catch (peloton::ConnectionException &exception) {
    LOG_INFO("[LaunchServer] exception in thread");
  }
  return NULL;
}

/**
 * Simple select query test
 */
void *SimpleQueryTest(int port) {
  try {
    // forcing the factory to generate psql protocol handler
    pqxx::connection C(StringUtil::Format(
        "host=127.0.0.1 port=%d user=postgres sslmode=disable application_name=psql", port));
    pqxx::work txn1(C);

    peloton::network::NetworkConnection *conn =
        peloton::network::NetworkManager::GetConnection(
            peloton::network::NetworkManager::recent_connfd);

    network::PostgresProtocolHandler *handler =
        dynamic_cast<network::PostgresProtocolHandler*>(conn->protocol_handler_.get());
    EXPECT_NE(handler, nullptr);

    // EXPECT_EQ(conn->state, peloton::network::CONN_READ);
    // create table and insert some data
    txn1.exec("DROP TABLE IF EXISTS employee;");
    txn1.exec("CREATE TABLE employee(id INT, name VARCHAR(100));");
    txn1.commit();

    pqxx::work txn2(C);
    txn2.exec("INSERT INTO employee VALUES (1, 'Han LI');");
    txn2.exec("INSERT INTO employee VALUES (2, 'Shaokun ZOU');");
    txn2.exec("INSERT INTO employee VALUES (3, 'Yilei CHU');");

    pqxx::result R = txn2.exec("SELECT name FROM employee where id=1;");
    txn2.commit();

    EXPECT_EQ(R.size(), 1);
  } catch (const std::exception &e) {
    LOG_INFO("[SimpleQueryTest] Exception occurred: %s", e.what());
    EXPECT_TRUE(false);
  }

  LOG_INFO("[SimpleQueryTest] Client has closed");
  return NULL;
}

/**
 * rollback test
 * YINGJUN: rewrite wanted.
 */
/*
void *RollbackTest(int port) {
  try {
    pqxx::connection C(StringUtil::Format(
            "host=127.0.0.1 port=%d user=postgres sslmode=disable",port));
    LOG_INFO("[RollbackTest] Connected to %s", C.dbname());
    pqxx::work W(C);

    peloton::network::NetworkConnection *conn =
        peloton::network::NetworkManager::GetConnection(
            peloton::network::NetworkManager::recent_connfd);

    EXPECT_TRUE(conn->protocol_handler_.is_started);
    // EXPECT_EQ(conn->state, peloton::network::CONN_READ);
    // create table and insert some data
    W.exec("DROP TABLE IF EXISTS employee;");
    W.exec("CREATE TABLE employee(id INT, name VARCHAR(100));");
    W.exec("INSERT INTO employee VALUES (1, 'Han LI');");

    W.abort();

    W.exec("INSERT INTO employee VALUES (2, 'Shaokun ZOU');");
    W.exec("INSERT INTO employee VALUES (3, 'Yilei CHU');");

    W.commit();

    // pqxx::result R = W.exec("SELECT name FROM employee where id=1;");

    // EXPECT_EQ(R.size(), 1);

    // LOG_INFO("[RollbackTest] Found %lu employees", R.size());
    // W.commit();

  } catch (const std::exception &e) {
    LOG_INFO("[RollbackTest] Exception occurred");
  }

  LOG_INFO("[RollbackTest] Client has closed");
  return NULL;
}
*/

/*
TEST_F(PacketManagerTests, RollbackTest) {
  peloton::PelotonInit::Initialize();
  LOG_INFO("Server initialized");
  int port = 15721;
  peloton::network::NetworkManager network_manager;
  std::thread serverThread(LaunchServer, network_manager,port);
  while (!network_manager.GetIsStarted()) {
    sleep(1);
  }

  RollbackTest(port);

  network_manager.CloseServer();
  serverThread.join();
  LOG_INFO("Thread has joined");
  peloton::PelotonInit::Shutdown();
  LOG_INFO("Peloton has shut down");
}
*/

/**
 * Use std::thread to initiate peloton server and pqxx client in separate
 * threads
 * Simple query test to guarantee both sides run correctly
 * Callback method to close server after client finishes
 */
TEST_F(SimpleQueryTests, SimpleQueryTest) {
  peloton::PelotonInit::Initialize();
  LOG_INFO("Server initialized");
  peloton::network::NetworkManager network_manager;

  int port = 15721;
  std::thread serverThread(LaunchServer, network_manager, port);
  while (!network_manager.GetIsStarted()) {
    sleep(1);
  }

  // server & client running correctly
  SimpleQueryTest(port);

  network_manager.CloseServer();
  serverThread.join();
  LOG_INFO("Peloton is shutting down");
  peloton::PelotonInit::Shutdown();
  LOG_INFO("Peloton has shut down");
}

///**
// * Scalability test
// * Open 2 servers in threads concurrently
// * Both conduct simple query job
// */
// TEST_F(PacketManagerTests, ScalabilityTest) {
//  peloton::PelotonInit::Initialize();
//
//  /* launch 2 network managers in different port */
//  // first server
//  int port1 = 15721;
//  peloton::network::NetworkManager network_manager1;
//  std::thread serverThread1(LaunchServer, network_manager1,port1);
//
//  // second server
//  int port2 = 15722;
//  peloton::network::NetworkManager network_manager2;
//  std::thread serverThread2(LaunchServer, network_manager2,port2);
//
//  while (!network_manager1.is_started || !network_manager2.is_started) {
//    sleep(1);
//  }
//
//  /* launch 2 clients to do simple query separately */
//  SimpleQueryTest(port1);
//  SimpleQueryTest(port2);
//
//  network_manager1.CloseServer();
//  network_manager2.CloseServer();
//
//  serverThread1.join();
//  serverThread2.join();
//
//  LOG_INFO("[ScalabilityTest] Threads have joined");
//  peloton::PelotonInit::Shutdown();
//  LOG_INFO("[ScalabilityTest] Peloton has shut down");
//}

}  // namespace test
}  // namespace peloton
<commit_msg>Add testcase<commit_after>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// simple_query_test.cpp
//
// Identification: test/network/simple_query_test.cpp
//
// Copyright (c) 2016-17, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "common/harness.h"
#include "gtest/gtest.h"
#include "common/logger.h"
#include "network/network_manager.h"
#include "network/protocol_handler_factory.h"
#include "util/string_util.h"
#include <pqxx/pqxx> /* libpqxx is used to instantiate C++ client */
#include <include/network/postgres_protocol_handler.h>

#define NUM_THREADS 1

namespace peloton {
namespace test {

//===--------------------------------------------------------------------===//
// Simple Query Tests
//===--------------------------------------------------------------------===//

class SimpleQueryTests : public PelotonTest {};

static void *LaunchServer(peloton::network::NetworkManager network_manager,
                          int port) {
  try {
    network_manager.SetPort(port);
    network_manager.StartServer();
  } catch (peloton::ConnectionException &exception) {
    LOG_INFO("[LaunchServer] exception in thread");
  }
  return NULL;
}

/**
 * Simple select query test
 */
void *SimpleQueryTest(int port) {
  try {
    // forcing the factory to generate psql protocol handler
    pqxx::connection C(StringUtil::Format(
        "host=127.0.0.1 port=%d user=postgres sslmode=disable application_name=psql", port));
    pqxx::work txn1(C);

    peloton::network::NetworkConnection *conn =
        peloton::network::NetworkManager::GetConnection(
            peloton::network::NetworkManager::recent_connfd);

    network::PostgresProtocolHandler *handler =
        dynamic_cast<network::PostgresProtocolHandler*>(conn->protocol_handler_.get());
    EXPECT_NE(handler, nullptr);

    // EXPECT_EQ(conn->state, peloton::network::CONN_READ);
    // create table and insert some data
    txn1.exec("DROP TABLE IF EXISTS employee;");
    txn1.exec("CREATE TABLE employee(id INT, name VARCHAR(100));");
    txn1.commit();

    pqxx::work txn2(C);
    txn2.exec("INSERT INTO employee VALUES (1, 'Han LI');");
    txn2.exec("INSERT INTO employee VALUES (2, 'Shaokun ZOU');");
    txn2.exec("INSERT INTO employee VALUES (3, 'Yilei CHU');");

    pqxx::result R = txn2.exec("SELECT name FROM employee where id=1;");
    txn2.commit();
    EXPECT_EQ(R.size(), 1);

    pqxx::work txn3(C);
    txn3.exec("DROP TABLE IF EXISTS foo;");
    txn3.exec("CREATE TABLE foo(length DECIMAL);");
    txn3.commit();

    pqxx::work txn4(C);
    txn4.exec("PREPARE func AS INSERT INTO foo VALUES($1);");
    txn4.exec("EXECUTE func(1);");
    txn4.exec("EXECUTE func(1+1);");
    txn4.exec("EXECUTE func(SQRT(9.0));");

    pqxx::result R2 = txn4.exec("SELECT * FROM foo;");
    txn4.commit();
    EXPECT_EQ(R2.size(), 3);

  } catch (const std::exception &e) {
    LOG_INFO("[SimpleQueryTest] Exception occurred: %s", e.what());
    EXPECT_TRUE(false);
  }

  LOG_INFO("[SimpleQueryTest] Client has closed");
  return NULL;
}

/**
 * rollback test
 * YINGJUN: rewrite wanted.
 */
/*
void *RollbackTest(int port) {
  try {
    pqxx::connection C(StringUtil::Format(
            "host=127.0.0.1 port=%d user=postgres sslmode=disable",port));
    LOG_INFO("[RollbackTest] Connected to %s", C.dbname());
    pqxx::work W(C);

    peloton::network::NetworkConnection *conn =
        peloton::network::NetworkManager::GetConnection(
            peloton::network::NetworkManager::recent_connfd);

    EXPECT_TRUE(conn->protocol_handler_.is_started);
    // EXPECT_EQ(conn->state, peloton::network::CONN_READ);
    // create table and insert some data
    W.exec("DROP TABLE IF EXISTS employee;");
    W.exec("CREATE TABLE employee(id INT, name VARCHAR(100));");
    W.exec("INSERT INTO employee VALUES (1, 'Han LI');");

    W.abort();

    W.exec("INSERT INTO employee VALUES (2, 'Shaokun ZOU');");
    W.exec("INSERT INTO employee VALUES (3, 'Yilei CHU');");

    W.commit();

    // pqxx::result R = W.exec("SELECT name FROM employee where id=1;");

    // EXPECT_EQ(R.size(), 1);

    // LOG_INFO("[RollbackTest] Found %lu employees", R.size());
    // W.commit();

  } catch (const std::exception &e) {
    LOG_INFO("[RollbackTest] Exception occurred");
  }

  LOG_INFO("[RollbackTest] Client has closed");
  return NULL;
}
*/

/*
TEST_F(PacketManagerTests, RollbackTest) {
  peloton::PelotonInit::Initialize();
  LOG_INFO("Server initialized");
  int port = 15721;
  peloton::network::NetworkManager network_manager;
  std::thread serverThread(LaunchServer, network_manager,port);
  while (!network_manager.GetIsStarted()) {
    sleep(1);
  }

  RollbackTest(port);

  network_manager.CloseServer();
  serverThread.join();
  LOG_INFO("Thread has joined");
  peloton::PelotonInit::Shutdown();
  LOG_INFO("Peloton has shut down");
}
*/

/**
 * Use std::thread to initiate peloton server and pqxx client in separate
 * threads
 * Simple query test to guarantee both sides run correctly
 * Callback method to close server after client finishes
 */
TEST_F(SimpleQueryTests, SimpleQueryTest) {
  peloton::PelotonInit::Initialize();
  LOG_INFO("Server initialized");
  peloton::network::NetworkManager network_manager;

  int port = 15721;
  std::thread serverThread(LaunchServer, network_manager, port);
  while (!network_manager.GetIsStarted()) {
    sleep(1);
  }

  // server & client running correctly
  SimpleQueryTest(port);

  network_manager.CloseServer();
  serverThread.join();
  LOG_INFO("Peloton is shutting down");
  peloton::PelotonInit::Shutdown();
  LOG_INFO("Peloton has shut down");
}

///**
// * Scalability test
// * Open 2 servers in threads concurrently
// * Both conduct simple query job
// */
// TEST_F(PacketManagerTests, ScalabilityTest) {
//  peloton::PelotonInit::Initialize();
//
//  /* launch 2 network managers in different port */
//  // first server
//  int port1 = 15721;
//  peloton::network::NetworkManager network_manager1;
//  std::thread serverThread1(LaunchServer, network_manager1,port1);
//
//  // second server
//  int port2 = 15722;
//  peloton::network::NetworkManager network_manager2;
//  std::thread serverThread2(LaunchServer, network_manager2,port2);
//
//  while (!network_manager1.is_started || !network_manager2.is_started) {
//    sleep(1);
//  }
//
//  /* launch 2 clients to do simple query separately */
//  SimpleQueryTest(port1);
//  SimpleQueryTest(port2);
//
//  network_manager1.CloseServer();
//  network_manager2.CloseServer();
//
//  serverThread1.join();
//  serverThread2.join();
//
//  LOG_INFO("[ScalabilityTest] Threads have joined");
//  peloton::PelotonInit::Shutdown();
//  LOG_INFO("[ScalabilityTest] Peloton has shut down");
//}

}  // namespace test
}  // namespace peloton
<|endoftext|>


<commit_before>#include "inflater.hpp"

#include "widgets/container.hpp"

#include "widgets/group/Column.hpp"
#include "widgets/group/Row.hpp"
#include "widgets/group/Pile.hpp"
#include "widgets/group/ScrollArea.hpp"
#include "widgets/group/SizeContainer.hpp"
#include "widgets/group/Overlay.hpp"
#include "widgets/group/List.hpp"

#include "widgets/proxy/KeyProxy.hpp"
#include "widgets/proxy/MouseProxy.hpp"
#include "widgets/proxy/ResizeProxy.hpp"

#include "Morda.hpp"

#include "util/util.hpp"

using namespace morda;

inflater::inflater(){
	this->register_widget<Widget>("Widget");
	this->register_widget<Container>("Container");
	this->register_widget<SizeContainer>("SizeContainer");
	this->register_widget<Row>("Row");
	this->register_widget<Column>("Column");
	this->register_widget<Pile>("Pile");
	this->register_widget<MouseProxy>("MouseProxy");
	this->register_widget<ScrollArea>("ScrollArea");
	this->register_widget<KeyProxy>("KeyProxy");
	this->register_widget<Overlay>("Overlay");
	this->register_widget<ResizeProxy>("ResizeProxy");
	this->register_widget<HList>("HList");
	this->register_widget<VList>("VList");
}

void inflater::add_factory(std::string&& widgetName, decltype(factories)::value_type::second_type&& factory){
	auto ret = this->factories.insert(std::make_pair(
			std::move(widgetName),
			std::move(factory)
		));
	if(!ret.second){
		throw utki::invalid_state("Failed registering widget type, widget type with given name is already added");
	}
}

bool inflater::unregister_widget(const std::string& widgetName)noexcept{
	if(this->factories.erase(widgetName) == 0){
		return false;
	}
	return true;
}

std::shared_ptr<morda::Widget> inflater::inflate(const papki::File& fi) {
	return this->inflate(stob_to_puu(*stob::load(fi)));
}

namespace{
const char* defs_c = "defs";
}

namespace{
void substitute_vars(puu::trees& to, const std::function<const puu::trees*(const std::string&)>& findVar){
	if(!findVar){
		return;
	}

	for(auto i = to.begin(); i != to.end();){
		if(i->value == "@" || i->value == "$"){ //TODO: @ is deprecated, remove check for @.

			//TODO: deprecated, remove.
			if(i->value == "@"){
				TRACE_ALWAYS(<< "DEPRACATED: use $ instead of @" << std::endl)
			}

			if(i->children.empty()){
				throw Exc("malformed GUI definition: error: reference to a variable holds no variable name");
			}

			if(i->children.size() != 1){
				throw Exc("malformed GUI definition: reference to variable holds more than one variable name");
			}

			if(!i->children.front().children.empty()){
				throw Exc("malformed GUI definition: variable name has children");
			}

			if(auto var = findVar(i->children.front().value.to_string())){
				if(var->size() != 0){
					i = to.insert(i, var->begin(), var->end());
					i = std::next(i, std::distance(var->begin(), var->end()));
					i = to.erase(i);
					continue;
				}
			}
		}else{
			substitute_vars(i->children, findVar);
		}
		++i;
	}
}
}

namespace{
puu::trees merge_gui(const puu::trees& tmplChain, const std::set<std::string>& varNames, puu::trees&& trees){
	if(trees.empty()){
		if(tmplChain.empty()){
			return puu::trees();
		}
	}

	std::unique_ptr<stob::Node> ret;
	if(!tmplChain.empty()){
		ret = puu_to_stob(tmplChain);
	}

	std::unique_ptr<puu::node> chain = puu_to_stob(trees);

	//prepare variables and remove them from 'chain'
	std::map<std::string, puu::trees> vars;
	{
		auto childrenChain = utki::makeUnique<stob::Node>();
		stob::Node* lastChild = childrenChain.get();
		for(; chain;){
			if(!chain->isProperty()){
				ASSERT(lastChild)
				ASSERT(!lastChild->next())
				ASSERT(chain)
				auto tail = chain->chopNext();
				ASSERT(!chain->next())
				lastChild->insertNext(std::move(chain));
				chain = std::move(tail);
				lastChild = lastChild->next();
				continue;
			}else if(varNames.find(chain->value()) != varNames.end()){
				vars[chain->value()] = stob_to_puu(*chain->removeChildren());
				chain = chain->chopNext();
				continue;
			}else if(ret){
				if(auto n = ret->thisOrNext(chain->value()).get_node()){
					n->setChildren(chain->removeChildren());
					chain = chain->chopNext();
					continue;
				}
			}
			auto tail = chain->chopNext();
			chain->setNext(std::move(ret));
			ret = std::move(chain);
			chain = std::move(tail);
		}
		vars["children"] = stob_to_puu(*childrenChain->chopNext());
	}

	ASSERT(ret)

	auto retret = stob_to_puu(*ret);

	substitute_vars(
			retret,
			[&vars](const std::string& name) -> const puu::trees*{
//				TRACE(<< "looking for var = " << name << std::endl)
				auto i = vars.find(name);
				if(i == vars.end()){
//					TRACE(<< "not found" << std::endl)
					return nullptr;
				}
//				TRACE(<< "found = " << i->second.get() << std::endl)
				return &i->second;
			}
		);

	return retret;
}
}

const decltype(inflater::factories)::value_type::second_type& inflater::find_factory(const std::string& widgetName) {
	auto i = this->factories.find(widgetName);

	if(i == this->factories.end()){
		std::stringstream ss;
		ss << "inflater::find_factory(): widget name '" << widgetName << "' not found";
		throw utki::not_found(ss.str());
	}

	return i->second;
}

std::shared_ptr<widget> inflater::inflate(const char* str){
	return this->inflate(puu::read(str));
}

std::shared_ptr<widget> inflater::inflate(const puu::trees& gui_script){
	return this->inflate(*puu_to_stob(gui_script));
}

std::shared_ptr<morda::Widget> inflater::inflate(const stob::Node& chain){
//	TODO:
//	if(!App::inst().thisIsUIThread()){
//		throw Exc("Inflate called not from UI thread");
//	}

	const stob::Node* n = &chain;
	for(; n && n->isProperty(); n = n->next()){
		if(*n == defs_c){
			if(n->child()){
				this->push_defs(stob_to_puu(*n->child()));
			}
		}else{
			throw Exc("inflater::Inflate(): unknown declaration encountered before first widget");
		}
	}

	if(!n){
		return nullptr;
	}

	ASSERT(!n->isProperty())

	std::unique_ptr<stob::Node> cloned;
//	TRACE(<< "inflating = " << n->value() << std::endl)
	if(auto tmpl = this->find_template(n->value())){
//		TRACE(<< "template name = " << n->value() << std::endl)

		cloned = utki::makeUnique<stob::Node>(tmpl->templ.value.to_string());
		cloned->setChildren(
				puu_to_stob(
						merge_gui(tmpl->templ.children, tmpl->vars, stob_to_puu(*n->cloneChildren()))
					)
			);
		n = cloned.get();
//		TRACE(<< "n = " << n->chainToString(true) << std::endl)
	}

	auto fac = this->find_factory(n->value());

	unsigned numPopDefs = 0;
	utki::ScopeExit scopeExit([this, &numPopDefs](){
		for(unsigned i = 0; i != numPopDefs; ++i){
			this->pop_defs();
		}
	});

	for(auto v = n->child(defs_c).get_node(); v; v = v->next(defs_c).get_node()){
		if(v->child()){
			this->push_defs(stob_to_puu(*v->child()));
			++numPopDefs;
		}
	}

	if(cloned){
		cloned = cloned->removeChildren();
	}else{
		if(n->child()){
			cloned = n->child()->cloneChain();
		}
	}

	if(cloned){
		auto c = stob_to_puu(*cloned);

		substitute_vars(
				c,
				[this](const std::string& name) -> const puu::trees*{
					return this->find_variable(name);
				}
			);
		return fac(c);
	}else{
		return fac(puu::trees());
	}
}

inflater::widget_template inflater::parse_template(const puu::trees& chain){
	widget_template ret;

	for(auto& n : chain){
		//template definition
		if(!ret.templ.value.empty()){
			break;
		}

		if(is_leaf_property(n.value)){
			//possibly variable name
			if(!n.children.empty()){
				throw std::invalid_argument("inflater::parse_template(): template argument name has children");
			}
			ret.vars.insert(n.value.to_string());
			continue;
		}

		ASSERT(!is_leaf_property(n.value))
		// TRACE(<< "template name = " << n.value.to_string() << std::endl)

		ret.templ = n;
	}
	if(ret.templ.value.empty()){
		throw std::invalid_argument("inflater::parse_template(): template has no definition");
	}

	// for each variable create a stub property if needed
	for(auto& v : ret.vars){
		auto i = std::find(ret.templ.children.begin(), ret.templ.children.end(), v);
		if(i == ret.templ.children.end()){
			ret.templ.children.push_back(puu::tree(v));
			ret.templ.children.back().children.push_back(puu::tree());
		}
	}

	// TRACE(<< "stubbed template = " << ret.t->chainToString(true) << std::endl)

	if(auto tmpl = this->find_template(ret.templ.value.to_string())){
		ret.templ.value = tmpl->templ.value;
		ret.templ.children = merge_gui(tmpl->templ.children, tmpl->vars, std::move(ret.templ.children));

		ret.vars.insert(tmpl->vars.begin(), tmpl->vars.end()); // forward all variables
	}

	// TRACE(<< "parsed template = " << ret.t->chainToString(true) << std::endl)
	return ret;
}

void inflater::push_defs(const puu::trees& chain) {
	this->push_variables(chain);
	this->push_templates(chain);
}

void inflater::pop_defs() {
	this->pop_variables();
	this->pop_templates();
}

void inflater::push_templates(const puu::trees& chain){
	decltype(this->templates)::value_type m;

	for(auto& c : chain){
		if(is_property(c)){
			continue;
		}

		if(c.children.empty()){
			throw std::invalid_argument("inflater::pushTemplates(): template name has no children, error.");
		}
//		TRACE(<< "pushing template = " << c->value() << std::endl)

		auto res = m.insert(
				std::make_pair(c.value.to_string(), parse_template(c.children))
			);

		if(!res.second){
			throw Exc("inflater::PushTemplates(): template name is already defined in given templates chain, error.");
		}
	}

	this->templates.push_back(std::move(m));

//#ifdef DEBUG
//	TRACE(<< "Templates Stack:" << std::endl)
//	for(auto& i : this->templates){
//		TRACE(<< "\tTemplates:" << std::endl)
//		for(auto& j : i){
//			TRACE(<< "\t\t" << j.first << " = " << j.second->ChainToString() << std::endl)
//		}
//	}
//#endif
}

void inflater::pop_templates(){
	ASSERT(this->templates.size() != 0)
	this->templates.pop_back();
}

const inflater::widget_template* inflater::find_template(const std::string& name)const{
	for(auto i = this->templates.rbegin(); i != this->templates.rend(); ++i){
		auto r = i->find(name);
		if(r != i->end()){
			return &r->second;
		}
	}
//	TRACE(<< "inflater::FindTemplate(): template '" << name <<"' not found!!!" << std::endl)
	return nullptr;
}

const puu::trees* inflater::find_variable(const std::string& name)const{
	for(auto i = this->variables.rbegin(); i != this->variables.rend(); ++i){
		auto r = i->find(name);
		if(r != i->end()){
			return &r->second;
		}
	}
//	TRACE(<< "inflater::findVariable(): variable '" << name <<"' not found!!!" << std::endl)
	return nullptr;
}

void inflater::pop_variables(){
	ASSERT(this->variables.size() != 0)
	this->variables.pop_back();
}

void inflater::push_variables(const puu::trees& trees){
	decltype(this->variables)::value_type m;

	for(auto& t : trees){
		if(!is_property(t)){
			continue;
		}

		auto value = t.children;

		this->substitute_variables(value);

		if(!m.insert(
				std::make_pair(t.value.to_string(), std::move(value))
			).second)
		{
			std::stringstream ss;
			ss << "inflater::push_variables(): failed to add variable '" << t.value.to_string() << "', variable with same name is already defined in this defs block";
			throw utki::invalid_state(ss.str());
		}
	}

	this->variables.push_back(std::move(m));

//#ifdef DEBUG
//	TRACE(<< "Variables Stack:" << std::endl)
//	for(auto& i : this->variables){
//		TRACE(<< "\tVariables:" << std::endl)
//		for(auto& j : i){
//			TRACE(<< "\t\t" << j.second.first << "|" << j.first << " = " << j.second.second << std::endl)
//		}
//	}
//#endif
}

void inflater::substitute_variables(puu::trees& to)const{
	substitute_vars(
			to,
			[this](const std::string& name) -> const puu::trees*{
				return this->find_variable(name);
			}
		);
}
<commit_msg>stuff<commit_after>#include "inflater.hpp"

#include "widgets/container.hpp"

#include "widgets/group/Column.hpp"
#include "widgets/group/Row.hpp"
#include "widgets/group/Pile.hpp"
#include "widgets/group/ScrollArea.hpp"
#include "widgets/group/SizeContainer.hpp"
#include "widgets/group/Overlay.hpp"
#include "widgets/group/List.hpp"

#include "widgets/proxy/KeyProxy.hpp"
#include "widgets/proxy/MouseProxy.hpp"
#include "widgets/proxy/ResizeProxy.hpp"

#include "Morda.hpp"

#include "util/util.hpp"

using namespace morda;

inflater::inflater(){
	this->register_widget<Widget>("Widget");
	this->register_widget<Container>("Container");
	this->register_widget<SizeContainer>("SizeContainer");
	this->register_widget<Row>("Row");
	this->register_widget<Column>("Column");
	this->register_widget<Pile>("Pile");
	this->register_widget<MouseProxy>("MouseProxy");
	this->register_widget<ScrollArea>("ScrollArea");
	this->register_widget<KeyProxy>("KeyProxy");
	this->register_widget<Overlay>("Overlay");
	this->register_widget<ResizeProxy>("ResizeProxy");
	this->register_widget<HList>("HList");
	this->register_widget<VList>("VList");
}

void inflater::add_factory(std::string&& widgetName, decltype(factories)::value_type::second_type&& factory){
	auto ret = this->factories.insert(std::make_pair(
			std::move(widgetName),
			std::move(factory)
		));
	if(!ret.second){
		throw utki::invalid_state("Failed registering widget type, widget type with given name is already added");
	}
}

bool inflater::unregister_widget(const std::string& widgetName)noexcept{
	if(this->factories.erase(widgetName) == 0){
		return false;
	}
	return true;
}

std::shared_ptr<morda::Widget> inflater::inflate(const papki::File& fi) {
	return this->inflate(stob_to_puu(*stob::load(fi)));
}

namespace{
const char* defs_c = "defs";
}

namespace{
void substitute_vars(puu::trees& to, const std::function<const puu::trees*(const std::string&)>& findVar){
	if(!findVar){
		return;
	}

	for(auto i = to.begin(); i != to.end();){
		if(i->value == "@" || i->value == "$"){ //TODO: @ is deprecated, remove check for @.

			//TODO: deprecated, remove.
			if(i->value == "@"){
				TRACE_ALWAYS(<< "DEPRACATED: use $ instead of @" << std::endl)
			}

			if(i->children.empty()){
				throw Exc("malformed GUI definition: error: reference to a variable holds no variable name");
			}

			if(i->children.size() != 1){
				throw Exc("malformed GUI definition: reference to variable holds more than one variable name");
			}

			if(!i->children.front().children.empty()){
				throw Exc("malformed GUI definition: variable name has children");
			}

			if(auto var = findVar(i->children.front().value.to_string())){
				if(var->size() != 0){
					i = to.insert(i, var->begin(), var->end());
					i = std::next(i, std::distance(var->begin(), var->end()));
					i = to.erase(i);
					continue;
				}
			}
		}else{
			substitute_vars(i->children, findVar);
		}
		++i;
	}
}
}

namespace{
puu::trees merge_gui(const puu::trees& tmplChain, const std::set<std::string>& varNames, puu::trees&& trees){
	if(trees.empty()){
		if(tmplChain.empty()){
			return puu::trees();
		}
	}

	std::unique_ptr<stob::Node> ret;
	if(!tmplChain.empty()){
		ret = puu_to_stob(tmplChain);
	}

	std::unique_ptr<puu::node> chain = puu_to_stob(trees);

	//prepare variables and remove them from 'chain'
	std::map<std::string, puu::trees> vars;
	{
		auto childrenChain = utki::makeUnique<stob::Node>();
		stob::Node* lastChild = childrenChain.get();
		for(; chain;){
			auto tail = chain->chopNext();
			if(!chain->isProperty()){
				ASSERT(lastChild)
				ASSERT(!lastChild->next())
				ASSERT(chain)
				ASSERT(!chain->next())
				lastChild->insertNext(std::move(chain));
				lastChild = lastChild->next();
				chain = std::move(tail);
				continue;
			}else if(varNames.find(chain->value()) != varNames.end()){
				vars[chain->value()] = stob_to_puu(*chain->removeChildren());
				chain = std::move(tail);
				continue;
			}else if(ret){
				if(auto n = ret->thisOrNext(chain->value()).get_node()){
					n->setChildren(chain->removeChildren());
					chain = std::move(tail);
					continue;
				}
			}
			chain->setNext(std::move(ret));
			ret = std::move(chain);

			chain = std::move(tail);
		}
		vars["children"] = stob_to_puu(*childrenChain->chopNext());
	}

	ASSERT(ret)

	auto retret = stob_to_puu(*ret);

	substitute_vars(
			retret,
			[&vars](const std::string& name) -> const puu::trees*{
//				TRACE(<< "looking for var = " << name << std::endl)
				auto i = vars.find(name);
				if(i == vars.end()){
//					TRACE(<< "not found" << std::endl)
					return nullptr;
				}
//				TRACE(<< "found = " << i->second.get() << std::endl)
				return &i->second;
			}
		);

	return retret;
}
}

const decltype(inflater::factories)::value_type::second_type& inflater::find_factory(const std::string& widgetName) {
	auto i = this->factories.find(widgetName);

	if(i == this->factories.end()){
		std::stringstream ss;
		ss << "inflater::find_factory(): widget name '" << widgetName << "' not found";
		throw utki::not_found(ss.str());
	}

	return i->second;
}

std::shared_ptr<widget> inflater::inflate(const char* str){
	return this->inflate(puu::read(str));
}

std::shared_ptr<widget> inflater::inflate(const puu::trees& gui_script){
	return this->inflate(*puu_to_stob(gui_script));
}

std::shared_ptr<morda::Widget> inflater::inflate(const stob::Node& chain){
//	TODO:
//	if(!App::inst().thisIsUIThread()){
//		throw Exc("Inflate called not from UI thread");
//	}

	const stob::Node* n = &chain;
	for(; n && n->isProperty(); n = n->next()){
		if(*n == defs_c){
			if(n->child()){
				this->push_defs(stob_to_puu(*n->child()));
			}
		}else{
			throw Exc("inflater::Inflate(): unknown declaration encountered before first widget");
		}
	}

	if(!n){
		return nullptr;
	}

	ASSERT(!n->isProperty())

	std::unique_ptr<stob::Node> cloned;
//	TRACE(<< "inflating = " << n->value() << std::endl)
	if(auto tmpl = this->find_template(n->value())){
//		TRACE(<< "template name = " << n->value() << std::endl)

		cloned = utki::makeUnique<stob::Node>(tmpl->templ.value.to_string());
		cloned->setChildren(
				puu_to_stob(
						merge_gui(tmpl->templ.children, tmpl->vars, stob_to_puu(*n->cloneChildren()))
					)
			);
		n = cloned.get();
//		TRACE(<< "n = " << n->chainToString(true) << std::endl)
	}

	auto fac = this->find_factory(n->value());

	unsigned numPopDefs = 0;
	utki::ScopeExit scopeExit([this, &numPopDefs](){
		for(unsigned i = 0; i != numPopDefs; ++i){
			this->pop_defs();
		}
	});

	for(auto v = n->child(defs_c).get_node(); v; v = v->next(defs_c).get_node()){
		if(v->child()){
			this->push_defs(stob_to_puu(*v->child()));
			++numPopDefs;
		}
	}

	if(cloned){
		cloned = cloned->removeChildren();
	}else{
		if(n->child()){
			cloned = n->child()->cloneChain();
		}
	}

	if(cloned){
		auto c = stob_to_puu(*cloned);

		substitute_vars(
				c,
				[this](const std::string& name) -> const puu::trees*{
					return this->find_variable(name);
				}
			);
		return fac(c);
	}else{
		return fac(puu::trees());
	}
}

inflater::widget_template inflater::parse_template(const puu::trees& chain){
	widget_template ret;

	for(auto& n : chain){
		//template definition
		if(!ret.templ.value.empty()){
			break;
		}

		if(is_leaf_property(n.value)){
			//possibly variable name
			if(!n.children.empty()){
				throw std::invalid_argument("inflater::parse_template(): template argument name has children");
			}
			ret.vars.insert(n.value.to_string());
			continue;
		}

		ASSERT(!is_leaf_property(n.value))
		// TRACE(<< "template name = " << n.value.to_string() << std::endl)

		ret.templ = n;
	}
	if(ret.templ.value.empty()){
		throw std::invalid_argument("inflater::parse_template(): template has no definition");
	}

	// for each variable create a stub property if needed
	for(auto& v : ret.vars){
		auto i = std::find(ret.templ.children.begin(), ret.templ.children.end(), v);
		if(i == ret.templ.children.end()){
			ret.templ.children.push_back(
					puu::tree(puu::leaf(v), {puu::tree()})
				);
		}
	}

	// TRACE(<< "stubbed template = " << ret.t->chainToString(true) << std::endl)

	if(auto tmpl = this->find_template(ret.templ.value.to_string())){
		ret.templ.value = tmpl->templ.value;
		ret.templ.children = merge_gui(tmpl->templ.children, tmpl->vars, std::move(ret.templ.children));

		ret.vars.insert(tmpl->vars.begin(), tmpl->vars.end()); // forward all variables
	}

	// TRACE(<< "parsed template = " << ret.t->chainToString(true) << std::endl)
	return ret;
}

void inflater::push_defs(const puu::trees& chain) {
	this->push_variables(chain);
	this->push_templates(chain);
}

void inflater::pop_defs() {
	this->pop_variables();
	this->pop_templates();
}

void inflater::push_templates(const puu::trees& chain){
	decltype(this->templates)::value_type m;

	for(auto& c : chain){
		if(is_property(c)){
			continue;
		}

		if(c.children.empty()){
			throw std::invalid_argument("inflater::pushTemplates(): template name has no children, error.");
		}
//		TRACE(<< "pushing template = " << c->value() << std::endl)

		auto res = m.insert(
				std::make_pair(c.value.to_string(), parse_template(c.children))
			);

		if(!res.second){
			throw Exc("inflater::PushTemplates(): template name is already defined in given templates chain, error.");
		}
	}

	this->templates.push_back(std::move(m));

//#ifdef DEBUG
//	TRACE(<< "Templates Stack:" << std::endl)
//	for(auto& i : this->templates){
//		TRACE(<< "\tTemplates:" << std::endl)
//		for(auto& j : i){
//			TRACE(<< "\t\t" << j.first << " = " << j.second->ChainToString() << std::endl)
//		}
//	}
//#endif
}

void inflater::pop_templates(){
	ASSERT(this->templates.size() != 0)
	this->templates.pop_back();
}

const inflater::widget_template* inflater::find_template(const std::string& name)const{
	for(auto i = this->templates.rbegin(); i != this->templates.rend(); ++i){
		auto r = i->find(name);
		if(r != i->end()){
			return &r->second;
		}
	}
//	TRACE(<< "inflater::FindTemplate(): template '" << name <<"' not found!!!" << std::endl)
	return nullptr;
}

const puu::trees* inflater::find_variable(const std::string& name)const{
	for(auto i = this->variables.rbegin(); i != this->variables.rend(); ++i){
		auto r = i->find(name);
		if(r != i->end()){
			return &r->second;
		}
	}
//	TRACE(<< "inflater::findVariable(): variable '" << name <<"' not found!!!" << std::endl)
	return nullptr;
}

void inflater::pop_variables(){
	ASSERT(this->variables.size() != 0)
	this->variables.pop_back();
}

void inflater::push_variables(const puu::trees& trees){
	decltype(this->variables)::value_type m;

	for(auto& t : trees){
		if(!is_property(t)){
			continue;
		}

		auto value = t.children;

		this->substitute_variables(value);

		if(!m.insert(
				std::make_pair(t.value.to_string(), std::move(value))
			).second)
		{
			std::stringstream ss;
			ss << "inflater::push_variables(): failed to add variable '" << t.value.to_string() << "', variable with same name is already defined in this defs block";
			throw utki::invalid_state(ss.str());
		}
	}

	this->variables.push_back(std::move(m));

//#ifdef DEBUG
//	TRACE(<< "Variables Stack:" << std::endl)
//	for(auto& i : this->variables){
//		TRACE(<< "\tVariables:" << std::endl)
//		for(auto& j : i){
//			TRACE(<< "\t\t" << j.second.first << "|" << j.first << " = " << j.second.second << std::endl)
//		}
//	}
//#endif
}

void inflater::substitute_variables(puu::trees& to)const{
	substitute_vars(
			to,
			[this](const std::string& name) -> const puu::trees*{
				return this->find_variable(name);
			}
		);
}
<|endoftext|>


<commit_before>/*
**  Copyright 2009 MERETHIS
**  This file is part of CentreonBroker.
**
**  CentreonBroker is free software: you can redistribute it and/or modify it
**  under the terms of the GNU General Public License as published by the Free
**  Software Foundation, either version 2 of the License, or (at your option)
**  any later version.
**
**  CentreonBroker 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 General Public License
**  for more details.
**
**  You should have received a copy of the GNU General Public License along
**  with CentreonBroker.  If not, see <http://www.gnu.org/licenses/>.
**
**  For more information : contact@centreon.com
*/

#include <cassert>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include "conf/lexer.h"

using namespace CentreonBroker::Conf;

/******************************************************************************
*                                                                             *
*                                                                             *
*                                    Token                                    *
*                                                                             *
*                                                                             *
******************************************************************************/

/**************************************
*                                     *
*           Public Methods            *
*                                     *
**************************************/

/**
 *  \brief Token default constructor.
 *
 *  The resulting token will be undefined ; text will be empty and type UKNOWN.
 */
Token::Token() : type_(UNKNOWN) {}

/**
 *  \brief Token copy constructor.
 *
 *  Copy type and text from the argument.
 *
 *  \param[in] token Object to copy data from.
 */
Token::Token(const Token& token) : text_(token.text_), type_(token.type_) {}

/**
 *  Token destructor.
 */
Token::~Token() {}

/**
 *  \brief Overload of the assignment operator.
 *
 *  Copy type and text from the argument.
 *
 *  \param[in] token Object to copy data from.
 *
 *  \return *this
 */
Token& Token::operator=(const Token& token)
{
  this->text_ = token.text_;
  this->type_ = token.type_;
  return (*this);
}

/**
 *  Get the text associated with the token (if necessary).
 *
 *  \return The text associated with the token.
 */
const std::string& Token::GetText() const throw ()
{
  return (this->text_);
}

/**
 *  Get the type of the token.
 *
 *  \return The type of the token.
 */
Token::Type Token::GetType() const throw ()
{
  return (this->type_);
}

/**
 *  Set the text associated with the token.
 *
 *  \param[in] text Text to associate to the token.
 */
void Token::SetText(const std::string& text)
{
  this->text_ = text;
  return ;
}

/**
 *  Set the type of the token.
 *
 *  \param[in] type The type of the token.
 */
void Token::SetType(Token::Type type) throw ()
{
  this->type_ = type;
  return ;
}

/******************************************************************************
*                                                                             *
*                                                                             *
*                                    Lexer                                    *
*                                                                             *
*                                                                             *
******************************************************************************/

/**************************************
*                                     *
*           Private Methods           *
*                                     *
**************************************/

/**
 *  \brief Lexer copy constructor.
 *
 *  A lexer is not copyable for the moment. Any attempt to use the copy
 *  constructor will result in a call to abort().
 *
 *  \param[in] lexer Unused.
 */
Lexer::Lexer(const Lexer& lexer)
{
  (void)lexer;
  assert(false);
  abort();
}

/**
 *  \brief Overload of the assignment operator.
 *
 *  A lexer is not copyable for the moment. Any attempt to use the assignment
 *  operator will result in a call to abort().
 *
 *  \param[in] lexer Unused.
 *
 *  \return *this
 */
Lexer& Lexer::operator=(const Lexer& lexer)
{
  (void)lexer;
  assert(false);
  abort();
  return (*this);
}

/**
 *  Get the next available byte in the byte stream.
 */
char Lexer::NextChar()
{
  char ret;

  // If buffer is empty, read more data.
  if (this->current_ >= this->size_)
    {
      this->current_ = 0;
      // Read data from the byte stream.
      this->size_ = this->stream_->Receive(this->buffer_,
                                           sizeof(this->buffer_) - 1);
      // Read failed, stream is probably closed.
      if (!this->size_)
        ret = '\0';
      else
        {
          this->buffer_[this->size_] = '\0';
          ret = this->NextChar();
        }
    }
  // If buffer is not empty, extract the next available character.
  else
    ret = this->buffer_[this->current_++];
  return (ret);
}

/**
 *  Push a character back to the buffer.
 */
void Lexer::UngetChar(char c) throw ()
{
  this->buffer_[--this->current_] = c;
  return ;
}

/**************************************
*                                     *
*           Public Methods            *
*                                     *
**************************************/

/**
 *  \brief Lexer constructor.
 *
 *  Build a lexer by giving it the data stream on which it will extract tokens.
 *  Once successfully constructed, the lexer will own the stream (ie. it will
 *  handle its destruction).
 *
 *  \param[in] stream Byte stream from which tokens will be generated.
 */
Lexer::Lexer(IO::Stream* stream) : current_(0), size_(0), stream_(stream) {}

/**
 *  Lexer destructor.
 */
Lexer::~Lexer() {}

/**
 *  \brief Destroy the most recently saved context.
 *
 *  The Lexer class holds internally a context list that can be expanded by
 *  ContextSave(). If one want to destroy the most recently saved one, it has
 *  to call ContextPop().
 */
void Lexer::ContextPop()
{
  this->saves_.pop_front();
  return ;
}

/**
 *  \brief Restore the most recently saved context.
 *
 *  The Lexer class holds internally a context list that can be expanded by
 *  ContextSave(). If one want to restore a previous context (typically because
 *  the parsing failed on a particular rule and want to test alternatives), it
 *  has to call ContextRestore().
 */
void Lexer::ContextRestore()
{
  this->context_ = this->saves_.front();
  this->saves_.pop_front();
  return ;
}

/**
 *  \brief Save the current context.
 *
 *  The Lexer class can backup token contexts. This can be useful when parsing
 *  and an alternative of the syntax failed.
 */
void Lexer::ContextSave()
{
  this->saves_.push_front(this->context_);
  return ;
}

/**
 *  \brief Get the next token.
 *
 *  Extract the next token from the data stream.
 *
 *  \param[out] token Next token.
 *
 *  \return true if there is no more tokens.
 */
bool Lexer::GetToken(Token& token)
{
  bool ret;

  // If the context is not empty, some token has already been parsed.
  if (!this->context_.empty())
    {
      token = this->context_.front();
      this->context_.pop_front();
      ret = false;
    }
  // Else we got to extract a token from the byte stream.
  else
    {
      char c;

      // Skip all whitespaces.
      for (c = this->NextChar(); c && isspace(c); c = this->NextChar())
        ;

      // We reached the end of the byte stream.
      if (!c)
        {
          token.SetType(Token::END);
          ret = true;
        }
      // There's some tokenizable data.
      else
        {
          ret = false;
          switch (c)
            {
             case '=':
              token.SetType(Token::ASSIGNMENT);
              break ;
             case '{':
              token.SetType(Token::BLOCK_START);
              break ;
             case '}':
              token.SetType(Token::BLOCK_END);
              break ;
             case '#': // Comment
              // Skip until the next line
              do
                {
                  c = this->NextChar();
                } while (c && c != '\n');
              if (!c)
                {
                  token.SetType(Token::END);
                  ret = true;
                }
              // If we haven't reach the end of the file,
              // retrieve the next token
              else
                ret = this->GetToken(token);
              break ;
             case '|':
              token.SetType(Token::PIPE);
              break ;
             default:
              {
                std::string str;

                // Standard string, extract it until we reach a separator.
                do
                  {
                    str.push_back(c);
                    c = this->NextChar();
                  } while (c && (isalnum(c)
                                 || ('.' == c)
                                 || ('-' == c)
                                 || ('_' == c)
                                 || ('/' == c)));
                this->UngetChar(c);
                token.SetText(str);
                token.SetType(Token::STRING);
              }
            }
          // Insert token in all saved contexts.
          if (!ret)
            for (std::list<std::list<Token> >::iterator it =
                   this->saves_.begin();
                 it != this->saves_.end();
                 ++it)
              (*it).push_back(token);
        }
    }
  return (ret);
}
<commit_msg>Configuration file fields can now be escaped using double quotes.<commit_after>/*
**  Copyright 2009 MERETHIS
**  This file is part of CentreonBroker.
**
**  CentreonBroker is free software: you can redistribute it and/or modify it
**  under the terms of the GNU General Public License as published by the Free
**  Software Foundation, either version 2 of the License, or (at your option)
**  any later version.
**
**  CentreonBroker 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 General Public License
**  for more details.
**
**  You should have received a copy of the GNU General Public License along
**  with CentreonBroker.  If not, see <http://www.gnu.org/licenses/>.
**
**  For more information : contact@centreon.com
*/

#include <cassert>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include "conf/lexer.h"

using namespace CentreonBroker::Conf;

/******************************************************************************
*                                                                             *
*                                                                             *
*                                    Token                                    *
*                                                                             *
*                                                                             *
******************************************************************************/

/**************************************
*                                     *
*           Public Methods            *
*                                     *
**************************************/

/**
 *  \brief Token default constructor.
 *
 *  The resulting token will be undefined ; text will be empty and type UKNOWN.
 */
Token::Token() : type_(UNKNOWN) {}

/**
 *  \brief Token copy constructor.
 *
 *  Copy type and text from the argument.
 *
 *  \param[in] token Object to copy data from.
 */
Token::Token(const Token& token) : text_(token.text_), type_(token.type_) {}

/**
 *  Token destructor.
 */
Token::~Token() {}

/**
 *  \brief Overload of the assignment operator.
 *
 *  Copy type and text from the argument.
 *
 *  \param[in] token Object to copy data from.
 *
 *  \return *this
 */
Token& Token::operator=(const Token& token)
{
  this->text_ = token.text_;
  this->type_ = token.type_;
  return (*this);
}

/**
 *  Get the text associated with the token (if necessary).
 *
 *  \return The text associated with the token.
 */
const std::string& Token::GetText() const throw ()
{
  return (this->text_);
}

/**
 *  Get the type of the token.
 *
 *  \return The type of the token.
 */
Token::Type Token::GetType() const throw ()
{
  return (this->type_);
}

/**
 *  Set the text associated with the token.
 *
 *  \param[in] text Text to associate to the token.
 */
void Token::SetText(const std::string& text)
{
  this->text_ = text;
  return ;
}

/**
 *  Set the type of the token.
 *
 *  \param[in] type The type of the token.
 */
void Token::SetType(Token::Type type) throw ()
{
  this->type_ = type;
  return ;
}

/******************************************************************************
*                                                                             *
*                                                                             *
*                                    Lexer                                    *
*                                                                             *
*                                                                             *
******************************************************************************/

/**************************************
*                                     *
*           Private Methods           *
*                                     *
**************************************/

/**
 *  \brief Lexer copy constructor.
 *
 *  A lexer is not copyable for the moment. Any attempt to use the copy
 *  constructor will result in a call to abort().
 *
 *  \param[in] lexer Unused.
 */
Lexer::Lexer(const Lexer& lexer)
{
  (void)lexer;
  assert(false);
  abort();
}

/**
 *  \brief Overload of the assignment operator.
 *
 *  A lexer is not copyable for the moment. Any attempt to use the assignment
 *  operator will result in a call to abort().
 *
 *  \param[in] lexer Unused.
 *
 *  \return *this
 */
Lexer& Lexer::operator=(const Lexer& lexer)
{
  (void)lexer;
  assert(false);
  abort();
  return (*this);
}

/**
 *  Get the next available byte in the byte stream.
 */
char Lexer::NextChar()
{
  char ret;

  // If buffer is empty, read more data.
  if (this->current_ >= this->size_)
    {
      this->current_ = 0;
      // Read data from the byte stream.
      this->size_ = this->stream_->Receive(this->buffer_,
                                           sizeof(this->buffer_) - 1);
      // Read failed, stream is probably closed.
      if (!this->size_)
        ret = '\0';
      else
        {
          this->buffer_[this->size_] = '\0';
          ret = this->NextChar();
        }
    }
  // If buffer is not empty, extract the next available character.
  else
    ret = this->buffer_[this->current_++];
  return (ret);
}

/**
 *  Push a character back to the buffer.
 */
void Lexer::UngetChar(char c) throw ()
{
  this->buffer_[--this->current_] = c;
  return ;
}

/**************************************
*                                     *
*           Public Methods            *
*                                     *
**************************************/

/**
 *  \brief Lexer constructor.
 *
 *  Build a lexer by giving it the data stream on which it will extract tokens.
 *  Once successfully constructed, the lexer will own the stream (ie. it will
 *  handle its destruction).
 *
 *  \param[in] stream Byte stream from which tokens will be generated.
 */
Lexer::Lexer(IO::Stream* stream) : current_(0), size_(0), stream_(stream) {}

/**
 *  Lexer destructor.
 */
Lexer::~Lexer() {}

/**
 *  \brief Destroy the most recently saved context.
 *
 *  The Lexer class holds internally a context list that can be expanded by
 *  ContextSave(). If one want to destroy the most recently saved one, it has
 *  to call ContextPop().
 */
void Lexer::ContextPop()
{
  this->saves_.pop_front();
  return ;
}

/**
 *  \brief Restore the most recently saved context.
 *
 *  The Lexer class holds internally a context list that can be expanded by
 *  ContextSave(). If one want to restore a previous context (typically because
 *  the parsing failed on a particular rule and want to test alternatives), it
 *  has to call ContextRestore().
 */
void Lexer::ContextRestore()
{
  this->context_ = this->saves_.front();
  this->saves_.pop_front();
  return ;
}

/**
 *  \brief Save the current context.
 *
 *  The Lexer class can backup token contexts. This can be useful when parsing
 *  and an alternative of the syntax failed.
 */
void Lexer::ContextSave()
{
  this->saves_.push_front(this->context_);
  return ;
}

/**
 *  \brief Get the next token.
 *
 *  Extract the next token from the data stream.
 *
 *  \param[out] token Next token.
 *
 *  \return true if there is no more tokens.
 */
bool Lexer::GetToken(Token& token)
{
  bool ret;

  // If the context is not empty, some token has already been parsed.
  if (!this->context_.empty())
    {
      token = this->context_.front();
      this->context_.pop_front();
      ret = false;
    }
  // Else we got to extract a token from the byte stream.
  else
    {
      char c;

      // Skip all whitespaces.
      for (c = this->NextChar(); c && isspace(c); c = this->NextChar())
        ;

      // We reached the end of the byte stream.
      if (!c)
        {
          token.SetType(Token::END);
          ret = true;
        }
      // There's some tokenizable data.
      else
        {
          ret = false;
          switch (c)
            {
             case '=':
              token.SetType(Token::ASSIGNMENT);
              break ;
             case '{':
              token.SetType(Token::BLOCK_START);
              break ;
             case '}':
              token.SetType(Token::BLOCK_END);
              break ;
             case '#': // Comment
              // Skip until the next line
              do
                {
                  c = this->NextChar();
                } while (c && c != '\n');
              if (!c)
                {
                  token.SetType(Token::END);
                  ret = true;
                }
              // If we haven't reach the end of the file,
              // retrieve the next token
              else
                ret = this->GetToken(token);
              break ;
             case '|':
              token.SetType(Token::PIPE);
              break ;
             case '"':
              {
                std::string str;

                for (c = this->NextChar(); c && c != '"'; c = this->NextChar())
                  str.push_back(c);
                token.SetText(str);
                token.SetType(Token::STRING);
              }
              break ;
             default:
              {
                std::string str;

                // Standard string, extract it until we reach a separator.
                do
                  {
                    str.push_back(c);
                    c = this->NextChar();
                  } while (c && (isalnum(c)
                                 || ('.' == c)
                                 || ('-' == c)
                                 || ('_' == c)
                                 || ('/' == c)));
                this->UngetChar(c);
                token.SetText(str);
                token.SetType(Token::STRING);
              }
            }
          // Insert token in all saved contexts.
          if (!ret)
            for (std::list<std::list<Token> >::iterator it =
                   this->saves_.begin();
                 it != this->saves_.end();
                 ++it)
              (*it).push_back(token);
        }
    }
  return (ret);
}
<|endoftext|>