<commit_before>#include "Utility/String.h"

#include <string>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include <cctype>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <cmath>
#include <cctype>
#include <iterator>

std::vector<std::string> String::split(const std::string& s, const std::string& delim, const size_t count) noexcept
{
    if(delim.empty())
    {
        auto ss = std::istringstream(s);
        std::vector<std::string> result;
        std::copy(std::istream_iterator<std::string>(ss), std::istream_iterator<std::string>(), std::back_inserter(result));
        return result;
    }

    if(s.empty())
    {
        return {};
    }

    std::vector<std::string> result;
    size_t start_index = 0;
    size_t end_index = 0;
    size_t split_count = 0;
    while(end_index < s.size() && split_count < count)
    {
        end_index = std::min(s.find(delim, start_index), s.size());
        result.push_back(s.substr(start_index, end_index - start_index));
        start_index = end_index + delim.size();
        ++split_count;
    }

    if(start_index <= s.size())
    {
        result.push_back(s.substr(start_index));
    }

    return result;
}

bool String::starts_with(const std::string& s, const std::string& beginning) noexcept
{
    return std::mismatch(beginning.begin(), beginning.end(), s.begin(), s.end()).first == beginning.end();
}

std::string String::trim_outer_whitespace(const std::string& s) noexcept
{
    const auto text_start = std::find_if_not(s.begin(), s.end(), [](auto c) { return std::isspace(c); });
    if(text_start == s.end())
    {
        return {};
    }

    const auto text_end = std::find_if_not(s.rbegin(), s.rend(), [](auto c) { return std::isspace(c); }).base();
    return std::string(text_start, text_end);
}

std::string String::remove_extra_whitespace(const std::string& s) noexcept
{
    const auto words = split(s);
    return join(words.begin(), words.end(), " ");
}

std::string String::strip_comments(const std::string& str, const std::string& comment) noexcept
{
    return trim_outer_whitespace(str.substr(0, str.find(comment)));
}

std::string String::strip_block_comment(const std::string& str, const std::string& start, const std::string& end)
{
    const auto start_comment_index = str.find(start);
    const auto end_comment_index = str.find(end);

    if(start_comment_index == std::string::npos && end_comment_index == std::string::npos)
    {
        return trim_outer_whitespace(str);
    }

    if(start_comment_index == std::string::npos || end_comment_index == std::string::npos)
    {
        throw std::invalid_argument("\"" + str + "\" is missing a comment delimiter: " + start + end);
    }

    if(start_comment_index >= end_comment_index)
    {
        throw std::invalid_argument("\"" + str + "\" contains bad comment delimiters: " + start + end);
    }

    try
    {
        const auto first_part = str.substr(0, start_comment_index);
        const auto last_part = str.substr(end_comment_index + end.size());
        return strip_block_comment(trim_outer_whitespace(first_part) + " " + trim_outer_whitespace(last_part), start, end);
    }
    catch(const std::invalid_argument& e)
    {
        throw std::invalid_argument(e.what() + std::string("\nOriginal line: ") + str);
    }
}

std::string String::strip_nested_block_comments(const std::string& str, const std::string& start, const std::string& end)
{
    if(contains(start, end) || contains(end, start))
    {
        throw std::invalid_argument("Delimiters cannot share substrings: " + start + "," + end + ".");
    }

    const auto error_message = "Invalid nesting of delimiters " + start + "," + end + ": " + str;
    std::string result;
    auto depth = 0;
    size_t index = 0;
    while(index < str.size())
    {
        auto start_index = str.find(start, index);
        auto end_index = str.find(end, index);
        if(start_index < end_index)
        {
            if(depth == 0)
            {
                result += str.substr(index, start_index - index);
            }
            ++depth;
            index = start_index + start.size();
        }
        else if(end_index < start_index)
        {
            if(depth == 0)
            {
                throw std::invalid_argument(error_message);
            }
            --depth;
            index = end_index + end.size();
        }
        else // start_index == end_index == std::string::npos
        {
            result += str.substr(index);
            break;
        }
    }

    if(depth != 0)
    {
        throw std::invalid_argument(error_message);
    }

    return result;
}

std::string String::remove_pgn_comments(const std::string& line)
{
    const auto index = line.find_first_of(";({");
    const auto delimiter = index < std::string::npos ? line[index] : '\0';

    switch(delimiter)
    {
        case ';' : return remove_pgn_comments(strip_comments(line, ";"));
        case '(' : return remove_pgn_comments(strip_nested_block_comments(line, "(", ")"));
        case '{' : return remove_pgn_comments(strip_block_comment(line, "{", "}"));
        default  : return remove_extra_whitespace(line);
    }
}

std::string String::extract_delimited_text(const std::string& str, const std::string& start, const std::string& end)
{
    const auto first_delimiter_index = str.find(start);
    if(first_delimiter_index == std::string::npos)
    {
        throw std::invalid_argument("Starting delimiter not found in \"" + str + "\": " + start + " " + end);
    }
    const auto text_start_index = first_delimiter_index + start.size();

    const auto second_delimiter_index = str.find(end, text_start_index);
    if(second_delimiter_index == std::string::npos)
    {
        throw std::invalid_argument("Ending delimiter not found in \"" + str + "\": " + start + " " + end);
    }
    const auto text_length = second_delimiter_index - text_start_index;

    return str.substr(text_start_index, text_length);
}

char String::tolower(const char letter) noexcept
{
    return char(std::tolower(letter));
}

char String::toupper(const char letter) noexcept
{
    return char(std::toupper(letter));
}

std::string String::lowercase(std::string s) noexcept
{
    std::transform(s.begin(), s.end(), s.begin(), String::tolower);
    return s;
}

std::string String::round_to_decimals(const double x, const size_t decimal_places) noexcept
{
    auto result = std::ostringstream();
    result << std::fixed << std::setprecision(int(decimal_places)) << x;
    return result.str();
}

std::string String::date_and_time_format(const std::chrono::system_clock::time_point& point_in_time,
                                         const std::string& format) noexcept
{
    const auto time_c = std::chrono::system_clock::to_time_t(point_in_time);
    std::tm time_out;
#ifdef _WIN32
    localtime_s(&time_out, &time_c);
#elif defined(__linux__)
    localtime_r(&time_c, &time_out);
#endif
    auto ss = std::ostringstream{};
    ss << std::put_time(&time_out, format.c_str());
    return ss.str();
}

std::string String::add_to_file_name(const std::string& original_file_name, const std::string& addition) noexcept
{
    const auto dot_index = std::min(original_file_name.find_last_of('.'), original_file_name.size());
    return original_file_name.substr(0, dot_index) + addition + original_file_name.substr(dot_index);
}

std::string String::pluralize(int count, const std::string& noun) noexcept
{
    return std::to_string(count) + " " + noun + (count == 1 ? "" : "s");
}
<commit_msg>Clean up code in String::split<commit_after>#include "Utility/String.h"

#include <string>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include <cctype>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <cmath>
#include <cctype>
#include <iterator>

std::vector<std::string> String::split(const std::string& s, const std::string& delim, const size_t count) noexcept
{
    if(s.empty())
    {
        return {};
    }

    std::vector<std::string> result;
    if(delim.empty())
    {
        auto ss = std::istringstream(s);
        std::copy(std::istream_iterator<std::string>(ss), std::istream_iterator<std::string>(), std::back_inserter(result));
    }
    else
    {
        size_t start_index = 0;
        size_t end_index = 0;
        size_t split_count = 0;
        while(end_index < s.size() && split_count < count)
        {
            end_index = std::min(s.find(delim, start_index), s.size());
            result.push_back(s.substr(start_index, end_index - start_index));
            start_index = end_index + delim.size();
            ++split_count;
        }

        if(start_index <= s.size())
        {
            result.push_back(s.substr(start_index));
        }
    }

    return result;
}

bool String::starts_with(const std::string& s, const std::string& beginning) noexcept
{
    return std::mismatch(beginning.begin(), beginning.end(), s.begin(), s.end()).first == beginning.end();
}

std::string String::trim_outer_whitespace(const std::string& s) noexcept
{
    const auto text_start = std::find_if_not(s.begin(), s.end(), [](auto c) { return std::isspace(c); });
    if(text_start == s.end())
    {
        return {};
    }

    const auto text_end = std::find_if_not(s.rbegin(), s.rend(), [](auto c) { return std::isspace(c); }).base();
    return std::string(text_start, text_end);
}

std::string String::remove_extra_whitespace(const std::string& s) noexcept
{
    const auto words = split(s);
    return join(words.begin(), words.end(), " ");
}

std::string String::strip_comments(const std::string& str, const std::string& comment) noexcept
{
    return trim_outer_whitespace(str.substr(0, str.find(comment)));
}

std::string String::strip_block_comment(const std::string& str, const std::string& start, const std::string& end)
{
    const auto start_comment_index = str.find(start);
    const auto end_comment_index = str.find(end);

    if(start_comment_index == std::string::npos && end_comment_index == std::string::npos)
    {
        return trim_outer_whitespace(str);
    }

    if(start_comment_index == std::string::npos || end_comment_index == std::string::npos)
    {
        throw std::invalid_argument("\"" + str + "\" is missing a comment delimiter: " + start + end);
    }

    if(start_comment_index >= end_comment_index)
    {
        throw std::invalid_argument("\"" + str + "\" contains bad comment delimiters: " + start + end);
    }

    try
    {
        const auto first_part = str.substr(0, start_comment_index);
        const auto last_part = str.substr(end_comment_index + end.size());
        return strip_block_comment(trim_outer_whitespace(first_part) + " " + trim_outer_whitespace(last_part), start, end);
    }
    catch(const std::invalid_argument& e)
    {
        throw std::invalid_argument(e.what() + std::string("\nOriginal line: ") + str);
    }
}

std::string String::strip_nested_block_comments(const std::string& str, const std::string& start, const std::string& end)
{
    if(contains(start, end) || contains(end, start))
    {
        throw std::invalid_argument("Delimiters cannot share substrings: " + start + "," + end + ".");
    }

    const auto error_message = "Invalid nesting of delimiters " + start + "," + end + ": " + str;
    std::string result;
    auto depth = 0;
    size_t index = 0;
    while(index < str.size())
    {
        auto start_index = str.find(start, index);
        auto end_index = str.find(end, index);
        if(start_index < end_index)
        {
            if(depth == 0)
            {
                result += str.substr(index, start_index - index);
            }
            ++depth;
            index = start_index + start.size();
        }
        else if(end_index < start_index)
        {
            if(depth == 0)
            {
                throw std::invalid_argument(error_message);
            }
            --depth;
            index = end_index + end.size();
        }
        else // start_index == end_index == std::string::npos
        {
            result += str.substr(index);
            break;
        }
    }

    if(depth != 0)
    {
        throw std::invalid_argument(error_message);
    }

    return result;
}

std::string String::remove_pgn_comments(const std::string& line)
{
    const auto index = line.find_first_of(";({");
    const auto delimiter = index < std::string::npos ? line[index] : '\0';

    switch(delimiter)
    {
        case ';' : return remove_pgn_comments(strip_comments(line, ";"));
        case '(' : return remove_pgn_comments(strip_nested_block_comments(line, "(", ")"));
        case '{' : return remove_pgn_comments(strip_block_comment(line, "{", "}"));
        default  : return remove_extra_whitespace(line);
    }
}

