<commit_before>#include "./object.hpp"

#include <iostream>

using namespace json;

regex ObjectVector::tokenRgx = regex ("^(?:\\.)?(?:\\[)?(\\d+)(:?\\])?(:?\\.)?");
regex ObjectMap::tokenRgx = regex    ("^(?:\\.)?(?:\\[')?(\\w+)(:?'\\])?(:?\\.)?");

void AbstractObject::txtIndent(string &txt, int indentLvl) {
  for (int i = 0; i < indentLvl; i++)
    txt.append(INDENT);
}

AbstractObject* AbstractObject::copy (const AbstractObject* obj) {
  switch (obj->getType()) {
    case BOOL:
      return new ObjectFinalBool (*((ObjectFinalBool*)obj));
      break;
    case NUMBER_FLOAT:
      return new ObjectFinalNumberFloat (*((ObjectFinalNumberFloat*)obj));
      break;
    case NUMBER_INT:
      return new ObjectFinalNumberInt (*((ObjectFinalNumberInt*)obj));
      break;
    case STRING:
      return new ObjectFinalString (*((ObjectFinalString*)obj));
      break;
    case MAP:
      return new ObjectMap (*((ObjectMap*)obj));
      break;
    case VECTOR:
      return new ObjectVector (*((ObjectVector*)obj));
      break;
    }
}

ObjectFinalBool::ObjectFinalBool (const ObjectFinalBool &obj) :
  ObjectFinal (BOOL),
  boolean(obj.getContent())
{}

ObjectFinalNumberFloat::ObjectFinalNumberFloat (const ObjectFinalNumberFloat &obj) :
  ObjectFinal (NUMBER_FLOAT),
  number(obj.getContent())
{}

ObjectFinalNumberInt::ObjectFinalNumberInt (const ObjectFinalNumberInt &obj) :
  ObjectFinal (NUMBER_INT),
  number(obj.getContent())
{}

ObjectFinalString::ObjectFinalString (const ObjectFinalString &obj) :
  ObjectFinal (STRING),
  text(obj.getContent())
{}

ObjectMap::ObjectMap (const ObjectMap &obj) :
  ObjectContainer (MAP),
  keys(obj.getKeys())
  {
    for (string key : keys) {
      hash[key] = copy (obj.getContent().at(key));
    }
  }

ObjectVector::ObjectVector (const ObjectVector &obj) :
  ObjectContainer (VECTOR),
  array(obj.size())
  {
    for (int i = 0; i < obj.size(); i++){
      array[i] = copy (obj.getContent()[i]);
    }
  }

void ObjectFinalBool::replaceValue (string value) {
  if (value == "true")
    boolean = true;
  else
    boolean = false;
}

void ObjectFinalNumberFloat::replaceValue (string value) {
  number = stod (value);
}

void ObjectFinalNumberInt::replaceValue (string value) {
  number = atol (value.c_str());
}

void ObjectFinalString::replaceValue (string value) {
  text = value;
}

AbstractObject* ObjectVector::operator[] (unsigned index) {
  if (index >= array.size()) // changed to unsigned
    return nullptr;
  else
    return array [index];
}

AbstractObject* ObjectMap::operator[](string key) {
  if (hash.count (key))
    return hash[key];
  else
    return nullptr;
}

bool ObjectVector::insert (string key, AbstractObject* obj) {
  if (key == "") {
    array.push_back (obj);
    return true;
  } else {
    delete obj;
    return false;
  }
}

bool ObjectMap::insert (string key, AbstractObject* obj) {
  if (key != "" && !hash.count (key)) {
    keys.push_back (key);
    hash.insert(pair <string, AbstractObject*> (key, obj));
    return true;
  } else {
    delete obj;
    return false;
  }
}

bool ObjectMap::replace (string key, AbstractObject* obj) {
  if (!hash.count (key))
    return false;
  keys.erase(std::remove(keys.begin(), keys.end(), key), keys.end());
  hash.erase (key);
  return insert (key, obj);
}

bool ObjectMap::set (string key, AbstractObject* obj) {
  if (!hash.count (key))
    return insert (key, obj);
  else
    return replace (key, obj);
}

AbstractObject* ObjectVector::get (string path) {
  smatch matcher;
  if (path.size() == 0)
    return this;
  if (regex_search (path, matcher, tokenRgx)) {
    AbstractObject* son = operator[](stoi (matcher[1]));
    path = path.substr(matcher[0].length(), path.size());
    if (son != nullptr) {
      return son->get (path);
    }
  }
  return nullptr;
}

AbstractObject* ObjectMap::get (string path) {
  smatch matcher;
  if (path.empty() || path == ".")
    return this;
  if (regex_search (path, matcher, tokenRgx)) {
    AbstractObject* son = operator[](matcher[1]);
    path = path.substr(matcher[0].length(), path.size());
    if (son != nullptr)
      return son->get (path);
  }
  return nullptr;
}

AbstractObject* ObjectFinal::get (string path) {
  if (path.size () == 0)
    return this;
  else
    return nullptr;
}

bool ObjectMap::remove (string key) {
  AbstractObject* aux = operator[](key);
  if (aux == nullptr)
    return true;
  keys.erase(std::remove(keys.begin(), keys.end(), key), keys.end());
  hash.erase (key);
  delete aux;
  return true;
}

bool ObjectMap::erase (string key) {
  if (!hash.count (key))
    return false;
  else
    return remove (key);
}

bool ObjectMap::add (string path, AbstractObject* obj) {
  smatch matcher;
  if (regex_search (path, matcher, tokenRgx)) {
    AbstractObject* son = operator[](matcher[1]);
    string newPath = path.substr(matcher[0].length(), path.size());
    if (son != nullptr) {
      if (!newPath.empty() || son->getType() == VECTOR) {
        return son->add (newPath, obj);
      } else {
        delete obj;
        return false;
      }
    } else {
      if (!newPath.empty()) {
        son = new ObjectMap();
        if (insert (matcher[1], son))
          return son->add (newPath, obj);
        else
          return false;
      } else {
        return insert (path, obj);
      }
    }
  }
  delete obj;
  return false;
}

bool ObjectVector::add (string path, AbstractObject* obj) {
  smatch matcher;
  AbstractObject* son;
  if (regex_search (path, matcher, tokenRgx)) {
    son = operator[](stoi (matcher[1]));
    string newPath = path.substr(matcher[0].length(), path.size());
    if (son != nullptr) {
      return son->add (newPath, obj);
    } else {
      delete obj;
      return false;
    }
  } else if (path.empty()) {
    return insert ("", obj);
  } else {
    son = new ObjectMap();
    if(insert ("", son))
      return son->add (path, obj);
    else
      return false;
  }
  delete obj;
  return false;
}

bool ObjectFinal::add (string path, AbstractObject* obj) {
  delete obj;
  return false;
}

void ObjectVector::toTxt (string& txt, int indentLvl) {
  txt.append("[");
  indentLvl++;
  AbstractObject* obj = this;
  for (int i = 0; i < size(); i++) {
    if (obj->getType() != NUMBER_INT) {
      txt.append(END_LINE);
      txtIndent (txt, indentLvl);
    } else {
      txt.append(" ");
    }
    obj = operator[](i);
    obj->toTxt(txt, indentLvl);
    txt.append(COMMA);
  }
  if (size() > 0)
    txt.pop_back();
  indentLvl--;
  txt.append(END_LINE);
  txtIndent (txt, indentLvl);
  txt.append("]");
}

void ObjectMap::toTxt (string& txt, int indentLvl) {
  txt.append("{");
  indentLvl++;
  for (int i = 0; i < getKeys().size(); i++) {
    txt.append(END_LINE);
    txtIndent (txt, indentLvl);
    txt.append(QUOTE).append(getKeys()[i]).append(QUOTE).append(POINTS);
    operator[](getKeys()[i])->toTxt(txt, indentLvl);
    txt.append(COMMA);
  }
  if (getKeys().size() > 0)
    txt.pop_back();
  indentLvl--;
  txt.append(END_LINE);
  txtIndent (txt, indentLvl);
  txt.append("}");
}

void ObjectFinalBool::toTxt (string& txt, int indentLvl) {
  if (getContent())
    txt.append("true");
  else
    txt.append("false");
}

void ObjectFinalNumberFloat::toTxt (string& txt, int indentLvl) {
  txt.append(to_string(getContent()));
}

void ObjectFinalNumberInt::toTxt (string& txt, int indentLvl) {
  txt.append(to_string(getContent()));
}

void ObjectFinalString::toTxt (string& txt, int indentLvl) {
  txt.append(QUOTE).append(getContent()).append(QUOTE);
}

ObjectVector::~ObjectVector () {
  for (auto element : array)
    delete element;
  array.clear();
}

ObjectMap::~ObjectMap () {
  for (auto element : hash)
    delete element.second;
  hash.clear();
}

ObjectFinalBool::~ObjectFinalBool () {

}

ObjectFinalNumberFloat::~ObjectFinalNumberFloat () {

}

ObjectFinalNumberInt::~ObjectFinalNumberInt () {

}

ObjectFinalString::~ObjectFinalString () {

}

AbstractObject::~AbstractObject() {}

ObjectContainer::~ObjectContainer() {}

ObjectFinal::~ObjectFinal() {}

<commit_msg>Regex bug fixed<commit_after>#include "./object.hpp"

#include <iostream>

using namespace json;

regex ObjectVector::tokenRgx = regex ("^(?:\\.)?(?:\\[)?(\\d+)(?:\\])?(?:\\.)?");
regex ObjectMap::tokenRgx = regex    ("^(?:\\.)?(?:\\[')?(\\w+)(?:'\\])?(?:\\.)?");

void AbstractObject::txtIndent(string &txt, int indentLvl) {
  for (int i = 0; i < indentLvl; i++)
    txt.append(INDENT);
}

AbstractObject* AbstractObject::copy (const AbstractObject* obj) {
  switch (obj->getType()) {
    case BOOL:
      return new ObjectFinalBool (*((ObjectFinalBool*)obj));
      break;
    case NUMBER_FLOAT:
      return new ObjectFinalNumberFloat (*((ObjectFinalNumberFloat*)obj));
      break;
    case NUMBER_INT:
      return new ObjectFinalNumberInt (*((ObjectFinalNumberInt*)obj));
      break;
    case STRING:
      return new ObjectFinalString (*((ObjectFinalString*)obj));
      break;
    case MAP:
      return new ObjectMap (*((ObjectMap*)obj));
      break;
    case VECTOR:
      return new ObjectVector (*((ObjectVector*)obj));
      break;
    }
}

