<commit_before>/// @file   characterUtilities.cpp
/// @author Enrico Fraccaroli
/// @date   Jan 29 2017
/// @copyright
/// Copyright (c) 2017 Enrico Fraccaroli <enrico.fraccaroli@gmail.com>
/// Permission is hereby granted, free of charge, to any person obtaining a
/// copy of this software and associated documentation files (the "Software"),
/// to deal in the Software without restriction, including without limitation
/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following conditions:
///     The above copyright notice and this permission notice shall be included
///     in all copies or substantial portions of the Software.
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
/// DEALINGS IN THE SOFTWARE.

#include "characterUtilities.hpp"
#include "rangedWeaponItem.hpp"
#include "meleeWeaponItem.hpp"
#include "resourceModel.hpp"
#include "toolModel.hpp"
#include "character.hpp"
#include "logger.hpp"
#include "room.hpp"

std::vector<MeleeWeaponItem *> GetActiveMeleeWeapons(Character * character)
{
    std::vector<MeleeWeaponItem *> weapons;
    for (auto item : character->equipment)
    {
        // If at least one of the occupied body parts can be used to wield
        // a weapon, consider it an active weapon.
        for (auto bodyPart : item->occupiedBodyParts)
        {
            if (HasFlag(bodyPart->flags, BodyPartFlag::CanWield))
            {
                if (item->getType() == ModelType::MeleeWeapon)
                {
                    auto wpn = static_cast<MeleeWeaponItem *>(item);
                    weapons.emplace_back(wpn);
                    // Break the loop otherwise the weapon would be added
                    // for each occupied body part.
                    break;
                }
            }
        }
    }
    return weapons;
}

std::vector<RangedWeaponItem *> GetActiveRangedWeapons(Character * character)
{
    std::vector<RangedWeaponItem *> weapons;
    for (auto item : character->equipment)
    {
        // If at least one of the occupied body parts can be used to wield
        // a range weapon, consider it an active weapon.
        for (auto bodyPart : item->occupiedBodyParts)
        {
            if (HasFlag(bodyPart->flags, BodyPartFlag::CanWield))
            {
                if (item->getType() == ModelType::RangedWeapon)
                {
                    auto wpn = static_cast<RangedWeaponItem *>(item);
                    weapons.emplace_back(wpn);
                    // Break the loop otherwise the weapon would be added
                    // for each occupied body part.
                    break;
                }
            }
        }
    }
    return weapons;
}

std::vector<std::shared_ptr<BodyPart::BodyWeapon>> GetActiveNaturalWeapons(
    Character * character)
{
    std::vector<std::shared_ptr<BodyPart::BodyWeapon>> naturalWeapons;
    for (auto bodyPart : character->race->bodyParts)
    {
        // If the body part is holding an item, skip it.
        if (character->findItemAtBodyPart(bodyPart) != nullptr) continue;
        // If the body part cannot be used as natural weapon, skip it.
        if (bodyPart->weapon == nullptr) continue;
        // Add the natural weapon to the vector.
        naturalWeapons.emplace_back(bodyPart->weapon);
    }
    return naturalWeapons;
}

ItemVector FindPosessedCoins(Character * character)
{
    ItemVector foundCoins;
    auto FindCoinInContainer = [&](Item * item)
    {
        if (item->getType() == ModelType::Container)
        {
            // Cast the item to container.
            auto containerItem = static_cast<ContainerItem *>(item);
            for (auto content : containerItem->content)
            {
                if (content->getType() == ModelType::Currency)
                {
                    foundCoins.emplace_back(content);
                }
            }
        }
    };
    for (auto it : character->equipment)
    {
        FindCoinInContainer(it);
    }
    for (auto it : character->inventory)
    {
        if (it->getType() == ModelType::Currency)
        {
            foundCoins.emplace_back(it);
        }
        else
        {
            FindCoinInContainer(it);
        }
    }
    foundCoins.orderBy(ItemVector::ByPrice);
    return foundCoins;
}

// -----------------------------------------------------------------------------
SearchOptionsCharacter::SearchOptionsCharacter() :
    searchInRoom(),
    searchInInventory(),
    searchInEquipment()
{
    // Nothing to do.
}

SearchOptionsCharacter::SearchOptionsCharacter(
    const SearchOptionsCharacter & other) :
    searchInRoom(other.searchInRoom),
    searchInInventory(other.searchInInventory),
    searchInEquipment(other.searchInEquipment)
{
    // Nothing to do.
}

SearchOptionsCharacter::~SearchOptionsCharacter()
{
    // Nothing to do.
}
// -----------------------------------------------------------------------------

bool FindNearbyResouces(
    Character * character,
    std::map<ResourceType, unsigned int> requiredResources,
    std::vector<std::pair<Item *, unsigned int>> & foundResources,
    const SearchOptionsCharacter & searchOptions)
{
    // Create a function which checks if the given item is of the required type.
    auto IsValidResource = [](Item * item, const ResourceType & resourceType)
    {
        // Check the pointer to the model.
        if (item->model == nullptr) return false;
        // Check if the item is a resource.
        if (item->model->getType() != ModelType::Resource) return false;
        // Cast the model to ResourceModel.
        auto resourceModel = item->model->toResource();
        // Check if the type of resource is the same.
        return (resourceModel->resourceType == resourceType);
    };
    // Create a function which reduces the required quantity and checks if
    // the zero has been reached.
    auto DecrementRequired = [&foundResources](Item * item,
                                               unsigned int & requiredQuantity)
    {
        // Get the available quantity.
        auto quantityAvailable = item->quantity;
        // Set the used quantity.
        auto quantityUsed = quantityAvailable;
        if (quantityAvailable > requiredQuantity)
        {
            quantityUsed = (quantityAvailable - requiredQuantity);
        }
        // Add the item to the list of used resources.
        foundResources.emplace_back(std::make_pair(item, quantityUsed));
        // Reduce the quantity needed.
        requiredQuantity -= quantityUsed;
        // Returns if we've reached the needed quantity.
        return (requiredQuantity == 0);
    };
    for (auto resource : requiredResources)
    {
        // Quantity of ingredients that has to be found.
        auto quantityNeeded = resource.second;
        // Check if we need to search inside the room.
        // TODO: Probably I need to check if a given item is not selected twice.
        if (searchOptions.searchInRoom)
        {
            for (auto item : character->room->items)
            {
                // Check if the item is a valid resource.
                if (!IsValidResource(item, resource.first)) continue;
                // Decrement the required quantity. If we've reached the
                // needed quantity stop the loop.
                if (DecrementRequired(item, quantityNeeded)) break;
            }
        }
        // Check if we need to search inside the character's equipment.
        if (searchOptions.searchInEquipment)
        {
            for (auto item : character->equipment)
            {
                // Check if the item is a valid resource.
                if (!IsValidResource(item, resource.first)) continue;
                // Decrement the required quantity. If we've reached the
                // needed quantity stop the loop.
                if (DecrementRequired(item, quantityNeeded)) break;
            }
        }
        // Check if we need to search inside the character's inventory.
        if (searchOptions.searchInInventory)
        {
            for (auto item : character->inventory)
            {
                // Check if the item is a valid resource.
                if (!IsValidResource(item, resource.first)) continue;
                // Decrement the required quantity. If we've reached the
                // needed quantity stop the loop.
                if (DecrementRequired(item, quantityNeeded)) break;
            }
        }
        // If the ingredients are still not enough, return false.
        if (quantityNeeded > 0) return false;
    }
    return true;
}

Item * FindNearbyTool(
    Character * character,
    const ToolType & toolType,
    const ItemVector & exceptions,
    const SearchOptionsCharacter & searchOptions)
{
    // Create a function which checks if the given item is of the required type.
    auto IsValidTool = [toolType, exceptions](Item * item)
    {
        // Check the pointer to the model.
        if (item->model == nullptr) return false;
        // Check if the item is a resource.
        if (item->model->getType() != ModelType::Tool) return false;
        // Cast the model to ResourceModel.
        auto toolModel = item->model->toTool();
        // Check if the type of resource is the same.
        if (toolModel->toolType != toolType) return false;
        // Check if the item is inside the exception list.
        return std::find_if(exceptions.begin(),
                            exceptions.end(),
                            [item](Item * exception)
                            {
                                return (item->vnum == exception->vnum);
                            }) == exceptions.end();
    };
    if (searchOptions.searchInRoom)
    {
        for (auto item : character->room->items)
        {
            // Check if it is a valid item.
            if (IsValidTool(item)) return item;
        }
    }
    if (searchOptions.searchInEquipment)
    {
        for (auto item : character->equipment)
        {
            // Check if it is a valid item.
            if (IsValidTool(item)) return item;
        }
    }
    if (searchOptions.searchInInventory)
    {
        for (auto item : character->inventory)
        {
            // Check if it is a valid item.
            if (IsValidTool(item)) return item;
        }
    }
    return nullptr;
}

bool FindNearbyTools(
    Character * character,
    std::vector<ToolType> requiredTools,
    ItemVector & foundTools,
    const SearchOptionsCharacter & searchOptions)
{
    for (auto requiredTool : requiredTools)
    {
        auto tool = FindNearbyTool(character,
                                   requiredTool,
                                   foundTools,
                                   searchOptions);
        if (tool == nullptr) return false;
        foundTools.emplace_back(tool);
    }
    return true;
}

Item * FindNearbyBuilding(
    Character * character,
    std::shared_ptr<ItemModel> buildingModel,
    const SearchOptionsCharacter & searchOptions)
{
    // Create a function which checks if the given item is of the required type.
    auto IsValidBuilding = [buildingModel](Item * item)
    {
        // Check the pointer to the model.
        if (item->model == nullptr) return false;
        return buildingModel->vnum == item->model->vnum;
    };
    if (searchOptions.searchInRoom)
    {
        for (auto item : character->room->items)
        {
            // Check if it is a valid item.
            if (IsValidBuilding(item)) return item;
        }
    }
    if (searchOptions.searchInEquipment)
    {
        for (auto item : character->equipment)
        {
            // Check if it is a valid item.
            if (IsValidBuilding(item)) return item;
        }
    }
    if (searchOptions.searchInInventory)
    {
        for (auto item : character->inventory)
        {
            // Check if it is a valid item.
            if (IsValidBuilding(item)) return item;
        }
    }
    return nullptr;
}

bool MoveCharacterTo(
    Character * character,
    Room * destination,
    const std::string & msgDepart,
    const std::string & msgArrive,
    const std::string & msgChar)
{
    // Check if the function has received a valid character.
    if (WrongAssert(character == nullptr)) return false;
    // Check if the function has received a valid destination.
    if (WrongAssert(destination == nullptr)) return false;
    // Check if the character is inside a valid room.
    if (WrongAssert(character->room == nullptr)) return false;
    // Activate the entrance event for every mobile in the room.
    for (auto mobile : character->room->getAllMobile(character))
    {
        if (mobile->canSee(character))
        {
            mobile->triggerEventExit(character);
        }
    }
    // Show a message to the character, if is set.
    if (!msgChar.empty()) character->sendMsg(msgChar);
    // Tell others where the character went and remove s/he from the old room.
    character->room->sendToAll(msgDepart, {character});
    // Remove the player from the current room.
    character->room->removeCharacter(character);
    // Add the character to the destionation room.
    destination->addCharacter(character);
    // Look around new room.
    character->doCommand("look");
    // Tell others s/he has arrived and move the character to the new room.
    destination->sendToAll(msgArrive, {character});
    // Activate the entrance event for every mobile in the room.
    for (auto mobile : character->room->getAllMobile(character))
    {
        if (mobile->canSee(character))
        {
            mobile->triggerEventEnter(character);
        }
    }
    return true;
}