std::string String::extract_delimited_text(const std::string& str, const std::string& start, const std::string& end)
{
    const auto first_delimiter_index = str.find(start);
    if(first_delimiter_index == std::string::npos)
    {
        throw std::invalid_argument("Starting delimiter not found in \"" + str + "\": " + start + " " + end);
    }
    const auto text_start_index = first_delimiter_index + start.size();

    const auto second_delimiter_index = str.find(end, text_start_index);
    if(second_delimiter_index == std::string::npos)
    {
        throw std::invalid_argument("Ending delimiter not found in \"" + str + "\": " + start + " " + end);
    }
    const auto text_length = second_delimiter_index - text_start_index;

    return str.substr(text_start_index, text_length);
}

char String::tolower(const char letter) noexcept
{
    return char(std::tolower(letter));
}

char String::toupper(const char letter) noexcept
{
    return char(std::toupper(letter));
}

std::string String::lowercase(std::string s) noexcept
{
    std::transform(s.begin(), s.end(), s.begin(), String::tolower);
    return s;
}

std::string String::round_to_decimals(const double x, const size_t decimal_places) noexcept
{
    auto result = std::ostringstream();
    result << std::fixed << std::setprecision(int(decimal_places)) << x;
    return result.str();
}

std::string String::date_and_time_format(const std::chrono::system_clock::time_point& point_in_time,
                                         const std::string& format) noexcept
{
    const auto time_c = std::chrono::system_clock::to_time_t(point_in_time);
    std::tm time_out;
#ifdef _WIN32
    localtime_s(&time_out, &time_c);
#elif defined(__linux__)
    localtime_r(&time_c, &time_out);
#endif
    auto ss = std::ostringstream{};
    ss << std::put_time(&time_out, format.c_str());
    return ss.str();
}

std::string String::add_to_file_name(const std::string& original_file_name, const std::string& addition) noexcept
{
    const auto dot_index = std::min(original_file_name.find_last_of('.'), original_file_name.size());
    return original_file_name.substr(0, dot_index) + addition + original_file_name.substr(dot_index);
}

std::string String::pluralize(int count, const std::string& noun) noexcept
{
    return std::to_string(count) + " " + noun + (count == 1 ? "" : "s");
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: unohelp.cxx,v $
 *
 *  $Revision: 1.30 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 11:43:58 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <unohelp.hxx>

#ifndef _COM_SUN_STAR_LANG_XSINGLESERVICEFACTORY_HPP_
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#endif

#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif

#ifndef _COMPHELPER_PROCESSFACTORY_HXX_
#include <comphelper/processfactory.hxx>
#endif

#ifndef _COM_SUN_STAR_TEXT_XBREAKITERATOR_HPP_
#include <com/sun/star/i18n/XBreakIterator.hpp>
#endif

#ifndef _COM_SUN_STAR_I18N_XCHARACTERCLASSIFICATION_HPP_
#include <com/sun/star/i18n/XCharacterClassification.hpp>
#endif

#ifndef _COM_SUN_STAR_UTIL_XCOLLATOR_HPP_
#include <com/sun/star/i18n/XCollator.hpp>
#endif

#ifndef _COM_SUN_STAR_AWT_XEXTENDEDTOOLKIT_HPP_
#include <com/sun/star/awt/XExtendedToolkit.hpp>
#endif

#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLEEVENTOBJECT_HPP_
#include <com/sun/star/accessibility/AccessibleEventObject.hpp>
#endif

#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLESTATETYPE_HPP_
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#endif

#include <com/sun/star/registry/XImplementationRegistration.hpp>

#include <cppuhelper/servicefactory.hxx>

#include <tools/tempfile.hxx>
#include <osl/file.hxx>

#include <svdata.hxx>
#include <svapp.hxx>

using namespace ::com::sun::star;
using namespace ::rtl;

#define DOSTRING( x )                       #x
#define STRING( x )                         DOSTRING( x )

struct VCLRegServiceInfo
{
    const sal_Char*     pLibName;
    sal_Bool            bHasSUPD;
};

static VCLRegServiceInfo aVCLComponentsArray[] =
{
    {"i18n", sal_True},
    {"i18npool", sal_True},
#ifdef UNX
#ifdef MACOSX
    {"dtransaqua", sal_True},
#else
    {"dtransX11", sal_True},
#endif
#endif
#ifdef WNT
    {"sysdtrans", sal_False},
#endif
    {"dtrans", sal_False},
    {"mcnttype", sal_False},
    {"ftransl", sal_False},
    {"dnd", sal_False},
    {NULL, sal_False}
};

uno::Reference< lang::XMultiServiceFactory > vcl::unohelper::GetMultiServiceFactory()
{
    ImplSVData* pSVData = ImplGetSVData();
    if ( !pSVData->maAppData.mxMSF.is() )
    {
        pSVData->maAppData.mxMSF = ::comphelper::getProcessServiceFactory();
    }
    if ( !pSVData->maAppData.mxMSF.is() )
    {
        TempFile aTempFile;
        OUString aTempFileName;
        osl::FileBase::getSystemPathFromFileURL( aTempFile.GetName(), aTempFileName );
        pSVData->maAppData.mpMSFTempFileName = new String(aTempFileName);

        pSVData->maAppData.mxMSF = ::cppu::createRegistryServiceFactory( aTempFileName, rtl::OUString(), sal_False );

        uno::Reference < registry::XImplementationRegistration > xReg(
            pSVData->maAppData.mxMSF->createInstance( OUString::createFromAscii( "com.sun.star.registry.ImplementationRegistration" )), uno::UNO_QUERY );

        sal_Int32 nCompCount = 0;

        while ( aVCLComponentsArray[ nCompCount ].pLibName )
        {
            OUString aComponentPathString = CreateLibraryName( aVCLComponentsArray[ nCompCount ].pLibName,  aVCLComponentsArray[ nCompCount ].bHasSUPD );
            if (aComponentPathString.getLength() )
            {
                try
                {
                    xReg->registerImplementation(
                        OUString::createFromAscii( "com.sun.star.loader.SharedLibrary" ),aComponentPathString, NULL );
                }
                catch( ::com::sun::star::uno::Exception & )
                {
                }
            }
            nCompCount++;
        }
    }
    return pSVData->maAppData.mxMSF;
}

uno::Reference < i18n::XBreakIterator > vcl::unohelper::CreateBreakIterator()
{
    uno::Reference < i18n::XBreakIterator > xB;
    uno::Reference< lang::XMultiServiceFactory > xMSF = GetMultiServiceFactory();
    if ( xMSF.is() )
    {
        uno::Reference < uno::XInterface > xI = xMSF->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) );
        if ( xI.is() )
        {
            uno::Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XBreakIterator >*)0) );
            x >>= xB;
        }
    }
    return xB;
}

uno::Reference < i18n::XCharacterClassification > vcl::unohelper::CreateCharacterClassification()
{
    uno::Reference < i18n::XCharacterClassification > xB;
    uno::Reference< lang::XMultiServiceFactory > xMSF = GetMultiServiceFactory();
    if ( xMSF.is() )
    {
        uno::Reference < uno::XInterface > xI = xMSF->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.i18n.CharacterClassification" ) );
        if ( xI.is() )
        {
            uno::Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XCharacterClassification >*)0) );
            x >>= xB;
        }
    }
    return xB;
}

uno::Reference < i18n::XCollator > vcl::unohelper::CreateCollator()
{
    uno::Reference < i18n::XCollator > xB;
    uno::Reference< lang::XMultiServiceFactory > xMSF = GetMultiServiceFactory();
    if ( xMSF.is() )
    {
        uno::Reference < uno::XInterface > xI = xMSF->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.i18n.Collator" ) );
        if ( xI.is() )
        {
            uno::Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XCollator >*)0) );
            x >>= xB;
        }
    }
    return xB;
}

::rtl::OUString vcl::unohelper::CreateLibraryName( const sal_Char* pModName, BOOL bSUPD )
{
    // create variable library name suffixes
    OUString aSUPDString( OUString::valueOf( (sal_Int32)SUPD, 10 ));
    OUString aDLLSuffix = OUString::createFromAscii( STRING(DLLSUFFIX) );

    OUString aLibName;

#ifdef WNT
    aLibName = OUString::createFromAscii( pModName );
    if ( bSUPD )
    {
        aLibName += aSUPDString;
        aLibName += aDLLSuffix;
    }
    aLibName += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".dll" ));
#else
    aLibName = OUString( RTL_CONSTASCII_USTRINGPARAM( "lib" ));
    aLibName += OUString::createFromAscii( pModName );
    if ( bSUPD )
    {
        aLibName += aSUPDString;
        aLibName += aDLLSuffix;
    }
#ifdef MACOSX
    aLibName += OUString( RTL_CONSTASCII_USTRINGPARAM( ".dylib" ));
#else
    aLibName += OUString( RTL_CONSTASCII_USTRINGPARAM( ".so" ));
#endif
#endif

    return aLibName;
}

void vcl::unohelper::NotifyAccessibleStateEventGlobally( const ::com::sun::star::accessibility::AccessibleEventObject& rEventObject )
{
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XExtendedToolkit > xExtToolkit( Application::GetVCLToolkit(), uno::UNO_QUERY );
    if ( xExtToolkit.is() )
    {
        // Only for focus events
        sal_Int16 nType;
        rEventObject.NewValue >>= nType;
        if ( nType == ::com::sun::star::accessibility::AccessibleStateType::FOCUSED )
            xExtToolkit->fireFocusGained( rEventObject.Source );
        else
        {
            rEventObject.OldValue >>= nType;
            if ( nType == ::com::sun::star::accessibility::AccessibleStateType::FOCUSED )
                xExtToolkit->fireFocusLost( rEventObject.Source );
        }

    }
}
<commit_msg>INTEGRATION: CWS ause048 (1.30.168); FILE MERGED 2006/02/06 14:43:43 hjs 1.30.168.1: #i61266# DLLSUFFIX is obsolete. use DLLPOSTFIX instead<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: unohelp.cxx,v $
 *
 *  $Revision: 1.31 $
 *
 *  last change: $Author: rt $ $Date: 2006-03-08 13:54:35 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <unohelp.hxx>

#ifndef _COM_SUN_STAR_LANG_XSINGLESERVICEFACTORY_HPP_
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#endif

#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif

#ifndef _COMPHELPER_PROCESSFACTORY_HXX_
#include <comphelper/processfactory.hxx>
#endif

#ifndef _COM_SUN_STAR_TEXT_XBREAKITERATOR_HPP_
#include <com/sun/star/i18n/XBreakIterator.hpp>
#endif

#ifndef _COM_SUN_STAR_I18N_XCHARACTERCLASSIFICATION_HPP_
#include <com/sun/star/i18n/XCharacterClassification.hpp>
#endif

#ifndef _COM_SUN_STAR_UTIL_XCOLLATOR_HPP_
#include <com/sun/star/i18n/XCollator.hpp>
#endif

#ifndef _COM_SUN_STAR_AWT_XEXTENDEDTOOLKIT_HPP_
#include <com/sun/star/awt/XExtendedToolkit.hpp>
#endif

#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLEEVENTOBJECT_HPP_
#include <com/sun/star/accessibility/AccessibleEventObject.hpp>
#endif

#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLESTATETYPE_HPP_
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#endif

#include <com/sun/star/registry/XImplementationRegistration.hpp>

#include <cppuhelper/servicefactory.hxx>

#include <tools/tempfile.hxx>
#include <osl/file.hxx>

#include <svdata.hxx>
#include <svapp.hxx>

using namespace ::com::sun::star;
using namespace ::rtl;

#define DOSTRING( x )                       #x
#define STRING( x )                         DOSTRING( x )

struct VCLRegServiceInfo
{
    const sal_Char*     pLibName;
    sal_Bool            bHasSUPD;
};