ObjectFinalBool::ObjectFinalBool (const ObjectFinalBool &obj) :
  ObjectFinal (BOOL),
  boolean(obj.getContent())
{}

ObjectFinalNumberFloat::ObjectFinalNumberFloat (const ObjectFinalNumberFloat &obj) :
  ObjectFinal (NUMBER_FLOAT),
  number(obj.getContent())
{}

ObjectFinalNumberInt::ObjectFinalNumberInt (const ObjectFinalNumberInt &obj) :
  ObjectFinal (NUMBER_INT),
  number(obj.getContent())
{}

ObjectFinalString::ObjectFinalString (const ObjectFinalString &obj) :
  ObjectFinal (STRING),
  text(obj.getContent())
{}

ObjectMap::ObjectMap (const ObjectMap &obj) :
  ObjectContainer (MAP),
  keys(obj.getKeys())
  {
    for (string key : keys) {
      hash[key] = copy (obj.getContent().at(key));
    }
  }

ObjectVector::ObjectVector (const ObjectVector &obj) :
  ObjectContainer (VECTOR),
  array(obj.size())
  {
    for (int i = 0; i < obj.size(); i++){
      array[i] = copy (obj.getContent()[i]);
    }
  }

void ObjectFinalBool::replaceValue (string value) {
  if (value == "true")
    boolean = true;
  else
    boolean = false;
}

void ObjectFinalNumberFloat::replaceValue (string value) {
  number = stod (value);
}

void ObjectFinalNumberInt::replaceValue (string value) {
  number = atol (value.c_str());
}

void ObjectFinalString::replaceValue (string value) {
  text = value;
}

AbstractObject* ObjectVector::operator[] (unsigned index) {
  if (index >= array.size()) // changed to unsigned
    return nullptr;
  else
    return array [index];
}

AbstractObject* ObjectMap::operator[](string key) {
  if (hash.count (key))
    return hash[key];
  else
    return nullptr;
}

bool ObjectVector::insert (string key, AbstractObject* obj) {
  if (key == "") {
    array.push_back (obj);
    return true;
  } else {
    delete obj;
    return false;
  }
}

bool ObjectMap::insert (string key, AbstractObject* obj) {
  if (key != "" && !hash.count (key)) {
    keys.push_back (key);
    hash.insert(pair <string, AbstractObject*> (key, obj));
    return true;
  } else {
    delete obj;
    return false;
  }
}

bool ObjectMap::replace (string key, AbstractObject* obj) {
  if (!hash.count (key))
    return false;
  keys.erase(std::remove(keys.begin(), keys.end(), key), keys.end());
  hash.erase (key);
  return insert (key, obj);
}

bool ObjectMap::set (string key, AbstractObject* obj) {
  if (!hash.count (key))
    return insert (key, obj);
  else
    return replace (key, obj);
}

AbstractObject* ObjectVector::get (string path) {
  smatch matcher;
  if (path.size() == 0)
    return this;
  if (regex_search (path, matcher, tokenRgx)) {
    AbstractObject* son = operator[](stoi (matcher[1]));
    path = path.substr(matcher[0].length(), path.size());
    if (son != nullptr) {
      return son->get (path);
    }
  }
  return nullptr;
}

AbstractObject* ObjectMap::get (string path) {
  smatch matcher;
  if (path.empty() || path == ".")
    return this;
  if (regex_search (path, matcher, tokenRgx)) {
    AbstractObject* son = operator[](matcher[1]);
    path = path.substr(matcher[0].length(), path.size());
    if (son != nullptr)
      return son->get (path);
  }
  return nullptr;
}

AbstractObject* ObjectFinal::get (string path) {
  if (path.size () == 0)
    return this;
  else
    return nullptr;
}

bool ObjectMap::remove (string key) {
  AbstractObject* aux = operator[](key);
  if (aux == nullptr)
    return true;
  keys.erase(std::remove(keys.begin(), keys.end(), key), keys.end());
  hash.erase (key);
  delete aux;
  return true;
}

bool ObjectMap::erase (string key) {
  if (!hash.count (key))
    return false;
  else
    return remove (key);
}

bool ObjectMap::add (string path, AbstractObject* obj) {
  smatch matcher;
  if (regex_search (path, matcher, tokenRgx)) {
    AbstractObject* son = operator[](matcher[1]);
    string newPath = path.substr(matcher[0].length(), path.size());
    if (son != nullptr) {
      if (!newPath.empty() || son->getType() == VECTOR) {
        return son->add (newPath, obj);
      } else {
        delete obj;
        return false;
      }
    } else {
      if (!newPath.empty()) {
        son = new ObjectMap();
        if (insert (matcher[1], son))
          return son->add (newPath, obj);
        else
          return false;
      } else {
        return insert (path, obj);
      }
    }
  }
  delete obj;
  return false;
}

bool ObjectVector::add (string path, AbstractObject* obj) {
  smatch matcher;
  AbstractObject* son;
  if (regex_search (path, matcher, tokenRgx)) {
    son = operator[](stoi (matcher[1]));
    string newPath = path.substr(matcher[0].length(), path.size());
    if (son != nullptr) {
      return son->add (newPath, obj);
    } else {
      delete obj;
      return false;
    }
  } else if (path.empty()) {
    return insert ("", obj);
  } else {
    son = new ObjectMap();
    if(insert ("", son))
      return son->add (path, obj);
    else
      return false;
  }
  delete obj;
  return false;
}

bool ObjectFinal::add (string path, AbstractObject* obj) {
  delete obj;
  return false;
}

void ObjectVector::toTxt (string& txt, int indentLvl) {
  txt.append("[");
  indentLvl++;
  AbstractObject* obj = this;
  for (int i = 0; i < size(); i++) {
    if (obj->getType() != NUMBER_INT) {
      txt.append(END_LINE);
      txtIndent (txt, indentLvl);
    } else {
      txt.append(" ");
    }
    obj = operator[](i);
    obj->toTxt(txt, indentLvl);
    txt.append(COMMA);
  }
  if (size() > 0)
    txt.pop_back();
  indentLvl--;
  txt.append(END_LINE);
  txtIndent (txt, indentLvl);
  txt.append("]");
}

void ObjectMap::toTxt (string& txt, int indentLvl) {
  txt.append("{");
  indentLvl++;
  for (int i = 0; i < getKeys().size(); i++) {
    txt.append(END_LINE);
    txtIndent (txt, indentLvl);
    txt.append(QUOTE).append(getKeys()[i]).append(QUOTE).append(POINTS);
    operator[](getKeys()[i])->toTxt(txt, indentLvl);
    txt.append(COMMA);
  }
  if (getKeys().size() > 0)
    txt.pop_back();
  indentLvl--;
  txt.append(END_LINE);
  txtIndent (txt, indentLvl);
  txt.append("}");
}

void ObjectFinalBool::toTxt (string& txt, int indentLvl) {
  if (getContent())
    txt.append("true");
  else
    txt.append("false");
}

void ObjectFinalNumberFloat::toTxt (string& txt, int indentLvl) {
  txt.append(to_string(getContent()));
}

void ObjectFinalNumberInt::toTxt (string& txt, int indentLvl) {
  txt.append(to_string(getContent()));
}

void ObjectFinalString::toTxt (string& txt, int indentLvl) {
  txt.append(QUOTE).append(getContent()).append(QUOTE);
}

ObjectVector::~ObjectVector () {
  for (auto element : array)
    delete element;
  array.clear();
}

ObjectMap::~ObjectMap () {
  for (auto element : hash)
    delete element.second;
  hash.clear();
}

ObjectFinalBool::~ObjectFinalBool () {

}

ObjectFinalNumberFloat::~ObjectFinalNumberFloat () {

}

ObjectFinalNumberInt::~ObjectFinalNumberInt () {

}

ObjectFinalString::~ObjectFinalString () {

}

AbstractObject::~AbstractObject() {}

ObjectContainer::~ObjectContainer() {}

ObjectFinal::~ObjectFinal() {}

<|endoftext|>