bool HasRequiredKnowledge(Character * character, Production * production)
{
    if (WrongAssert(character == nullptr)) return false;
    if (WrongAssert(production == nullptr)) return false;
    for (auto knowledge : production->requiredKnowledge)
    {
        if (character->effectManager.getKnowledge(knowledge) <= 0) return false;
    }
    return true;
}<commit_msg>Update knowledge check when performing profession<commit_after>/// @file   characterUtilities.cpp
/// @author Enrico Fraccaroli
/// @date   Jan 29 2017
/// @copyright
/// Copyright (c) 2017 Enrico Fraccaroli <enrico.fraccaroli@gmail.com>
/// Permission is hereby granted, free of charge, to any person obtaining a
/// copy of this software and associated documentation files (the "Software"),
/// to deal in the Software without restriction, including without limitation
/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following conditions:
///     The above copyright notice and this permission notice shall be included
///     in all copies or substantial portions of the Software.
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
/// DEALINGS IN THE SOFTWARE.

#include "characterUtilities.hpp"
#include "rangedWeaponItem.hpp"
#include "meleeWeaponItem.hpp"
#include "resourceModel.hpp"
#include "toolModel.hpp"
#include "character.hpp"
#include "logger.hpp"
#include "room.hpp"

std::vector<MeleeWeaponItem *> GetActiveMeleeWeapons(Character * character)
{
    std::vector<MeleeWeaponItem *> weapons;
    for (auto item : character->equipment)
    {
        // If at least one of the occupied body parts can be used to wield
        // a weapon, consider it an active weapon.
        for (auto bodyPart : item->occupiedBodyParts)
        {
            if (HasFlag(bodyPart->flags, BodyPartFlag::CanWield))
            {
                if (item->getType() == ModelType::MeleeWeapon)
                {
                    auto wpn = static_cast<MeleeWeaponItem *>(item);
                    weapons.emplace_back(wpn);
                    // Break the loop otherwise the weapon would be added
                    // for each occupied body part.
                    break;
                }
            }
        }
    }
    return weapons;
}

std::vector<RangedWeaponItem *> GetActiveRangedWeapons(Character * character)
{
    std::vector<RangedWeaponItem *> weapons;
    for (auto item : character->equipment)
    {
        // If at least one of the occupied body parts can be used to wield
        // a range weapon, consider it an active weapon.
        for (auto bodyPart : item->occupiedBodyParts)
        {
            if (HasFlag(bodyPart->flags, BodyPartFlag::CanWield))
            {
                if (item->getType() == ModelType::RangedWeapon)
                {
                    auto wpn = static_cast<RangedWeaponItem *>(item);
                    weapons.emplace_back(wpn);
                    // Break the loop otherwise the weapon would be added
                    // for each occupied body part.
                    break;
                }
            }
        }
    }
    return weapons;
}

std::vector<std::shared_ptr<BodyPart::BodyWeapon>> GetActiveNaturalWeapons(
    Character * character)
{
    std::vector<std::shared_ptr<BodyPart::BodyWeapon>> naturalWeapons;
    for (auto bodyPart : character->race->bodyParts)
    {
        // If the body part is holding an item, skip it.
        if (character->findItemAtBodyPart(bodyPart) != nullptr) continue;
        // If the body part cannot be used as natural weapon, skip it.
        if (bodyPart->weapon == nullptr) continue;
        // Add the natural weapon to the vector.
        naturalWeapons.emplace_back(bodyPart->weapon);
    }
    return naturalWeapons;
}

ItemVector FindPosessedCoins(Character * character)
{
    ItemVector foundCoins;
    auto FindCoinInContainer = [&](Item * item)
    {
        if (item->getType() == ModelType::Container)
        {
            // Cast the item to container.
            auto containerItem = static_cast<ContainerItem *>(item);
            for (auto content : containerItem->content)
            {
                if (content->getType() == ModelType::Currency)
                {
                    foundCoins.emplace_back(content);
                }
            }
        }
    };
    for (auto it : character->equipment)
    {
        FindCoinInContainer(it);
    }
    for (auto it : character->inventory)
    {
        if (it->getType() == ModelType::Currency)
        {
            foundCoins.emplace_back(it);
        }
        else
        {
            FindCoinInContainer(it);
        }
    }
    foundCoins.orderBy(ItemVector::ByPrice);
    return foundCoins;
}

// -----------------------------------------------------------------------------
SearchOptionsCharacter::SearchOptionsCharacter() :
    searchInRoom(),
    searchInInventory(),
    searchInEquipment()
{
    // Nothing to do.
}

SearchOptionsCharacter::SearchOptionsCharacter(
    const SearchOptionsCharacter & other) :
    searchInRoom(other.searchInRoom),
    searchInInventory(other.searchInInventory),
    searchInEquipment(other.searchInEquipment)
{
    // Nothing to do.
}

SearchOptionsCharacter::~SearchOptionsCharacter()
{
    // Nothing to do.
}
// -----------------------------------------------------------------------------

bool FindNearbyResouces(
    Character * character,
    std::map<ResourceType, unsigned int> requiredResources,
    std::vector<std::pair<Item *, unsigned int>> & foundResources,
    const SearchOptionsCharacter & searchOptions)
{
    // Create a function which checks if the given item is of the required type.
    auto IsValidResource = [](Item * item, const ResourceType & resourceType)
    {
        // Check the pointer to the model.
        if (item->model == nullptr) return false;
        // Check if the item is a resource.
        if (item->model->getType() != ModelType::Resource) return false;
        // Cast the model to ResourceModel.
        auto resourceModel = item->model->toResource();
        // Check if the type of resource is the same.
        return (resourceModel->resourceType == resourceType);
    };
    // Create a function which reduces the required quantity and checks if
    // the zero has been reached.
    auto DecrementRequired = [&foundResources](Item * item,
                                               unsigned int & requiredQuantity)
    {
        // Get the available quantity.
        auto quantityAvailable = item->quantity;
        // Set the used quantity.
        auto quantityUsed = quantityAvailable;
        if (quantityAvailable > requiredQuantity)
        {
            quantityUsed = (quantityAvailable - requiredQuantity);
        }
        // Add the item to the list of used resources.
        foundResources.emplace_back(std::make_pair(item, quantityUsed));
        // Reduce the quantity needed.
        requiredQuantity -= quantityUsed;
        // Returns if we've reached the needed quantity.
        return (requiredQuantity == 0);
    };
    for (auto resource : requiredResources)
    {
        // Quantity of ingredients that has to be found.
        auto quantityNeeded = resource.second;
        // Check if we need to search inside the room.
        // TODO: Probably I need to check if a given item is not selected twice.
        if (searchOptions.searchInRoom)
        {
            for (auto item : character->room->items)
            {
                // Check if the item is a valid resource.
                if (!IsValidResource(item, resource.first)) continue;
                // Decrement the required quantity. If we've reached the
                // needed quantity stop the loop.
                if (DecrementRequired(item, quantityNeeded)) break;
            }
        }
        // Check if we need to search inside the character's equipment.
        if (searchOptions.searchInEquipment)
        {
            for (auto item : character->equipment)
            {
                // Check if the item is a valid resource.
                if (!IsValidResource(item, resource.first)) continue;
                // Decrement the required quantity. If we've reached the
                // needed quantity stop the loop.
                if (DecrementRequired(item, quantityNeeded)) break;
            }
        }
        // Check if we need to search inside the character's inventory.
        if (searchOptions.searchInInventory)
        {
            for (auto item : character->inventory)
            {
                // Check if the item is a valid resource.
                if (!IsValidResource(item, resource.first)) continue;
                // Decrement the required quantity. If we've reached the
                // needed quantity stop the loop.
                if (DecrementRequired(item, quantityNeeded)) break;
            }
        }
        // If the ingredients are still not enough, return false.
        if (quantityNeeded > 0) return false;
    }
    return true;
}

Item * FindNearbyTool(
    Character * character,
    const ToolType & toolType,
    const ItemVector & exceptions,
    const SearchOptionsCharacter & searchOptions)
{
    // Create a function which checks if the given item is of the required type.
    auto IsValidTool = [toolType, exceptions](Item * item)
    {
        // Check the pointer to the model.
        if (item->model == nullptr) return false;
        // Check if the item is a resource.
        if (item->model->getType() != ModelType::Tool) return false;
        // Cast the model to ResourceModel.
        auto toolModel = item->model->toTool();
        // Check if the type of resource is the same.
        if (toolModel->toolType != toolType) return false;
        // Check if the item is inside the exception list.
        return std::find_if(exceptions.begin(),
                            exceptions.end(),
                            [item](Item * exception)
                            {
                                return (item->vnum == exception->vnum);
                            }) == exceptions.end();
    };
    if (searchOptions.searchInRoom)
    {
        for (auto item : character->room->items)
        {
            // Check if it is a valid item.
            if (IsValidTool(item)) return item;
        }
    }
    if (searchOptions.searchInEquipment)
    {
        for (auto item : character->equipment)
        {
            // Check if it is a valid item.
            if (IsValidTool(item)) return item;
        }
    }
    if (searchOptions.searchInInventory)
    {
        for (auto item : character->inventory)
        {
            // Check if it is a valid item.
            if (IsValidTool(item)) return item;
        }
    }
    return nullptr;
}

bool FindNearbyTools(
    Character * character,
    std::vector<ToolType> requiredTools,
    ItemVector & foundTools,
    const SearchOptionsCharacter & searchOptions)
{
    for (auto requiredTool : requiredTools)
    {
        auto tool = FindNearbyTool(character,
                                   requiredTool,
                                   foundTools,
                                   searchOptions);
        if (tool == nullptr) return false;
        foundTools.emplace_back(tool);
    }
    return true;
}

Item * FindNearbyBuilding(
    Character * character,
    std::shared_ptr<ItemModel> buildingModel,
    const SearchOptionsCharacter & searchOptions)
{
    // Create a function which checks if the given item is of the required type.
    auto IsValidBuilding = [buildingModel](Item * item)
    {
        // Check the pointer to the model.
        if (item->model == nullptr) return false;
        return buildingModel->vnum == item->model->vnum;
    };
    if (searchOptions.searchInRoom)
    {
        for (auto item : character->room->items)
        {
            // Check if it is a valid item.
            if (IsValidBuilding(item)) return item;
        }
    }
    if (searchOptions.searchInEquipment)
    {
        for (auto item : character->equipment)
        {
            // Check if it is a valid item.
            if (IsValidBuilding(item)) return item;
        }
    }
    if (searchOptions.searchInInventory)
    {
        for (auto item : character->inventory)
        {
            // Check if it is a valid item.
            if (IsValidBuilding(item)) return item;
        }
    }
    return nullptr;
}

bool MoveCharacterTo(
    Character * character,
    Room * destination,
    const std::string & msgDepart,
    const std::string & msgArrive,
    const std::string & msgChar)
{
    // Check if the function has received a valid character.
    if (WrongAssert(character == nullptr)) return false;
    // Check if the function has received a valid destination.
    if (WrongAssert(destination == nullptr)) return false;
    // Check if the character is inside a valid room.
    if (WrongAssert(character->room == nullptr)) return false;
    // Activate the entrance event for every mobile in the room.
    for (auto mobile : character->room->getAllMobile(character))
    {
        if (mobile->canSee(character))
        {
            mobile->triggerEventExit(character);
        }
    }
    // Show a message to the character, if is set.
    if (!msgChar.empty()) character->sendMsg(msgChar);
    // Tell others where the character went and remove s/he from the old room.
    character->room->sendToAll(msgDepart, {character});
    // Remove the player from the current room.
    character->room->removeCharacter(character);
    // Add the character to the destionation room.
    destination->addCharacter(character);
    // Look around new room.
    character->doCommand("look");
    // Tell others s/he has arrived and move the character to the new room.
    destination->sendToAll(msgArrive, {character});
    // Activate the entrance event for every mobile in the room.
    for (auto mobile : character->room->getAllMobile(character))
    {
        if (mobile->canSee(character))
        {
            mobile->triggerEventEnter(character);
        }
    }
    return true;
}