static VCLRegServiceInfo aVCLComponentsArray[] =
{
    {"i18n", sal_True},
    {"i18npool", sal_True},
#ifdef UNX
#ifdef MACOSX
    {"dtransaqua", sal_True},
#else
    {"dtransX11", sal_True},
#endif
#endif
#ifdef WNT
    {"sysdtrans", sal_False},
#endif
    {"dtrans", sal_False},
    {"mcnttype", sal_False},
    {"ftransl", sal_False},
    {"dnd", sal_False},
    {NULL, sal_False}
};

uno::Reference< lang::XMultiServiceFactory > vcl::unohelper::GetMultiServiceFactory()
{
    ImplSVData* pSVData = ImplGetSVData();
    if ( !pSVData->maAppData.mxMSF.is() )
    {
        pSVData->maAppData.mxMSF = ::comphelper::getProcessServiceFactory();
    }
    if ( !pSVData->maAppData.mxMSF.is() )
    {
        TempFile aTempFile;
        OUString aTempFileName;
        osl::FileBase::getSystemPathFromFileURL( aTempFile.GetName(), aTempFileName );
        pSVData->maAppData.mpMSFTempFileName = new String(aTempFileName);

        pSVData->maAppData.mxMSF = ::cppu::createRegistryServiceFactory( aTempFileName, rtl::OUString(), sal_False );

        uno::Reference < registry::XImplementationRegistration > xReg(
            pSVData->maAppData.mxMSF->createInstance( OUString::createFromAscii( "com.sun.star.registry.ImplementationRegistration" )), uno::UNO_QUERY );

        sal_Int32 nCompCount = 0;

        while ( aVCLComponentsArray[ nCompCount ].pLibName )
        {
            OUString aComponentPathString = CreateLibraryName( aVCLComponentsArray[ nCompCount ].pLibName,  aVCLComponentsArray[ nCompCount ].bHasSUPD );
            if (aComponentPathString.getLength() )
            {
                try
                {
                    xReg->registerImplementation(
                        OUString::createFromAscii( "com.sun.star.loader.SharedLibrary" ),aComponentPathString, NULL );
                }
                catch( ::com::sun::star::uno::Exception & )
                {
                }
            }
            nCompCount++;
        }
    }
    return pSVData->maAppData.mxMSF;
}

uno::Reference < i18n::XBreakIterator > vcl::unohelper::CreateBreakIterator()
{
    uno::Reference < i18n::XBreakIterator > xB;
    uno::Reference< lang::XMultiServiceFactory > xMSF = GetMultiServiceFactory();
    if ( xMSF.is() )
    {
        uno::Reference < uno::XInterface > xI = xMSF->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) );
        if ( xI.is() )
        {
            uno::Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XBreakIterator >*)0) );
            x >>= xB;
        }
    }
    return xB;
}

uno::Reference < i18n::XCharacterClassification > vcl::unohelper::CreateCharacterClassification()
{
    uno::Reference < i18n::XCharacterClassification > xB;
    uno::Reference< lang::XMultiServiceFactory > xMSF = GetMultiServiceFactory();
    if ( xMSF.is() )
    {
        uno::Reference < uno::XInterface > xI = xMSF->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.i18n.CharacterClassification" ) );
        if ( xI.is() )
        {
            uno::Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XCharacterClassification >*)0) );
            x >>= xB;
        }
    }
    return xB;
}

uno::Reference < i18n::XCollator > vcl::unohelper::CreateCollator()
{
    uno::Reference < i18n::XCollator > xB;
    uno::Reference< lang::XMultiServiceFactory > xMSF = GetMultiServiceFactory();
    if ( xMSF.is() )
    {
        uno::Reference < uno::XInterface > xI = xMSF->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.i18n.Collator" ) );
        if ( xI.is() )
        {
            uno::Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XCollator >*)0) );
            x >>= xB;
        }
    }
    return xB;
}

::rtl::OUString vcl::unohelper::CreateLibraryName( const sal_Char* pModName, BOOL bSUPD )
{
    // create variable library name suffixes
    OUString aSUPDString( OUString::valueOf( (sal_Int32)SUPD, 10 ));
    OUString aDLLSuffix = OUString::createFromAscii( STRING(DLLPOSTFIX) );

    OUString aLibName;

#ifdef WNT
    aLibName = OUString::createFromAscii( pModName );
    if ( bSUPD )
    {
        aLibName += aSUPDString;
        aLibName += aDLLSuffix;
    }
    aLibName += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".dll" ));
#else
    aLibName = OUString( RTL_CONSTASCII_USTRINGPARAM( "lib" ));
    aLibName += OUString::createFromAscii( pModName );
    if ( bSUPD )
    {
        aLibName += aSUPDString;
        aLibName += aDLLSuffix;
    }
#ifdef MACOSX
    aLibName += OUString( RTL_CONSTASCII_USTRINGPARAM( ".dylib" ));
#else
    aLibName += OUString( RTL_CONSTASCII_USTRINGPARAM( ".so" ));
#endif
#endif

    return aLibName;
}

void vcl::unohelper::NotifyAccessibleStateEventGlobally( const ::com::sun::star::accessibility::AccessibleEventObject& rEventObject )
{
    ::com::sun::star::uno::Reference< ::com::sun::star::awt::XExtendedToolkit > xExtToolkit( Application::GetVCLToolkit(), uno::UNO_QUERY );
    if ( xExtToolkit.is() )
    {
        // Only for focus events
        sal_Int16 nType;
        rEventObject.NewValue >>= nType;
        if ( nType == ::com::sun::star::accessibility::AccessibleStateType::FOCUSED )
            xExtToolkit->fireFocusGained( rEventObject.Source );
        else
        {
            rEventObject.OldValue >>= nType;
            if ( nType == ::com::sun::star::accessibility::AccessibleStateType::FOCUSED )
                xExtToolkit->fireFocusLost( rEventObject.Source );
        }

    }
}
<|endoftext|>


<commit_before>#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>

#include <improbable/standard_library.h>
#include <improbable/view.h>
#include <improbable/worker.h>
#include <shoveler.h>

extern "C" {
#include <glib.h>

#include <shoveler/executor.h>
#include <shoveler/log.h>
}

using shoveler::Bootstrap;
using shoveler::Client;
using shoveler::Heartbeat;
using shoveler::Color;
using shoveler::CreateClientEntityRequest;
using shoveler::CreateClientEntityResponse;
using shoveler::Drawable;
using shoveler::DrawableType;
using shoveler::Light;
using shoveler::LightType;
using shoveler::Material;
using shoveler::MaterialType;
using shoveler::Model;
using shoveler::PolygonMode;
using improbable::EntityAcl;
using improbable::EntityAclData;
using improbable::Metadata;
using improbable::Persistence;
using improbable::Position;
using improbable::WorkerAttributeSet;
using improbable::WorkerRequirementSet;

using CreateClientEntity = shoveler::Bootstrap::Commands::CreateClientEntity;
using ClientPing = shoveler::Bootstrap::Commands::ClientPing;

struct ClientCleanupTickContext {
	worker::Connection *connection;
	int64_t maxHeartbeatTimeoutMs;
	std::map<worker::EntityId, std::pair<std::string, int64_t>> *lastHeartbeatMicrosMap;
};

static void clientCleanupTick(void *clientCleanupTickContextPointer);