<commit_before>/* Copyright 2019 Axel Huebl, Benjamin Worpitz, Matthias Werner, René Widera
 *
 * This file is part of alpaka.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <alpaka/block/shared/dyn/Traits.hpp>

#include <alpaka/test/acc/TestAccs.hpp>
#include <alpaka/test/queue/Queue.hpp>
#include <alpaka/test/KernelExecutionFixture.hpp>

#include <catch2/catch.hpp>

//#############################################################################
class BlockSharedMemDynTestKernel
{
public:
    //-----------------------------------------------------------------------------
    ALPAKA_NO_HOST_ACC_WARNING
    template<
        typename TAcc>
    ALPAKA_FN_ACC auto operator()(
        TAcc const & acc,
        bool * success) const
    -> void
    {
        // Assure that the pointer is non null.
        auto && a = alpaka::block::shared::dyn::getMem<std::uint32_t>(acc);
        ALPAKA_CHECK(*success, static_cast<std::uint32_t *>(nullptr) != a);

        // Each call should return the same pointer ...
        auto && b = alpaka::block::shared::dyn::getMem<std::uint32_t>(acc);
        ALPAKA_CHECK(*success, a == b);

        // ... even for different types.
        auto && c = alpaka::block::shared::dyn::getMem<float>(acc);
        ALPAKA_CHECK(*success, a == reinterpret_cast<std::uint32_t *>(c));
    }
};

namespace alpaka
{
    namespace kernel
    {
        namespace traits
        {
            //#############################################################################
            //! The trait for getting the size of the block shared dynamic memory for a kernel.
            template<
                typename TAcc>
            struct BlockSharedMemDynSizeBytes<
                BlockSharedMemDynTestKernel,
                TAcc>
            {
                //-----------------------------------------------------------------------------
                //! \return The size of the shared memory allocated for a block.
                template<
                    typename TVec>
                ALPAKA_FN_HOST_ACC static auto getBlockSharedMemDynSizeBytes(
                    BlockSharedMemDynTestKernel const & blockSharedMemDyn,
                    TVec const & blockThreadExtent,
                    TVec const & threadElemExtent,
                    bool * success)
                -> idx::Idx<TAcc>
                {
                    alpaka::ignore_unused(blockSharedMemDyn);
                    alpaka::ignore_unused(success);
                    return
                        static_cast<idx::Idx<TAcc>>(sizeof(std::uint32_t)) * blockThreadExtent.prod() * threadElemExtent.prod();
                }
            };
        }
    }
}

//-----------------------------------------------------------------------------
TEMPLATE_LIST_TEST_CASE( "sameNonNullAdress", "[blockSharedMemDyn]", alpaka::test::acc::TestAccs)
{
    using Acc = TestType;
    using Dim = alpaka::dim::Dim<Acc>;
    using Idx = alpaka::idx::Idx<Acc>;

    alpaka::test::KernelExecutionFixture<Acc> fixture(
        alpaka::vec::Vec<Dim, Idx>::ones());

    BlockSharedMemDynTestKernel kernel;

    REQUIRE(
        fixture(
            kernel));
}
<commit_msg>Get dynamic shared memory by raw pointer in tests<commit_after>/* Copyright 2019 Axel Huebl, Benjamin Worpitz, Matthias Werner, René Widera
 *
 * This file is part of alpaka.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <alpaka/block/shared/dyn/Traits.hpp>

#include <alpaka/test/acc/TestAccs.hpp>
#include <alpaka/test/queue/Queue.hpp>
#include <alpaka/test/KernelExecutionFixture.hpp>

#include <catch2/catch.hpp>

//#############################################################################
class BlockSharedMemDynTestKernel
{
public:
    //-----------------------------------------------------------------------------
    ALPAKA_NO_HOST_ACC_WARNING
    template<
        typename TAcc>
    ALPAKA_FN_ACC auto operator()(
        TAcc const & acc,
        bool * success) const
    -> void
    {
        // Assure that the pointer is non null.
        auto a = alpaka::block::shared::dyn::getMem<std::uint32_t>(acc);
        ALPAKA_CHECK(*success, static_cast<std::uint32_t *>(nullptr) != a);

        // Each call should return the same pointer ...
        auto b = alpaka::block::shared::dyn::getMem<std::uint32_t>(acc);
        ALPAKA_CHECK(*success, a == b);

        // ... even for different types.
        auto c = alpaka::block::shared::dyn::getMem<float>(acc);
        ALPAKA_CHECK(*success, a == reinterpret_cast<std::uint32_t *>(c));
    }
};

namespace alpaka
{
    namespace kernel
    {
        namespace traits
        {
            //#############################################################################
            //! The trait for getting the size of the block shared dynamic memory for a kernel.
            template<
                typename TAcc>
            struct BlockSharedMemDynSizeBytes<
                BlockSharedMemDynTestKernel,
                TAcc>
            {
                //-----------------------------------------------------------------------------
                //! \return The size of the shared memory allocated for a block.
                template<
                    typename TVec>
                ALPAKA_FN_HOST_ACC static auto getBlockSharedMemDynSizeBytes(
                    BlockSharedMemDynTestKernel const & blockSharedMemDyn,
                    TVec const & blockThreadExtent,
                    TVec const & threadElemExtent,
                    bool * success)
                -> idx::Idx<TAcc>
                {
                    alpaka::ignore_unused(blockSharedMemDyn);
                    alpaka::ignore_unused(success);
                    return
                        static_cast<idx::Idx<TAcc>>(sizeof(std::uint32_t)) * blockThreadExtent.prod() * threadElemExtent.prod();
                }
            };
        }
    }
}

//-----------------------------------------------------------------------------
TEMPLATE_LIST_TEST_CASE( "sameNonNullAdress", "[blockSharedMemDyn]", alpaka::test::acc::TestAccs)
{
    using Acc = TestType;
    using Dim = alpaka::dim::Dim<Acc>;
    using Idx = alpaka::idx::Idx<Acc>;

    alpaka::test::KernelExecutionFixture<Acc> fixture(
        alpaka::vec::Vec<Dim, Idx>::ones());

    BlockSharedMemDynTestKernel kernel;

    REQUIRE(
        fixture(
            kernel));
}
<|endoftext|>


<commit_before>/****************************************************************************
 * This file is part of Qt AccountsService Addon.
 *
 * Copyright (C) 2012-2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
 *
 * Author(s):
 *    Pier Luigi Fiorini
 *
 * $BEGIN_LICENSE:LGPL2.1+$
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $END_LICENSE$
 ***************************************************************************/

#include <QtCore/QDebug>

#include "accountsmanager.h"
#include "accountsmanager_p.h"

namespace QtAccountsService {

/*
 * AccountsManagerPrivate
 */

AccountsManagerPrivate::AccountsManagerPrivate(const QDBusConnection &bus)
{
    interface = new OrgFreedesktopAccountsInterface(
        QLatin1String("org.freedesktop.Accounts"),
        QLatin1String("/org/freedesktop/Accounts"),
        bus);
}

AccountsManagerPrivate::~AccountsManagerPrivate()
{
    delete interface;
}

void AccountsManagerPrivate::_q_userAdded(const QDBusObjectPath &path)
{
    Q_Q(AccountsManager);
    emit q->userAdded(new UserAccount(path.path()));
}

void AccountsManagerPrivate::_q_userDeleted(const QDBusObjectPath &path)
{
    Q_Q(AccountsManager);
    emit q->userDeleted(new UserAccount(path.path()));
}

/*!
    \class AccountsManager
    \brief AccountsManager provides the backend for user management.

    The AccountsManager class allows for querying and manipiulating user
    account information.

    \author Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
*/

/*!
    Constructs a AccountsManager object.
*/
AccountsManager::AccountsManager(const QDBusConnection &bus)
    : d_ptr(new AccountsManagerPrivate(bus))
{
    d_ptr->q_ptr = this;

    connect(d_ptr->interface, SIGNAL(UserAdded(QDBusObjectPath)),
            this, SLOT(_q_userAdded(QDBusObjectPath)));
    connect(d_ptr->interface, SIGNAL(UserDeleted(QDBusObjectPath)),
            this, SLOT(_q_userDeleted(QDBusObjectPath)));
}

/*!
    Destroy a AccountsManager object.
*/
AccountsManager::~AccountsManager()
{
    delete d_ptr;
}

/*!
    Caches a user account, so that it shows up in listCachedUsers() output.
    The user name may be a remote user, but the system must be able to lookup
    the user name and resolve the user information.

    \param userName The user name for the user.
*/
UserAccount *AccountsManager::cacheUser(const QString &userName)
{
    Q_D(AccountsManager);

    QDBusPendingReply<QDBusObjectPath> reply = d->interface->CacheUser(userName);
    reply.waitForFinished();

    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't cache user %s: %s",
                 userName.toUtf8().constData(),
                 error.errorString(error.type()).toUtf8().constData());
        return 0;
    }

    QDBusObjectPath path = reply.argumentAt<0>();
    return new UserAccount(path.path());
}

/*!
    Releases all metadata about a user account, including icon, language and session.
    If the user account is from a remote server and the user has never logged in
    before, then that account will no longer show up in listCachedUsers() output.

    \param userName The user name for the user.
*/
void AccountsManager::uncacheUser(const QString &userName)
{
    Q_D(AccountsManager);

    d->interface->UncacheUser(userName);
}

/*!
    Releases all metadata about a user account, including icon, language and session.
    If the user account is from a remote server and the user has never logged in
    before, then that account will no longer show up in listCachedUsers() output.

    \param account The account for the user.
*/
void AccountsManager::uncacheUser(UserAccount *account)
{
    return uncacheUser(account->userName());
}

/*!
    Returns a list of user accounts.

    \param systemUsers If true, returns also system users.
*/
UserAccountList AccountsManager::listCachedUsers()
{
    Q_D(AccountsManager);

    UserAccountList list;

    QDBusPendingReply< QList<QDBusObjectPath> > reply = d->interface->ListCachedUsers();
    reply.waitForFinished();

    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't list cached users: %s",
                 error.errorString(error.type()).toUtf8().constData());
        return list;
    }

    QList<QDBusObjectPath> value = reply.argumentAt<0>();
    for (int i = 0; i < value.size(); i++)
        list.append(new UserAccount(value.at(i).path()));

    return list;
}

/*!
    Finds a user by \a uid.

    \param uid The uid to look up.
    \return the corresponding UserAccount object.
*/
UserAccount *AccountsManager::findUserById(uid_t uid)
{
    Q_D(AccountsManager);

    QDBusPendingReply<QDBusObjectPath> reply = d->interface->FindUserById(uid);
    reply.waitForFinished();

    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't find user by uid %d: %s", uid,
                 error.errorString(error.type()).toUtf8().constData());
        return 0;
    }

    QDBusObjectPath path = reply.argumentAt<0>();
    return new UserAccount(path.path());
}

/*!
    Finds a user by user \a userName

    \param uid The user name to look up.
    \return the corresponding UserAccount object.
*/
UserAccount *AccountsManager::findUserByName(const QString &userName)
{
    Q_D(AccountsManager);

    QDBusPendingReply<QDBusObjectPath> reply = d->interface->FindUserByName(userName);
    reply.waitForFinished();

    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't find user by user name %s: %s",
                 userName.toUtf8().constData(),
                 error.errorString(error.type()).toUtf8().constData());
        return 0;
    }

    QDBusObjectPath path = reply.argumentAt<0>();
    return new UserAccount(path.path());
}

/*!
    Creates a new \a accountType type user account whose name is \a userName,
    real name is \a fullName.

    \param userName The name of the new user to be created.
    \param fullName First name and last name.
    \param accountType The account type.
    \return whether the user was created successfully.
*/
bool AccountsManager::createUser(const QString &userName,
                                 const QString &fullName,
                                 UserAccount::AccountType accountType)
{
    Q_D(AccountsManager);

    QDBusPendingReply<QDBusObjectPath> reply = d->interface->CreateUser(userName, fullName, accountType);
    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't create user %s: %s", userName.toUtf8().constData(),
                 error.errorString(error.type()).toUtf8().constData());
        return false;
    }

    return true;
}

/*!
    Deletes the user designated by \a uid.

    \param uid The user identifier.
    \param removeFiles If true all files owned by the user will be removed.
    \return whether the user was deleted successfully.
*/
bool AccountsManager::deleteUser(uid_t uid, bool removeFiles)
{
    Q_D(AccountsManager);

    QDBusPendingReply<> reply = d->interface->DeleteUser(uid, removeFiles);
    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't delete user %d: %s", uid,
                 error.errorString(error.type()).toUtf8().constData());
        return false;
    }

    return true;
}

/*!
    Deletes the specified \a account.

    \param account The account to remove.
    \param removeFiles If true all files owned by the user will be removed.
    \return whether the user was deleted successfully.
*/
bool AccountsManager::deleteUser(UserAccount *account, bool removeFiles)
{
    return deleteUser(account->userId(), removeFiles);
}

}