bool HasRequiredKnowledge(Character * character, Production * production)
{
    if (WrongAssert(character == nullptr)) return false;
    if (WrongAssert(production == nullptr)) return false;
    for (auto const & it : production->requiredKnowledge)
    {
        if ((character->skillManager.getKnowledge(it) <= 0) &&
            (character->effectManager.getKnowledge(it) <= 0))
        {
            return false;
        }
    }
    return true;
}<|endoftext|>


<commit_before><commit_msg>adding header support<commit_after><|endoftext|>


<commit_before>/*
 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/base/platform_thread.h"

#include "webrtc/base/atomicops.h"
#include "webrtc/base/checks.h"

#if defined(WEBRTC_LINUX)
#include <sys/prctl.h>
#include <sys/syscall.h>
#endif

namespace rtc {

PlatformThreadId CurrentThreadId() {
  PlatformThreadId ret;
#if defined(WEBRTC_WIN)
  ret = GetCurrentThreadId();
#elif defined(WEBRTC_POSIX)
#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
  ret = pthread_mach_thread_np(pthread_self());
#elif defined(WEBRTC_LINUX)
  ret =  syscall(__NR_gettid);
#elif defined(WEBRTC_ANDROID)
  ret = gettid();
#else
  // Default implementation for nacl and solaris.
  ret = reinterpret_cast<pid_t>(pthread_self());
#endif
#endif  // defined(WEBRTC_POSIX)
  RTC_DCHECK(ret);
  return ret;
}

PlatformThreadRef CurrentThreadRef() {
#if defined(WEBRTC_WIN)
  return GetCurrentThreadId();
#elif defined(WEBRTC_POSIX)
  return pthread_self();
#endif
}

bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b) {
#if defined(WEBRTC_WIN)
  return a == b;
#elif defined(WEBRTC_POSIX)
  return pthread_equal(a, b);
#endif
}

void SetCurrentThreadName(const char* name) {
#if defined(WEBRTC_WIN)
  struct {
    DWORD dwType;
    LPCSTR szName;
    DWORD dwThreadID;
    DWORD dwFlags;
  } threadname_info = {0x1000, name, static_cast<DWORD>(-1), 0};

  __try {
    ::RaiseException(0x406D1388, 0, sizeof(threadname_info) / sizeof(DWORD),
                     reinterpret_cast<ULONG_PTR*>(&threadname_info));
  } __except (EXCEPTION_EXECUTE_HANDLER) {
  }
#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)
  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name));
#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
  pthread_setname_np(name);
#endif
}

namespace {
#if defined(WEBRTC_WIN)
void CALLBACK RaiseFlag(ULONG_PTR param) {
  *reinterpret_cast<bool*>(param) = true;
}
#else
struct ThreadAttributes {
  ThreadAttributes() { pthread_attr_init(&attr); }
  ~ThreadAttributes() { pthread_attr_destroy(&attr); }
  pthread_attr_t* operator&() { return &attr; }
  pthread_attr_t attr;
};
#endif  // defined(WEBRTC_WIN)
}

PlatformThread::PlatformThread(ThreadRunFunctionDeprecated func,
                               void* obj,
                               const char* thread_name)
    : run_function_deprecated_(func),
      obj_(obj),
      name_(thread_name ? thread_name : "webrtc") {
  RTC_DCHECK(func);
  RTC_DCHECK(name_.length() < 64);
  spawned_thread_checker_.DetachFromThread();
}

PlatformThread::PlatformThread(ThreadRunFunction func,
                               void* obj,
                               const char* thread_name,
                               ThreadPriority priority /*= kNormalPriority*/)
    : run_function_(func), priority_(priority), obj_(obj), name_(thread_name) {
  RTC_DCHECK(func);
  RTC_DCHECK(!name_.empty());
  // TODO(tommi): Consider lowering the limit to 15 (limit on Linux).
  RTC_DCHECK(name_.length() < 64);
  spawned_thread_checker_.DetachFromThread();
}

PlatformThread::~PlatformThread() {
  RTC_DCHECK(thread_checker_.CalledOnValidThread());
#if defined(WEBRTC_WIN)
  RTC_DCHECK(!thread_);
  RTC_DCHECK(!thread_id_);
#endif  // defined(WEBRTC_WIN)
}

#if defined(WEBRTC_WIN)
DWORD WINAPI PlatformThread::StartThread(void* param) {
  // The GetLastError() function only returns valid results when it is called
  // after a Win32 API function that returns a "failed" result. A crash dump
  // contains the result from GetLastError() and to make sure it does not
  // falsely report a Windows error we call SetLastError here.
  ::SetLastError(ERROR_SUCCESS);
  static_cast<PlatformThread*>(param)->Run();
  return 0;
}
#else
void* PlatformThread::StartThread(void* param) {
  static_cast<PlatformThread*>(param)->Run();
  return 0;
}
#endif  // defined(WEBRTC_WIN)

void PlatformThread::Start() {
  RTC_DCHECK(thread_checker_.CalledOnValidThread());
  RTC_DCHECK(!thread_) << "Thread already started?";
#if defined(WEBRTC_WIN)
  stop_ = false;

  // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION.
  // Set the reserved stack stack size to 1M, which is the default on Windows
  // and Linux.
  thread_ = ::CreateThread(NULL, 1024 * 1024, &StartThread, this,
                           STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id_);
  RTC_CHECK(thread_) << "CreateThread failed";
  RTC_DCHECK(thread_id_);
#else
  ThreadAttributes attr;
  // Set the stack stack size to 1M.
  pthread_attr_setstacksize(&attr, 1024 * 1024);
  RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this));
#endif  // defined(WEBRTC_WIN)
}

bool PlatformThread::IsRunning() const {
  RTC_DCHECK(thread_checker_.CalledOnValidThread());
#if defined(WEBRTC_WIN)
  return thread_ != nullptr;
#else
  return thread_ != 0;
#endif  // defined(WEBRTC_WIN)
}

PlatformThreadRef PlatformThread::GetThreadRef() const {
#if defined(WEBRTC_WIN)
  return thread_id_;
#else
  return thread_;
#endif  // defined(WEBRTC_WIN)
}

void PlatformThread::Stop() {
  RTC_DCHECK(thread_checker_.CalledOnValidThread());
  if (!IsRunning())
    return;

#if defined(WEBRTC_WIN)
  // Set stop_ to |true| on the worker thread.
  bool queued = QueueAPC(&RaiseFlag, reinterpret_cast<ULONG_PTR>(&stop_));
  // Queuing the APC can fail if the thread is being terminated.
  RTC_CHECK(queued || GetLastError() == ERROR_GEN_FAILURE);
  WaitForSingleObject(thread_, INFINITE);
  CloseHandle(thread_);
  thread_ = nullptr;
  thread_id_ = 0;
#else
  if (!run_function_)
    RTC_CHECK_EQ(1, AtomicOps::Increment(&stop_flag_));
  RTC_CHECK_EQ(0, pthread_join(thread_, nullptr));
  if (!run_function_)
    AtomicOps::ReleaseStore(&stop_flag_, 0);
  thread_ = 0;
#endif  // defined(WEBRTC_WIN)
  spawned_thread_checker_.DetachFromThread();
}

// TODO(tommi): Deprecate the loop behavior in PlatformThread.
// * Introduce a new callback type that returns void.
// * Remove potential for a busy loop in PlatformThread.
// * Delegate the responsibility for how to stop the thread, to the
//   implementation that actually uses the thread.
// All implementations will need to be aware of how the thread should be stopped
// and encouraging a busy polling loop, can be costly in terms of power and cpu.
void PlatformThread::Run() {
  // Attach the worker thread checker to this thread.
  RTC_DCHECK(spawned_thread_checker_.CalledOnValidThread());
  rtc::SetCurrentThreadName(name_.c_str());

  if (run_function_) {
    SetPriority(priority_);
    run_function_(obj_);
    return;
  }
// TODO(tommi): Delete the below.
#if !defined(WEBRTC_MAC) && !defined(WEBRTC_WIN)
  const struct timespec ts_null = {0};
#endif
  do {
    // The interface contract of Start/Stop is that for a successful call to
    // Start, there should be at least one call to the run function.  So we
    // call the function before checking |stop_|.
    if (!run_function_deprecated_(obj_))
      break;
#if defined(WEBRTC_WIN)
    // Alertable sleep to permit RaiseFlag to run and update |stop_|.
    SleepEx(0, true);
  } while (!stop_);
#else
#if defined(WEBRTC_MAC)
    sched_yield();
#else
    nanosleep(&ts_null, nullptr);
#endif
  } while (!AtomicOps::AcquireLoad(&stop_flag_));
#endif  // defined(WEBRTC_WIN)
}

bool PlatformThread::SetPriority(ThreadPriority priority) {
#if RTC_DCHECK_IS_ON
  if (run_function_) {
    // The non-deprecated way of how this function gets called, is that it must
    // be called on the worker thread itself.
    RTC_DCHECK(spawned_thread_checker_.CalledOnValidThread());
  } else {
    // In the case of deprecated use of this method, it must be called on the
    // same thread as the PlatformThread object is constructed on.
    RTC_DCHECK(thread_checker_.CalledOnValidThread());
    RTC_DCHECK(IsRunning());
  }
#endif

#if defined(WEBRTC_WIN)
  return SetThreadPriority(thread_, priority) != FALSE;
#elif defined(__native_client__)
  // Setting thread priorities is not supported in NaCl.
  return true;
#elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX)
  // TODO(tommi): Switch to the same mechanism as Chromium uses for changing
  // thread priorities.
  return true;
#else
#ifdef WEBRTC_THREAD_RR
  const int policy = SCHED_RR;
#else
  const int policy = SCHED_FIFO;
#endif
  const int min_prio = sched_get_priority_min(policy);
  const int max_prio = sched_get_priority_max(policy);
  if (min_prio == -1 || max_prio == -1) {
    return false;
  }

  if (max_prio - min_prio <= 2)
    return false;

  // Convert webrtc priority to system priorities:
  sched_param param;
  const int top_prio = max_prio - 1;
  const int low_prio = min_prio + 1;
  switch (priority) {
    case kLowPriority:
      param.sched_priority = low_prio;
      break;
    case kNormalPriority:
      // The -1 ensures that the kHighPriority is always greater or equal to
      // kNormalPriority.
      param.sched_priority = (low_prio + top_prio - 1) / 2;
      break;
    case kHighPriority:
      param.sched_priority = std::max(top_prio - 2, low_prio);
      break;
    case kHighestPriority:
      param.sched_priority = std::max(top_prio - 1, low_prio);
      break;
    case kRealtimePriority:
      param.sched_priority = top_prio;
      break;
  }
  return pthread_setschedparam(thread_, policy, &param) == 0;
#endif  // defined(WEBRTC_WIN)
}