int main(int argc, char **argv) {
	if (argc != 5) {
		return 1;
	}

	worker::ConnectionParameters parameters;
	parameters.WorkerType = "ShovelerServer";
	parameters.Network.ConnectionType = worker::NetworkConnectionType::kTcp;
	parameters.Network.UseExternalIp = false;

	const std::string workerId = argv[1];
	const std::string hostname = argv[2];
	const std::uint16_t port = static_cast<std::uint16_t>(std::stoi(argv[3]));
	const std::string logFileLocation = argv[4];
	const int tickRateHz = 10;
	const int clientCleanupTickRateHz = 2;
	const int64_t maxHeartbeatTimeoutMs = 5000;

	FILE *logFile = fopen(logFileLocation.c_str(), "r+");
	if(logFile == NULL) {
		std::cerr << "Failed to open output log file at " << logFileLocation << ": " << strerror(errno) << std::endl;
		return 1;
	}

	shovelerLogInit("shoveler-spatialos/workers/cmake/", SHOVELER_LOG_LEVEL_INFO_UP, logFile);
	shovelerLogInfo("Connecting as worker %s to %s:%d.", workerId.c_str(), hostname.c_str(), port);

	auto components = worker::Components<
		shoveler::Bootstrap,
		shoveler::Client,
		shoveler::Heartbeat,
		shoveler::Light,
		shoveler::Model,
		improbable::EntityAcl,
		improbable::Metadata,
		improbable::Persistence,
		improbable::Position>{};

	worker::Connection connection = worker::Connection::ConnectAsync(components, hostname, port, workerId, parameters).Get();
	bool disconnected = false;
	worker::View view{components};
	worker::Dispatcher& dispatcher = view;

	ShovelerExecutor *tickExecutor = shovelerExecutorCreateDirect();
	std::map<worker::EntityId, std::pair<std::string, int64_t>> lastHeartbeatMicrosMap;

	dispatcher.OnDisconnect([&](const worker::DisconnectOp& op) {
		shovelerLogError("Disconnected from SpatialOS: %s", op.Reason.c_str());
		disconnected = true;
	});

	dispatcher.OnMetrics([&](const worker::MetricsOp& op) {
		auto metrics = op.Metrics;
		connection.SendMetrics(metrics);
	});

	dispatcher.OnLogMessage([&](const worker::LogMessageOp& op) {
		switch(op.Level) {
			case worker::LogLevel::kDebug:
				shovelerLogTrace(op.Message.c_str());
				break;
			case worker::LogLevel::kInfo:
				shovelerLogInfo(op.Message.c_str());
				break;
			case worker::LogLevel::kWarn:
				shovelerLogWarning(op.Message.c_str());
				break;
			case worker::LogLevel::kError:
				shovelerLogError(op.Message.c_str());
				break;
			case worker::LogLevel::kFatal:
				shovelerLogError(op.Message.c_str());
				std::terminate();
			default:
				break;
		}
	});

	dispatcher.OnAddEntity([&](const worker::AddEntityOp& op) {
		shovelerLogInfo("Adding entity %lld.", op.EntityId);
	});

	dispatcher.OnRemoveEntity([&](const worker::RemoveEntityOp& op) {
		shovelerLogInfo("Removing entity %lld.", op.EntityId);
	});

	dispatcher.OnAddComponent<Bootstrap>([&](const worker::AddComponentOp<Bootstrap>& op) {
		shovelerLogInfo("Adding bootstrap to entity %lld.", op.EntityId);
	});

	dispatcher.OnComponentUpdate<Bootstrap>([&](const worker::ComponentUpdateOp<Bootstrap>& op) {
		shovelerLogInfo("Updating bootstrap for entity %lld.", op.EntityId);
	});

	dispatcher.OnRemoveComponent<Bootstrap>([&](const worker::RemoveComponentOp& op) {
		shovelerLogInfo("Removing bootstrap from entity %lld.", op.EntityId);
	});

	dispatcher.OnAuthorityChange<Bootstrap>([&](const worker::AuthorityChangeOp& op) {
		shovelerLogInfo("Authority change to %d for entity %lld.", op.Authority, op.EntityId);
	});

	dispatcher.OnAddComponent<Heartbeat>([&](const worker::AddComponentOp<Heartbeat>& op) {
		lastHeartbeatMicrosMap[op.EntityId] = std::make_pair("(unknown)", view.Entities[op.EntityId].Get<Heartbeat>()->last_server_heartbeat());
		shovelerLogInfo("Added client heartbeat for entity %lld.", op.EntityId);
	});

	dispatcher.OnComponentUpdate<Heartbeat>([&](const worker::ComponentUpdateOp<Heartbeat>& op) {
		if (lastHeartbeatMicrosMap.find(op.EntityId) != lastHeartbeatMicrosMap.end()) {
			std::string clientWorkerId = "(unknown)";
			const auto &clientComponentOption = view.Entities[op.EntityId].Get<Client>();
			if (clientComponentOption) {
				clientWorkerId = clientComponentOption->worker_id();
			}

			lastHeartbeatMicrosMap[op.EntityId] = std::make_pair(clientWorkerId, view.Entities[op.EntityId].Get<Heartbeat>()->last_server_heartbeat());
		}
		shovelerLogTrace("Updated client heartbeat for entity %lld.", op.EntityId);
	});

	dispatcher.OnRemoveComponent<Heartbeat>([&](const worker::RemoveComponentOp& op) {
		shovelerLogInfo("Removed client heartbeat for entity %lld.", op.EntityId);
	});

	dispatcher.OnAuthorityChange<Heartbeat>([&](const worker::AuthorityChangeOp& op) {
		shovelerLogInfo("Changing heartbeat authority for entity %lld to %d.", op.EntityId, op.Authority);
		if (op.Authority == worker::Authority::kAuthoritative) {
			int64_t now = g_get_monotonic_time();
			lastHeartbeatMicrosMap[op.EntityId] = std::make_pair("(unknown)", now);

			Heartbeat::Update heartbeatUpdate;
			heartbeatUpdate.set_last_server_heartbeat(now);
			connection.SendComponentUpdate<Heartbeat>(op.EntityId, heartbeatUpdate);
		} else if (op.Authority == worker::Authority::kNotAuthoritative) {
			lastHeartbeatMicrosMap.erase(op.EntityId);
		}
	});

	dispatcher.OnCommandRequest<CreateClientEntity>([&](const worker::CommandRequestOp<CreateClientEntity>& op) {
		shovelerLogInfo("Received create client entity request from: %s", op.CallerWorkerId.c_str());

		Color whiteColor{1.0f, 1.0f, 1.0f};

		Drawable pointDrawable{DrawableType::POINT};
		Material whiteParticleMaterial{MaterialType::PARTICLE, whiteColor, {}};

		worker::Entity clientEntity;
		clientEntity.Add<Metadata>({"client"});
		clientEntity.Add<Client>({op.CallerWorkerId});
		clientEntity.Add<Heartbeat>({0, 0});
		clientEntity.Add<Persistence>({});
		clientEntity.Add<Position>({{0, 0, 0}});
		clientEntity.Add<Model>({pointDrawable, whiteParticleMaterial, {0.0f, 0.0f, 0.0f}, {0.1f, 0.1f, 0.0f}, true, true, false, false, PolygonMode::FILL});
		clientEntity.Add<Light>({LightType::POINT, 1024, 1024, 1, 0.01f, 80.0f, {0.1f, 0.1f, 0.1f}, {}});

		WorkerAttributeSet clientAttributeSet({"client"});
		WorkerAttributeSet serverAttributeSet({"server"});
		WorkerRequirementSet specificClientRequirementSet({op.CallerAttributeSet});
		WorkerRequirementSet serverRequirementSet({serverAttributeSet});
		WorkerRequirementSet clientAndServerRequirementSet({clientAttributeSet, serverAttributeSet});
		worker::Map<std::uint32_t, WorkerRequirementSet> clientEntityComponentAclMap;
		clientEntityComponentAclMap.insert({{Client::ComponentId, specificClientRequirementSet}});
		clientEntityComponentAclMap.insert({{Heartbeat::ComponentId, serverRequirementSet}});
		clientEntityComponentAclMap.insert({{Position::ComponentId, specificClientRequirementSet}});
		EntityAclData clientEntityAclData(clientAndServerRequirementSet, clientEntityComponentAclMap);
		clientEntity.Add<EntityAcl>(clientEntityAclData);
		connection.SendCreateEntityRequest(clientEntity, {}, {});
		connection.SendCommandResponse<CreateClientEntity>(op.RequestId, {});
	});

	dispatcher.OnCommandRequest<ClientPing>([&](const worker::CommandRequestOp<ClientPing>& op) {
		worker::EntityId clientEntityId = op.Request.client_entity_id();
		worker::Map<worker::EntityId, worker::Map<worker::ComponentId, worker::Authority>>::const_iterator entityAuthorityQuery = view.ComponentAuthority.find(clientEntityId);
		if(entityAuthorityQuery == view.ComponentAuthority.end()) {
			shovelerLogWarning("Received client ping from %s for unknown client entity %lld, ignoring.", op.CallerWorkerId.c_str(), clientEntityId);
			return;
		}
		const worker::Map<worker::ComponentId, worker::Authority>& componentAuthorityMap = entityAuthorityQuery->second;

		worker::Map<worker::ComponentId, worker::Authority>::const_iterator componentAuthorityQuery = componentAuthorityMap.find(Heartbeat::ComponentId);
		if(componentAuthorityQuery == componentAuthorityMap.end()) {
			shovelerLogWarning("Received client ping from %s for client entity %lld without client heartbeat component, ignoring.", op.CallerWorkerId.c_str(), clientEntityId);
			return;
		}
		const worker::Authority& HeartbeatAuthority = componentAuthorityQuery->second;

		if(HeartbeatAuthority != worker::Authority::kAuthoritative) {
			shovelerLogWarning("Received client ping from %s for client entity %lld without authoritative client heartbeat component, ignoring.", op.CallerWorkerId.c_str(), clientEntityId);
			return;
		}

		int64_t now = g_get_monotonic_time();

		Heartbeat::Update heartbeatUpdate;
		heartbeatUpdate.set_last_client_heartbeat(op.Request.client_heartbeat());
		heartbeatUpdate.set_last_server_heartbeat(now);
		connection.SendComponentUpdate<Heartbeat>(clientEntityId, heartbeatUpdate);
		connection.SendCommandResponse<ClientPing>(op.RequestId, {now});

		shovelerLogTrace("Received client ping from %s for client entity %lld.", op.CallerWorkerId.c_str(), clientEntityId);
	});

	ClientCleanupTickContext cleanupTickContext{&connection, maxHeartbeatTimeoutMs, &lastHeartbeatMicrosMap};
	uint32_t clientCleanupTickPeriod = (uint32_t) (1000.0 / (double) clientCleanupTickRateHz);
	shovelerExecutorSchedulePeriodic(tickExecutor, 0, clientCleanupTickPeriod, clientCleanupTick, &cleanupTickContext);

	uint32_t executorTickPeriod = (uint32_t) (1000.0 / (double) tickRateHz);
	while(!disconnected) {
		dispatcher.Process(connection.GetOpList(executorTickPeriod));
		shovelerExecutorUpdateNow(tickExecutor);
	}

	shovelerExecutorFree(tickExecutor);
}

static void clientCleanupTick(void *clientCleanupTickContextPointer)
{
	ClientCleanupTickContext *context = (ClientCleanupTickContext *) clientCleanupTickContextPointer;

	int64_t now = g_get_monotonic_time();
	for(const auto& item: *context->lastHeartbeatMicrosMap) {
		worker::EntityId entityId = item.first;
		const std::string &workerId = item.second.first;
		int64_t last = item.second.second;
		int64_t diff = now - last;
		if (diff > 1000 * context->maxHeartbeatTimeoutMs) {
			shovelerLogWarning("Removing client entity %lld of worker %s because it exceeded the max ping timeout of %lldms.", entityId, workerId.c_str(), context->maxHeartbeatTimeoutMs);
			context->connection->SendDeleteEntityRequest(item.first, {});
		}
	}
}
<commit_msg>assign random colors to clients<commit_after>#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>

#include <improbable/standard_library.h>
#include <improbable/view.h>
#include <improbable/worker.h>
#include <shoveler.h>

extern "C" {
#include <glib.h>

#include <shoveler/color.h>
#include <shoveler/executor.h>
#include <shoveler/log.h>
#include <shoveler/types.h>
}

using shoveler::Bootstrap;
using shoveler::Client;
using shoveler::Heartbeat;
using shoveler::Color;
using shoveler::CreateClientEntityRequest;
using shoveler::CreateClientEntityResponse;
using shoveler::Drawable;
using shoveler::DrawableType;
using shoveler::Light;
using shoveler::LightType;
using shoveler::Material;
using shoveler::MaterialType;
using shoveler::Model;
using shoveler::PolygonMode;
using improbable::EntityAcl;
using improbable::EntityAclData;
using improbable::Metadata;
using improbable::Persistence;
using improbable::Position;
using improbable::WorkerAttributeSet;
using improbable::WorkerRequirementSet;

using CreateClientEntity = shoveler::Bootstrap::Commands::CreateClientEntity;
using ClientPing = shoveler::Bootstrap::Commands::ClientPing;

struct ClientCleanupTickContext {
	worker::Connection *connection;
	int64_t maxHeartbeatTimeoutMs;
	std::map<worker::EntityId, std::pair<std::string, int64_t>> *lastHeartbeatMicrosMap;
};

static void clientCleanupTick(void *clientCleanupTickContextPointer);
static Color colorFromHsv(float h, float s, float v);