#include "moc_accountsmanager.cpp"
<commit_msg>Return Q_NULLPTR when a user account has not been found<commit_after>/****************************************************************************
 * This file is part of Qt AccountsService Addon.
 *
 * Copyright (C) 2012-2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
 *
 * Author(s):
 *    Pier Luigi Fiorini
 *
 * $BEGIN_LICENSE:LGPL2.1+$
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $END_LICENSE$
 ***************************************************************************/

#include <QtCore/QDebug>

#include "accountsmanager.h"
#include "accountsmanager_p.h"

namespace QtAccountsService {

/*
 * AccountsManagerPrivate
 */

AccountsManagerPrivate::AccountsManagerPrivate(const QDBusConnection &bus)
{
    interface = new OrgFreedesktopAccountsInterface(
        QLatin1String("org.freedesktop.Accounts"),
        QLatin1String("/org/freedesktop/Accounts"),
        bus);
}

AccountsManagerPrivate::~AccountsManagerPrivate()
{
    delete interface;
}

void AccountsManagerPrivate::_q_userAdded(const QDBusObjectPath &path)
{
    Q_Q(AccountsManager);
    emit q->userAdded(new UserAccount(path.path()));
}

void AccountsManagerPrivate::_q_userDeleted(const QDBusObjectPath &path)
{
    Q_Q(AccountsManager);
    emit q->userDeleted(new UserAccount(path.path()));
}

/*!
    \class AccountsManager
    \brief AccountsManager provides the backend for user management.

    The AccountsManager class allows for querying and manipiulating user
    account information.

    \author Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
*/

/*!
    Constructs a AccountsManager object.
*/
AccountsManager::AccountsManager(const QDBusConnection &bus)
    : d_ptr(new AccountsManagerPrivate(bus))
{
    d_ptr->q_ptr = this;

    connect(d_ptr->interface, SIGNAL(UserAdded(QDBusObjectPath)),
            this, SLOT(_q_userAdded(QDBusObjectPath)));
    connect(d_ptr->interface, SIGNAL(UserDeleted(QDBusObjectPath)),
            this, SLOT(_q_userDeleted(QDBusObjectPath)));
}

/*!
    Destroy a AccountsManager object.
*/
AccountsManager::~AccountsManager()
{
    delete d_ptr;
}

/*!
    Caches a user account, so that it shows up in listCachedUsers() output.
    The user name may be a remote user, but the system must be able to lookup
    the user name and resolve the user information.

    \param userName The user name for the user.
*/
UserAccount *AccountsManager::cacheUser(const QString &userName)
{
    Q_D(AccountsManager);

    QDBusPendingReply<QDBusObjectPath> reply = d->interface->CacheUser(userName);
    reply.waitForFinished();

    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't cache user %s: %s",
                 userName.toUtf8().constData(),
                 error.errorString(error.type()).toUtf8().constData());
        return 0;
    }

    QDBusObjectPath path = reply.argumentAt<0>();
    if (path.path().isEmpty())
        return Q_NULLPTR;
    return new UserAccount(path.path());
}

/*!
    Releases all metadata about a user account, including icon, language and session.
    If the user account is from a remote server and the user has never logged in
    before, then that account will no longer show up in listCachedUsers() output.

    \param userName The user name for the user.
*/
void AccountsManager::uncacheUser(const QString &userName)
{
    Q_D(AccountsManager);

    d->interface->UncacheUser(userName);
}

/*!
    Releases all metadata about a user account, including icon, language and session.
    If the user account is from a remote server and the user has never logged in
    before, then that account will no longer show up in listCachedUsers() output.

    \param account The account for the user.
*/
void AccountsManager::uncacheUser(UserAccount *account)
{
    return uncacheUser(account->userName());
}

/*!
    Returns a list of user accounts.

    \param systemUsers If true, returns also system users.
*/
UserAccountList AccountsManager::listCachedUsers()
{
    Q_D(AccountsManager);

    UserAccountList list;

    QDBusPendingReply< QList<QDBusObjectPath> > reply = d->interface->ListCachedUsers();
    reply.waitForFinished();

    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't list cached users: %s",
                 error.errorString(error.type()).toUtf8().constData());
        return list;
    }

    QList<QDBusObjectPath> value = reply.argumentAt<0>();
    for (int i = 0; i < value.size(); i++)
        list.append(new UserAccount(value.at(i).path()));

    return list;
}

/*!
    Finds a user by \a uid.

    \param uid The uid to look up.
    \return the corresponding UserAccount object.
*/
UserAccount *AccountsManager::findUserById(uid_t uid)
{
    Q_D(AccountsManager);

    QDBusPendingReply<QDBusObjectPath> reply = d->interface->FindUserById(uid);
    reply.waitForFinished();

    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't find user by uid %d: %s", uid,
                 error.errorString(error.type()).toUtf8().constData());
        return 0;
    }

    QDBusObjectPath path = reply.argumentAt<0>();
    if (path.path().isEmpty())
        return Q_NULLPTR;
    return new UserAccount(path.path());
}

/*!
    Finds a user by user \a userName

    \param uid The user name to look up.
    \return the corresponding UserAccount object.
*/
UserAccount *AccountsManager::findUserByName(const QString &userName)
{
    Q_D(AccountsManager);

    QDBusPendingReply<QDBusObjectPath> reply = d->interface->FindUserByName(userName);
    reply.waitForFinished();

    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't find user by user name %s: %s",
                 userName.toUtf8().constData(),
                 error.errorString(error.type()).toUtf8().constData());
        return 0;
    }

    QDBusObjectPath path = reply.argumentAt<0>();
    if (path.path().isEmpty())
        return Q_NULLPTR;
    return new UserAccount(path.path());
}

/*!
    Creates a new \a accountType type user account whose name is \a userName,
    real name is \a fullName.

    \param userName The name of the new user to be created.
    \param fullName First name and last name.
    \param accountType The account type.
    \return whether the user was created successfully.
*/
bool AccountsManager::createUser(const QString &userName,
                                 const QString &fullName,
                                 UserAccount::AccountType accountType)
{
    Q_D(AccountsManager);

    QDBusPendingReply<QDBusObjectPath> reply = d->interface->CreateUser(userName, fullName, accountType);
    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't create user %s: %s", userName.toUtf8().constData(),
                 error.errorString(error.type()).toUtf8().constData());
        return false;
    }

    return true;
}

/*!
    Deletes the user designated by \a uid.

    \param uid The user identifier.
    \param removeFiles If true all files owned by the user will be removed.
    \return whether the user was deleted successfully.
*/
bool AccountsManager::deleteUser(uid_t uid, bool removeFiles)
{
    Q_D(AccountsManager);

    QDBusPendingReply<> reply = d->interface->DeleteUser(uid, removeFiles);
    if (reply.isError()) {
        QDBusError error = reply.error();
        qWarning("Couldn't delete user %d: %s", uid,
                 error.errorString(error.type()).toUtf8().constData());
        return false;
    }

    return true;
}

/*!
    Deletes the specified \a account.

    \param account The account to remove.
    \param removeFiles If true all files owned by the user will be removed.
    \return whether the user was deleted successfully.
*/
bool AccountsManager::deleteUser(UserAccount *account, bool removeFiles)
{
    return deleteUser(account->userId(), removeFiles);
}

}

#include "moc_accountsmanager.cpp"
<|endoftext|>


<commit_before>/*
** Copyright 2014-2015 The Genome Analysis Centre
**
** 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 <QHBoxLayout>
#include <QJsonDocument>
#include <QDebug>
#include <QAction>
#include <QMenu>

#include "json_viewer.h"
#include "math_utils.h"
#include "string_utils.h"
#include "json_util.h"
#include "streams.h"

Q_DECLARE_METATYPE (json_t *)

JSONViewer ::	JSONViewer (QWidget *parent_p)
: QWidget (parent_p)
{
	QHBoxLayout *layout_p = new QHBoxLayout;

	jv_tree_p = new QTreeWidget (this);
	jv_tree_p -> setContextMenuPolicy (Qt :: CustomContextMenu);
	connect (jv_tree_p, &QTreeWidget :: customContextMenuRequested, this, &JSONViewer :: PrepareMenu);

	layout_p -> addWidget (jv_tree_p);

	jv_viewer_p = new QTextEdit (this);
	jv_viewer_p -> setReadOnly (true);
	layout_p -> addWidget (jv_viewer_p);

	setLayout (layout_p);

	setWindowTitle ("JSON Viewer");
}

void JSONViewer :: PrepareMenu (const QPoint &pos_r)
{
	QTreeWidgetItem *item_p = jv_tree_p -> itemAt (pos_r);

	if (item_p)
		{
			QVariant v = item_p -> data (0, Qt :: DisplayRole);

			if (!v.isNull ())
				{
					json_t *json_p = v.value <json_t *> ();

					if (json_p)
						{
							QAction *run_action_p = new QAction (QIcon ("images/run"), tr ("&Run"), jv_tree_p);
							QString s ("Run ");

							s.append (item_p -> text (0));
							QByteArray ba = s.toLocal8Bit ();
							const char *value_s = ba.constData ();

							run_action_p -> setStatusTip (tr (value_s));

							QVariant var = QVariant :: fromValue (json_p);
							run_action_p -> setData (var);

							connect (run_action_p, &QAction :: triggered, this, &JSONViewer :: RunLinkedService);

							QMenu menu (this);
							menu.addAction (run_action_p);

							menu.exec (jv_tree_p -> mapToGlobal (pos_r));

							#if JSON_VIEWER_DEBUG >= STM_LEVEL_FINE
							PrintJSONToLog (STM_LEVEL_FINE, __FILE__, __LINE__, json_p, "service run json:\n");
							#endif

						}
				}

		}		/* if (item_p) */
}

void JSONViewer :: RunLinkedService (bool checked_flag)
{
	QAction *action_p = qobject_cast <QAction *> (sender ());

	if (action_p)
		{
			QVariant var = action_p -> data ();

			if (!var.isNull ())
				{
					json_t *json_p = var.value <json_t *> ();

					if (json_p)
						{
							emit RunServiceRequested (json_p);
						}
				}
		}

}

const char *JSONViewer :: GetText () const
{
	QString s = jv_viewer_p ->  toPlainText ();
	QByteArray ba = s.toLocal8Bit ();

	return ba.constData ();
}