#if defined(WEBRTC_WIN)
bool PlatformThread::QueueAPC(PAPCFUNC function, ULONG_PTR data) {
  RTC_DCHECK(thread_checker_.CalledOnValidThread());
  RTC_DCHECK(IsRunning());

  return QueueUserAPC(function, thread_, data) != FALSE;
}
#endif

}  // namespace rtc
<commit_msg>Use sched_yield on all POSIX platforms in PlatformThread. (not only MacOS)<commit_after>/*
 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/base/platform_thread.h"

#include "webrtc/base/atomicops.h"
#include "webrtc/base/checks.h"

#if defined(WEBRTC_LINUX)
#include <sys/prctl.h>
#include <sys/syscall.h>
#endif

namespace rtc {

PlatformThreadId CurrentThreadId() {
  PlatformThreadId ret;
#if defined(WEBRTC_WIN)
  ret = GetCurrentThreadId();
#elif defined(WEBRTC_POSIX)
#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
  ret = pthread_mach_thread_np(pthread_self());
#elif defined(WEBRTC_LINUX)
  ret =  syscall(__NR_gettid);
#elif defined(WEBRTC_ANDROID)
  ret = gettid();
#else
  // Default implementation for nacl and solaris.
  ret = reinterpret_cast<pid_t>(pthread_self());
#endif
#endif  // defined(WEBRTC_POSIX)
  RTC_DCHECK(ret);
  return ret;
}

PlatformThreadRef CurrentThreadRef() {
#if defined(WEBRTC_WIN)
  return GetCurrentThreadId();
#elif defined(WEBRTC_POSIX)
  return pthread_self();
#endif
}

bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b) {
#if defined(WEBRTC_WIN)
  return a == b;
#elif defined(WEBRTC_POSIX)
  return pthread_equal(a, b);
#endif
}

void SetCurrentThreadName(const char* name) {
#if defined(WEBRTC_WIN)
  struct {
    DWORD dwType;
    LPCSTR szName;
    DWORD dwThreadID;
    DWORD dwFlags;
  } threadname_info = {0x1000, name, static_cast<DWORD>(-1), 0};

  __try {
    ::RaiseException(0x406D1388, 0, sizeof(threadname_info) / sizeof(DWORD),
                     reinterpret_cast<ULONG_PTR*>(&threadname_info));
  } __except (EXCEPTION_EXECUTE_HANDLER) {
  }
#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)
  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name));
#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
  pthread_setname_np(name);
#endif
}

namespace {
#if defined(WEBRTC_WIN)
void CALLBACK RaiseFlag(ULONG_PTR param) {
  *reinterpret_cast<bool*>(param) = true;
}
#else
struct ThreadAttributes {
  ThreadAttributes() { pthread_attr_init(&attr); }
  ~ThreadAttributes() { pthread_attr_destroy(&attr); }
  pthread_attr_t* operator&() { return &attr; }
  pthread_attr_t attr;
};
#endif  // defined(WEBRTC_WIN)
}

PlatformThread::PlatformThread(ThreadRunFunctionDeprecated func,
                               void* obj,
                               const char* thread_name)
    : run_function_deprecated_(func),
      obj_(obj),
      name_(thread_name ? thread_name : "webrtc") {
  RTC_DCHECK(func);
  RTC_DCHECK(name_.length() < 64);
  spawned_thread_checker_.DetachFromThread();
}

PlatformThread::PlatformThread(ThreadRunFunction func,
                               void* obj,
                               const char* thread_name,
                               ThreadPriority priority /*= kNormalPriority*/)
    : run_function_(func), priority_(priority), obj_(obj), name_(thread_name) {
  RTC_DCHECK(func);
  RTC_DCHECK(!name_.empty());
  // TODO(tommi): Consider lowering the limit to 15 (limit on Linux).
  RTC_DCHECK(name_.length() < 64);
  spawned_thread_checker_.DetachFromThread();
}

PlatformThread::~PlatformThread() {
  RTC_DCHECK(thread_checker_.CalledOnValidThread());
#if defined(WEBRTC_WIN)
  RTC_DCHECK(!thread_);
  RTC_DCHECK(!thread_id_);
#endif  // defined(WEBRTC_WIN)
}

#if defined(WEBRTC_WIN)
DWORD WINAPI PlatformThread::StartThread(void* param) {
  // The GetLastError() function only returns valid results when it is called
  // after a Win32 API function that returns a "failed" result. A crash dump
  // contains the result from GetLastError() and to make sure it does not
  // falsely report a Windows error we call SetLastError here.
  ::SetLastError(ERROR_SUCCESS);
  static_cast<PlatformThread*>(param)->Run();
  return 0;
}
#else
void* PlatformThread::StartThread(void* param) {
  static_cast<PlatformThread*>(param)->Run();
  return 0;
}
#endif  // defined(WEBRTC_WIN)

void PlatformThread::Start() {
  RTC_DCHECK(thread_checker_.CalledOnValidThread());
  RTC_DCHECK(!thread_) << "Thread already started?";
#if defined(WEBRTC_WIN)
  stop_ = false;

  // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION.
  // Set the reserved stack stack size to 1M, which is the default on Windows
  // and Linux.
  thread_ = ::CreateThread(NULL, 1024 * 1024, &StartThread, this,
                           STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id_);
  RTC_CHECK(thread_) << "CreateThread failed";
  RTC_DCHECK(thread_id_);
#else
  ThreadAttributes attr;
  // Set the stack stack size to 1M.
  pthread_attr_setstacksize(&attr, 1024 * 1024);
  RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this));
#endif  // defined(WEBRTC_WIN)
}

bool PlatformThread::IsRunning() const {
  RTC_DCHECK(thread_checker_.CalledOnValidThread());
#if defined(WEBRTC_WIN)
  return thread_ != nullptr;
#else
  return thread_ != 0;
#endif  // defined(WEBRTC_WIN)
}

PlatformThreadRef PlatformThread::GetThreadRef() const {
#if defined(WEBRTC_WIN)
  return thread_id_;
#else
  return thread_;
#endif  // defined(WEBRTC_WIN)
}

void PlatformThread::Stop() {
  RTC_DCHECK(thread_checker_.CalledOnValidThread());
  if (!IsRunning())
    return;

#if defined(WEBRTC_WIN)
  // Set stop_ to |true| on the worker thread.
  bool queued = QueueAPC(&RaiseFlag, reinterpret_cast<ULONG_PTR>(&stop_));
  // Queuing the APC can fail if the thread is being terminated.
  RTC_CHECK(queued || GetLastError() == ERROR_GEN_FAILURE);
  WaitForSingleObject(thread_, INFINITE);
  CloseHandle(thread_);
  thread_ = nullptr;
  thread_id_ = 0;
#else
  if (!run_function_)
    RTC_CHECK_EQ(1, AtomicOps::Increment(&stop_flag_));
  RTC_CHECK_EQ(0, pthread_join(thread_, nullptr));
  if (!run_function_)
    AtomicOps::ReleaseStore(&stop_flag_, 0);
  thread_ = 0;
#endif  // defined(WEBRTC_WIN)
  spawned_thread_checker_.DetachFromThread();
}

// TODO(tommi): Deprecate the loop behavior in PlatformThread.
// * Introduce a new callback type that returns void.
// * Remove potential for a busy loop in PlatformThread.
// * Delegate the responsibility for how to stop the thread, to the
//   implementation that actually uses the thread.
// All implementations will need to be aware of how the thread should be stopped
// and encouraging a busy polling loop, can be costly in terms of power and cpu.
void PlatformThread::Run() {
  // Attach the worker thread checker to this thread.
  RTC_DCHECK(spawned_thread_checker_.CalledOnValidThread());
  rtc::SetCurrentThreadName(name_.c_str());

  if (run_function_) {
    SetPriority(priority_);
    run_function_(obj_);
    return;
  }
// TODO(tommi): Delete the below.
  do {
    // The interface contract of Start/Stop is that for a successful call to
    // Start, there should be at least one call to the run function.  So we
    // call the function before checking |stop_|.
    if (!run_function_deprecated_(obj_))
      break;
#if defined(WEBRTC_WIN)
    // Alertable sleep to permit RaiseFlag to run and update |stop_|.
    SleepEx(0, true);
  } while (!stop_);
#else
    sched_yield();
  } while (!AtomicOps::AcquireLoad(&stop_flag_));
#endif  // defined(WEBRTC_WIN)
}

bool PlatformThread::SetPriority(ThreadPriority priority) {
#if RTC_DCHECK_IS_ON
  if (run_function_) {
    // The non-deprecated way of how this function gets called, is that it must
    // be called on the worker thread itself.
    RTC_DCHECK(spawned_thread_checker_.CalledOnValidThread());
  } else {
    // In the case of deprecated use of this method, it must be called on the
    // same thread as the PlatformThread object is constructed on.
    RTC_DCHECK(thread_checker_.CalledOnValidThread());
    RTC_DCHECK(IsRunning());
  }
#endif

#if defined(WEBRTC_WIN)
  return SetThreadPriority(thread_, priority) != FALSE;
#elif defined(__native_client__)
  // Setting thread priorities is not supported in NaCl.
  return true;
#elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX)
  // TODO(tommi): Switch to the same mechanism as Chromium uses for changing
  // thread priorities.
  return true;
#else
#ifdef WEBRTC_THREAD_RR
  const int policy = SCHED_RR;
#else
  const int policy = SCHED_FIFO;
#endif
  const int min_prio = sched_get_priority_min(policy);
  const int max_prio = sched_get_priority_max(policy);
  if (min_prio == -1 || max_prio == -1) {
    return false;
  }

  if (max_prio - min_prio <= 2)
    return false;

  // Convert webrtc priority to system priorities:
  sched_param param;
  const int top_prio = max_prio - 1;
  const int low_prio = min_prio + 1;
  switch (priority) {
    case kLowPriority:
      param.sched_priority = low_prio;
      break;
    case kNormalPriority:
      // The -1 ensures that the kHighPriority is always greater or equal to
      // kNormalPriority.
      param.sched_priority = (low_prio + top_prio - 1) / 2;
      break;
    case kHighPriority:
      param.sched_priority = std::max(top_prio - 2, low_prio);
      break;
    case kHighestPriority:
      param.sched_priority = std::max(top_prio - 1, low_prio);
      break;
    case kRealtimePriority:
      param.sched_priority = top_prio;
      break;
  }
  return pthread_setschedparam(thread_, policy, &param) == 0;
#endif  // defined(WEBRTC_WIN)
}

#if defined(WEBRTC_WIN)
bool PlatformThread::QueueAPC(PAPCFUNC function, ULONG_PTR data) {
  RTC_DCHECK(thread_checker_.CalledOnValidThread());
  RTC_DCHECK(IsRunning());

  return QueueUserAPC(function, thread_, data) != FALSE;
}
#endif

}  // namespace rtc
<|endoftext|>


<commit_before>#include "telnetpp/routing_visitor.hpp"
#include "telnetpp/options/naws/client.hpp"
#include "telnetpp/options/naws.hpp"
#include "telnetpp/protocol.hpp"
#include "expect_elements.hpp"
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>

class routing_visitor_test : public CppUnit::TestFixture
{
public :
    CPPUNIT_TEST_SUITE(routing_visitor_test);
        CPPUNIT_TEST(text_routes_to_text_function);
        CPPUNIT_TEST(text_does_not_route_to_null_function);
        CPPUNIT_TEST(command_routes_to_command_router);
        CPPUNIT_TEST(negotiation_routes_to_negotiation_router);
        CPPUNIT_TEST(subnegotiation_routes_to_subnegotiation_router);
        CPPUNIT_TEST(subnegotiation_accumulates_responses);
    CPPUNIT_TEST_SUITE_END();

private :
    void text_routes_to_text_function();
    void text_does_not_route_to_null_function();
    void command_routes_to_command_router();
    void negotiation_routes_to_negotiation_router();
    void subnegotiation_routes_to_subnegotiation_router();
    void subnegotiation_accumulates_responses();
};