int main(int argc, char **argv) {
	if (argc != 5) {
		return 1;
	}

	srand(0);

	worker::ConnectionParameters parameters;
	parameters.WorkerType = "ShovelerServer";
	parameters.Network.ConnectionType = worker::NetworkConnectionType::kTcp;
	parameters.Network.UseExternalIp = false;

	const std::string workerId = argv[1];
	const std::string hostname = argv[2];
	const std::uint16_t port = static_cast<std::uint16_t>(std::stoi(argv[3]));
	const std::string logFileLocation = argv[4];
	const int tickRateHz = 10;
	const int clientCleanupTickRateHz = 2;
	const int64_t maxHeartbeatTimeoutMs = 5000;

	FILE *logFile = fopen(logFileLocation.c_str(), "r+");
	if(logFile == NULL) {
		std::cerr << "Failed to open output log file at " << logFileLocation << ": " << strerror(errno) << std::endl;
		return 1;
	}

	shovelerLogInit("shoveler-spatialos/workers/cmake/", SHOVELER_LOG_LEVEL_INFO_UP, logFile);
	shovelerLogInfo("Connecting as worker %s to %s:%d.", workerId.c_str(), hostname.c_str(), port);

	auto components = worker::Components<
		shoveler::Bootstrap,
		shoveler::Client,
		shoveler::Heartbeat,
		shoveler::Light,
		shoveler::Model,
		improbable::EntityAcl,
		improbable::Metadata,
		improbable::Persistence,
		improbable::Position>{};

	worker::Connection connection = worker::Connection::ConnectAsync(components, hostname, port, workerId, parameters).Get();
	bool disconnected = false;
	worker::View view{components};
	worker::Dispatcher& dispatcher = view;

	ShovelerExecutor *tickExecutor = shovelerExecutorCreateDirect();
	std::map<worker::EntityId, std::pair<std::string, int64_t>> lastHeartbeatMicrosMap;

	dispatcher.OnDisconnect([&](const worker::DisconnectOp& op) {
		shovelerLogError("Disconnected from SpatialOS: %s", op.Reason.c_str());
		disconnected = true;
	});

	dispatcher.OnMetrics([&](const worker::MetricsOp& op) {
		auto metrics = op.Metrics;
		connection.SendMetrics(metrics);
	});

	dispatcher.OnLogMessage([&](const worker::LogMessageOp& op) {
		switch(op.Level) {
			case worker::LogLevel::kDebug:
				shovelerLogTrace(op.Message.c_str());
				break;
			case worker::LogLevel::kInfo:
				shovelerLogInfo(op.Message.c_str());
				break;
			case worker::LogLevel::kWarn:
				shovelerLogWarning(op.Message.c_str());
				break;
			case worker::LogLevel::kError:
				shovelerLogError(op.Message.c_str());
				break;
			case worker::LogLevel::kFatal:
				shovelerLogError(op.Message.c_str());
				std::terminate();
			default:
				break;
		}
	});

	dispatcher.OnAddEntity([&](const worker::AddEntityOp& op) {
		shovelerLogInfo("Adding entity %lld.", op.EntityId);
	});

	dispatcher.OnRemoveEntity([&](const worker::RemoveEntityOp& op) {
		shovelerLogInfo("Removing entity %lld.", op.EntityId);
	});

	dispatcher.OnAddComponent<Bootstrap>([&](const worker::AddComponentOp<Bootstrap>& op) {
		shovelerLogInfo("Adding bootstrap to entity %lld.", op.EntityId);
	});

	dispatcher.OnComponentUpdate<Bootstrap>([&](const worker::ComponentUpdateOp<Bootstrap>& op) {
		shovelerLogInfo("Updating bootstrap for entity %lld.", op.EntityId);
	});

	dispatcher.OnRemoveComponent<Bootstrap>([&](const worker::RemoveComponentOp& op) {
		shovelerLogInfo("Removing bootstrap from entity %lld.", op.EntityId);
	});

	dispatcher.OnAuthorityChange<Bootstrap>([&](const worker::AuthorityChangeOp& op) {
		shovelerLogInfo("Authority change to %d for entity %lld.", op.Authority, op.EntityId);
	});

	dispatcher.OnAddComponent<Heartbeat>([&](const worker::AddComponentOp<Heartbeat>& op) {
		lastHeartbeatMicrosMap[op.EntityId] = std::make_pair("(unknown)", view.Entities[op.EntityId].Get<Heartbeat>()->last_server_heartbeat());
		shovelerLogInfo("Added client heartbeat for entity %lld.", op.EntityId);
	});

	dispatcher.OnComponentUpdate<Heartbeat>([&](const worker::ComponentUpdateOp<Heartbeat>& op) {
		if (lastHeartbeatMicrosMap.find(op.EntityId) != lastHeartbeatMicrosMap.end()) {
			std::string clientWorkerId = "(unknown)";
			const auto &clientComponentOption = view.Entities[op.EntityId].Get<Client>();
			if (clientComponentOption) {
				clientWorkerId = clientComponentOption->worker_id();
			}

			lastHeartbeatMicrosMap[op.EntityId] = std::make_pair(clientWorkerId, view.Entities[op.EntityId].Get<Heartbeat>()->last_server_heartbeat());
		}
		shovelerLogTrace("Updated client heartbeat for entity %lld.", op.EntityId);
	});

	dispatcher.OnRemoveComponent<Heartbeat>([&](const worker::RemoveComponentOp& op) {
		shovelerLogInfo("Removed client heartbeat for entity %lld.", op.EntityId);
	});

	dispatcher.OnAuthorityChange<Heartbeat>([&](const worker::AuthorityChangeOp& op) {
		shovelerLogInfo("Changing heartbeat authority for entity %lld to %d.", op.EntityId, op.Authority);
		if (op.Authority == worker::Authority::kAuthoritative) {
			int64_t now = g_get_monotonic_time();
			lastHeartbeatMicrosMap[op.EntityId] = std::make_pair("(unknown)", now);

			Heartbeat::Update heartbeatUpdate;
			heartbeatUpdate.set_last_server_heartbeat(now);
			connection.SendComponentUpdate<Heartbeat>(op.EntityId, heartbeatUpdate);
		} else if (op.Authority == worker::Authority::kNotAuthoritative) {
			lastHeartbeatMicrosMap.erase(op.EntityId);
		}
	});

	dispatcher.OnCommandRequest<CreateClientEntity>([&](const worker::CommandRequestOp<CreateClientEntity>& op) {
		shovelerLogInfo("Received create client entity request from: %s", op.CallerWorkerId.c_str());

		float hue = (float) rand() / RAND_MAX;
		Color playerParticleColor = colorFromHsv(hue, 1.0f, 0.9f);
		Color playerLightColor = colorFromHsv(hue, 1.0f, 0.1f);

		Drawable pointDrawable{DrawableType::POINT};
		Material playerParticleMaterial{MaterialType::PARTICLE, playerParticleColor, {}};

		worker::Entity clientEntity;
		clientEntity.Add<Metadata>({"client"});
		clientEntity.Add<Client>({op.CallerWorkerId});
		clientEntity.Add<Heartbeat>({0, 0});
		clientEntity.Add<Persistence>({});
		clientEntity.Add<Position>({{0, 0, 0}});
		clientEntity.Add<Model>({pointDrawable, playerParticleMaterial, {0.0f, 0.0f, 0.0f}, {0.1f, 0.1f, 0.0f}, true, true, false, false, PolygonMode::FILL});
		clientEntity.Add<Light>({LightType::POINT, 1024, 1024, 1, 0.01f, 80.0f, playerLightColor, {}});

		WorkerAttributeSet clientAttributeSet({"client"});
		WorkerAttributeSet serverAttributeSet({"server"});
		WorkerRequirementSet specificClientRequirementSet({op.CallerAttributeSet});
		WorkerRequirementSet serverRequirementSet({serverAttributeSet});
		WorkerRequirementSet clientAndServerRequirementSet({clientAttributeSet, serverAttributeSet});
		worker::Map<std::uint32_t, WorkerRequirementSet> clientEntityComponentAclMap;
		clientEntityComponentAclMap.insert({{Client::ComponentId, specificClientRequirementSet}});
		clientEntityComponentAclMap.insert({{Heartbeat::ComponentId, serverRequirementSet}});
		clientEntityComponentAclMap.insert({{Position::ComponentId, specificClientRequirementSet}});
		EntityAclData clientEntityAclData(clientAndServerRequirementSet, clientEntityComponentAclMap);
		clientEntity.Add<EntityAcl>(clientEntityAclData);
		connection.SendCreateEntityRequest(clientEntity, {}, {});
		connection.SendCommandResponse<CreateClientEntity>(op.RequestId, {});
	});

	dispatcher.OnCommandRequest<ClientPing>([&](const worker::CommandRequestOp<ClientPing>& op) {
		worker::EntityId clientEntityId = op.Request.client_entity_id();
		worker::Map<worker::EntityId, worker::Map<worker::ComponentId, worker::Authority>>::const_iterator entityAuthorityQuery = view.ComponentAuthority.find(clientEntityId);
		if(entityAuthorityQuery == view.ComponentAuthority.end()) {
			shovelerLogWarning("Received client ping from %s for unknown client entity %lld, ignoring.", op.CallerWorkerId.c_str(), clientEntityId);
			return;
		}
		const worker::Map<worker::ComponentId, worker::Authority>& componentAuthorityMap = entityAuthorityQuery->second;

		worker::Map<worker::ComponentId, worker::Authority>::const_iterator componentAuthorityQuery = componentAuthorityMap.find(Heartbeat::ComponentId);
		if(componentAuthorityQuery == componentAuthorityMap.end()) {
			shovelerLogWarning("Received client ping from %s for client entity %lld without client heartbeat component, ignoring.", op.CallerWorkerId.c_str(), clientEntityId);
			return;
		}
		const worker::Authority& HeartbeatAuthority = componentAuthorityQuery->second;

		if(HeartbeatAuthority != worker::Authority::kAuthoritative) {
			shovelerLogWarning("Received client ping from %s for client entity %lld without authoritative client heartbeat component, ignoring.", op.CallerWorkerId.c_str(), clientEntityId);
			return;
		}

		int64_t now = g_get_monotonic_time();

		Heartbeat::Update heartbeatUpdate;
		heartbeatUpdate.set_last_client_heartbeat(op.Request.client_heartbeat());
		heartbeatUpdate.set_last_server_heartbeat(now);
		connection.SendComponentUpdate<Heartbeat>(clientEntityId, heartbeatUpdate);
		connection.SendCommandResponse<ClientPing>(op.RequestId, {now});

		shovelerLogTrace("Received client ping from %s for client entity %lld.", op.CallerWorkerId.c_str(), clientEntityId);
	});

	ClientCleanupTickContext cleanupTickContext{&connection, maxHeartbeatTimeoutMs, &lastHeartbeatMicrosMap};
	uint32_t clientCleanupTickPeriod = (uint32_t) (1000.0 / (double) clientCleanupTickRateHz);
	shovelerExecutorSchedulePeriodic(tickExecutor, 0, clientCleanupTickPeriod, clientCleanupTick, &cleanupTickContext);

	uint32_t executorTickPeriod = (uint32_t) (1000.0 / (double) tickRateHz);
	while(!disconnected) {
		dispatcher.Process(connection.GetOpList(executorTickPeriod));
		shovelerExecutorUpdateNow(tickExecutor);
	}

	shovelerExecutorFree(tickExecutor);
}

static void clientCleanupTick(void *clientCleanupTickContextPointer)
{
	ClientCleanupTickContext *context = (ClientCleanupTickContext *) clientCleanupTickContextPointer;

	int64_t now = g_get_monotonic_time();
	for(const auto& item: *context->lastHeartbeatMicrosMap) {
		worker::EntityId entityId = item.first;
		const std::string &workerId = item.second.first;
		int64_t last = item.second.second;
		int64_t diff = now - last;
		if (diff > 1000 * context->maxHeartbeatTimeoutMs) {
			shovelerLogWarning("Removing client entity %lld of worker %s because it exceeded the max ping timeout of %lldms.", entityId, workerId.c_str(), context->maxHeartbeatTimeoutMs);
			context->connection->SendDeleteEntityRequest(item.first, {});
		}
	}
}

static Color colorFromHsv(float h, float s, float v)
{
	ShovelerColor colorRgb = shovelerColorFromHsv(h, s, v);
	ShovelerVector3 colorFloat = shovelerColorToVector3(colorRgb);

	return Color{colorFloat.values[0], colorFloat.values[1], colorFloat.values[2]};
}
<|endoftext|>


<commit_before>#include <cassert>
#include <cstdio>
#include <ctype.h>
#include <windows.h>
#include <vector>

struct Property
{
	char type[32];
	char scene[32];
	char object[32];
	char subobject[32];
	char property[32];
};

template <typename F>
struct privDefer {
	F f;
	privDefer(F f) : f(f) {}
	~privDefer() { f(); }
};

template <typename F>
privDefer<F> defer_func(F f) {
	return privDefer<F>(f);
}

#define DEFER_1(x, y) x##y
#define DEFER_2(x, y) DEFER_1(x, y)
#define DEFER_3(x)    DEFER_2(x, __COUNTER__)
#define defer(code)   auto DEFER_3(_defer_) = defer_func([&](){code;})

template <int N>
const char* copyIdentifier(char(&out)[N], const char* src)
{
	char* c = out;
	while (*src != ',' && *src != ')')
	{
		*c = *src;
		++c;
		++src;
	}
	*c = '\0';
	while (!isalnum(*src) && *src != '_') ++src;
	return src;
}