QWidget *JSONViewer :: GetWidget ()
{
	return this;
}

QTreeWidgetItem *JSONViewer :: InsertData (QTreeWidgetItem *parent_p, const char *key_s, json_t *data_p)
{
	QTreeWidgetItem *child_node_p = 0;

	if (parent_p)
		{
			child_node_p = new QTreeWidgetItem (parent_p);

			int num_children = parent_p -> childCount ();
			parent_p -> insertChild (num_children, child_node_p);
		}
	else
		{
			child_node_p = new QTreeWidgetItem (jv_tree_p);

			jv_tree_p -> addTopLevelItem (child_node_p);
		}

	if (key_s)
		{
			QString s = child_node_p -> text (0);

			if (s.isEmpty() || s.isNull ())
				{
					child_node_p -> setText (0, key_s);
				}
		}

	if (json_is_object (data_p))
		{
			json_t *child_json_p;
			const char *child_key_s;

			json_object_foreach (data_p, child_key_s, child_json_p)
				{
					InsertData (child_node_p, child_key_s, child_json_p);
				}
		}
	else if (json_is_array (data_p))
		{
			json_t *child_json_p;
			size_t i;

			json_array_foreach (data_p, i, child_json_p)
				{
					char *value_s = ConvertIntegerToString (i);

					if (value_s)
						{
							InsertData (child_node_p, value_s, child_json_p);

							FreeCopiedString (value_s);
						}
				}
		}
	else if (json_is_string (data_p))
		{
			const char *value_s = json_string_value (data_p);

			child_node_p -> setText (1, value_s);
		}
	else if (json_is_integer (data_p))
		{
			json_int_t i = json_integer_value (data_p);
			char *value_s = ConvertIntegerToString (i);

			if (value_s)
				{
					child_node_p -> setText (1, value_s);
					FreeCopiedString (value_s);
				}
		}
	else if (json_is_real (data_p))
		{
			double d = json_real_value (data_p);
			char *value_s = ConvertDoubleToString (d);

			if (value_s)
				{
					child_node_p -> setText (1, value_s);
					FreeCopiedString (value_s);
				}
		}
	else if (json_is_true (data_p))
		{
			child_node_p -> setText (1, "true");
		}
	else if (json_is_false (data_p))
		{
			child_node_p -> setText (1, "false");
		}

	return child_node_p;
}

void JSONViewer :: SetJSONData (json_t *data_p)
{
	jv_data_p = data_p;

	jv_tree_p -> clear ();
	jv_viewer_p -> clear ();

	jv_tree_p -> setColumnCount (2);

	if (json_is_array (data_p))
		{
			size_t i;
			json_t *item_p;

			json_array_foreach (data_p, i, item_p)
				{
					char *value_s = ConvertIntegerToString (i);

					AddTopLevelNode (value_s, item_p);

					if (value_s)
						{
							FreeCopiedString (value_s);
						}
				}
		}
	else
		{
			AddTopLevelNode ("0", data_p);
		}

	char *value_s = json_dumps (data_p, 0);
	if (value_s)
		{
			jv_viewer_p -> setPlainText (value_s);
			free (value_s);
		}

	jv_tree_p -> repaint ();
}

void JSONViewer :: AddTopLevelNode (const char *key_s, json_t *data_p)
{
	QTreeWidgetItem *top_level_node_p = InsertData (NULL, key_s, data_p);

	if (top_level_node_p)
		{
			int i;
			QString services_name (LINKED_SERVICES_S);
			QTreeWidgetItem *services_node_p = 0;

			for (i = top_level_node_p -> childCount () - 1; i >= 0; -- i)
				{
					QTreeWidgetItem *child_node_p = top_level_node_p -> child (i);
					QString child_text = child_node_p -> text (0);

					if (services_name.compare (child_text) == 0)
						{
							services_node_p = child_node_p;
							i = -1;		/* force exit from loop */
						}

				}		/* for (i = top_level_node_p -> childCount (); i >= 0; -- i) */

			if (services_node_p)
				{
					json_t *services_json_p = json_object_get (data_p, LINKED_SERVICES_S);

					if (services_json_p)
						{
							if (json_is_array (services_json_p))
								{
									const size_t num_nodes = services_node_p -> childCount ();
									const size_t num_json_children = json_array_size (services_json_p);

									if (num_json_children == num_nodes)
										{
											for (i = 0; i < (int) num_nodes; ++ i)
												{
													QTreeWidgetItem *service_node_p = services_node_p -> child (i);
													json_t *service_json_p = json_array_get (services_json_p, i);

													const char *service_name_s = GetJSONString (service_json_p, SERVICE_NAME_S);
													QVariant var = QVariant :: fromValue (service_json_p);

													#if JSON_VIEWER_DEBUG >= STM_LEVEL_FINE
													PrintLog (STM_LEVEL_FINE, __FILE__, __LINE__, "service name \"%s\"", service_name_s);
													#endif

													/*
													 * Set the node name to the service that it will run
													 */

													service_node_p -> setIcon (0, QIcon ("images/list_use"));
													if (service_name_s)
														{
															service_node_p -> setText (0, service_name_s);
															service_node_p -> setToolTip (0, service_name_s);
														}

													service_node_p -> setData (0, Qt :: DisplayRole, var);

												}		/* for (i = top_level_node_p -> childCount (); i >= 0; -- i) */

										}

								}		/* if (json_is_array (services_json_p) */

						}		/* if (services_json_p) */

				}		/* if (services_node_p) */
		}

}

<commit_msg>fixed display of qtreewidgetitems for running linked services<commit_after>/*
** Copyright 2014-2015 The Genome Analysis Centre
**
** 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 <QHBoxLayout>
#include <QJsonDocument>
#include <QDebug>
#include <QAction>
#include <QMenu>

#include "json_viewer.h"
#include "math_utils.h"
#include "string_utils.h"
#include "json_util.h"
#include "streams.h"

#define JSON_VIEWER_DEBUG (STM_LEVEL_FINE)

Q_DECLARE_METATYPE (json_t *)

JSONViewer ::	JSONViewer (QWidget *parent_p)
: QWidget (parent_p)
{
	QHBoxLayout *layout_p = new QHBoxLayout;

	jv_tree_p = new QTreeWidget (this);
	jv_tree_p -> setContextMenuPolicy (Qt :: CustomContextMenu);
	connect (jv_tree_p, &QTreeWidget :: customContextMenuRequested, this, &JSONViewer :: PrepareMenu);

	layout_p -> addWidget (jv_tree_p);

	jv_viewer_p = new QTextEdit (this);
	jv_viewer_p -> setReadOnly (true);
	layout_p -> addWidget (jv_viewer_p);

	setLayout (layout_p);

	setWindowTitle ("JSON Viewer");
}

void JSONViewer :: PrepareMenu (const QPoint &pos_r)
{
	QTreeWidgetItem *item_p = jv_tree_p -> itemAt (pos_r);

	if (item_p)
		{
			QVariant v = item_p -> data (0, Qt :: UserRole);

			if (!v.isNull ())
				{
					json_t *json_p = v.value <json_t *> ();

					if (json_p)
						{
							QAction *run_action_p = new QAction (QIcon ("images/run"), tr ("&Run"), jv_tree_p);
							QString s ("Run ");

							s.append (item_p -> text (0));
							QByteArray ba = s.toLocal8Bit ();
							const char *value_s = ba.constData ();

							run_action_p -> setStatusTip (tr (value_s));

							QVariant var = QVariant :: fromValue (json_p);
							run_action_p -> setData (var);

							connect (run_action_p, &QAction :: triggered, this, &JSONViewer :: RunLinkedService);

							QMenu menu (this);
							menu.addAction (run_action_p);

							menu.exec (jv_tree_p -> mapToGlobal (pos_r));

							#if JSON_VIEWER_DEBUG >= STM_LEVEL_FINE
							PrintJSONToLog (STM_LEVEL_FINE, __FILE__, __LINE__, json_p, "service run json:\n");
							#endif

						}
				}

		}		/* if (item_p) */
}

void JSONViewer :: RunLinkedService (bool checked_flag)
{
	QAction *action_p = qobject_cast <QAction *> (sender ());

	if (action_p)
		{
			QVariant var = action_p -> data ();

			if (!var.isNull ())
				{
					json_t *json_p = var.value <json_t *> ();

					if (json_p)
						{
							emit RunServiceRequested (json_p);
						}
				}
		}

}

const char *JSONViewer :: GetText () const
{
	QString s = jv_viewer_p ->  toPlainText ();
	QByteArray ba = s.toLocal8Bit ();

	return ba.constData ();
}

QWidget *JSONViewer :: GetWidget ()
{
	return this;
}

QTreeWidgetItem *JSONViewer :: InsertData (QTreeWidgetItem *parent_p, const char *key_s, json_t *data_p)
{
	QTreeWidgetItem *child_node_p = 0;

	if (parent_p)
		{
			child_node_p = new QTreeWidgetItem (parent_p);

			parent_p -> addChild (child_node_p);
		}
	else
		{
			child_node_p = new QTreeWidgetItem (jv_tree_p);

			jv_tree_p -> addTopLevelItem (child_node_p);
		}

	if (key_s)
		{
			child_node_p -> setText (0, key_s);
		}

	if (json_is_object (data_p))
		{
			json_t *child_json_p;
			const char *child_key_s;

			json_object_foreach (data_p, child_key_s, child_json_p)
				{
					InsertData (child_node_p, child_key_s, child_json_p);
				}
		}
	else if (json_is_array (data_p))
		{
			json_t *child_json_p;
			size_t i;

			if (strcmp (key_s, LINKED_SERVICES_S) == 0)
				{
					json_array_foreach (data_p, i, child_json_p)
						{
							const char *service_name_s = GetJSONString (child_json_p, SERVICE_NAME_S);

							#if JSON_VIEWER_DEBUG >= STM_LEVEL_FINE
							qDebug () << "service name \"" << service_name_s << "\"" << endl;
							#endif

							QTreeWidgetItem *item_p = InsertData (child_node_p, service_name_s, child_json_p);

							if (item_p)
								{
									item_p -> setIcon (0, QIcon ("images/list_use"));

									if (service_name_s)
										{
											QString s ("Run ");
											s.append (service_name_s);

											item_p -> setText (0, s);
										}

									QVariant var = QVariant :: fromValue (child_json_p);
									item_p -> setData (0, Qt :: UserRole, var);
								}		/* if (item_p) */

						}		/* json_array_foreach (data_p, i, child_json_p) */

				}		/* if (strcmp (key_s, LINKED_SERVICES_S) == 0) */
			else
				{
					json_array_foreach (data_p, i, child_json_p)
						{
							char *value_s = ConvertIntegerToString (i);

							if (value_s)
								{
									InsertData (child_node_p, value_s, child_json_p);

									FreeCopiedString (value_s);
								}
						}

				}

		}
	else if (json_is_string (data_p))
		{
			const char *value_s = json_string_value (data_p);

			child_node_p -> setText (1, value_s);
		}
	else if (json_is_integer (data_p))
		{
			json_int_t i = json_integer_value (data_p);
			char *value_s = ConvertIntegerToString (i);

			if (value_s)
				{
					child_node_p -> setText (1, value_s);
					FreeCopiedString (value_s);
				}
		}
	else if (json_is_real (data_p))
		{
			double d = json_real_value (data_p);
			char *value_s = ConvertDoubleToString (d);

			if (value_s)
				{
					child_node_p -> setText (1, value_s);
					FreeCopiedString (value_s);
				}
		}
	else if (json_is_true (data_p))
		{
			child_node_p -> setText (1, "true");
		}
	else if (json_is_false (data_p))
		{
			child_node_p -> setText (1, "false");
		}

	return child_node_p;
}