CPPUNIT_TEST_SUITE_REGISTRATION(routing_visitor_test);

void routing_visitor_test::text_routes_to_text_function()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;

    std::string text;
    std::string expected_text = "expected text";

    telnetpp::routing_visitor visitor(
        [&text](auto &&new_text) -> std::vector<telnetpp::token>
        {
            text = new_text;
            return {};
        },
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    telnetpp::element text_token(expected_text);
    boost::apply_visitor(visitor, text_token);

    CPPUNIT_ASSERT_EQUAL(expected_text, text);
}

void routing_visitor_test::text_does_not_route_to_null_function()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;

    telnetpp::routing_visitor visitor(
        nullptr,
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    telnetpp::token text_token(telnetpp::element("text token"));
    boost::apply_visitor(visitor, text_token);
}

void routing_visitor_test::command_routes_to_command_router()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;

    telnetpp::command cmd(0x00);
    telnetpp::command expected_command(telnetpp::ayt);

    cmd_router.register_route(expected_command,
        [&cmd](auto &&new_cmd) -> std::vector<telnetpp::token>
        {
            cmd = new_cmd;
            return {};
        });

    telnetpp::routing_visitor visitor(
        [](auto &&) -> std::vector<telnetpp::token>
        {
            return {};
        },
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    telnetpp::element cmd_token(expected_command);
    boost::apply_visitor(visitor, cmd_token);

    CPPUNIT_ASSERT_EQUAL(expected_command, cmd);
}

void routing_visitor_test::negotiation_routes_to_negotiation_router()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;
    telnetpp::options::naws::client client;
    client.activate();

    bool state_changed = false;
    client.on_state_changed.connect(
        [&state_changed]() -> std::vector<telnetpp::token>
        {
            state_changed = true;
            return {};
        });

    telnetpp::register_client_option(client, neg_router, sub_router);

    telnetpp::routing_visitor visitor(
        [](auto &&) -> std::vector<telnetpp::token>
        {
            return {};
        },
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    telnetpp::element neg_token(
        telnetpp::negotiation(
            telnetpp::will, telnetpp::options::naws::option));

    boost::apply_visitor(visitor, neg_token);

    CPPUNIT_ASSERT_EQUAL(true, state_changed);
    CPPUNIT_ASSERT_EQUAL(true, client.is_active());
}

void routing_visitor_test::subnegotiation_routes_to_subnegotiation_router()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;
    telnetpp::options::naws::client client;
    client.activate();
    client.negotiate(telnetpp::will);

    telnetpp::u16 width = 0;
    telnetpp::u16 height = 0;
    client.on_window_size_changed.connect(
        [&width, &height](auto &&new_width, auto &&new_height)
            -> std::vector<telnetpp::token>
        {
            width = new_width;
            height = new_height;
            return {};
        });

    telnetpp::register_route_from_subnegotiation_to_option(
        sub_router, client);

    telnetpp::routing_visitor visitor(
        [](auto &&) -> std::vector<telnetpp::token>
        {
            return {};
        },
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    telnetpp::element sub_token(
        telnetpp::subnegotiation(
            telnetpp::options::naws::option,
            {0, 80, 0, 24}));

    boost::apply_visitor(visitor, sub_token);

    telnetpp::u16 expected_width = 80;
    telnetpp::u16 expected_height = 24;

    CPPUNIT_ASSERT_EQUAL(expected_width, width);
    CPPUNIT_ASSERT_EQUAL(expected_height, height);
}

void routing_visitor_test::subnegotiation_accumulates_responses()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;
    telnetpp::options::naws::client client;

    telnetpp::register_client_option(client, neg_router, sub_router);

    client.on_window_size_changed.connect(
        [&client](auto &&, auto &&)
        {
            return client.deactivate();
        });

    telnetpp::routing_visitor visitor(
        [](auto &&) -> std::vector<telnetpp::token>
        {
            return {};
        },
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    client.activate();
    telnetpp::element do_negotiation_token(
        telnetpp::negotiation(telnetpp::will, telnetpp::options::naws::option));

    expect_elements(
        {},
        boost::apply_visitor(visitor, do_negotiation_token));

    telnetpp::element sub_token(
        telnetpp::subnegotiation(
            telnetpp::options::naws::option,
            {0, 80, 0, 24}));

    expect_elements({
        telnetpp::negotiation(
            telnetpp::dont, telnetpp::options::naws::option)
        },
        boost::apply_visitor(visitor, sub_token));
}
<commit_msg>Added a routing test for arbitrary objects.<commit_after>#include "telnetpp/routing_visitor.hpp"
#include "telnetpp/options/naws/client.hpp"
#include "telnetpp/options/naws.hpp"
#include "telnetpp/protocol.hpp"
#include "expect_elements.hpp"
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>

class routing_visitor_test : public CppUnit::TestFixture
{
public :
    CPPUNIT_TEST_SUITE(routing_visitor_test);
        CPPUNIT_TEST(text_routes_to_text_function);
        CPPUNIT_TEST(text_does_not_route_to_null_function);
        CPPUNIT_TEST(command_routes_to_command_router);
        CPPUNIT_TEST(negotiation_routes_to_negotiation_router);
        CPPUNIT_TEST(subnegotiation_routes_to_subnegotiation_router);
        CPPUNIT_TEST(subnegotiation_accumulates_responses);
        CPPUNIT_TEST(arbitrary_object_routes_to_pass_through);
    CPPUNIT_TEST_SUITE_END();

private :
    void text_routes_to_text_function();
    void text_does_not_route_to_null_function();
    void command_routes_to_command_router();
    void negotiation_routes_to_negotiation_router();
    void subnegotiation_routes_to_subnegotiation_router();
    void subnegotiation_accumulates_responses();
    void arbitrary_object_routes_to_pass_through();
};

CPPUNIT_TEST_SUITE_REGISTRATION(routing_visitor_test);

void routing_visitor_test::text_routes_to_text_function()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;

    std::string text;
    std::string expected_text = "expected text";

    telnetpp::routing_visitor visitor(
        [&text](auto &&new_text) -> std::vector<telnetpp::token>
        {
            text = new_text;
            return {};
        },
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    telnetpp::element text_token(expected_text);
    boost::apply_visitor(visitor, text_token);

    CPPUNIT_ASSERT_EQUAL(expected_text, text);
}

void routing_visitor_test::text_does_not_route_to_null_function()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;

    telnetpp::routing_visitor visitor(
        nullptr,
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    telnetpp::token text_token(telnetpp::element("text token"));
    boost::apply_visitor(visitor, text_token);
}

void routing_visitor_test::command_routes_to_command_router()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;

    telnetpp::command cmd(0x00);
    telnetpp::command expected_command(telnetpp::ayt);

    cmd_router.register_route(expected_command,
        [&cmd](auto &&new_cmd) -> std::vector<telnetpp::token>
        {
            cmd = new_cmd;
            return {};
        });

    telnetpp::routing_visitor visitor(
        [](auto &&) -> std::vector<telnetpp::token>
        {
            return {};
        },
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    telnetpp::element cmd_token(expected_command);
    boost::apply_visitor(visitor, cmd_token);

    CPPUNIT_ASSERT_EQUAL(expected_command, cmd);
}

void routing_visitor_test::negotiation_routes_to_negotiation_router()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;
    telnetpp::options::naws::client client;
    client.activate();

    bool state_changed = false;
    client.on_state_changed.connect(
        [&state_changed]() -> std::vector<telnetpp::token>
        {
            state_changed = true;
            return {};
        });

    telnetpp::register_client_option(client, neg_router, sub_router);

    telnetpp::routing_visitor visitor(
        [](auto &&) -> std::vector<telnetpp::token>
        {
            return {};
        },
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    telnetpp::element neg_token(
        telnetpp::negotiation(
            telnetpp::will, telnetpp::options::naws::option));

    boost::apply_visitor(visitor, neg_token);

    CPPUNIT_ASSERT_EQUAL(true, state_changed);
    CPPUNIT_ASSERT_EQUAL(true, client.is_active());
}

void routing_visitor_test::subnegotiation_routes_to_subnegotiation_router()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;
    telnetpp::options::naws::client client;
    client.activate();
    client.negotiate(telnetpp::will);

    telnetpp::u16 width = 0;
    telnetpp::u16 height = 0;
    client.on_window_size_changed.connect(
        [&width, &height](auto &&new_width, auto &&new_height)
            -> std::vector<telnetpp::token>
        {
            width = new_width;
            height = new_height;
            return {};
        });

    telnetpp::register_route_from_subnegotiation_to_option(
        sub_router, client);

    telnetpp::routing_visitor visitor(
        [](auto &&) -> std::vector<telnetpp::token>
        {
            return {};
        },
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    telnetpp::element sub_token(
        telnetpp::subnegotiation(
            telnetpp::options::naws::option,
            {0, 80, 0, 24}));

    boost::apply_visitor(visitor, sub_token);

    telnetpp::u16 expected_width = 80;
    telnetpp::u16 expected_height = 24;

    CPPUNIT_ASSERT_EQUAL(expected_width, width);
    CPPUNIT_ASSERT_EQUAL(expected_height, height);
}

void routing_visitor_test::subnegotiation_accumulates_responses()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;
    telnetpp::options::naws::client client;

    telnetpp::register_client_option(client, neg_router, sub_router);

    client.on_window_size_changed.connect(
        [&client](auto &&, auto &&)
        {
            return client.deactivate();
        });

    telnetpp::routing_visitor visitor(
        [](auto &&) -> std::vector<telnetpp::token>
        {
            return {};
        },
        nullptr,
        cmd_router,
        neg_router,
        sub_router);

    client.activate();
    telnetpp::element do_negotiation_token(
        telnetpp::negotiation(telnetpp::will, telnetpp::options::naws::option));

    expect_elements(
        {},
        boost::apply_visitor(visitor, do_negotiation_token));

    telnetpp::element sub_token(
        telnetpp::subnegotiation(
            telnetpp::options::naws::option,
            {0, 80, 0, 24}));

    expect_elements({
        telnetpp::negotiation(
            telnetpp::dont, telnetpp::options::naws::option)
        },
        boost::apply_visitor(visitor, sub_token));
}

void routing_visitor_test::arbitrary_object_routes_to_pass_through()
{
    telnetpp::command_router cmd_router;
    telnetpp::negotiation_router neg_router;
    telnetpp::subnegotiation_router sub_router;
    telnetpp::options::naws::client client;

    boost::any any;
    telnetpp::routing_visitor visitor(
        nullptr,
        [&any](auto &&object) -> std::vector<telnetpp::token>
        {
            any = object;
            return {};
        },
        cmd_router,
        neg_router,
        sub_router);

    std::string test_string = "TEST";
    telnetpp::token test_token(boost::any{test_string});

    boost::apply_visitor(visitor, test_token);

    CPPUNIT_ASSERT_EQUAL(
        test_string, boost::any_cast<std::string const&>(any));
}<|endoftext|>


<commit_before>// Copyright (c) 2021 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "SessionSetup/TargetLabel.h"

#include <QAction>
#include <QDesktopServices>
#include <QImage>
#include <QMenu>
#include <QPalette>
#include <QPixmap>
#include <QUrl>
#include <memory>
#include <optional>