bool addProperty(const char* str, std::vector<Property>& properties, bool is_subproperty)
{
	assert(str);

	Property prop;
	const char* c = str;
	c += is_subproperty ? strlen("CSHARP_SUBPROPERTY(") : strlen("CSHARP_PROPERTY(");
	c = copyIdentifier(prop.type, c);
	if (strcmp(prop.type, "Type") == 0) return true;
	if (!*c) return false;
	c = copyIdentifier(prop.scene, c);
	if (!*c) return false;
	c = copyIdentifier(prop.object, c);
	if (!*c) return false;
	if (is_subproperty)
	{
		c = copyIdentifier(prop.subobject, c);
	}
	else
	{
		prop.subobject[0] = '\0';
	}
	if (!*c) return false;
	copyIdentifier(prop.property, c);
	properties.push_back(prop);
	return true;
}

bool parse(const char* path, std::vector<Property>& properties)
{
	FILE* fp = fopen(path, "rb");
	if (!fp) return false;

	defer(fclose(fp));

	fseek(fp, 0, SEEK_END);
	int size = (int)ftell(fp);
	fseek(fp, 0, SEEK_SET);

	std::vector<char> text;
	text.resize(size + 1);

	if (fread(&text[0], 1, size, fp) != size) return false;

	text[size] = 0;

	const char* prop_str = &text[0];
	while (prop_str = strstr(prop_str, "CSHARP_PROPERTY("))
	{
		if (!addProperty(prop_str, properties, false)) return false;
		++prop_str;
	}

	prop_str = &text[0];
	while (prop_str = strstr(prop_str, "CSHARP_SUBPROPERTY("))
	{
		if (!addProperty(prop_str, properties, true)) return false;
		++prop_str;
	}

	return true;
}

void writeCSharpFooter(FILE* fp, const char* obj, const std::vector<Property>& properties)
{
	const char* last_subobj = "";
	for (const Property& prop : properties)
	{
		if (!prop.subobject[0]) continue;
		if (strcmp(prop.object, obj)) continue;

		if (strcmp(prop.subobject, last_subobj))
		{
			if(last_subobj[0]) fprintf(fp, "%s", "\t\t}\n\n");
			last_subobj = prop.subobject;
			static const char* container_text =
				"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
				"		private extern static int get%sCount(IntPtr scene, int cmp);\n"
				"\n"
				"\n"
				"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
				"		private extern static void add%s(IntPtr scene, int cmp);\n"
				"\n"
				"\n"
				"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
				"		private extern static void remove%s(IntPtr scene, int cmp, int index);\n"
				"\n"
				"\n"
				"		public class %sContainer\n"
				"		{\n"
				"			public int Length { get { return %s.getGrassCount(_object.scene, _object.component_id); } }\n"
				"\n"
				"			public %sType this[int index]\n"
				"			{\n"
				"				get {\n"
				"					return new %sType(_object, index);\n"
				"				}\n"
				"			}\n"
				"\n"
				"			public void add()\n"
				"			{\n"
				"				%s.add%s(_object.scene, _object.component_id);\n"
				"			}\n"
				"\n"
				"			public void remove(int index)\n"
				"			{\n"
				"				%s.remove%s(_object.scene, _object.component_id, index);\n"
				"			}\n"
				"\n"
				"			public %s _object;\n"
				"		}\n"
				"\n"
				"\n"
				"		public %sContainer %s = new %sContainer();\n"
				"\n"
				"\n";

			fprintf(fp, container_text, prop.subobject, prop.subobject, prop.subobject, prop.subobject, prop.object, prop.subobject, prop.subobject, prop.object, prop.subobject, prop.object, prop.subobject, prop.object, prop.subobject, prop.subobject, prop.subobject);

			static const char* text =
				"		public class %sType\n"
				"		{\n"
				"			private int _index;\n"
				"			private %s _object;\n"
				"\n"
				"			public %sType(%s obj, int index)\n"
				"			{\n"
				"				_index = index;\n"
				"				_object = obj;\n"
				"			}\n"
				"\n"
				;
			fprintf(fp, text, prop.subobject, prop.object, prop.subobject, prop.object);
		}

		const char* prop_text =
			"			public %s %s\n"
			"			{\n"
			"				get{ return %s.get%s%s(_object.scene, _object.component_id, _index); }\n"
			"				set{ %s.set%s%s(_object.scene, _object.component_id, value, _index); }\n"
			"			}\n"
			"\n"
			"\n";
		fprintf(fp, prop_text, prop.type, prop.property, prop.object, prop.subobject, prop.property, prop.object, prop.subobject, prop.property);
	}
	if (last_subobj[0]) fprintf(fp, "%s", "\t\t}\n\n");

	fprintf(fp, "\t}\n\n}\n");
}

void writeCSharpHeader(FILE* fp, const char* obj, const std::vector<Property>& properties)
{
	static const char* header =
		"using System;\n"
		"using System.Runtime.InteropServices;\n"
		"using System.Runtime.CompilerServices;\n"
		"\n"
		"\n"
		"namespace Lumix\n"
		"{\n"
		"\n"
		"\n"
		"	public class %s : NativeComponent\n"
		"	{\n"
		"		private int component_id;\n"
		"		private IntPtr scene;\n"
		"\n"
		"\n"
		"		public override void create()\n"
		"		{\n"
		"			component_id = create(entity._universe, entity._entity_id, \"%s\");\n"
		"			if (component_id < 0) throw new Exception(\"Failed to create component\");\n"
		"			scene = getScene(entity._universe, \"%s\");\n";

	char cmp_name[128];
	char* c = cmp_name;
	const char* in = obj;
	while (*in && c - cmp_name < sizeof(cmp_name) - 3)
	{
		if (isupper(*in))
		{
			if (in != obj)
			{
				*c = '_';
				++c;
			}
			*c = *in - 'A' + 'a';
		}
		else
		{
			*c = *in;
		}
		++c;
		++in;
	}
	*c = '\0';
	fprintf(fp, header, obj, cmp_name, cmp_name);

	static const char* header_end =
		"		}\n"
		"\n"
		"\n";

	const char* last_subobj = "";
	for (const Property& prop : properties)
	{
		if (!prop.subobject[0]) continue;
		if (strcmp(prop.object, obj)) continue;
		if (strcmp(last_subobj, prop.subobject) == 0) continue;

		last_subobj = prop.subobject;
		fprintf(fp, "			%s._object = this;\n", prop.subobject);
	}

	fprintf(fp, "%s", header_end);
}

void writeCSharpProperty(FILE* fp, const Property& prop)
{
	static const char* text =
		"		/* %s */\n"
		"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
		"		private extern static void set%s(IntPtr scene, int cmp, %s source);\n"
		"		\n"
		"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
		"		private extern static %s get%s(IntPtr scene, int cmp);\n"
		"		\n"
		"		public %s %s\n"
		"		{\n"
		"			get{ return get%s(scene, component_id); }\n"
		"			set{ set%s(scene, component_id, value); }\n"
		"		}\n"
		"\n"
		"\n";

	const char* type = prop.type;
	if (strcmp(type, "const char*") == 0) type = "string";
	else if (strcmp(type, "Path") == 0) type = "string";
	const char* name = prop.property;
	fprintf(fp, text, name, name, type, type, name, type, name, name, name);
}

void writeCSharpSubproperty(FILE* fp, const Property& prop)
{
	static const char* text =
		"		/* %s %s */\n"
		"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
		"		private extern static void set%s%s(IntPtr scene, int cmp, int index, %s source);\n"
		"		\n"
		"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
		"		private extern static %s get%s%s(IntPtr scene, int cmp, int index);\n"
		"\n"
		"\n";

	const char* type = prop.type;
	if (strcmp(type, "const char*") == 0) type = "string";
	else if (strcmp(type, "Path") == 0) type = "string";
	const char* name = prop.property;
	const char* subobject = prop.subobject;
	fprintf(fp, text, subobject, name, subobject, name, type, type, subobject, name);
}

bool writeCSharp(const char* out_dir, std::vector<Property>& properties)
{
	auto cmp = [](const void* a, const void* b) -> int {
		auto* prop_a = (Property*)a;
		auto* prop_b = (Property*)b;
		int cmp = strcmp(prop_a->object, prop_b->object);
		if (cmp) return cmp;
		return strcmp(prop_a->subobject, prop_b->subobject);
	};
	qsort(&properties[0], properties.size(), sizeof(properties[0]), cmp);

	FILE* fp = nullptr;
	const char* last_obj = "";
	for (const Property& prop : properties)
	{
		if (strcmp(last_obj, prop.object) != 0)
		{
			if (fp)
			{
				writeCSharpFooter(fp, last_obj, properties);
				fclose(fp);
			}
			char path[256];
			strcpy_s(path, out_dir);
			strcat_s(path, prop.object);
			strcat_s(path, ".cs");
			fp = fopen(path, "wb");
			last_obj = prop.object;
			if (!fp) return false;

			writeCSharpHeader(fp, prop.object, properties);
		}
		if (prop.subobject[0])
		{
			writeCSharpSubproperty(fp, prop);
		}
		else
		{
			writeCSharpProperty(fp, prop);
		}
	}
	writeCSharpFooter(fp, last_obj, properties);
	if (fp) fclose(fp);
	return true;
}

int main()
{
	std::vector<Property> properties;
	parse("C:\\projects\\lumixengine_csharp\\src\\csharp.cpp", properties);
	writeCSharp("C:\\projects\\lumixengine_csharp\\cs\\", properties);
}<commit_msg>cleanup<commit_after>#include <cassert>
#include <cstdio>
#include <ctype.h>
#include <windows.h>
#include <vector>

struct Property
{
	char type[32];
	char scene[32];
	char object[32];
	char subobject[32];
	char property[32];
};

template <int N>
const char* copyIdentifier(char(&out)[N], const char* src)
{
	char* c = out;
	while (*src != ',' && *src != ')')
	{
		*c = *src;
		++c;
		++src;
	}
	*c = '\0';
	while (!isalnum(*src) && *src != '_') ++src;
	return src;
}

bool addProperty(const char* str, std::vector<Property>& properties, bool is_subproperty)
{
	assert(str);

	Property prop;
	const char* c = str;
	c += is_subproperty ? strlen("CSHARP_SUBPROPERTY(") : strlen("CSHARP_PROPERTY(");
	c = copyIdentifier(prop.type, c);
	if (strcmp(prop.type, "Type") == 0) return true;
	if (!*c) return false;
	c = copyIdentifier(prop.scene, c);
	if (!*c) return false;
	c = copyIdentifier(prop.object, c);
	if (!*c) return false;
	if (is_subproperty)
	{
		c = copyIdentifier(prop.subobject, c);
	}
	else
	{
		prop.subobject[0] = '\0';
	}
	if (!*c) return false;
	copyIdentifier(prop.property, c);
	properties.push_back(prop);
	return true;
}

bool parse(const char* path, std::vector<Property>& properties)
{
	FILE* fp = fopen(path, "rb");
	if (!fp) return false;

	fseek(fp, 0, SEEK_END);
	int size = (int)ftell(fp);
	fseek(fp, 0, SEEK_SET);

	std::vector<char> text;
	text.resize(size + 1);

	if (fread(&text[0], 1, size, fp) != size)
	{
		fclose(fp);
		return false;
	}
	fclose(fp);

	text[size] = 0;

	const char* prop_str = &text[0];
	while (prop_str = strstr(prop_str, "CSHARP_PROPERTY("))
	{
		if (!addProperty(prop_str, properties, false)) return false;
		++prop_str;
	}

	prop_str = &text[0];
	while (prop_str = strstr(prop_str, "CSHARP_SUBPROPERTY("))
	{
		if (!addProperty(prop_str, properties, true)) return false;
		++prop_str;
	}

	return true;
}