void JSONViewer :: SetJSONData (json_t *data_p)
{
	jv_data_p = data_p;

	jv_tree_p -> clear ();
	jv_viewer_p -> clear ();

	jv_tree_p -> setColumnCount (2);

	if (json_is_array (data_p))
		{
			size_t i;
			json_t *item_p;

			json_array_foreach (data_p, i, item_p)
				{
					char *value_s = ConvertIntegerToString (i);

					AddTopLevelNode (value_s, item_p);

					if (value_s)
						{
							FreeCopiedString (value_s);
						}
				}
		}
	else
		{
			AddTopLevelNode ("0", data_p);
		}

	char *value_s = json_dumps (data_p, JSON_INDENT (2));
	if (value_s)
		{
			jv_viewer_p -> setPlainText (value_s);
			free (value_s);
		}

	jv_tree_p -> repaint ();
}

void JSONViewer :: AddTopLevelNode (const char *key_s, json_t *data_p)
{
	QTreeWidgetItem *top_level_node_p = InsertData (NULL, key_s, data_p);

}

<|endoftext|>


<commit_before>// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// 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 "ConsoleDashboard.h"

#include <sys/ioctl.h>
#include <termios.h>
#include <string.h>
#include <ctype.h>

#include "base/Debug.h"

namespace ekam {

class ConsoleDashboard::LogFormatter {
public:
  LogFormatter(const std::string& text)
      : text(text.data()), end(text.data() + text.size()), wrapped(false) {
    eatWhitespace();
  }

  inline bool atEnd() {
    return text == end;
  }

  std::string getLine(int startColumn, int windowWidth) {
    std::string result;
    int column = startColumn;
    result.reserve((windowWidth - column) * 2);

    if (wrapped) {
      result.append("  ");
      column += 2;
      wrapped = false;
    }

    while (text < end && *text != '\n' && column < windowWidth) {
      if (isalnum(*text)) {
        const char* wordEnd = text;
        do {
          ++wordEnd;
        } while (wordEnd < end && isalnum(*wordEnd));

        int length = wordEnd - text;

        if (column + length <= windowWidth) {
          // Word fits on this line.
          bool isColored = false;
          if (strncasecmp(text, "error", length) == 0 ||
              strncasecmp(text, "fail", length) == 0 ||
              strncasecmp(text, "failed", length) == 0 ||
              strncasecmp(text, "fatal", length) == 0) {
            result.append(ANSI_COLOR_CODES[RED]);
            isColored = true;
          } else if (strncasecmp(text, "warning", length) == 0) {
            result.append(ANSI_COLOR_CODES[YELLOW]);
            isColored = true;
          }

          result.append(text, wordEnd);
          text = wordEnd;
          column += length;

          if (isColored) {
            result.append(ANSI_CLEAR_COLOR);
          }
        } else if (length >= 20 || column == startColumn) {
          // Really long word.  Break it.
          int spaceAvailable = windowWidth - column;
          result.append(text, spaceAvailable);
          text += spaceAvailable;
          column = windowWidth;
        } else {
          // Word doesn't fit in remaining space.  End the line.
          break;
        }
      } else {
        switch (*text) {
          case '\t':
            column = (column & ~0x7) + 8;
            result.push_back(*text);
            break;
          case '\033':  // escape
            // ignore -- could be harmful
            // TODO:  Parse and remove the subsequent terminal instruction to make the output not
            //   suck.  Or better yet, parse the sequence and interpret it.  If it is an SGR code
            //   (e.g. color), pass it through, and just make sure to reset later.
            break;
          default:
            if (*text >= '\0' && *text < ' ') {
              // Ignore control character or weird whitespace.
            } else {
              result.push_back(*text);
              ++column;
            }
            break;
        }
        ++text;
      }
    }

    wrapped = !eatWhitespace();

    return result;
  }

private:
  const char* text;
  const char* end;
  bool wrapped;

  // Eat whitespace up to and including a newline.  Returns true if a newline was eaten.
  bool eatWhitespace() {
    while (text < end && isspace(*text) && *text != '\n') {
      ++text;
    }
    if (text < end && *text == '\n') {
      ++text;
      return true;
    } else {
      return false;
    }
  }

  bool tryHighlight(const char* word, Color color, int windowWidth,
                    int* column, std::string* output) {
    int length = strlen(word);

    if (windowWidth - *column < length) {
      // Don't highlight words broken at line wrapping.
      return false;
    }

    if (end - text < length) {
      // Not enough characters left to match word.
      return false;
    }

    if (strncasecmp(text, word, length) != 0) {
      // Does not match.
      return false;
    }

    if (end - text > length && isalpha(text[length])) {
      // Character after word is a letter.  Don't highlight.
      return false;
    }

    if (!output->empty() && isalpha((*output)[output->size() - 1])) {
      // Character before word was a letter.  Don't highlight.
      return false;
    }

    output->append(ANSI_COLOR_CODES[color]);
    output->append(text, text + length);
    output->append(ANSI_CLEAR_COLOR);
    text += length;
    *column += length;
    return true;
  }
};

class ConsoleDashboard::TaskImpl : public Dashboard::Task {
public:
  TaskImpl(ConsoleDashboard* dashboard, const std::string& verb,
           const std::string& noun, Silence silence);
  ~TaskImpl();

  // implements Task ---------------------------------------------------------------------
  void setState(TaskState state);
  void addOutput(const std::string& text);

private:
  ConsoleDashboard* dashboard;
  TaskState state;
  Silence silence;
  std::string verb;
  std::string noun;
  std::string outputText;

  void removeFromRunning();
  void writeFinalLog(Color verbColor);

  friend class ConsoleDashboard;
};

ConsoleDashboard::TaskImpl::TaskImpl(ConsoleDashboard* dashboard,
                                     const std::string& verb, const std::string& noun,
                                     Silence silence)
    : dashboard(dashboard), state(PENDING), silence(silence), verb(verb), noun(noun) {}
ConsoleDashboard::TaskImpl::~TaskImpl() {
  if (state == RUNNING) {
    dashboard->clearRunning();
    removeFromRunning();
    dashboard->drawRunning();
  }
}

void ConsoleDashboard::TaskImpl::setState(TaskState state) {
  // If state was previously BLOCKED, and we managed to un-block, then we don't care about the
  // reason why we were blocked, so clear the text.
  if (this->state == BLOCKED && (state == PENDING || state == RUNNING)) {
    outputText.clear();
  }

  if (this->state == RUNNING) {
    if (silence != SILENT) removeFromRunning();
  }

  this->state = state;

  dashboard->clearRunning();

  switch (state) {
    case PENDING:
      // Don't display.
      break;
    case RUNNING:
      if (silence != SILENT) dashboard->runningTasks.push_back(this);
      break;
    case DONE:
      writeFinalLog(DONE_COLOR);
      break;
    case PASSED:
      writeFinalLog(PASSED_COLOR);
      break;
    case FAILED:
      writeFinalLog(FAILED_COLOR);
      break;
    case BLOCKED:
      // Don't display.
      break;
  }

  dashboard->drawRunning();
}

void ConsoleDashboard::TaskImpl::addOutput(const std::string& text) {
  outputText.append(text);
}

void ConsoleDashboard::TaskImpl::removeFromRunning() {
  for (std::vector<TaskImpl*>::iterator iter = dashboard->runningTasks.begin();
       iter != dashboard->runningTasks.end(); ++iter) {
    if (*iter == this) {
      dashboard->runningTasks.erase(iter);
      break;
    }
  }
}

void ConsoleDashboard::TaskImpl::writeFinalLog(Color verbColor) {
  // Silent tasks should not be written to the log, unless they had error messages.
  if (silence != SILENT || !outputText.empty()) {
    fprintf(dashboard->out, "%s%s:%s %s\n",
            ANSI_COLOR_CODES[verbColor], verb.c_str(), ANSI_CLEAR_COLOR, noun.c_str());

    // Write any output we have buffered.
    if (!outputText.empty()) {
      LogFormatter formatter(outputText);
      struct winsize windowSize;
      ioctl(dashboard->fd, TIOCGWINSZ, &windowSize);

      for (int i = 0; i < dashboard->maxDisplayedLogLines && !formatter.atEnd(); i++) {
        std::string line = formatter.getLine(2, windowSize.ws_col);
        fprintf(dashboard->out, "  %s\n", line.c_str());
      }

      if (!formatter.atEnd()) {
        fprintf(dashboard->out, "  ...(log truncated; use -l to increase log limit)...\n");
      }

      outputText.clear();
    }
  }
}

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

const char* const ConsoleDashboard::ANSI_COLOR_CODES[] = {
  "\033[30m",
  "\033[31m",
  "\033[32m",
  "\033[33m",
  "\033[34m",
  "\033[35m",
  "\033[36m",
  "\033[37m",
  "\033[1;30m",
  "\033[1;31m",
  "\033[1;32m",
  "\033[1;33m",
  "\033[1;34m",
  "\033[1;35m",
  "\033[1;36m",
  "\033[1;37m"
};

const char* const ConsoleDashboard::ANSI_CLEAR_COLOR = "\033[0m";

// Note:  \033[%dF (move cursor up %d lines and to beginning of line) doesn't work on some
//   terminals.  \033[%dA (move cursor up %d lines) does appear to work, so tack \r on to that
//   to go to the beginning of the line.
const char* const ConsoleDashboard::ANSI_MOVE_CURSOR_UP = "\033[%dA\r";
const char* const ConsoleDashboard::ANSI_CLEAR_BELOW_CURSOR = "\033[0J";

const ConsoleDashboard::Color ConsoleDashboard::DONE_COLOR = BRIGHT_BLUE;
const ConsoleDashboard::Color ConsoleDashboard::PASSED_COLOR = BRIGHT_GREEN;
const ConsoleDashboard::Color ConsoleDashboard::FAILED_COLOR = BRIGHT_RED;
const ConsoleDashboard::Color ConsoleDashboard::RUNNING_COLOR = BRIGHT_FUCHSIA;

ConsoleDashboard::ConsoleDashboard(FILE* output, int maxDisplayedLogLines)
    : fd(fileno(output)), out(output), maxDisplayedLogLines(maxDisplayedLogLines),
      runningTasksLineCount(0), lastDebugMessageCount(DebugMessage::getMessageCount()) {}
ConsoleDashboard::~ConsoleDashboard() {}

OwnedPtr<Dashboard::Task> ConsoleDashboard::beginTask(
    const std::string& verb, const std::string& noun, Silence silence) {
  return newOwned<TaskImpl>(this, verb, noun, silence);
}

void ConsoleDashboard::clearRunning() {
  if (lastDebugMessageCount != DebugMessage::getMessageCount()) {
    // Some debug messages were printed.  We don't want to clobber them.  So we can't clear.
    return;
  }

  if (runningTasksLineCount > 0) {
    fprintf(out, ANSI_MOVE_CURSOR_UP, runningTasksLineCount);
    fputs(ANSI_CLEAR_BELOW_CURSOR, out);
  }
}

void ConsoleDashboard::drawRunning() {
  struct winsize windowSize;
  ioctl(fd, TIOCGWINSZ, &windowSize);

  // Leave a few lines for completed tasks.
  int spaceForTasks = windowSize.ws_row - 4;

  bool allTasksShown = static_cast<int>(runningTasks.size()) < spaceForTasks;
  int displayCount = allTasksShown ? runningTasks.size() : spaceForTasks - 1;
  runningTasksLineCount = allTasksShown ? runningTasks.size() : spaceForTasks;

  for (int i = 0; i < displayCount; i++) {
    TaskImpl* task = runningTasks[i];
    int spaceForNoun = windowSize.ws_col - task->verb.size() - 2;

    fprintf(out, "%s%s:%s ", ANSI_COLOR_CODES[RUNNING_COLOR], task->verb.c_str(), ANSI_CLEAR_COLOR);

    if (static_cast<int>(task->noun.size()) > spaceForNoun) {
      std::string shortenedNoun = task->noun.substr(task->noun.size() - spaceForNoun + 3);
      fprintf(out, "...%s", shortenedNoun.c_str());
    } else {
      fputs(task->noun.c_str(), out);
    }

    putc('\n', out);
  }

  if (!allTasksShown) {
    fputs("...(more)...\n", out);
  }

  fflush(out);

  lastDebugMessageCount = DebugMessage::getMessageCount();
}

}  // namespace ekam
<commit_msg>Make Ekam output friendly to the colorblind, grayscale screens, etc.<commit_after>// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// 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 "ConsoleDashboard.h"