#include "SessionSetup/DoubleClickableLabel.h"
#include "ui_TargetLabel.h"

namespace {
const QColor kDefaultTextColor{"white"};
const QColor kGreenColor{"#66BB6A"};
const QColor kOrangeColor{"orange"};
const QColor kRedColor{"#E64646"};
const QString kLocalhostName{"localhost"};

QPixmap ColorizeIcon(const QPixmap& pixmap, const QColor& color) {
  QImage colored_image = pixmap.toImage();

  for (int y = 0; y < colored_image.height(); y++) {
    for (int x = 0; x < colored_image.width(); x++) {
      QColor color_with_alpha = color;
      color_with_alpha.setAlpha(colored_image.pixelColor(x, y).alpha());
      colored_image.setPixelColor(x, y, color_with_alpha);
    }
  }

  return QPixmap::fromImage(std::move(colored_image));
}

QPixmap GetGreenConnectedIcon() {
  const static QPixmap kGreenConnectedIcon =
      ColorizeIcon(QPixmap{":/actions/connected"}, kGreenColor);
  return kGreenConnectedIcon;
}

QPixmap GetOrangeDisconnectedIcon() {
  const static QPixmap kOrangeDisconnectedIcon =
      ColorizeIcon(QPixmap{":/actions/alert"}, kOrangeColor);
  return kOrangeDisconnectedIcon;
}

QPixmap GetRedDisconnectedIcon() {
  const static QPixmap kRedDisconnectedIcon =
      ColorizeIcon(QPixmap{":/actions/disconnected"}, kRedColor);
  return kRedDisconnectedIcon;
}

}  // namespace