void writeCSharpFooter(FILE* fp, const char* obj, const std::vector<Property>& properties)
{
	const char* last_subobj = "";
	for (const Property& prop : properties)
	{
		if (!prop.subobject[0]) continue;
		if (strcmp(prop.object, obj)) continue;

		if (strcmp(prop.subobject, last_subobj))
		{
			if(last_subobj[0]) fprintf(fp, "%s", "\t\t}\n\n");
			last_subobj = prop.subobject;
			static const char* container_text =
				"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
				"		private extern static int get%sCount(IntPtr scene, int cmp);\n"
				"\n"
				"\n"
				"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
				"		private extern static void add%s(IntPtr scene, int cmp);\n"
				"\n"
				"\n"
				"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
				"		private extern static void remove%s(IntPtr scene, int cmp, int index);\n"
				"\n"
				"\n"
				"		public class %sContainer\n"
				"		{\n"
				"			public int Length { get { return %s.getGrassCount(_object.scene, _object.component_id); } }\n"
				"\n"
				"			public %sType this[int index]\n"
				"			{\n"
				"				get {\n"
				"					return new %sType(_object, index);\n"
				"				}\n"
				"			}\n"
				"\n"
				"			public void add()\n"
				"			{\n"
				"				%s.add%s(_object.scene, _object.component_id);\n"
				"			}\n"
				"\n"
				"			public void remove(int index)\n"
				"			{\n"
				"				%s.remove%s(_object.scene, _object.component_id, index);\n"
				"			}\n"
				"\n"
				"			public %s _object;\n"
				"		}\n"
				"\n"
				"\n"
				"		public %sContainer %s = new %sContainer();\n"
				"\n"
				"\n";

			fprintf(fp, container_text, prop.subobject, prop.subobject, prop.subobject, prop.subobject, prop.object, prop.subobject, prop.subobject, prop.object, prop.subobject, prop.object, prop.subobject, prop.object, prop.subobject, prop.subobject, prop.subobject);

			static const char* text =
				"		public class %sType\n"
				"		{\n"
				"			private int _index;\n"
				"			private %s _object;\n"
				"\n"
				"			public %sType(%s obj, int index)\n"
				"			{\n"
				"				_index = index;\n"
				"				_object = obj;\n"
				"			}\n"
				"\n"
				;
			fprintf(fp, text, prop.subobject, prop.object, prop.subobject, prop.object);
		}

		const char* prop_text =
			"			public %s %s\n"
			"			{\n"
			"				get{ return %s.get%s%s(_object.scene, _object.component_id, _index); }\n"
			"				set{ %s.set%s%s(_object.scene, _object.component_id, value, _index); }\n"
			"			}\n"
			"\n"
			"\n";
		fprintf(fp, prop_text, prop.type, prop.property, prop.object, prop.subobject, prop.property, prop.object, prop.subobject, prop.property);
	}
	if (last_subobj[0]) fprintf(fp, "%s", "\t\t}\n\n");

	fprintf(fp, "\t}\n\n}\n");
}

void writeCSharpHeader(FILE* fp, const char* obj, const std::vector<Property>& properties)
{
	static const char* header =
		"using System;\n"
		"using System.Runtime.InteropServices;\n"
		"using System.Runtime.CompilerServices;\n"
		"\n"
		"\n"
		"namespace Lumix\n"
		"{\n"
		"\n"
		"\n"
		"	public class %s : NativeComponent\n"
		"	{\n"
		"		private int component_id;\n"
		"		private IntPtr scene;\n"
		"\n"
		"\n"
		"		public override void create()\n"
		"		{\n"
		"			component_id = create(entity._universe, entity._entity_id, \"%s\");\n"
		"			if (component_id < 0) throw new Exception(\"Failed to create component\");\n"
		"			scene = getScene(entity._universe, \"%s\");\n";

	char cmp_name[128];
	char* c = cmp_name;
	const char* in = obj;
	while (*in && c - cmp_name < sizeof(cmp_name) - 3)
	{
		if (isupper(*in))
		{
			if (in != obj)
			{
				*c = '_';
				++c;
			}
			*c = *in - 'A' + 'a';
		}
		else
		{
			*c = *in;
		}
		++c;
		++in;
	}
	*c = '\0';
	fprintf(fp, header, obj, cmp_name, cmp_name);

	static const char* header_end =
		"		}\n"
		"\n"
		"\n";

	const char* last_subobj = "";
	for (const Property& prop : properties)
	{
		if (!prop.subobject[0]) continue;
		if (strcmp(prop.object, obj)) continue;
		if (strcmp(last_subobj, prop.subobject) == 0) continue;

		last_subobj = prop.subobject;
		fprintf(fp, "			%s._object = this;\n", prop.subobject);
	}

	fprintf(fp, "%s", header_end);
}

void writeCSharpProperty(FILE* fp, const Property& prop)
{
	static const char* text =
		"		/* %s */\n"
		"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
		"		private extern static void set%s(IntPtr scene, int cmp, %s source);\n"
		"		\n"
		"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
		"		private extern static %s get%s(IntPtr scene, int cmp);\n"
		"		\n"
		"		public %s %s\n"
		"		{\n"
		"			get{ return get%s(scene, component_id); }\n"
		"			set{ set%s(scene, component_id, value); }\n"
		"		}\n"
		"\n"
		"\n";

	const char* type = prop.type;
	if (strcmp(type, "const char*") == 0) type = "string";
	else if (strcmp(type, "Path") == 0) type = "string";
	const char* name = prop.property;
	fprintf(fp, text, name, name, type, type, name, type, name, name, name);
}

void writeCSharpSubproperty(FILE* fp, const Property& prop)
{
	static const char* text =
		"		/* %s %s */\n"
		"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
		"		private extern static void set%s%s(IntPtr scene, int cmp, int index, %s source);\n"
		"		\n"
		"		[MethodImplAttribute(MethodImplOptions.InternalCall)]\n"
		"		private extern static %s get%s%s(IntPtr scene, int cmp, int index);\n"
		"\n"
		"\n";

	const char* type = prop.type;
	if (strcmp(type, "const char*") == 0) type = "string";
	else if (strcmp(type, "Path") == 0) type = "string";
	const char* name = prop.property;
	const char* subobject = prop.subobject;
	fprintf(fp, text, subobject, name, subobject, name, type, type, subobject, name);
}

bool writeCSharp(const char* out_dir, std::vector<Property>& properties)
{
	auto cmp = [](const void* a, const void* b) -> int {
		auto* prop_a = (Property*)a;
		auto* prop_b = (Property*)b;
		int cmp = strcmp(prop_a->object, prop_b->object);
		if (cmp) return cmp;
		return strcmp(prop_a->subobject, prop_b->subobject);
	};
	qsort(&properties[0], properties.size(), sizeof(properties[0]), cmp);

	FILE* fp = nullptr;
	const char* last_obj = "";
	for (const Property& prop : properties)
	{
		if (strcmp(last_obj, prop.object) != 0)
		{
			if (fp)
			{
				writeCSharpFooter(fp, last_obj, properties);
				fclose(fp);
			}
			char path[256];
			strcpy_s(path, out_dir);
			strcat_s(path, prop.object);
			strcat_s(path, ".cs");
			fp = fopen(path, "wb");
			last_obj = prop.object;
			if (!fp) return false;

			writeCSharpHeader(fp, prop.object, properties);
		}
		if (prop.subobject[0])
		{
			writeCSharpSubproperty(fp, prop);
		}
		else
		{
			writeCSharpProperty(fp, prop);
		}
	}
	writeCSharpFooter(fp, last_obj, properties);
	if (fp) fclose(fp);
	return true;
}

int main()
{
	std::vector<Property> properties;
	parse("C:\\projects\\lumixengine_csharp\\src\\csharp.cpp", properties);
	writeCSharp("C:\\projects\\lumixengine_csharp\\cs\\", properties);
}<|endoftext|>


<commit_before>#include "Variant.h"
#include "split.h"
#include "cdflib.hpp"
#include "pdflib.hpp"
#include "var.hpp"

#include <string>
#include <iostream>
#include <math.h>
#include <cmath>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <getopt.h>

using namespace std;
using namespace vcf;

void printVersion(void){
  cerr << "INFO: version 1.0.0 ; date: April 2014 ; author: Zev Kronenberg; email : zev.kronenberg@utah.edu " << endl;
}

void printHelp(void){

  cerr << endl << endl;
  cerr << "INFO: help" << endl;
  cerr << "INFO: description:" << endl;
  cerr << "      General population genetic statistics for each SNP                                                                    " << endl << endl;

  cerr << "Output : 9 columns :                 "    << endl;
  cerr << "     1. seqid                        "    << endl;
  cerr << "     2. position                     "    << endl;
  cerr << "     3. target allele frequency      "    << endl;
  cerr << "     4. expected heterozygosity      "    << endl;
  cerr << "     5. observed heterozygosity      "    << endl;
  cerr << "     6. number of hets               "    << endl;
  cerr << "     7. number of homozygous ref     "    << endl;
  cerr << "     8. number of homozygous alt     "    << endl;
  cerr << "     9. target Fis                   "    << endl;

  cerr << "INFO: usage:  popStat --type PL --target 0,1,2,3,4,5,6,7 --file my.vcf                                                                " << endl;
  cerr << endl;
  cerr << "INFO: required: t,target     -- a zero based comma seperated list of target individuals corrisponding to VCF columns        " << endl;
  cerr << "INFO: required: f,file       -- proper formatted VCF                                                                        " << endl;
  cerr << "INFO: required, y,type       -- genotype likelihood format; genotype : GL,PL,GP                                             " << endl;
  cerr << "INFO: optional, r,region     -- a tabix compliant region : chr1:1-1000 or chr1                                              " << endl;

  printVersion();
}

double bound(double v){
  if(v <= 0.00001){
    return  0.00001;
  }
  if(v >= 0.99999){
    return 0.99999;
  }
  return v;
}

void loadIndices(map<int, int> & index, string set){

  vector<string>  indviduals = split(set, ",");

  vector<string>::iterator it = indviduals.begin();

  for(; it != indviduals.end(); it++){
    index[ atoi( (*it).c_str() ) ] = 1;
  }
}