#include <sys/ioctl.h>
#include <termios.h>
#include <string.h>
#include <ctype.h>

#include "base/Debug.h"

namespace ekam {

class ConsoleDashboard::LogFormatter {
public:
  LogFormatter(const std::string& text)
      : text(text.data()), end(text.data() + text.size()), wrapped(false) {
    eatWhitespace();
  }

  inline bool atEnd() {
    return text == end;
  }

  std::string getLine(int startColumn, int windowWidth) {
    std::string result;
    int column = startColumn;
    result.reserve((windowWidth - column) * 2);

    if (wrapped) {
      result.append("  ");
      column += 2;
      wrapped = false;
    }

    while (text < end && *text != '\n' && column < windowWidth) {
      if (isalnum(*text)) {
        const char* wordEnd = text;
        do {
          ++wordEnd;
        } while (wordEnd < end && isalnum(*wordEnd));

        int length = wordEnd - text;

        if (column + length <= windowWidth) {
          // Word fits on this line.
          bool isColored = false;
          if (strncasecmp(text, "error", length) == 0 ||
              strncasecmp(text, "fail", length) == 0 ||
              strncasecmp(text, "failed", length) == 0 ||
              strncasecmp(text, "fatal", length) == 0) {
            result.append(ANSI_COLOR_CODES[RED]);
            isColored = true;
          } else if (strncasecmp(text, "warning", length) == 0) {
            result.append(ANSI_COLOR_CODES[YELLOW]);
            isColored = true;
          }

          result.append(text, wordEnd);
          text = wordEnd;
          column += length;

          if (isColored) {
            result.append(ANSI_CLEAR_COLOR);
          }
        } else if (length >= 20 || column == startColumn) {
          // Really long word.  Break it.
          int spaceAvailable = windowWidth - column;
          result.append(text, spaceAvailable);
          text += spaceAvailable;
          column = windowWidth;
        } else {
          // Word doesn't fit in remaining space.  End the line.
          break;
        }
      } else {
        switch (*text) {
          case '\t':
            column = (column & ~0x7) + 8;
            result.push_back(*text);
            break;
          case '\033':  // escape
            // ignore -- could be harmful
            // TODO:  Parse and remove the subsequent terminal instruction to make the output not
            //   suck.  Or better yet, parse the sequence and interpret it.  If it is an SGR code
            //   (e.g. color), pass it through, and just make sure to reset later.
            break;
          default:
            if (*text >= '\0' && *text < ' ') {
              // Ignore control character or weird whitespace.
            } else {
              result.push_back(*text);
              ++column;
            }
            break;
        }
        ++text;
      }
    }

    wrapped = !eatWhitespace();

    return result;
  }

private:
  const char* text;
  const char* end;
  bool wrapped;

  // Eat whitespace up to and including a newline.  Returns true if a newline was eaten.
  bool eatWhitespace() {
    while (text < end && isspace(*text) && *text != '\n') {
      ++text;
    }
    if (text < end && *text == '\n') {
      ++text;
      return true;
    } else {
      return false;
    }
  }

  bool tryHighlight(const char* word, Color color, int windowWidth,
                    int* column, std::string* output) {
    int length = strlen(word);

    if (windowWidth - *column < length) {
      // Don't highlight words broken at line wrapping.
      return false;
    }

    if (end - text < length) {
      // Not enough characters left to match word.
      return false;
    }

    if (strncasecmp(text, word, length) != 0) {
      // Does not match.
      return false;
    }

    if (end - text > length && isalpha(text[length])) {
      // Character after word is a letter.  Don't highlight.
      return false;
    }

    if (!output->empty() && isalpha((*output)[output->size() - 1])) {
      // Character before word was a letter.  Don't highlight.
      return false;
    }

    output->append(ANSI_COLOR_CODES[color]);
    output->append(text, text + length);
    output->append(ANSI_CLEAR_COLOR);
    text += length;
    *column += length;
    return true;
  }
};

class ConsoleDashboard::TaskImpl : public Dashboard::Task {
public:
  TaskImpl(ConsoleDashboard* dashboard, const std::string& verb,
           const std::string& noun, Silence silence);
  ~TaskImpl();

  // implements Task ---------------------------------------------------------------------
  void setState(TaskState state);
  void addOutput(const std::string& text);

private:
  ConsoleDashboard* dashboard;
  TaskState state;
  Silence silence;
  std::string verb;
  std::string noun;
  std::string outputText;

  void removeFromRunning();
  void writeFinalLog(Color verbColor, const char* icon);