namespace orbit_session_setup {

namespace fs = std::filesystem;

TargetLabel::TargetLabel(QWidget* parent)
    : QWidget(parent), ui_(std::make_unique<Ui::TargetLabel>()) {
  ui_->setupUi(this);

  QObject::connect(ui_->fileLabel, &DoubleClickableLabel::DoubleClicked, this,
                   &TargetLabel::OpenContainingFolder);

  QObject::connect(ui_->fileLabel, &DoubleClickableLabel::customContextMenuRequested, this,
                   [this](const QPoint& pos) {
                     QAction action{QIcon(":/actions/folder"), "Open Containing Folder", this};
                     QObject::connect(&action, &QAction::triggered, this,
                                      &TargetLabel::OpenContainingFolder);

                     QMenu menu;
                     menu.addAction(&action);
                     menu.exec(mapToGlobal(pos));
                   });
}

TargetLabel::~TargetLabel() = default;

void TargetLabel::ChangeToFileTarget(const FileTarget& file_target) {
  ChangeToFileTarget(file_target.GetCaptureFilePath());
}

void TargetLabel::SetFile(const std::filesystem::path& file_path) {
  file_path_ = file_path;
  ui_->fileLabel->setText(QString::fromStdString(file_path.filename().string()));
  ui_->fileLabel->setToolTip(QString::fromStdString(file_path.string()));
  ui_->fileLabel->setVisible(true);
  emit SizeChanged();
}

void TargetLabel::ChangeToFileTarget(const fs::path& path) {
  Clear();
  SetFile(path);
  ui_->targetLabel->setVisible(false);
  emit SizeChanged();
}

void TargetLabel::ChangeToStadiaTarget(const StadiaTarget& stadia_target) {
  ChangeToStadiaTarget(*stadia_target.GetProcess(), stadia_target.GetConnection()->GetInstance());
}

void TargetLabel::ChangeToStadiaTarget(const orbit_client_data::ProcessData& process,
                                       const orbit_ggp::Instance& instance) {
  ChangeToStadiaTarget(QString::fromStdString(process.name()), process.cpu_usage(),
                       instance.display_name);
}

void TargetLabel::ChangeToStadiaTarget(const QString& process_name, double cpu_usage,
                                       const QString& instance_name) {
  Clear();
  process_ = process_name;
  machine_ = instance_name;
  SetProcessCpuUsageInPercent(cpu_usage);
  ui_->targetLabel->setVisible(true);
  ui_->fileLabel->setVisible(false);
}

void TargetLabel::ChangeToLocalTarget(const LocalTarget& local_target) {
  ChangeToLocalTarget(*local_target.GetProcess());
}

void TargetLabel::ChangeToLocalTarget(const orbit_client_data::ProcessData& process) {
  ChangeToLocalTarget(QString::fromStdString(process.name()), process.cpu_usage());
}

void TargetLabel::ChangeToLocalTarget(const QString& process_name, double cpu_usage) {
  Clear();
  process_ = process_name;
  machine_ = kLocalhostName;
  SetProcessCpuUsageInPercent(cpu_usage);
  ui_->targetLabel->setVisible(true);
  ui_->fileLabel->setVisible(false);
}

bool TargetLabel::SetProcessCpuUsageInPercent(double cpu_usage) {
  if (process_.isEmpty() || machine_.isEmpty()) return false;

  ui_->targetLabel->setText(
      QString{"%1 (%2%) @ %3"}.arg(process_).arg(cpu_usage, 0, 'f', 0).arg(machine_));
  SetColor(kGreenColor);
  setToolTip({});
  SetIcon(IconType::kGreenConnectedIcon);
  emit SizeChanged();
  return true;
}

bool TargetLabel::SetProcessEnded() {
  if (process_.isEmpty() || machine_.isEmpty()) return false;

  ui_->targetLabel->setText(QString{"%1 @ %2"}.arg(process_, machine_));
  SetColor(kOrangeColor);
  setToolTip("The process ended.");
  SetIcon(IconType::kOrangeDisconnectedIcon);
  emit SizeChanged();
  return true;
}

bool TargetLabel::SetConnectionDead(const QString& error_message) {
  if (process_.isEmpty() || machine_.isEmpty()) return false;

  ui_->targetLabel->setText(QString{"%1 @ %2"}.arg(process_, machine_));
  SetColor(kRedColor);
  setToolTip(error_message);
  SetIcon(IconType::kRedDisconnectedIcon);
  emit SizeChanged();
  return true;
}

void TargetLabel::Clear() {
  process_ = "";
  machine_ = "";
  file_path_.reset();
  ui_->fileLabel->setText({});
  ui_->targetLabel->setText({});
  ui_->fileLabel->setVisible(false);
  ui_->targetLabel->setVisible(false);
  SetColor(kDefaultTextColor);
  setToolTip({});
  ClearIcon();
  emit SizeChanged();
}

QColor TargetLabel::GetTargetColor() const {
  return ui_->targetLabel->palette().color(QPalette::WindowText);
}

QString TargetLabel::GetTargetText() const { return ui_->targetLabel->text(); }

QString TargetLabel::GetFileText() const { return ui_->fileLabel->text(); }

void TargetLabel::SetColor(const QColor& color) {
  // This class is used in a QFrame and in QMenuBar. To make the coloring work in a QFrame the
  // QColorRole QPalette::WindowText needs to be set. For QMenuBar QPalette::ButtonText needs to be
  // set.
  QPalette palette{};
  palette.setColor(QPalette::WindowText, color);
  palette.setColor(QPalette::ButtonText, color);
  ui_->targetLabel->setPalette(palette);
}

void TargetLabel::SetIcon(IconType icon_type) {
  icon_type_ = icon_type;
  switch (icon_type) {
    case IconType::kGreenConnectedIcon:
      ui_->iconLabel->setPixmap(GetGreenConnectedIcon());
      break;
    case IconType::kOrangeDisconnectedIcon:
      ui_->iconLabel->setPixmap(GetOrangeDisconnectedIcon());
      break;
    case IconType::kRedDisconnectedIcon:
      ui_->iconLabel->setPixmap(GetRedDisconnectedIcon());
      break;
  }
  ui_->iconLabel->setVisible(true);
}

void TargetLabel::ClearIcon() {
  icon_type_ = std::nullopt;
  ui_->iconLabel->setPixmap(QPixmap{});
  ui_->iconLabel->setVisible(false);
}

void TargetLabel::OpenContainingFolder() {
  if (!file_path_.has_value()) return;
  QUrl url = QUrl::fromLocalFile(QString::fromStdString(file_path_->parent_path().string()));
  if (!QDesktopServices::openUrl(url)) {
    ERROR("Opening containing folder of \"%s\"", file_path_->string());
  }
}

}  // namespace orbit_session_setup
<commit_msg>TargetLabel is no longer updated unless needed (#2812)<commit_after>// Copyright (c) 2021 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "SessionSetup/TargetLabel.h"

#include <QAction>
#include <QDesktopServices>
#include <QImage>
#include <QMenu>
#include <QPalette>
#include <QPixmap>
#include <QUrl>
#include <memory>
#include <optional>

#include "SessionSetup/DoubleClickableLabel.h"
#include "ui_TargetLabel.h"

namespace {
const QColor kDefaultTextColor{"white"};
const QColor kGreenColor{"#66BB6A"};
const QColor kOrangeColor{"orange"};
const QColor kRedColor{"#E64646"};
const QString kLocalhostName{"localhost"};

QPixmap ColorizeIcon(const QPixmap& pixmap, const QColor& color) {
  QImage colored_image = pixmap.toImage();

  for (int y = 0; y < colored_image.height(); y++) {
    for (int x = 0; x < colored_image.width(); x++) {
      QColor color_with_alpha = color;
      color_with_alpha.setAlpha(colored_image.pixelColor(x, y).alpha());
      colored_image.setPixelColor(x, y, color_with_alpha);
    }
  }

  return QPixmap::fromImage(std::move(colored_image));
}

QPixmap GetGreenConnectedIcon() {
  const static QPixmap kGreenConnectedIcon =
      ColorizeIcon(QPixmap{":/actions/connected"}, kGreenColor);
  return kGreenConnectedIcon;
}

QPixmap GetOrangeDisconnectedIcon() {
  const static QPixmap kOrangeDisconnectedIcon =
      ColorizeIcon(QPixmap{":/actions/alert"}, kOrangeColor);
  return kOrangeDisconnectedIcon;
}

QPixmap GetRedDisconnectedIcon() {
  const static QPixmap kRedDisconnectedIcon =
      ColorizeIcon(QPixmap{":/actions/disconnected"}, kRedColor);
  return kRedDisconnectedIcon;
}

}  // namespace

namespace orbit_session_setup {

namespace fs = std::filesystem;

TargetLabel::TargetLabel(QWidget* parent)
    : QWidget(parent), ui_(std::make_unique<Ui::TargetLabel>()) {
  ui_->setupUi(this);

  QObject::connect(ui_->fileLabel, &DoubleClickableLabel::DoubleClicked, this,
                   &TargetLabel::OpenContainingFolder);

  QObject::connect(ui_->fileLabel, &DoubleClickableLabel::customContextMenuRequested, this,
                   [this](const QPoint& pos) {
                     QAction action{QIcon(":/actions/folder"), "Open Containing Folder", this};
                     QObject::connect(&action, &QAction::triggered, this,
                                      &TargetLabel::OpenContainingFolder);

                     QMenu menu;
                     menu.addAction(&action);
                     menu.exec(mapToGlobal(pos));
                   });
}

TargetLabel::~TargetLabel() = default;

void TargetLabel::ChangeToFileTarget(const FileTarget& file_target) {
  ChangeToFileTarget(file_target.GetCaptureFilePath());
}

void TargetLabel::SetFile(const std::filesystem::path& file_path) {
  // Without this, the size of the target label is not correctly updated when a capture is opened
  // directly from the connection window. It's unclear why this happens, might be related to
  // multiple `SizeChanged` signals being emitted while the main window is still being shown.
  if (file_path_ == file_path) return;

  file_path_ = file_path;
  ui_->fileLabel->setText(QString::fromStdString(file_path.filename().string()));
  ui_->fileLabel->setToolTip(QString::fromStdString(file_path.string()));
  ui_->fileLabel->setVisible(true);
  emit SizeChanged();
}

void TargetLabel::ChangeToFileTarget(const fs::path& path) {
  Clear();
  SetFile(path);
  ui_->targetLabel->setVisible(false);
  emit SizeChanged();
}

void TargetLabel::ChangeToStadiaTarget(const StadiaTarget& stadia_target) {
  ChangeToStadiaTarget(*stadia_target.GetProcess(), stadia_target.GetConnection()->GetInstance());
}

void TargetLabel::ChangeToStadiaTarget(const orbit_client_data::ProcessData& process,
                                       const orbit_ggp::Instance& instance) {
  ChangeToStadiaTarget(QString::fromStdString(process.name()), process.cpu_usage(),
                       instance.display_name);
}

void TargetLabel::ChangeToStadiaTarget(const QString& process_name, double cpu_usage,
                                       const QString& instance_name) {
  Clear();
  process_ = process_name;
  machine_ = instance_name;
  SetProcessCpuUsageInPercent(cpu_usage);
  ui_->targetLabel->setVisible(true);
  ui_->fileLabel->setVisible(false);
}

void TargetLabel::ChangeToLocalTarget(const LocalTarget& local_target) {
  ChangeToLocalTarget(*local_target.GetProcess());
}

void TargetLabel::ChangeToLocalTarget(const orbit_client_data::ProcessData& process) {
  ChangeToLocalTarget(QString::fromStdString(process.name()), process.cpu_usage());
}

void TargetLabel::ChangeToLocalTarget(const QString& process_name, double cpu_usage) {
  Clear();
  process_ = process_name;
  machine_ = kLocalhostName;
  SetProcessCpuUsageInPercent(cpu_usage);
  ui_->targetLabel->setVisible(true);
  ui_->fileLabel->setVisible(false);
}

bool TargetLabel::SetProcessCpuUsageInPercent(double cpu_usage) {
  if (process_.isEmpty() || machine_.isEmpty()) return false;

  ui_->targetLabel->setText(
      QString{"%1 (%2%) @ %3"}.arg(process_).arg(cpu_usage, 0, 'f', 0).arg(machine_));
  SetColor(kGreenColor);
  setToolTip({});
  SetIcon(IconType::kGreenConnectedIcon);
  emit SizeChanged();
  return true;
}

bool TargetLabel::SetProcessEnded() {
  if (process_.isEmpty() || machine_.isEmpty()) return false;

  ui_->targetLabel->setText(QString{"%1 @ %2"}.arg(process_, machine_));
  SetColor(kOrangeColor);
  setToolTip("The process ended.");
  SetIcon(IconType::kOrangeDisconnectedIcon);
  emit SizeChanged();
  return true;
}

bool TargetLabel::SetConnectionDead(const QString& error_message) {
  if (process_.isEmpty() || machine_.isEmpty()) return false;

  ui_->targetLabel->setText(QString{"%1 @ %2"}.arg(process_, machine_));
  SetColor(kRedColor);
  setToolTip(error_message);
  SetIcon(IconType::kRedDisconnectedIcon);
  emit SizeChanged();
  return true;
}

void TargetLabel::Clear() {
  process_ = "";
  machine_ = "";
  file_path_.reset();
  ui_->fileLabel->setText({});
  ui_->targetLabel->setText({});
  ui_->fileLabel->setVisible(false);
  ui_->targetLabel->setVisible(false);
  SetColor(kDefaultTextColor);
  setToolTip({});
  ClearIcon();
  emit SizeChanged();
}

QColor TargetLabel::GetTargetColor() const {
  return ui_->targetLabel->palette().color(QPalette::WindowText);
}

QString TargetLabel::GetTargetText() const { return ui_->targetLabel->text(); }

QString TargetLabel::GetFileText() const { return ui_->fileLabel->text(); }

void TargetLabel::SetColor(const QColor& color) {
  // This class is used in a QFrame and in QMenuBar. To make the coloring work in a QFrame the
  // QColorRole QPalette::WindowText needs to be set. For QMenuBar QPalette::ButtonText needs to be
  // set.
  QPalette palette{};
  palette.setColor(QPalette::WindowText, color);
  palette.setColor(QPalette::ButtonText, color);
  ui_->targetLabel->setPalette(palette);
}

void TargetLabel::SetIcon(IconType icon_type) {
  icon_type_ = icon_type;
  switch (icon_type) {
    case IconType::kGreenConnectedIcon:
      ui_->iconLabel->setPixmap(GetGreenConnectedIcon());
      break;
    case IconType::kOrangeDisconnectedIcon:
      ui_->iconLabel->setPixmap(GetOrangeDisconnectedIcon());
      break;
    case IconType::kRedDisconnectedIcon:
      ui_->iconLabel->setPixmap(GetRedDisconnectedIcon());
      break;
  }
  ui_->iconLabel->setVisible(true);
}

void TargetLabel::ClearIcon() {
  icon_type_ = std::nullopt;
  ui_->iconLabel->setPixmap(QPixmap{});
  ui_->iconLabel->setVisible(false);
}

void TargetLabel::OpenContainingFolder() {
  if (!file_path_.has_value()) return;
  QUrl url = QUrl::fromLocalFile(QString::fromStdString(file_path_->parent_path().string()));
  if (!QDesktopServices::openUrl(url)) {
    ERROR("Opening containing folder of \"%s\"", file_path_->string());
  }
}

}  // namespace orbit_session_setup
<|endoftext|>


<commit_before>/*
   Copyright (C) 2014-2015 Alexandr Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

   This library 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 library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

 */

#include <QCoreApplication>
#include <QDebug>
#include <QFile>
#include <QNetworkAccessManager>
#include <QNetworkReply>

#include <QJsonDocument>

#include "GeneratorNG.hpp"

enum StatusCode {
    NoError,
    InvalidAction,
    InvalidArgument,
    SchemaReadError,
    NetworkError,
    ServerError,
    FileAccessError
};

enum SchemaFormat {
    JsonFormat,
    TextFormat,
};

/* Replacing helper */
bool replaceSection(const QString &fileName, const QString &startMarker, const QString &endMarker, const QString &newContent)
{
    QFile fileToProcess(fileName);

    if (!fileToProcess.open(QIODevice::ReadOnly))
        return false;

    QString fileContent = fileToProcess.readAll();
    fileToProcess.close();

    bool winNewLines = fileContent.indexOf(QLatin1String("\r\n")) > 0;

    if (winNewLines) {
        fileContent.replace(QLatin1String("\r\n"), QLatin1String("\n"));
    }

    int startPos = fileContent.indexOf(startMarker);
    int endPos   = fileContent.indexOf(endMarker);

    if (startPos >= 0) {
        if (endPos < 0) {
            printf("There is only start marker in the file %s. Error.\n", fileName.toLocal8Bit().constData());
            return false;
        }

        endPos += endMarker.length();
    } else {
        return false;
    }

    const QString previousContent = fileContent.mid(startPos, endPos - startPos);

    if (previousContent == startMarker + newContent + endMarker) {
        printf("Nothing to do: new and previous contents are exactly same.\n");
        return true;
    }

    fileContent.remove(startPos, endPos - startPos);
    fileContent.insert(startPos, startMarker + newContent + endMarker);

    if (!fileToProcess.open(QIODevice::WriteOnly)) {
        printf("Can not write file: %s.\n", fileName.toLocal8Bit().constData());
        return false;
    }

    if (winNewLines) {
        fileContent.replace(QLatin1String("\n"), QLatin1String("\r\n"));
    }

    fileToProcess.write(fileContent.toLatin1());

    fileToProcess.close();

    printf("Replacing is done.\n");

    return true;
}

bool replacingHelper(const QString &fileName, int spacing, const QString &marker, const QString &newContent)
{
    const QString space(spacing, QChar(' '));

    if (!replaceSection(fileName,
                        QString("%1// Generated %2\n").arg(space).arg(marker),
                        QString("%1// End of generated %2\n").arg(space).arg(marker), newContent)) {
        printf("Can not update file %s with marker %s.\n", fileName.toLatin1().constData(), marker.toLatin1().constData());
        return false;
    } else {
        return true;
    }
}

/* End of replacing helper */

void debugType(const TLType &type)
{
    qDebug() << type.name;
    foreach (const TLSubType &subType, type.subTypes) {
        qDebug() << "    " << subType.name;
        foreach (const TLParam &member, subType.members) {
            qDebug() << "        " << member.type << member.name;
        }
    }
}

StatusCode fetchJson(const QString &specFileName)
{
    QEventLoop eventLoop;
    QNetworkAccessManager mgr;
    QObject::connect(&mgr, &QNetworkAccessManager::finished, &eventLoop, &QEventLoop::quit);

    QNetworkRequest req(QUrl(QLatin1String("https://core.telegram.org/schema/json")));
    QNetworkReply *reply = mgr.get(req);

    eventLoop.exec();

    if (reply->error() != QNetworkReply::NoError) {
        delete reply;
        return NetworkError;
    }

    QByteArray data = reply->readAll();
    delete reply;

    if (data.isEmpty()) {
        return ServerError;
    }

    QFile specsOutFile(specFileName);
    if (!specsOutFile.open(QIODevice::WriteOnly)) {
        return FileAccessError;
    }

    QJsonDocument document = QJsonDocument::fromJson(data);
    specsOutFile.write(document.toJson());
    specsOutFile.close();

    printf("Spec file successfully downloaded (and formatted).\n");
    return NoError; // Not implemented
}

StatusCode format(const QString &specFileName)
{
    QFile specsFile(specFileName);
    if (!specsFile.open(QIODevice::ReadOnly)) {
        return FileAccessError;
    }

    QByteArray data = specsFile.readAll();

    specsFile.close();

    if (!specsFile.open(QIODevice::WriteOnly)) {
        return FileAccessError;
    }

    QJsonDocument document = QJsonDocument::fromJson(data);
    specsFile.write(document.toJson());
    specsFile.close();

    printf("Spec file successfully formatted.\n");
    return NoError;
}

StatusCode generate(SchemaFormat format, const QString &specFileName)
{
    QFile specsFile(specFileName);
    specsFile.open(QIODevice::ReadOnly);

    const QByteArray data = specsFile.readAll();

    if (data.isEmpty()) {
        printf("Unable to read the file.\n");
        return InvalidArgument;
    }

    specsFile.close();

    GeneratorNG generator;

    bool success = true;

    switch (format) {
    case JsonFormat:
        success = generator.loadDataFromJson(data);
        break;
    case TextFormat:
        success = generator.loadDataFromText(data);
        break;
    }

    if (!success) {
        printf("Unable to parse the scheme.\n");
        return SchemaReadError;
    }

    generator.generate();

    replacingHelper(QLatin1String("../TLValues.hpp"), 8, QLatin1String("TLValues"), generator.codeOfTLValues);
    replacingHelper(QLatin1String("../TLTypes.hpp"), 0, QLatin1String("TLTypes"), generator.codeOfTLTypes);
    replacingHelper(QLatin1String("../CTelegramStream.hpp"), 4, QLatin1String("read operators"), generator.codeStreamReadDeclarations);
    replacingHelper(QLatin1String("../CTelegramStream.cpp"), 0, QLatin1String("read operators implementation"), generator.codeStreamReadDefinitions);
    replacingHelper(QLatin1String("../CTelegramStream.cpp"), 0, QLatin1String("vector read templates instancing"), generator.codeStreamReadTemplateInstancing);
    replacingHelper(QLatin1String("../CTelegramStream.hpp"), 4, QLatin1String("write operators"), generator.codeStreamWriteDeclarations);
    replacingHelper(QLatin1String("../CTelegramStream.cpp"), 0, QLatin1String("write operators implementation"), generator.codeStreamWriteDefinitions);
    replacingHelper(QLatin1String("../CTelegramStream.cpp"), 0, QLatin1String("vector write templates instancing"), generator.codeStreamWriteTemplateInstancing);
    replacingHelper(QLatin1String("../CTelegramConnection.hpp"), 4, QLatin1String("Telegram API methods declaration"), generator.codeConnectionDeclarations);
    replacingHelper(QLatin1String("../CTelegramConnection.cpp"), 0, QLatin1String("Telegram API methods implementation"), generator.codeConnectionDefinitions);

    replacingHelper(QLatin1String("../TLTypesDebug.hpp"), 0, QLatin1String("TLTypes debug operators"), generator.codeDebugWriteDeclarations);
    replacingHelper(QLatin1String("../TLTypesDebug.cpp"), 0, QLatin1String("TLTypes debug operators"), generator.codeDebugWriteDefinitions);

    replacingHelper(QLatin1String("../TLRpcDebug.cpp"), 4, QLatin1String("RPC call debug cases"), generator.codeDebugRpcParse);

    printf("Spec file successfully used for generation.\n");
    return NoError;
}

StatusCode main2(const QStringList &arguments)
{
    if (arguments.count() < 3) {
        return InvalidArgument;
    }

    QString fileName;

    if (!arguments.last().startsWith(QLatin1Char('-'))) {
        fileName = arguments.last();
    } else {
        return InvalidArgument;
    }

    StatusCode code = NoError;

    if (arguments.contains(QLatin1String("--fetch-json"))) {
        code = fetchJson(fileName);
        if (code != NoError) {
            return code;
        }
    }

    if (arguments.contains(QLatin1String("--format-json"))) {
        code = format(fileName);
        if (code != NoError) {
            return code;
        }
    }

    if (arguments.contains(QLatin1String("--generate-from-json"))) {
        code = generate(JsonFormat, fileName);
        if (code != NoError) {
            return code;
        }
    }

    if (arguments.contains(QLatin1String("--generate-from-text"))) {
        code = generate(TextFormat, fileName);
        if (code != NoError) {
            return code;
        }
    }

    return code;
}

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    StatusCode code = main2(app.arguments());

    if (code == InvalidAction) {
        printf("Invalid arguments. Look at the sources for the list of possible arguments.\n");
    }

    return code;
}
<commit_msg>GeneratorNG: QCommandLineParser<commit_after>/*
   Copyright (C) 2014-2015 Alexandr Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

   This library 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 library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

 */