int main(int argc, char** argv) {

  // set the random seed for MCMC

  srand((unsigned)time(NULL));

  // the filename

  string filename = "NA";

  // set region to scaffold

  string region = "NA";

  // using vcflib; thanks to Erik Garrison

  VariantCallFile variantFile;

  // zero based index for the target and background indivudals

  map<int, int> it, ib;

  // genotype likelihood format

  string type = "NA";

    const struct option longopts[] =
      {
	{"version"   , 0, 0, 'v'},
	{"help"      , 0, 0, 'h'},
        {"file"      , 1, 0, 'f'},
	{"target"    , 1, 0, 't'},
	{"region"    , 1, 0, 'r'},
	{"type"      , 1, 0, 'y'},
	{0,0,0,0}
      };

    int index;
    int iarg=0;

    while(iarg != -1)
      {
	iarg = getopt_long(argc, argv, "y:r:d:t:b:f:chv", longopts, &index);

	switch (iarg)
	  {
	  case 'h':
	    printHelp();
	    return 0;
	  case 'v':
	    printVersion();
	    return 0;
	  case 't':
	    loadIndices(it, optarg);
	    cerr << "INFO: there are " << it.size() << " individuals in the target" << endl;
	    cerr << "INFO: target ids: " << optarg << endl;
	    break;
	  case 'b':
	    loadIndices(ib, optarg);
	    cerr << "INFO: there are " << ib.size() << " individuals in the background" << endl;
	    cerr << "INFO: background ids: " << optarg << endl;
	    break;
	  case 'f':
	    cerr << "INFO: file: " << optarg  <<  endl;
	    filename = optarg;
	    break;
	  case 'r':
            cerr << "INFO: set seqid region to : " << optarg << endl;
	    region = optarg;
	    break;
	  case 'y':
	    type = optarg;
	    cerr << "INFO: set genotype likelihood to: " << type << endl;
	    break;
	  default:
	    break;
	  }

      }

    if(filename == "NA"){
      cerr << "FATAL: failed to specify a file" << endl;
      printHelp();
    }

    if(!variantFile.open(filename)){
      cerr << "FATAL: could not open file for reading" << endl;
      printHelp();
    }

    if(region != "NA"){
      if(! variantFile.setRegion(region)){
	cerr <<"FATAL: unable to set region" << endl;
	return 1;
      }
    }

    if (!variantFile.is_open()) {
      cerr << "FATAL: could not open VCF for reading" << endl;
      printHelp();
      return 1;
    }

    map<string, int> okayGenotypeLikelihoods;
    okayGenotypeLikelihoods["PL"] = 1;
    okayGenotypeLikelihoods["GL"] = 1;
    okayGenotypeLikelihoods["GP"] = 1;
    okayGenotypeLikelihoods["GT"] = 1;

    if(type == "NA"){
      cerr << "FATAL: failed to specify genotype likelihood format : PL or GL" << endl;
      printHelp();
      return 1;
    }
    if(okayGenotypeLikelihoods.find(type) == okayGenotypeLikelihoods.end()){
      cerr << "FATAL: genotype likelihood is incorrectly formatted, only use: PL or GL" << endl;
      printHelp();
      return 1;
    }

    Variant var(variantFile);

    vector<string> samples = variantFile.sampleNames;
    int nsamples = samples.size();

    while (variantFile.getNextVariant(var)) {

	// biallelic sites naturally

	if(var.alt.size() > 1){
	  continue;
	}

	vector < map< string, vector<string> > > target, background, total;

	int index = 0;

	for(int nsamp = 0; nsamp < nsamples; nsamp++){

	  map<string, vector<string> > sample = var.samples[ samples[nsamp]];

	    if(sample["GT"].front() != "./."){
	      if(it.find(index) != it.end() ){
		target.push_back(sample);
	      }
	    }
	    index += 1;
	}

	genotype * populationTarget      ;
	genotype * populationBackground  ;

	if(type == "PL"){
	  populationTarget     = new pl();
	}
	if(type == "GL"){
	  populationTarget     = new gl();
	}
	if(type == "GP"){
	  populationTarget     = new gp();
	}
	if(type == "GT"){
          populationTarget     = new gt();
	}

	populationTarget->loadPop(target, var.sequenceName, var.position);

	 //cerr << "     3. target allele frequency      "    << endl;
	 //cerr << "     4. expected heterozygosity      "    << endl;
	 //cerr << "     5. observed heterozygosity      "    << endl;
	 //cerr << "     6. number of hets               "    << endl;
	 //cerr << "     7. number of homozygous ref     "    << endl;
	 //cerr << "     8. number of homozygous alt     "    << endl;
	 //cerr << "     9. target Fis                   "    << endl;

	if(populationTarget->af == -1){
	  continue;
	}

	double ehet = 2*(populationTarget->af * (1 - populationTarget->af));

	cout << var.sequenceName << "\t"  << var.position << "\t"
	     << populationTarget->af  << "\t"
	     << ehet << "\t"
	     << populationTarget->hfrq  << "\t"
	     << populationTarget->nhet  << "\t"
	     << populationTarget->nhomr << "\t"
	     << populationTarget->nhoma << "\t"
	     << populationTarget->fis   << endl;

    }
    return 0;
}
<commit_msg>Update popStats.cpp<commit_after>#include "Variant.h"
#include "split.h"
#include "cdflib.hpp"
#include "pdflib.hpp"
#include "var.hpp"

#include <string>
#include <iostream>
#include <math.h>
#include <cmath>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <getopt.h>

using namespace std;
using namespace vcf;

void printVersion(void){
  cerr << "INFO: version 1.0.0 ; date: April 2014 ; author: Zev Kronenberg; email : zev.kronenberg@utah.edu " << endl;
}

void printHelp(void){

  cerr << endl << endl;
  cerr << "INFO: help" << endl;
  cerr << "INFO: description:" << endl;
  cerr << "      General population genetic statistics for each SNP                                                                    " << endl << endl;

  cerr << "Output : 9 columns :                 "    << endl;
  cerr << "     1. seqid                        "    << endl;
  cerr << "     2. position                     "    << endl;
  cerr << "     3. target allele frequency      "    << endl;
  cerr << "     4. expected heterozygosity      "    << endl;
  cerr << "     5. observed heterozygosity      "    << endl;
  cerr << "     6. number of hets               "    << endl;
  cerr << "     7. number of homozygous ref     "    << endl;
  cerr << "     8. number of homozygous alt     "    << endl;
  cerr << "     9. target Fis                   "    << endl;

  cerr << "INFO: usage:  popStat --type PL --target 0,1,2,3,4,5,6,7 --file my.vcf                                                                " << endl;
  cerr << endl;
  cerr << "INFO: required: t,target     -- a zero based comma seperated list of target individuals corresponding to VCF columns        " << endl;
  cerr << "INFO: required: f,file       -- proper formatted VCF                                                                        " << endl;
  cerr << "INFO: required, y,type       -- genotype likelihood format; genotype : GL,PL,GP                                             " << endl;
  cerr << "INFO: optional, r,region     -- a tabix compliant region : chr1:1-1000 or chr1                                              " << endl;

  printVersion();
}

double bound(double v){
  if(v <= 0.00001){
    return  0.00001;
  }
  if(v >= 0.99999){
    return 0.99999;
  }
  return v;
}

void loadIndices(map<int, int> & index, string set){

  vector<string>  indviduals = split(set, ",");

  vector<string>::iterator it = indviduals.begin();

  for(; it != indviduals.end(); it++){
    index[ atoi( (*it).c_str() ) ] = 1;
  }
}

int main(int argc, char** argv) {

  // set the random seed for MCMC

  srand((unsigned)time(NULL));

  // the filename

  string filename = "NA";

  // set region to scaffold

  string region = "NA";

  // using vcflib; thanks to Erik Garrison

  VariantCallFile variantFile;

  // zero based index for the target and background indivudals

  map<int, int> it, ib;

  // genotype likelihood format

  string type = "NA";

    const struct option longopts[] =
      {
	{"version"   , 0, 0, 'v'},
	{"help"      , 0, 0, 'h'},
        {"file"      , 1, 0, 'f'},
	{"target"    , 1, 0, 't'},
	{"region"    , 1, 0, 'r'},
	{"type"      , 1, 0, 'y'},
	{0,0,0,0}
      };

    int index;
    int iarg=0;

    while(iarg != -1)
      {
	iarg = getopt_long(argc, argv, "y:r:d:t:b:f:chv", longopts, &index);

	switch (iarg)
	  {
	  case 'h':
	    printHelp();
	    return 0;
	  case 'v':
	    printVersion();
	    return 0;
	  case 't':
	    loadIndices(it, optarg);
	    cerr << "INFO: there are " << it.size() << " individuals in the target" << endl;
	    cerr << "INFO: target ids: " << optarg << endl;
	    break;
	  case 'b':
	    loadIndices(ib, optarg);
	    cerr << "INFO: there are " << ib.size() << " individuals in the background" << endl;
	    cerr << "INFO: background ids: " << optarg << endl;
	    break;
	  case 'f':
	    cerr << "INFO: file: " << optarg  <<  endl;
	    filename = optarg;
	    break;
	  case 'r':
            cerr << "INFO: set seqid region to : " << optarg << endl;
	    region = optarg;
	    break;
	  case 'y':
	    type = optarg;
	    cerr << "INFO: set genotype likelihood to: " << type << endl;
	    break;
	  default:
	    break;
	  }

      }

    if(filename == "NA"){
      cerr << "FATAL: failed to specify a file" << endl;
      printHelp();
    }

    if(!variantFile.open(filename)){
      cerr << "FATAL: could not open file for reading" << endl;
      printHelp();
    }

    if(region != "NA"){
      if(! variantFile.setRegion(region)){
	cerr <<"FATAL: unable to set region" << endl;
	return 1;
      }
    }

    if (!variantFile.is_open()) {
      cerr << "FATAL: could not open VCF for reading" << endl;
      printHelp();
      return 1;
    }

    map<string, int> okayGenotypeLikelihoods;
    okayGenotypeLikelihoods["PL"] = 1;
    okayGenotypeLikelihoods["GL"] = 1;
    okayGenotypeLikelihoods["GP"] = 1;
    okayGenotypeLikelihoods["GT"] = 1;

    if(type == "NA"){
      cerr << "FATAL: failed to specify genotype likelihood format : PL or GL" << endl;
      printHelp();
      return 1;
    }
    if(okayGenotypeLikelihoods.find(type) == okayGenotypeLikelihoods.end()){
      cerr << "FATAL: genotype likelihood is incorrectly formatted, only use: PL or GL" << endl;
      printHelp();
      return 1;
    }

    Variant var(variantFile);

    vector<string> samples = variantFile.sampleNames;
    int nsamples = samples.size();

    while (variantFile.getNextVariant(var)) {

	// biallelic sites naturally

	if(var.alt.size() > 1){
	  continue;
	}

	vector < map< string, vector<string> > > target, background, total;

	int index = 0;

	for(int nsamp = 0; nsamp < nsamples; nsamp++){

	  map<string, vector<string> > sample = var.samples[ samples[nsamp]];

	    if(sample["GT"].front() != "./."){
	      if(it.find(index) != it.end() ){
		target.push_back(sample);
	      }
	    }
	    index += 1;
	}

	genotype * populationTarget      ;
	genotype * populationBackground  ;

	if(type == "PL"){
	  populationTarget     = new pl();
	}
	if(type == "GL"){
	  populationTarget     = new gl();
	}
	if(type == "GP"){
	  populationTarget     = new gp();
	}
	if(type == "GT"){
          populationTarget     = new gt();
	}

	populationTarget->loadPop(target, var.sequenceName, var.position);

	 //cerr << "     3. target allele frequency      "    << endl;
	 //cerr << "     4. expected heterozygosity      "    << endl;
	 //cerr << "     5. observed heterozygosity      "    << endl;
	 //cerr << "     6. number of hets               "    << endl;
	 //cerr << "     7. number of homozygous ref     "    << endl;
	 //cerr << "     8. number of homozygous alt     "    << endl;
	 //cerr << "     9. target Fis                   "    << endl;

	if(populationTarget->af == -1){
	  continue;
	}

	double ehet = 2*(populationTarget->af * (1 - populationTarget->af));

	cout << var.sequenceName << "\t"  << var.position << "\t"
	     << populationTarget->af  << "\t"
	     << ehet << "\t"
	     << populationTarget->hfrq  << "\t"
	     << populationTarget->nhet  << "\t"
	     << populationTarget->nhomr << "\t"
	     << populationTarget->nhoma << "\t"
	     << populationTarget->fis   << endl;

    }
    return 0;
}
<|endoftext|>