  friend class ConsoleDashboard;
};

ConsoleDashboard::TaskImpl::TaskImpl(ConsoleDashboard* dashboard,
                                     const std::string& verb, const std::string& noun,
                                     Silence silence)
    : dashboard(dashboard), state(PENDING), silence(silence), verb(verb), noun(noun) {}
ConsoleDashboard::TaskImpl::~TaskImpl() {
  if (state == RUNNING) {
    dashboard->clearRunning();
    removeFromRunning();
    dashboard->drawRunning();
  }
}

void ConsoleDashboard::TaskImpl::setState(TaskState state) {
  // If state was previously BLOCKED, and we managed to un-block, then we don't care about the
  // reason why we were blocked, so clear the text.
  if (this->state == BLOCKED && (state == PENDING || state == RUNNING)) {
    outputText.clear();
  }

  if (this->state == RUNNING) {
    if (silence != SILENT) removeFromRunning();
  }

  this->state = state;

  dashboard->clearRunning();

  switch (state) {
    case PENDING:
      // Don't display.
      break;
    case RUNNING:
      if (silence != SILENT) dashboard->runningTasks.push_back(this);
      break;
    case DONE:
      writeFinalLog(DONE_COLOR, " ");
      break;
    case PASSED:
      writeFinalLog(PASSED_COLOR, "✔");
      break;
    case FAILED:
      writeFinalLog(FAILED_COLOR, "✘");
      break;
    case BLOCKED:
      // Don't display.
      break;
  }

  dashboard->drawRunning();
}

void ConsoleDashboard::TaskImpl::addOutput(const std::string& text) {
  outputText.append(text);
}

void ConsoleDashboard::TaskImpl::removeFromRunning() {
  for (std::vector<TaskImpl*>::iterator iter = dashboard->runningTasks.begin();
       iter != dashboard->runningTasks.end(); ++iter) {
    if (*iter == this) {
      dashboard->runningTasks.erase(iter);
      break;
    }
  }
}

void ConsoleDashboard::TaskImpl::writeFinalLog(Color verbColor, const char* icon) {
  // Silent tasks should not be written to the log, unless they had error messages.
  if (silence != SILENT || !outputText.empty()) {
    fprintf(dashboard->out, "%s%s %s:%s %s\n",
            ANSI_COLOR_CODES[verbColor], icon, verb.c_str(), ANSI_CLEAR_COLOR, noun.c_str());

    // Write any output we have buffered.
    if (!outputText.empty()) {
      LogFormatter formatter(outputText);
      struct winsize windowSize;
      ioctl(dashboard->fd, TIOCGWINSZ, &windowSize);

      for (int i = 0; i < dashboard->maxDisplayedLogLines && !formatter.atEnd(); i++) {
        std::string line = formatter.getLine(4, windowSize.ws_col);
        fprintf(dashboard->out, "    %s\n", line.c_str());
      }

      if (!formatter.atEnd()) {
        fprintf(dashboard->out, "    ...(log truncated; use -l to increase log limit)...\n");
      }

      outputText.clear();
    }
  }
}

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

const char* const ConsoleDashboard::ANSI_COLOR_CODES[] = {
  "\033[30m",
  "\033[31m",
  "\033[32m",
  "\033[33m",
  "\033[34m",
  "\033[35m",
  "\033[36m",
  "\033[37m",
  "\033[1;30m",
  "\033[1;31m",
  "\033[1;32m",
  "\033[1;33m",
  "\033[1;34m",
  "\033[1;35m",
  "\033[1;36m",
  "\033[1;37m"
};

const char* const ConsoleDashboard::ANSI_CLEAR_COLOR = "\033[0m";

// Note:  \033[%dF (move cursor up %d lines and to beginning of line) doesn't work on some
//   terminals.  \033[%dA (move cursor up %d lines) does appear to work, so tack \r on to that
//   to go to the beginning of the line.
const char* const ConsoleDashboard::ANSI_MOVE_CURSOR_UP = "\033[%dA\r";
const char* const ConsoleDashboard::ANSI_CLEAR_BELOW_CURSOR = "\033[0J";

const ConsoleDashboard::Color ConsoleDashboard::DONE_COLOR = BRIGHT_BLUE;
const ConsoleDashboard::Color ConsoleDashboard::PASSED_COLOR = BRIGHT_GREEN;
const ConsoleDashboard::Color ConsoleDashboard::FAILED_COLOR = BRIGHT_RED;
const ConsoleDashboard::Color ConsoleDashboard::RUNNING_COLOR = BRIGHT_FUCHSIA;

ConsoleDashboard::ConsoleDashboard(FILE* output, int maxDisplayedLogLines)
    : fd(fileno(output)), out(output), maxDisplayedLogLines(maxDisplayedLogLines),
      runningTasksLineCount(0), lastDebugMessageCount(DebugMessage::getMessageCount()) {}
ConsoleDashboard::~ConsoleDashboard() {}

OwnedPtr<Dashboard::Task> ConsoleDashboard::beginTask(
    const std::string& verb, const std::string& noun, Silence silence) {
  return newOwned<TaskImpl>(this, verb, noun, silence);
}

void ConsoleDashboard::clearRunning() {
  if (lastDebugMessageCount != DebugMessage::getMessageCount()) {
    // Some debug messages were printed.  We don't want to clobber them.  So we can't clear.
    return;
  }

  if (runningTasksLineCount > 0) {
    fprintf(out, ANSI_MOVE_CURSOR_UP, runningTasksLineCount);
    fputs(ANSI_CLEAR_BELOW_CURSOR, out);
  }
}

void ConsoleDashboard::drawRunning() {
  struct winsize windowSize;
  ioctl(fd, TIOCGWINSZ, &windowSize);

  // Leave a few lines for completed tasks.
  int spaceForTasks = windowSize.ws_row - 4;

  bool allTasksShown = static_cast<int>(runningTasks.size()) < spaceForTasks;
  int displayCount = allTasksShown ? runningTasks.size() : spaceForTasks - 1;
  runningTasksLineCount = allTasksShown ? runningTasks.size() : spaceForTasks;

  for (int i = 0; i < displayCount; i++) {
    TaskImpl* task = runningTasks[i];
    int spaceForNoun = windowSize.ws_col - task->verb.size() - 2;

    fprintf(out, "%s↺ %s:%s ", ANSI_COLOR_CODES[RUNNING_COLOR],
        task->verb.c_str(), ANSI_CLEAR_COLOR);

    if (static_cast<int>(task->noun.size()) > spaceForNoun) {
      std::string shortenedNoun = task->noun.substr(task->noun.size() - spaceForNoun + 3);
      fprintf(out, "...%s", shortenedNoun.c_str());
    } else {
      fputs(task->noun.c_str(), out);
    }

    putc('\n', out);
  }

  if (!allTasksShown) {
    fputs("...(more)...\n", out);
  }

  fflush(out);

  lastDebugMessageCount = DebugMessage::getMessageCount();
}

}  // namespace ekam
<|endoftext|>


<commit_before>#include "Base.h"
#include "AIStateMachine.h"
#include "AIAgent.h"
#include "AIMessage.h"
#include "Game.h"

namespace gameplay
{

AIStateMachine::AIStateMachine(AIAgent* agent)
    : _agent(agent)
{
    GP_ASSERT(agent);
    if (AIState::_empty)
        AIState::_empty->addRef();
    else
        AIState::_empty = new AIState("");
    _currentState = AIState::_empty;
}

AIStateMachine::~AIStateMachine()
{
    // Release all states
    for (std::list<AIState*>::iterator itr = _states.begin(); itr != _states.end(); ++itr)
    {
        (*itr)->release();
    }
    SAFE_RELEASE(AIState::_empty);
}

AIAgent* AIStateMachine::getAgent() const
{
    return _agent;
}

AIState* AIStateMachine::addState(const char* id)
{
    AIState* state = AIState::create(id);
    _states.push_back(state);
    return state;
}

void AIStateMachine::addState(AIState* state)
{
    state->addRef();
    _states.push_back(state);
}

void AIStateMachine::removeState(AIState* state)
{
    std::list<AIState*>::iterator itr = std::find(_states.begin(), _states.end(), state);
    if (itr != _states.end())
    {
        _states.erase(itr);
        state->release();
    }
}

AIState* AIStateMachine::getState(const char* id) const
{
    GP_ASSERT(id);

    AIState* state;
    for (std::list<AIState*>::const_iterator itr = _states.begin(); itr != _states.end(); ++itr)
    {
        state = (*itr);

        if (strcmp(id, state->getId()) == 0)
            return state;
    }

    return NULL;
}

AIState* AIStateMachine::getActiveState() const
{
    return _currentState;
}

bool AIStateMachine::hasState(AIState* state) const
{
    GP_ASSERT(state);

    return (std::find(_states.begin(), _states.end(), state) != _states.end());
}

AIState* AIStateMachine::setState(const char* id)
{
    AIState* state = getState(id);
    if (state)
        sendChangeStateMessage(state);
    return state;
}

bool AIStateMachine::setState(AIState* state)
{
    if (hasState(state))
    {
        sendChangeStateMessage(state);
        return true;
    }

    return false;
}

void AIStateMachine::sendChangeStateMessage(AIState* newState)
{
    AIMessage* message = AIMessage::create(0, _agent->getId(), _agent->getId(), 1);
    message->_messageType = AIMessage::MESSAGE_TYPE_STATE_CHANGE;
    message->setString(0, newState->getId());
    Game::getInstance()->getAIController()->sendMessage(message);
}

void AIStateMachine::setStateInternal(AIState* state)
{
    GP_ASSERT(hasState(state));

    // Fire the exit event for the current state
    _currentState->exit(this);

    // Set the new state
    _currentState = state;

    // Fire the enter event for the new state
    _currentState->enter(this);
}

void AIStateMachine::update(float elapsedTime)
{
    _currentState->update(this, elapsedTime);
}

}
<commit_msg>Fixed a bug in cleanup code for AIStateMachine.<commit_after>#include "Base.h"
#include "AIStateMachine.h"
#include "AIAgent.h"
#include "AIMessage.h"
#include "Game.h"

namespace gameplay
{

AIStateMachine::AIStateMachine(AIAgent* agent)
    : _agent(agent)
{
    GP_ASSERT(agent);
    if (AIState::_empty)
        AIState::_empty->addRef();
    else
        AIState::_empty = new AIState("");
    _currentState = AIState::_empty;
}

AIStateMachine::~AIStateMachine()
{
    // Release all states
    for (std::list<AIState*>::iterator itr = _states.begin(); itr != _states.end(); ++itr)
    {
        (*itr)->release();
    }

    if (AIState::_empty)
    {
        if (AIState::_empty->getRefCount() == 1)
        {
            SAFE_RELEASE(AIState::_empty);
        }
        else
        {
            AIState::_empty->release();
        }
    }
}

AIAgent* AIStateMachine::getAgent() const
{
    return _agent;
}

AIState* AIStateMachine::addState(const char* id)
{
    AIState* state = AIState::create(id);
    _states.push_back(state);
    return state;
}

void AIStateMachine::addState(AIState* state)
{
    state->addRef();
    _states.push_back(state);
}

void AIStateMachine::removeState(AIState* state)
{
    std::list<AIState*>::iterator itr = std::find(_states.begin(), _states.end(), state);
    if (itr != _states.end())
    {
        _states.erase(itr);
        state->release();
    }
}

AIState* AIStateMachine::getState(const char* id) const
{
    GP_ASSERT(id);

    AIState* state;
    for (std::list<AIState*>::const_iterator itr = _states.begin(); itr != _states.end(); ++itr)
    {
        state = (*itr);

        if (strcmp(id, state->getId()) == 0)
            return state;
    }

    return NULL;
}

AIState* AIStateMachine::getActiveState() const
{
    return _currentState;
}

bool AIStateMachine::hasState(AIState* state) const
{
    GP_ASSERT(state);

    return (std::find(_states.begin(), _states.end(), state) != _states.end());
}

AIState* AIStateMachine::setState(const char* id)
{
    AIState* state = getState(id);
    if (state)
        sendChangeStateMessage(state);
    return state;
}

bool AIStateMachine::setState(AIState* state)
{
    if (hasState(state))
    {
        sendChangeStateMessage(state);
        return true;
    }

    return false;
}

void AIStateMachine::sendChangeStateMessage(AIState* newState)
{
    AIMessage* message = AIMessage::create(0, _agent->getId(), _agent->getId(), 1);
    message->_messageType = AIMessage::MESSAGE_TYPE_STATE_CHANGE;
    message->setString(0, newState->getId());
    Game::getInstance()->getAIController()->sendMessage(message);
}

void AIStateMachine::setStateInternal(AIState* state)
{
    GP_ASSERT(hasState(state));

    // Fire the exit event for the current state
    _currentState->exit(this);

    // Set the new state
    _currentState = state;

    // Fire the enter event for the new state
    _currentState->enter(this);
}

void AIStateMachine::update(float elapsedTime)
{
    _currentState->update(this, elapsedTime);
}

}
<|endoftext|>