#include <QCoreApplication>
#include <QDebug>
#include <QFile>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QCommandLineParser>

#include <QJsonDocument>

#include "GeneratorNG.hpp"

enum StatusCode {
    NoError,
    InvalidAction,
    InvalidArgument,
    SchemaReadError,
    NetworkError,
    ServerError,
    FileAccessError
};

enum SchemaFormat {
    JsonFormat,
    TextFormat,
};

/* Replacing helper */
bool replaceSection(const QString &fileName, const QString &startMarker, const QString &endMarker, const QString &newContent)
{
    QFile fileToProcess(fileName);

    if (!fileToProcess.open(QIODevice::ReadOnly))
        return false;

    QString fileContent = fileToProcess.readAll();
    fileToProcess.close();

    bool winNewLines = fileContent.indexOf(QLatin1String("\r\n")) > 0;

    if (winNewLines) {
        fileContent.replace(QLatin1String("\r\n"), QLatin1String("\n"));
    }

    int startPos = fileContent.indexOf(startMarker);
    int endPos   = fileContent.indexOf(endMarker);

    if (startPos >= 0) {
        if (endPos < 0) {
            printf("There is only start marker in the file %s. Error.\n", fileName.toLocal8Bit().constData());
            return false;
        }

        endPos += endMarker.length();
    } else {
        return false;
    }

    const QString previousContent = fileContent.mid(startPos, endPos - startPos);

    if (previousContent == startMarker + newContent + endMarker) {
        printf("Nothing to do: new and previous contents are exactly same.\n");
        return true;
    }

    fileContent.remove(startPos, endPos - startPos);
    fileContent.insert(startPos, startMarker + newContent + endMarker);

    if (!fileToProcess.open(QIODevice::WriteOnly)) {
        printf("Can not write file: %s.\n", fileName.toLocal8Bit().constData());
        return false;
    }

    if (winNewLines) {
        fileContent.replace(QLatin1String("\n"), QLatin1String("\r\n"));
    }

    fileToProcess.write(fileContent.toLatin1());

    fileToProcess.close();

    printf("Replacing is done.\n");

    return true;
}

bool replacingHelper(const QString &fileName, int spacing, const QString &marker, const QString &newContent)
{
    const QString space(spacing, QChar(' '));

    if (!replaceSection(fileName,
                        QString("%1// Generated %2\n").arg(space).arg(marker),
                        QString("%1// End of generated %2\n").arg(space).arg(marker), newContent)) {
        printf("Can not update file %s with marker %s.\n", fileName.toLatin1().constData(), marker.toLatin1().constData());
        return false;
    } else {
        return true;
    }
}

/* End of replacing helper */

void debugType(const TLType &type)
{
    qDebug() << type.name;
    foreach (const TLSubType &subType, type.subTypes) {
        qDebug() << "    " << subType.name;
        foreach (const TLParam &member, subType.members) {
            qDebug() << "        " << member.type << member.name;
        }
    }
}

StatusCode fetchJson(const QString &specFileName)
{
    QEventLoop eventLoop;
    QNetworkAccessManager mgr;
    QObject::connect(&mgr, &QNetworkAccessManager::finished, &eventLoop, &QEventLoop::quit);

    QNetworkRequest req(QUrl(QLatin1String("https://core.telegram.org/schema/json")));
    QNetworkReply *reply = mgr.get(req);

    eventLoop.exec();

    if (reply->error() != QNetworkReply::NoError) {
        delete reply;
        return NetworkError;
    }

    QByteArray data = reply->readAll();
    delete reply;

    if (data.isEmpty()) {
        return ServerError;
    }

    QFile specsOutFile(specFileName);
    if (!specsOutFile.open(QIODevice::WriteOnly)) {
        return FileAccessError;
    }

    QJsonDocument document = QJsonDocument::fromJson(data);
    specsOutFile.write(document.toJson());
    specsOutFile.close();

    printf("Spec file successfully downloaded (and formatted).\n");
    return NoError;
}

StatusCode format(const QString &specFileName)
{
    QFile specsFile(specFileName);
    if (!specsFile.open(QIODevice::ReadOnly)) {
        return FileAccessError;
    }

    QByteArray data = specsFile.readAll();

    specsFile.close();

    if (!specsFile.open(QIODevice::WriteOnly)) {
        return FileAccessError;
    }

    QJsonDocument document = QJsonDocument::fromJson(data);
    specsFile.write(document.toJson());
    specsFile.close();

    printf("Spec file successfully formatted.\n");
    return NoError;
}

StatusCode generate(SchemaFormat format, const QString &specFileName)
{
    QFile specsFile(specFileName);
    specsFile.open(QIODevice::ReadOnly);

    const QByteArray data = specsFile.readAll();

    if (data.isEmpty()) {
        printf("Unable to read the file.\n");
        return InvalidArgument;
    }

    specsFile.close();

    GeneratorNG generator;

    bool success = true;

    switch (format) {
    case JsonFormat:
        success = generator.loadDataFromJson(data);
        break;
    case TextFormat:
        success = generator.loadDataFromText(data);
        break;
    }

    if (!success) {
        printf("Unable to parse the scheme.\n");
        return SchemaReadError;
    }

    generator.generate();

    replacingHelper(QLatin1String("../TLValues.hpp"), 8, QLatin1String("TLValues"), generator.codeOfTLValues);
    replacingHelper(QLatin1String("../TLTypes.hpp"), 0, QLatin1String("TLTypes"), generator.codeOfTLTypes);
    replacingHelper(QLatin1String("../CTelegramStream.hpp"), 4, QLatin1String("read operators"), generator.codeStreamReadDeclarations);
    replacingHelper(QLatin1String("../CTelegramStream.cpp"), 0, QLatin1String("read operators implementation"), generator.codeStreamReadDefinitions);
    replacingHelper(QLatin1String("../CTelegramStream.cpp"), 0, QLatin1String("vector read templates instancing"), generator.codeStreamReadTemplateInstancing);
    replacingHelper(QLatin1String("../CTelegramStream.hpp"), 4, QLatin1String("write operators"), generator.codeStreamWriteDeclarations);
    replacingHelper(QLatin1String("../CTelegramStream.cpp"), 0, QLatin1String("write operators implementation"), generator.codeStreamWriteDefinitions);
    replacingHelper(QLatin1String("../CTelegramStream.cpp"), 0, QLatin1String("vector write templates instancing"), generator.codeStreamWriteTemplateInstancing);
    replacingHelper(QLatin1String("../CTelegramConnection.hpp"), 4, QLatin1String("Telegram API methods declaration"), generator.codeConnectionDeclarations);
    replacingHelper(QLatin1String("../CTelegramConnection.cpp"), 0, QLatin1String("Telegram API methods implementation"), generator.codeConnectionDefinitions);

    replacingHelper(QLatin1String("../TLTypesDebug.hpp"), 0, QLatin1String("TLTypes debug operators"), generator.codeDebugWriteDeclarations);
    replacingHelper(QLatin1String("../TLTypesDebug.cpp"), 0, QLatin1String("TLTypes debug operators"), generator.codeDebugWriteDefinitions);

    replacingHelper(QLatin1String("../TLRpcDebug.cpp"), 4, QLatin1String("RPC call debug cases"), generator.codeDebugRpcParse);

    printf("Spec file successfully used for generation.\n");
    return NoError;
}

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QCommandLineParser parser;
    parser.addHelpOption();

    QCommandLineOption fetchJsonOption(QStringLiteral("fetch-json"));
    parser.addOption(fetchJsonOption);

    QCommandLineOption formatJsonOption(QStringLiteral("format-json"));
    parser.addOption(formatJsonOption);

    QCommandLineOption generateFromJsonOption(QStringLiteral("generate-from-json"));
    parser.addOption(generateFromJsonOption);

    QCommandLineOption generateFromTextOption(QStringLiteral("generate-from-text"));
    parser.addOption(generateFromTextOption);

    parser.addPositionalArgument(QStringLiteral("spec"), QStringLiteral("The specification file (text or json)"));

    parser.process(app);

    if (parser.positionalArguments().count() != 1) {
        parser.showHelp(InvalidArgument);
    }

    const QString fileName = parser.positionalArguments().first();

    StatusCode code;

    if (parser.isSet(fetchJsonOption)) {
        code = fetchJson(fileName);
        if (code != NoError) {
            return code;
        }
    }

    if (parser.isSet(formatJsonOption)) {
        code = format(fileName);
        if (code != NoError) {
            return code;
        }
    }

    if (parser.isSet(generateFromJsonOption)) {
        code = generate(JsonFormat, fileName);
        if (code != NoError) {
            return code;
        }
    }

    if (parser.isSet(generateFromTextOption)) {
        code = generate(TextFormat, fileName);
        if (code != NoError) {
            return code;
        }
    }

    return code;
}
<|endoftext|>