<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: Date.cxx,v $
 *
 *  $Revision: 1.23 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-16 23:48:14 $
 *
 *  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
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_forms.hxx"

#ifndef _FORMS_DATE_HXX_
#include "Date.hxx"
#endif

#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef _DATE_HXX
#include <tools/date.hxx>
#endif
#ifndef _DBHELPER_DBCONVERSION_HXX_
#include <connectivity/dbconversion.hxx>
#endif

#ifndef _COM_SUN_STAR_SDBC_DATATYPE_HPP_
#include <com/sun/star/sdbc/DataType.hpp>
#endif

using namespace dbtools;

//.........................................................................
namespace frm
{
//.........................................................................

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::form;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::lang;

//------------------------------------------------------------------
ODateControl::ODateControl(const Reference<XMultiServiceFactory>& _rxFactory)
               :OBoundControl(_rxFactory, VCL_CONTROL_DATEFIELD)
{
}

//------------------------------------------------------------------
InterfaceRef SAL_CALL ODateControl_CreateInstance(const Reference<XMultiServiceFactory>& _rxFactory)
{
    return *(new ODateControl(_rxFactory));
}

//------------------------------------------------------------------------------
Sequence<Type> ODateControl::_getTypes()
{
    return OBoundControl::_getTypes();
}

//------------------------------------------------------------------------------
StringSequence SAL_CALL ODateControl::getSupportedServiceNames() throw()
{
    StringSequence aSupported = OBoundControl::getSupportedServiceNames();
    aSupported.realloc(aSupported.getLength() + 1);

    ::rtl::OUString*pArray = aSupported.getArray();
    pArray[aSupported.getLength()-1] = FRM_SUN_CONTROL_DATEFIELD;
    return aSupported;
}

/*************************************************************************/
//------------------------------------------------------------------
InterfaceRef SAL_CALL ODateModel_CreateInstance(const Reference<XMultiServiceFactory>& _rxFactory)
{
    return *(new ODateModel(_rxFactory));
}

//------------------------------------------------------------------------------
Sequence<Type> ODateModel::_getTypes()
{
    return OEditBaseModel::_getTypes();
}

//------------------------------------------------------------------
DBG_NAME( ODateModel )
//------------------------------------------------------------------
ODateModel::ODateModel(const Reference<XMultiServiceFactory>& _rxFactory)
            :OEditBaseModel( _rxFactory, VCL_CONTROLMODEL_DATEFIELD, FRM_SUN_CONTROL_DATEFIELD, sal_True, sal_True )
                        // use the old control name for compytibility reasons
            ,OLimitedFormats( _rxFactory, FormComponentType::DATEFIELD )
{
    DBG_CTOR( ODateModel, NULL );

    m_nClassId = FormComponentType::DATEFIELD;
    initValueProperty( PROPERTY_DATE, PROPERTY_ID_DATE );

    setAggregateSet(m_xAggregateFastSet, getOriginalHandle(PROPERTY_ID_DATEFORMAT));

    osl_incrementInterlockedCount( &m_refCount );
    try
    {
        if ( m_xAggregateSet.is() )
            m_xAggregateSet->setPropertyValue( PROPERTY_DATEMIN, makeAny( (sal_Int32)( ::Date( 1, 1, 1800 ).GetDate() ) ) );
    }
    catch( const Exception& )
    {
        OSL_ENSURE( sal_False, "ODateModel::ODateModel: caught an exception!" );
    }
    osl_decrementInterlockedCount( &m_refCount );
}

//------------------------------------------------------------------------------
ODateModel::ODateModel( const ODateModel* _pOriginal, const Reference<XMultiServiceFactory>& _rxFactory )
    :OEditBaseModel( _pOriginal, _rxFactory )
    ,OLimitedFormats( _rxFactory, FormComponentType::DATEFIELD )
{
    DBG_CTOR( ODateModel, NULL );

    setAggregateSet( m_xAggregateFastSet, getOriginalHandle( PROPERTY_ID_DATEFORMAT ) );
}

//------------------------------------------------------------------------------
ODateModel::~ODateModel( )
{
    setAggregateSet(Reference< XFastPropertySet >(), -1);
    DBG_DTOR( ODateModel, NULL );
}

// XCloneable
//------------------------------------------------------------------------------
IMPLEMENT_DEFAULT_CLONING( ODateModel )

// XServiceInfo
//------------------------------------------------------------------------------
StringSequence SAL_CALL ODateModel::getSupportedServiceNames() throw()
{
    StringSequence aSupported = OBoundControlModel::getSupportedServiceNames();

    sal_Int32 nOldLen = aSupported.getLength();
    aSupported.realloc( nOldLen + 8 );
    ::rtl::OUString* pStoreTo = aSupported.getArray() + nOldLen;

    *pStoreTo++ = BINDABLE_CONTROL_MODEL;
    *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
    *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;

    *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
    *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;

    *pStoreTo++ = FRM_SUN_COMPONENT_DATEFIELD;
    *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_DATEFIELD;
    *pStoreTo++ = BINDABLE_DATABASE_DATE_FIELD;

    return aSupported;
}

//------------------------------------------------------------------------------
::rtl::OUString SAL_CALL ODateModel::getServiceName() throw ( ::com::sun::star::uno::RuntimeException)
{
    return FRM_COMPONENT_DATEFIELD; // old (non-sun) name for compatibility !
}

// XPropertySet
//------------------------------------------------------------------------------
Reference<XPropertySetInfo> SAL_CALL ODateModel::getPropertySetInfo() throw( RuntimeException )
{
    Reference<XPropertySetInfo>  xInfo( createPropertySetInfo( getInfoHelper() ) );
    return xInfo;
}

//------------------------------------------------------------------------------
void ODateModel::fillProperties(
        Sequence< Property >& _rProps,
        Sequence< Property >& _rAggregateProps ) const
{
    BEGIN_DESCRIBE_PROPERTIES( 4, OEditBaseModel )
        DECL_PROP3(DEFAULT_DATE,            sal_Int32,              BOUND, MAYBEDEFAULT, MAYBEVOID);
        DECL_PROP1(TABINDEX,                sal_Int16,              BOUND);
        DECL_PROP1(FORMATKEY,               sal_Int32,              TRANSIENT);
        DECL_IFACE_PROP2(FORMATSSUPPLIER,   XNumberFormatsSupplier, READONLY, TRANSIENT);
    END_DESCRIBE_PROPERTIES();
}

//------------------------------------------------------------------------------
::cppu::IPropertyArrayHelper& ODateModel::getInfoHelper()
{
    return *const_cast<ODateModel*>(this)->getArrayHelper();
}

//------------------------------------------------------------------------------
void SAL_CALL ODateModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle ) const
{
    switch (_nHandle)
    {
        case PROPERTY_ID_FORMATKEY:
            getFormatKeyPropertyValue(_rValue);
            break;
        case PROPERTY_ID_FORMATSSUPPLIER:
            _rValue <<= getFormatsSupplier();
            break;
        default:
            OEditBaseModel::getFastPropertyValue(_rValue, _nHandle);
            break;
    }
}

//------------------------------------------------------------------------------
sal_Bool SAL_CALL ODateModel::convertFastPropertyValue(Any& _rConvertedValue, Any& _rOldValue,
        sal_Int32 _nHandle, const Any& _rValue ) throw(IllegalArgumentException)
{
    if (PROPERTY_ID_FORMATKEY == _nHandle)
        return convertFormatKeyPropertyValue(_rConvertedValue, _rOldValue, _rValue);
    else
        return OEditBaseModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue );
}

//------------------------------------------------------------------------------
void SAL_CALL ODateModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue) throw ( ::com::sun::star::uno::Exception)
{
    if (PROPERTY_ID_FORMATKEY == _nHandle)
        setFormatKeyPropertyValue(_rValue);
    else
        OEditBaseModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
}

// XLoadListener
//------------------------------------------------------------------------------
void ODateModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
{
    OBoundControlModel::onConnectedDbColumn( _rxForm );
    Reference<XPropertySet> xField = getField();
    if (xField.is())
    {
        m_bDateTimeField = sal_False;
        try
        {
            sal_Int32 nFieldType;
            xField->getPropertyValue(PROPERTY_FIELDTYPE) >>= nFieldType;
            m_bDateTimeField = (nFieldType == DataType::TIMESTAMP);
        }
        catch(Exception&)
        {
        }
    }
}

//------------------------------------------------------------------------------
sal_Bool ODateModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
{
    Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
    if ( !compare( aControlValue, m_aSaveValue ) )
    {
        if ( !aControlValue.hasValue() )
            m_xColumnUpdate->updateNull();
        else
        {
            try
            {
                util::Date aDate;
                if ( !( aControlValue >>= aDate ) )
                {
                    sal_Int32 nAsInt(0);
                    aControlValue >>= nAsInt;
                    aDate = DBTypeConversion::toDate(nAsInt);
                }

                if ( !m_bDateTimeField )
                    m_xColumnUpdate->updateDate( aDate );
                else
                {
                    util::DateTime aDateTime = m_xColumn->getTimestamp();
                    aDateTime.Day = aDate.Day;
                    aDateTime.Month = aDate.Month;
                    aDateTime.Year = aDate.Year;
                    m_xColumnUpdate->updateTimestamp( aDateTime );
                }
            }
            catch(Exception&)
            {
                return sal_False;
            }
        }
        m_aSaveValue = aControlValue;
    }
    return sal_True;
}

//------------------------------------------------------------------------------
void ODateModel::impl_translateControlValueToUNODate( Any& _rUNOValue ) const
{
    _rUNOValue = getControlValue();
    if ( _rUNOValue.hasValue() )
    {
        sal_Int32 nDate = 0;
        OSL_VERIFY( _rUNOValue >>= nDate );
        _rUNOValue <<= DBTypeConversion::toDate( nDate );
    }
}

//------------------------------------------------------------------------------
Any ODateModel::translateControlValueToExternalValue( ) const
{
    Any aExternalValue;
    impl_translateControlValueToUNODate( aExternalValue );
    return aExternalValue;
}

//------------------------------------------------------------------------------
Any ODateModel::translateExternalValueToControlValue( ) const
{
    OSL_PRECOND( hasExternalValueBinding(),
        "ODateModel::translateExternalValueToControlValue: precondition not met!" );

    Any aControlValue;
    if ( hasExternalValueBinding() )
    {
        Any aExternalValue = getExternalValueBinding()->getValue( ::getCppuType( static_cast< util::Date* >( NULL ) ) );
        if ( aExternalValue.hasValue() )
        {
            util::Date aDate;
            OSL_VERIFY( aExternalValue >>= aDate );
            aControlValue <<= DBTypeConversion::toINT32( aDate );
        }
    }
    return aControlValue;
}

//------------------------------------------------------------------------------
Any ODateModel::translateControlValueToValidatableValue( ) const
{
    Any aValidatableValue;
    impl_translateControlValueToUNODate( aValidatableValue );
    return aValidatableValue;
}

//------------------------------------------------------------------------------
Any ODateModel::translateDbColumnToControlValue()
{
    util::Date aDate = m_xColumn->getDate();
    if (m_xColumn->wasNull())
        m_aSaveValue.clear();
    else
        // the aggregated set expects an Int32 as value ...
        m_aSaveValue <<= DBTypeConversion::toINT32(aDate);

    return m_aSaveValue;
}

//------------------------------------------------------------------------------
Any ODateModel::getDefaultForReset() const
{
    return m_aDefault;
}

//------------------------------------------------------------------------------
sal_Bool ODateModel::approveValueBinding( const Reference< binding::XValueBinding >& _rxBinding )
{
    OSL_PRECOND( _rxBinding.is(), "ODateModel::approveValueBinding: invalid binding!" );

    return  _rxBinding.is()
        &&  _rxBinding->supportsType( ::getCppuType( static_cast< util::Date* >( NULL ) ) );
}

//.........................................................................
}   // namespace frm
//.........................................................................

<commit_msg>INTEGRATION: CWS pj65 (1.23.22); FILE MERGED 2006/10/31 13:40:59 pjanik 1.23.22.1: #i71027#: prevent warnings on Mac OS X with gcc 4.0.1.<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: Date.cxx,v $
 *
 *  $Revision: 1.24 $
 *
 *  last change: $Author: vg $ $Date: 2006-11-21 17:40:26 $
 *
 *  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
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_forms.hxx"

#ifndef _FORMS_DATE_HXX_
#include "Date.hxx"
#endif

#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef _DATE_HXX
#include <tools/date.hxx>
#endif
#ifndef _DBHELPER_DBCONVERSION_HXX_
#include <connectivity/dbconversion.hxx>
#endif

#ifndef _COM_SUN_STAR_SDBC_DATATYPE_HPP_
#include <com/sun/star/sdbc/DataType.hpp>
#endif

using namespace dbtools;

//.........................................................................
namespace frm
{
//.........................................................................

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::form;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::lang;

//------------------------------------------------------------------
ODateControl::ODateControl(const Reference<XMultiServiceFactory>& _rxFactory)
               :OBoundControl(_rxFactory, VCL_CONTROL_DATEFIELD)
{
}

//------------------------------------------------------------------
InterfaceRef SAL_CALL ODateControl_CreateInstance(const Reference<XMultiServiceFactory>& _rxFactory)
{
    return *(new ODateControl(_rxFactory));
}

//------------------------------------------------------------------------------
Sequence<Type> ODateControl::_getTypes()
{
    return OBoundControl::_getTypes();
}

//------------------------------------------------------------------------------
StringSequence SAL_CALL ODateControl::getSupportedServiceNames() throw()
{
    StringSequence aSupported = OBoundControl::getSupportedServiceNames();
    aSupported.realloc(aSupported.getLength() + 1);

    ::rtl::OUString*pArray = aSupported.getArray();
    pArray[aSupported.getLength()-1] = FRM_SUN_CONTROL_DATEFIELD;
    return aSupported;
}

/*************************************************************************/
//------------------------------------------------------------------
InterfaceRef SAL_CALL ODateModel_CreateInstance(const Reference<XMultiServiceFactory>& _rxFactory)
{
    return *(new ODateModel(_rxFactory));
}

//------------------------------------------------------------------------------
Sequence<Type> ODateModel::_getTypes()
{
    return OEditBaseModel::_getTypes();
}

//------------------------------------------------------------------
DBG_NAME( ODateModel )
//------------------------------------------------------------------
ODateModel::ODateModel(const Reference<XMultiServiceFactory>& _rxFactory)
            :OEditBaseModel( _rxFactory, VCL_CONTROLMODEL_DATEFIELD, FRM_SUN_CONTROL_DATEFIELD, sal_True, sal_True )
                        // use the old control name for compytibility reasons
            ,OLimitedFormats( _rxFactory, FormComponentType::DATEFIELD )
{
    DBG_CTOR( ODateModel, NULL );

    m_nClassId = FormComponentType::DATEFIELD;
    initValueProperty( PROPERTY_DATE, PROPERTY_ID_DATE );

    setAggregateSet(m_xAggregateFastSet, getOriginalHandle(PROPERTY_ID_DATEFORMAT));

    osl_incrementInterlockedCount( &m_refCount );
    try
    {
        if ( m_xAggregateSet.is() )
            m_xAggregateSet->setPropertyValue( PROPERTY_DATEMIN, makeAny( (sal_Int32)( ::Date( 1, 1, 1800 ).GetDate() ) ) );
    }
    catch( const Exception& )
    {
        OSL_ENSURE( sal_False, "ODateModel::ODateModel: caught an exception!" );
    }
    osl_decrementInterlockedCount( &m_refCount );
}

//------------------------------------------------------------------------------
ODateModel::ODateModel( const ODateModel* _pOriginal, const Reference<XMultiServiceFactory>& _rxFactory )
    :OEditBaseModel( _pOriginal, _rxFactory )
    ,OLimitedFormats( _rxFactory, FormComponentType::DATEFIELD )
{
    DBG_CTOR( ODateModel, NULL );

    setAggregateSet( m_xAggregateFastSet, getOriginalHandle( PROPERTY_ID_DATEFORMAT ) );
}

//------------------------------------------------------------------------------
ODateModel::~ODateModel( )
{
    setAggregateSet(Reference< XFastPropertySet >(), -1);
    DBG_DTOR( ODateModel, NULL );
}

// XCloneable
//------------------------------------------------------------------------------
IMPLEMENT_DEFAULT_CLONING( ODateModel )

// XServiceInfo
//------------------------------------------------------------------------------
StringSequence SAL_CALL ODateModel::getSupportedServiceNames() throw()
{
    StringSequence aSupported = OBoundControlModel::getSupportedServiceNames();

    sal_Int32 nOldLen = aSupported.getLength();
    aSupported.realloc( nOldLen + 8 );
    ::rtl::OUString* pStoreTo = aSupported.getArray() + nOldLen;

    *pStoreTo++ = BINDABLE_CONTROL_MODEL;
    *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
    *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;

    *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
    *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;

    *pStoreTo++ = FRM_SUN_COMPONENT_DATEFIELD;
    *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_DATEFIELD;
    *pStoreTo++ = BINDABLE_DATABASE_DATE_FIELD;

    return aSupported;
}

//------------------------------------------------------------------------------
::rtl::OUString SAL_CALL ODateModel::getServiceName() throw ( ::com::sun::star::uno::RuntimeException)
{
    return FRM_COMPONENT_DATEFIELD; // old (non-sun) name for compatibility !
}

// XPropertySet
//------------------------------------------------------------------------------
Reference<XPropertySetInfo> SAL_CALL ODateModel::getPropertySetInfo() throw( RuntimeException )
{
    Reference<XPropertySetInfo>  xInfo( createPropertySetInfo( getInfoHelper() ) );
    return xInfo;
}

//------------------------------------------------------------------------------
void ODateModel::fillProperties(
        Sequence< Property >& _rProps,
        Sequence< Property >& _rAggregateProps ) const
{
    BEGIN_DESCRIBE_PROPERTIES( 4, OEditBaseModel )
        DECL_PROP3(DEFAULT_DATE,            sal_Int32,              BOUND, MAYBEDEFAULT, MAYBEVOID);
        DECL_PROP1(TABINDEX,                sal_Int16,              BOUND);
        DECL_PROP1(FORMATKEY,               sal_Int32,              TRANSIENT);
        DECL_IFACE_PROP2(FORMATSSUPPLIER,   XNumberFormatsSupplier, READONLY, TRANSIENT);
    END_DESCRIBE_PROPERTIES();
}

//------------------------------------------------------------------------------
::cppu::IPropertyArrayHelper& ODateModel::getInfoHelper()
{
    return *const_cast<ODateModel*>(this)->getArrayHelper();
}

//------------------------------------------------------------------------------
void SAL_CALL ODateModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle ) const
{
    switch (_nHandle)
    {
        case PROPERTY_ID_FORMATKEY:
            getFormatKeyPropertyValue(_rValue);
            break;
        case PROPERTY_ID_FORMATSSUPPLIER:
            _rValue <<= getFormatsSupplier();
            break;
        default:
            OEditBaseModel::getFastPropertyValue(_rValue, _nHandle);
            break;
    }
}

//------------------------------------------------------------------------------
sal_Bool SAL_CALL ODateModel::convertFastPropertyValue(Any& _rConvertedValue, Any& _rOldValue,
        sal_Int32 _nHandle, const Any& _rValue ) throw(IllegalArgumentException)
{
    if (PROPERTY_ID_FORMATKEY == _nHandle)
        return convertFormatKeyPropertyValue(_rConvertedValue, _rOldValue, _rValue);
    else
        return OEditBaseModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue );
}

//------------------------------------------------------------------------------
void SAL_CALL ODateModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue) throw ( ::com::sun::star::uno::Exception)
{
    if (PROPERTY_ID_FORMATKEY == _nHandle)
        setFormatKeyPropertyValue(_rValue);
    else
        OEditBaseModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
}

// XLoadListener
//------------------------------------------------------------------------------
void ODateModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
{
    OBoundControlModel::onConnectedDbColumn( _rxForm );
    Reference<XPropertySet> xField = getField();
    if (xField.is())
    {
        m_bDateTimeField = sal_False;
        try
        {
            sal_Int32 nFieldType = 0;
            xField->getPropertyValue(PROPERTY_FIELDTYPE) >>= nFieldType;
            m_bDateTimeField = (nFieldType == DataType::TIMESTAMP);
        }
        catch(Exception&)
        {
        }
    }
}

//------------------------------------------------------------------------------
sal_Bool ODateModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
{
    Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
    if ( !compare( aControlValue, m_aSaveValue ) )
    {
        if ( !aControlValue.hasValue() )
            m_xColumnUpdate->updateNull();
        else
        {
            try
            {
                util::Date aDate;
                if ( !( aControlValue >>= aDate ) )
                {
                    sal_Int32 nAsInt(0);
                    aControlValue >>= nAsInt;
                    aDate = DBTypeConversion::toDate(nAsInt);
                }

                if ( !m_bDateTimeField )
                    m_xColumnUpdate->updateDate( aDate );
                else
                {
                    util::DateTime aDateTime = m_xColumn->getTimestamp();
                    aDateTime.Day = aDate.Day;
                    aDateTime.Month = aDate.Month;
                    aDateTime.Year = aDate.Year;
                    m_xColumnUpdate->updateTimestamp( aDateTime );
                }
            }
            catch(Exception&)
            {
                return sal_False;
            }
        }
        m_aSaveValue = aControlValue;
    }
    return sal_True;
}

//------------------------------------------------------------------------------
void ODateModel::impl_translateControlValueToUNODate( Any& _rUNOValue ) const
{
    _rUNOValue = getControlValue();
    if ( _rUNOValue.hasValue() )
    {
        sal_Int32 nDate = 0;
        OSL_VERIFY( _rUNOValue >>= nDate );
        _rUNOValue <<= DBTypeConversion::toDate( nDate );
    }
}

//------------------------------------------------------------------------------
Any ODateModel::translateControlValueToExternalValue( ) const
{
    Any aExternalValue;
    impl_translateControlValueToUNODate( aExternalValue );
    return aExternalValue;
}

//------------------------------------------------------------------------------
Any ODateModel::translateExternalValueToControlValue( ) const
{
    OSL_PRECOND( hasExternalValueBinding(),
        "ODateModel::translateExternalValueToControlValue: precondition not met!" );

    Any aControlValue;
    if ( hasExternalValueBinding() )
    {
        Any aExternalValue = getExternalValueBinding()->getValue( ::getCppuType( static_cast< util::Date* >( NULL ) ) );
        if ( aExternalValue.hasValue() )
        {
            util::Date aDate;
            OSL_VERIFY( aExternalValue >>= aDate );
            aControlValue <<= DBTypeConversion::toINT32( aDate );
        }
    }
    return aControlValue;
}

//------------------------------------------------------------------------------
Any ODateModel::translateControlValueToValidatableValue( ) const
{
    Any aValidatableValue;
    impl_translateControlValueToUNODate( aValidatableValue );
    return aValidatableValue;
}

//------------------------------------------------------------------------------
Any ODateModel::translateDbColumnToControlValue()
{
    util::Date aDate = m_xColumn->getDate();
    if (m_xColumn->wasNull())
        m_aSaveValue.clear();
    else
        // the aggregated set expects an Int32 as value ...
        m_aSaveValue <<= DBTypeConversion::toINT32(aDate);

    return m_aSaveValue;
}

//------------------------------------------------------------------------------
Any ODateModel::getDefaultForReset() const
{
    return m_aDefault;
}

//------------------------------------------------------------------------------
sal_Bool ODateModel::approveValueBinding( const Reference< binding::XValueBinding >& _rxBinding )
{
    OSL_PRECOND( _rxBinding.is(), "ODateModel::approveValueBinding: invalid binding!" );

    return  _rxBinding.is()
        &&  _rxBinding->supportsType( ::getCppuType( static_cast< util::Date* >( NULL ) ) );
}

//.........................................................................
}   // namespace frm
//.........................................................................

<|endoftext|>


<commit_before>#include "styleParam.h"

#include "platform.h"
#include "util/builders.h" // for cap, join
#include "util/geom.h" // for CLAMP
#include <map>

namespace Tangram {

const std::map<std::string, StyleParamKey> s_StyleParamMap = {
    {"cap", StyleParamKey::cap},
    {"color", StyleParamKey::color},
    {"extrude", StyleParamKey::extrude},
    {"font:family", StyleParamKey::font_family},
    {"font:fill", StyleParamKey::font_fill},
    {"font:size", StyleParamKey::font_size},
    {"font:stroke", StyleParamKey::font_stroke},
    {"font:stroke_color", StyleParamKey::font_stroke_color},
    {"font:stroke_width", StyleParamKey::font_stroke_width},
    {"font:style", StyleParamKey::font_style},
    {"font:weight", StyleParamKey::font_weight},
    {"join", StyleParamKey::join},
    {"none", StyleParamKey::none},
    {"offset", StyleParamKey::offset},
    {"order", StyleParamKey::order},
    {"outline:cap", StyleParamKey::outline_cap},
    {"outline:color", StyleParamKey::outline_color},
    {"outline:join", StyleParamKey::outline_join},
    {"outline:width", StyleParamKey::outline_width},
    {"priority", StyleParamKey::priority},
    {"sprite", StyleParamKey::sprite},
    {"transform", StyleParamKey::transform},
    {"visible", StyleParamKey::visible},
    {"width", StyleParamKey::width},
};

static const char* keyName(StyleParamKey key) {
    static std::string empty = "";
    for (const auto& entry : s_StyleParamMap) {
        if (entry.second == key) { return entry.first.c_str(); }
    }
    return empty.c_str();
}

StyleParam::StyleParam(const std::string& _key, const std::string& _value) {

    auto it = s_StyleParamMap.find(_key);
    if (it == s_StyleParamMap.end()) {
        logMsg("Unknown StyleParam %s:%s\n", _key.c_str(), _value.c_str());
        key = StyleParamKey::none;
        value = none_type{};
        return;
    }

    key = it->second;
    value = parseString(key, _value);
}

StyleParam::Value StyleParam::parseString(StyleParamKey key, const std::string& _value) {

    switch (key) {
    case StyleParamKey::extrude: {
        if (_value == "true") { return glm::vec2(NAN, NAN); }
        if (_value == "false") { return glm::vec2(0, 0) ; }
        auto vec2 = glm::vec2(NAN, NAN);
        if (!parseVec2(_value, {"m", "px"}, vec2)) {
            logMsg("Warning: Badly formed extrude parameter %s.\n", _value.c_str());
        }
        return vec2;
    }
    case StyleParamKey::offset: {
        auto vec2 = glm::vec2(0.f, 0.f);
        if (!parseVec2(_value, {"px"}, vec2)) {
            logMsg("Warning: Badly formed offset parameter %s.\n", _value.c_str());
        }
        return vec2;
    }
    case StyleParamKey::font_family:
    case StyleParamKey::font_weight:
    case StyleParamKey::font_style:
    case StyleParamKey::transform:
    case StyleParamKey::sprite:
        return _value;
    case StyleParamKey::font_size: {
        float fontSize = 16;
        if (!parseFontSize(_value, fontSize)) {
            logMsg("Warning: Invalid font-size '%s'.\n", _value.c_str());
        }
        return fontSize;
    }
    case StyleParamKey::visible:
        if (_value == "true") { return true; }
        if (_value == "false") { return false; }
        logMsg("Warning: Bool value required for capitalized/visible. Using Default.");
        break;
    case StyleParamKey::order:
    case StyleParamKey::priority: {
        try {
            return static_cast<uint32_t>(std::stoi(_value));
        } catch (std::invalid_argument) {
        } catch (std::out_of_range) {}
        logMsg("Warning: Not an Integer '%s', key: '%s'",
               _value.c_str(), keyName(key));
        break;
    }
    case StyleParamKey::width:
    case StyleParamKey::outline_width:
    case StyleParamKey::font_stroke_width: {
        try {
            return static_cast<float>(std::stof(_value));
        } catch (std::invalid_argument) {
        } catch (std::out_of_range) {}
        logMsg("Warning: Not a Float '%s', key: '%s'",
               _value.c_str(), keyName(key));
        break;
    }
    case StyleParamKey::color:
    case StyleParamKey::outline_color:
    case StyleParamKey::font_fill:
    case StyleParamKey::font_stroke:
    case StyleParamKey::font_stroke_color:
        return parseColor(_value);

    case StyleParamKey::cap:
    case StyleParamKey::outline_cap:
        return static_cast<uint32_t>(CapTypeFromString(_value));

    case StyleParamKey::join:
    case StyleParamKey::outline_join:
        return static_cast<uint32_t>(JoinTypeFromString(_value));

    case StyleParamKey::none:
        break;
    }

    return none_type{};
}

std::string StyleParam::toString() const {

    std::string k(keyName(key));
    k += " : ";

    // TODO: cap, join and color toString()
    if (value.is<none_type>()) {
        return k + "undefined";
    }

    switch (key) {
    case StyleParamKey::extrude: {
        if (!value.is<glm::vec2>()) break;
        auto p = value.get<glm::vec2>();
        return k + "(" + std::to_string(p[0]) + ", " + std::to_string(p[1]) + ")";
    }
    case StyleParamKey::offset: {
        if (!value.is<glm::vec2>()) break;
        auto p = value.get<glm::vec2>();
        return "offset : (" + std::to_string(p.x) + "px, " + std::to_string(p.y) + "px)";
    }
    case StyleParamKey::font_family:
    case StyleParamKey::font_weight:
    case StyleParamKey::font_style:
    case StyleParamKey::transform:
    case StyleParamKey::sprite:
        if (!value.is<std::string>()) break;
        return k + value.get<std::string>();
    case StyleParamKey::visible:
        if (!value.is<bool>()) break;
        return k + std::to_string(value.get<bool>());
    case StyleParamKey::width:
    case StyleParamKey::outline_width:
    case StyleParamKey::font_stroke_width:
    case StyleParamKey::font_size:
        if (!value.is<float>()) break;
        return k + std::to_string(value.get<float>());
    case StyleParamKey::order:
    case StyleParamKey::priority:
    case StyleParamKey::color:
    case StyleParamKey::outline_color:
    case StyleParamKey::font_fill:
    case StyleParamKey::font_stroke:
    case StyleParamKey::font_stroke_color:
    case StyleParamKey::cap:
    case StyleParamKey::outline_cap:
    case StyleParamKey::join:
    case StyleParamKey::outline_join:
        if (!value.is<uint32_t>()) break;
        return k + std::to_string(value.get<uint32_t>());
    case StyleParamKey::none:
        break;
    }
    return k + "undefined";
}

bool StyleParam::parseVec2(const std::string& _value, const std::vector<std::string>& _allowedUnit, glm::vec2& _vec2) {
    if (_value.empty()) {
        return false;
    }

    std::string value = _value;

    // replace all unit occurences
    for (auto& unit : _allowedUnit) {
        auto i = value.find(unit);
        // TODO: conversion
        while (i != std::string::npos) {
            value.replace(i, unit.size(), "");
            i = value.find(unit);
        }
    }

    std::replace(value.begin(), value.end(), ',', ' ');

    if (std::any_of(value.begin(), value.end(), ::isalpha)) {
        return false;
    }

    float f1, f2;
    int num = std::sscanf(value.c_str(), "%f %f", &f1, &f2);

    switch(num) {
        case 1:
            _vec2 = { f1, NAN };
            break;
        case 2:
            _vec2 = { f1, f2 };
            break;
        case 0:
        default:
            return false;
    }

    return true;
}

uint32_t StyleParam::parseColor(const std::string& _color) {
    Color color;

    if (isdigit(_color.front())) {
        // try to parse as comma-separated rgba components
        float r, g, b, a = 1.;
        if (sscanf(_color.c_str(), "%f,%f,%f,%f", &r, &g, &b, &a) >= 3) {
            color = Color {
                static_cast<uint8_t>(CLAMP((r * 255.), 0, 255)),
                static_cast<uint8_t>(CLAMP((g * 255.), 0, 255)),
                static_cast<uint8_t>(CLAMP((b * 255.), 0, 255)),
                CLAMP(a, 0, 1)
            };
        }
    } else {
        // parse as css color or #hex-num
        color = CSSColorParser::parse(_color);
    }
    return color.getInt();
}

bool StyleParam::parseFontSize(const std::string& _str, float& _pxSize) {
    if (_str.empty()) {
        return false;
    }

    size_t index = 0;
    std::string kind;

    try {
        _pxSize = std::stof(_str, &index);
    } catch (std::invalid_argument) {
        return false;
    } catch (std::out_of_range) {
        return false;
    }

    if (index == _str.length() && (_str.find('.') == std::string::npos)) {
        return true;
    }

    kind = _str.substr(index, _str.length() - 1);

    if (kind == "px") {
        // px may not be fractional value
        if (_str.find('.') != std::string::npos)
            return false;
    } else if (kind == "em") {
        _pxSize *= 16.f;
    } else if (kind == "pt") {
        _pxSize /= 0.75f;
    } else if (kind == "%") {
        _pxSize /= 6.25f;
    } else {
        return false;
    }

    return true;
}

}
<commit_msg>Missing <algorithm> include<commit_after>#include "styleParam.h"

#include "platform.h"
#include "util/builders.h" // for cap, join
#include "util/geom.h" // for CLAMP
#include <algorithm>
#include <map>

namespace Tangram {

const std::map<std::string, StyleParamKey> s_StyleParamMap = {
    {"cap", StyleParamKey::cap},
    {"color", StyleParamKey::color},
    {"extrude", StyleParamKey::extrude},
    {"font:family", StyleParamKey::font_family},
    {"font:fill", StyleParamKey::font_fill},
    {"font:size", StyleParamKey::font_size},
    {"font:stroke", StyleParamKey::font_stroke},
    {"font:stroke_color", StyleParamKey::font_stroke_color},
    {"font:stroke_width", StyleParamKey::font_stroke_width},
    {"font:style", StyleParamKey::font_style},
    {"font:weight", StyleParamKey::font_weight},
    {"join", StyleParamKey::join},
    {"none", StyleParamKey::none},
    {"offset", StyleParamKey::offset},
    {"order", StyleParamKey::order},
    {"outline:cap", StyleParamKey::outline_cap},
    {"outline:color", StyleParamKey::outline_color},
    {"outline:join", StyleParamKey::outline_join},
    {"outline:width", StyleParamKey::outline_width},
    {"priority", StyleParamKey::priority},
    {"sprite", StyleParamKey::sprite},
    {"transform", StyleParamKey::transform},
    {"visible", StyleParamKey::visible},
    {"width", StyleParamKey::width},
};

static const char* keyName(StyleParamKey key) {
    static std::string empty = "";
    for (const auto& entry : s_StyleParamMap) {
        if (entry.second == key) { return entry.first.c_str(); }
    }
    return empty.c_str();
}

StyleParam::StyleParam(const std::string& _key, const std::string& _value) {

    auto it = s_StyleParamMap.find(_key);
    if (it == s_StyleParamMap.end()) {
        logMsg("Unknown StyleParam %s:%s\n", _key.c_str(), _value.c_str());
        key = StyleParamKey::none;
        value = none_type{};
        return;
    }

    key = it->second;
    value = parseString(key, _value);
}

StyleParam::Value StyleParam::parseString(StyleParamKey key, const std::string& _value) {

    switch (key) {
    case StyleParamKey::extrude: {
        if (_value == "true") { return glm::vec2(NAN, NAN); }
        if (_value == "false") { return glm::vec2(0, 0) ; }
        auto vec2 = glm::vec2(NAN, NAN);
        if (!parseVec2(_value, {"m", "px"}, vec2)) {
            logMsg("Warning: Badly formed extrude parameter %s.\n", _value.c_str());
        }
        return vec2;
    }
    case StyleParamKey::offset: {
        auto vec2 = glm::vec2(0.f, 0.f);
        if (!parseVec2(_value, {"px"}, vec2)) {
            logMsg("Warning: Badly formed offset parameter %s.\n", _value.c_str());
        }
        return vec2;
    }
    case StyleParamKey::font_family:
    case StyleParamKey::font_weight:
    case StyleParamKey::font_style:
    case StyleParamKey::transform:
    case StyleParamKey::sprite:
        return _value;
    case StyleParamKey::font_size: {
        float fontSize = 16;
        if (!parseFontSize(_value, fontSize)) {
            logMsg("Warning: Invalid font-size '%s'.\n", _value.c_str());
        }
        return fontSize;
    }
    case StyleParamKey::visible:
        if (_value == "true") { return true; }
        if (_value == "false") { return false; }
        logMsg("Warning: Bool value required for capitalized/visible. Using Default.");
        break;
    case StyleParamKey::order:
    case StyleParamKey::priority: {
        try {
            return static_cast<uint32_t>(std::stoi(_value));
        } catch (std::invalid_argument) {
        } catch (std::out_of_range) {}
        logMsg("Warning: Not an Integer '%s', key: '%s'",
               _value.c_str(), keyName(key));
        break;
    }
    case StyleParamKey::width:
    case StyleParamKey::outline_width:
    case StyleParamKey::font_stroke_width: {
        try {
            return static_cast<float>(std::stof(_value));
        } catch (std::invalid_argument) {
        } catch (std::out_of_range) {}
        logMsg("Warning: Not a Float '%s', key: '%s'",
               _value.c_str(), keyName(key));
        break;
    }
    case StyleParamKey::color:
    case StyleParamKey::outline_color:
    case StyleParamKey::font_fill:
    case StyleParamKey::font_stroke:
    case StyleParamKey::font_stroke_color:
        return parseColor(_value);

    case StyleParamKey::cap:
    case StyleParamKey::outline_cap:
        return static_cast<uint32_t>(CapTypeFromString(_value));

    case StyleParamKey::join:
    case StyleParamKey::outline_join:
        return static_cast<uint32_t>(JoinTypeFromString(_value));

    case StyleParamKey::none:
        break;
    }

    return none_type{};
}

std::string StyleParam::toString() const {

    std::string k(keyName(key));
    k += " : ";

    // TODO: cap, join and color toString()
    if (value.is<none_type>()) {
        return k + "undefined";
    }

    switch (key) {
    case StyleParamKey::extrude: {
        if (!value.is<glm::vec2>()) break;
        auto p = value.get<glm::vec2>();
        return k + "(" + std::to_string(p[0]) + ", " + std::to_string(p[1]) + ")";
    }
    case StyleParamKey::offset: {
        if (!value.is<glm::vec2>()) break;
        auto p = value.get<glm::vec2>();
        return "offset : (" + std::to_string(p.x) + "px, " + std::to_string(p.y) + "px)";
    }
    case StyleParamKey::font_family:
    case StyleParamKey::font_weight:
    case StyleParamKey::font_style:
    case StyleParamKey::transform:
    case StyleParamKey::sprite:
        if (!value.is<std::string>()) break;
        return k + value.get<std::string>();
    case StyleParamKey::visible:
        if (!value.is<bool>()) break;
        return k + std::to_string(value.get<bool>());
    case StyleParamKey::width:
    case StyleParamKey::outline_width:
    case StyleParamKey::font_stroke_width:
    case StyleParamKey::font_size:
        if (!value.is<float>()) break;
        return k + std::to_string(value.get<float>());
    case StyleParamKey::order:
    case StyleParamKey::priority:
    case StyleParamKey::color:
    case StyleParamKey::outline_color:
    case StyleParamKey::font_fill:
    case StyleParamKey::font_stroke:
    case StyleParamKey::font_stroke_color:
    case StyleParamKey::cap:
    case StyleParamKey::outline_cap:
    case StyleParamKey::join:
    case StyleParamKey::outline_join:
        if (!value.is<uint32_t>()) break;
        return k + std::to_string(value.get<uint32_t>());
    case StyleParamKey::none:
        break;
    }
    return k + "undefined";
}

bool StyleParam::parseVec2(const std::string& _value, const std::vector<std::string>& _allowedUnit, glm::vec2& _vec2) {
    if (_value.empty()) {
        return false;
    }

    std::string value = _value;

    // replace all unit occurences
    for (auto& unit : _allowedUnit) {
        auto i = value.find(unit);
        // TODO: conversion
        while (i != std::string::npos) {
            value.replace(i, unit.size(), "");
            i = value.find(unit);
        }
    }

    std::replace(value.begin(), value.end(), ',', ' ');

    if (std::any_of(value.begin(), value.end(), ::isalpha)) {
        return false;
    }

    float f1, f2;
    int num = std::sscanf(value.c_str(), "%f %f", &f1, &f2);

    switch(num) {
        case 1:
            _vec2 = { f1, NAN };
            break;
        case 2:
            _vec2 = { f1, f2 };
            break;
        case 0:
        default:
            return false;
    }

    return true;
}

uint32_t StyleParam::parseColor(const std::string& _color) {
    Color color;

    if (isdigit(_color.front())) {
        // try to parse as comma-separated rgba components
        float r, g, b, a = 1.;
        if (sscanf(_color.c_str(), "%f,%f,%f,%f", &r, &g, &b, &a) >= 3) {
            color = Color {
                static_cast<uint8_t>(CLAMP((r * 255.), 0, 255)),
                static_cast<uint8_t>(CLAMP((g * 255.), 0, 255)),
                static_cast<uint8_t>(CLAMP((b * 255.), 0, 255)),
                CLAMP(a, 0, 1)
            };
        }
    } else {
        // parse as css color or #hex-num
        color = CSSColorParser::parse(_color);
    }
    return color.getInt();
}

bool StyleParam::parseFontSize(const std::string& _str, float& _pxSize) {
    if (_str.empty()) {
        return false;
    }

    size_t index = 0;
    std::string kind;

    try {
        _pxSize = std::stof(_str, &index);
    } catch (std::invalid_argument) {
        return false;
    } catch (std::out_of_range) {
        return false;
    }

    if (index == _str.length() && (_str.find('.') == std::string::npos)) {
        return true;
    }

    kind = _str.substr(index, _str.length() - 1);

    if (kind == "px") {
        // px may not be fractional value
        if (_str.find('.') != std::string::npos)
            return false;
    } else if (kind == "em") {
        _pxSize *= 16.f;
    } else if (kind == "pt") {
        _pxSize /= 0.75f;
    } else if (kind == "%") {
        _pxSize /= 6.25f;
    } else {
        return false;
    }

    return true;
}

}
<|endoftext|>


<commit_before>// $Id$

/*
Copyright (c) 2007-2011, Trustees of The Leland Stanford Junior University
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of the Stanford University nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//////////////////////////////////////////////////////////////////////
//
//  File Name: channel.hpp
//
//  The Channel models a generic channel with a multi-cycle
//   transmission delay. The channel latency can be specified as
//   an integer number of simulator cycles.
//
/////
#ifndef _CHANNEL_HPP
#define _CHANNEL_HPP

#include <queue>
#include <cassert>

#include "globals.hpp"
#include "module.hpp"
#include "timed_module.hpp"

using namespace std;

template<typename T>
class Channel : public TimedModule {
public:
  Channel(Module * parent, string const & name);
  virtual ~Channel() {}

  // Physical Parameters
  void SetLatency(int cycles);
  int GetLatency() const { return _delay ; }

  // Send data
  virtual void Send(T * data);

  // Receive data
  virtual T * Receive();

  virtual void ReadInputs();
  virtual void Evaluate() {}
  virtual void WriteOutputs();

protected:
  int _delay;
  T * _input;
  T * _output;
  queue<pair<int, T *> > _wait_queue;

};

template<typename T>
Channel<T>::Channel(Module * parent, string const & name)
  : TimedModule(parent, name), _delay(1), _input(0), _output(0) {
}

template<typename T>
void Channel<T>::SetLatency(int cycles) {
  if(!cycles) {
    Error("Channel must have positive delay.");
  }
  _delay = cycles ;
}

template<typename T>
void Channel<T>::Send(T * data) {
  _input = data;
}

template<typename T>
T * Channel<T>::Receive() {
  return _output;
}

template<typename T>
void Channel<T>::ReadInputs() {
  if(_input) {
    _wait_queue.push(make_pair(GetSimTime() + _delay - 1, _input));
    _input = 0;
  }
}

template<typename T>
void Channel<T>::WriteOutputs() {
  _output = 0;
  if(_wait_queue.empty()) {
    return;
  }
  pair<int, T *> const & item = _wait_queue.front();
  int const & time = item.first;
  if(GetSimTime() < time) {
    return;
  }
  assert(GetSimTime() == time);
  _wait_queue.pop();
  _output = item.second;
  assert(_output);
}

#endif
<commit_msg>channel write output, element is popped before assignment, rare occasion causes segfault<commit_after>// $Id$

/*
Copyright (c) 2007-2011, Trustees of The Leland Stanford Junior University
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of the Stanford University nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//////////////////////////////////////////////////////////////////////
//
//  File Name: channel.hpp
//
//  The Channel models a generic channel with a multi-cycle
//   transmission delay. The channel latency can be specified as
//   an integer number of simulator cycles.
//
/////
#ifndef _CHANNEL_HPP
#define _CHANNEL_HPP

#include <queue>
#include <cassert>

#include "globals.hpp"
#include "module.hpp"
#include "timed_module.hpp"

using namespace std;

template<typename T>
class Channel : public TimedModule {
public:
  Channel(Module * parent, string const & name);
  virtual ~Channel() {}

  // Physical Parameters
  void SetLatency(int cycles);
  int GetLatency() const { return _delay ; }

  // Send data
  virtual void Send(T * data);

  // Receive data
  virtual T * Receive();

  virtual void ReadInputs();
  virtual void Evaluate() {}
  virtual void WriteOutputs();

protected:
  int _delay;
  T * _input;
  T * _output;
  queue<pair<int, T *> > _wait_queue;

};

template<typename T>
Channel<T>::Channel(Module * parent, string const & name)
  : TimedModule(parent, name), _delay(1), _input(0), _output(0) {
}

template<typename T>
void Channel<T>::SetLatency(int cycles) {
  if(!cycles) {
    Error("Channel must have positive delay.");
  }
  _delay = cycles ;
}

template<typename T>
void Channel<T>::Send(T * data) {
  _input = data;
}

template<typename T>
T * Channel<T>::Receive() {
  return _output;
}

template<typename T>
void Channel<T>::ReadInputs() {
  if(_input) {
    _wait_queue.push(make_pair(GetSimTime() + _delay - 1, _input));
    _input = 0;
  }
}

template<typename T>
void Channel<T>::WriteOutputs() {
  _output = 0;
  if(_wait_queue.empty()) {
    return;
  }
  pair<int, T *> const & item = _wait_queue.front();
  int const & time = item.first;
  if(GetSimTime() < time) {
    return;
  }
  assert(GetSimTime() == time);
  _output = item.second;
  _wait_queue.pop();
  assert(_output);
}

#endif
<|endoftext|>


<commit_before>#include "tileManager.h"

#include "data/dataSource.h"
#include "platform.h"
#include "scene/scene.h"
#include "tile/mapTile.h"
#include "view/view.h"

#include "glm/gtx/norm.hpp"

#include <chrono>
#include <algorithm>

TileManager::TileManager()
    : m_loadPending(0) {

    // Instantiate workers
    for (size_t i = 0; i < MAX_WORKERS; i++) {
        m_workers.push_back(std::unique_ptr<TileWorker>(new TileWorker(*this)));
    }
    m_dataCallback = std::bind(&TileManager::tileLoaded, this, std::placeholders::_1);
}

TileManager::~TileManager() {
    for (auto& worker : m_workers) {
        if (worker->isRunning()) {
            worker->abort();
            worker->drain();
        }
        // We stop all workers before we destroy the resources they use.
        // TODO: This will wait for any pending network requests to finish,
        // which could delay closing of the application.
    }
    m_dataSources.clear();
    m_tileSet.clear();
}

void TileManager::addDataSource(std::unique_ptr<DataSource> _source) {
    m_dataSources.push_back(std::move(_source));
}

void TileManager::tileLoaded(TileTask task) {
    std::lock_guard<std::mutex> lock(m_queueTileMutex);
    m_queuedTiles.push_back(std::move(task));
}

void TileManager::tileProcessed(TileTask task) {
    std::lock_guard<std::mutex> lock(m_readyTileMutex);
    m_readyTiles.push_back(std::move(task));
}

TileTask TileManager::pollProcessQueue() {
    std::lock_guard<std::mutex> lock(m_queueTileMutex);

    while (!m_queuedTiles.empty()) {
        auto task = std::move(m_queuedTiles.front());
        m_queuedTiles.pop_front();

        if (!setTileState(*(task->tile), TileState::processing)) {
            // Drop canceled task.
            continue;
        }
        return task;
    }

    return TileTask(nullptr);
}

bool TileManager::setTileState(MapTile& tile, TileState state) {
    std::lock_guard<std::mutex> lock(m_tileStateMutex);

    switch (tile.state()) {
    case TileState::none:
        if (state == TileState::loading) {
            tile.setState(state);
            m_loadPending++;
            return true;
        }
        break;

    case TileState::loading:
        if (state == TileState::processing) {
            tile.setState(state);
            m_loadPending--;
            return true;
        }
        if (state == TileState::canceled) {
            tile.setState(state);
            m_loadPending--;
            return true;
        }
        break;

    case TileState::processing:
        if (state == TileState::ready) {
            tile.setState(state);
            return true;
        }
        break;

    case TileState::canceled:
         return false;

    default:
        break;
    }

    if (state == TileState::canceled) {
        return false;
    }

    logMsg("Wrong state change %d -> %d<<<", tile.state(), state);
    assert(false);
}

void TileManager::enqueueLoadTask(const TileID& tileID, const glm::dvec2& viewCenter) {
    // Keep the items sorted by distance and limit list to MAX_DOWNLOADS

    auto tileCenter = m_view->getMapProjection().TileCenter(tileID);
    double distance = glm::length2(tileCenter - viewCenter);

    bool isFull = m_loadTasks.size() == MAX_DOWNLOADS;
    if (isFull && m_loadTasks.back().first < distance) {
        return;
    }

    auto iter = m_loadTasks.begin();
    while (iter != m_loadTasks.end()){
        if (iter->first > distance) {
            break;
        }
        ++iter;
    }

    if (!isFull || iter != m_loadTasks.end()) {
        m_loadTasks.insert(iter, { distance, &tileID });
    }
    if (isFull) {
        m_loadTasks.pop_back();
    }
}

void TileManager::updateTileSet() {

    m_tileSetChanged = false;

    // Check if any native worker needs to be dispatched.
    if (!m_queuedTiles.empty()) {

        for (auto& worker : m_workers) {
            if (worker->isRunning()) { continue; }

            worker->process(m_scene->getStyles());
        }
    }

    if (!m_readyTiles.empty()) {
        std::lock_guard<std::mutex> lock(m_readyTileMutex);
        auto it = m_readyTiles.begin();

        while (it != m_readyTiles.end()) {
            auto& task = *it;
            auto& tile = *(task->tile);

            if (setTileState(tile, TileState::ready)) {
                cleanProxyTiles(tile);
                m_tileSetChanged = true;
            }

            it = m_readyTiles.erase(it);
        }
    }

    if (m_view->changedOnLastUpdate() || m_tileSetChanged) {

        glm::dvec2 viewCenter(m_view->getPosition().x, -m_view->getPosition().y);

        const std::set<TileID>& visibleTiles = m_view->getVisibleTiles();

        // Loop over visibleTiles and add any needed tiles to tileSet
        {
            auto setTilesIter = m_tileSet.begin();
            auto visTilesIter = visibleTiles.begin();

            while (visTilesIter != visibleTiles.end()) {

                auto& visTileId = *visTilesIter;
                auto& curTileId = setTilesIter == m_tileSet.end() ? NOT_A_TILE : setTilesIter->first;

                if (visTileId == curTileId) {
                    if (setTilesIter->second->state() == TileState::none) {
                        //logMsg("[%d, %d, %d] - Enqueue\n", curTileId.z, curTileId.x, curTileId.y);
                        enqueueLoadTask(visTileId, viewCenter);
                    }

                    // tiles in both sets match
                    ++setTilesIter;
                    ++visTilesIter;

                } else if (curTileId == NOT_A_TILE || visTileId < curTileId) {
                    // tileSet is missing an element present in visibleTiles
                    addTile(visTileId);
                    enqueueLoadTask(visTileId, viewCenter);

                    ++visTilesIter;
                } else {
                    // visibleTiles is missing an element present in tileSet (handled below)
                    ++setTilesIter;
                }
            }
        }

        // Loop over tileSet and remove any tiles that are neither visible nor proxies
        {
            auto setTilesIter = m_tileSet.begin();
            auto visTilesIter = visibleTiles.begin();

            while (setTilesIter != m_tileSet.end()) {

                auto& visTileId = visTilesIter == visibleTiles.end() ? NOT_A_TILE : *visTilesIter;
                auto& curTileId = setTilesIter->first;

                if (visTileId == curTileId) {
                    // tiles in both sets match
                    ++setTilesIter;
                    ++visTilesIter;

                } else if (visTileId == NOT_A_TILE || curTileId < visTileId) {
                    // remove element from tileSet not present in visibleTiles
                    const auto& tile = setTilesIter->second;
                    if (tile->getProxyCounter() <= 0) {
                        removeTile(setTilesIter);
                    } else {
                        ++setTilesIter;
                    }
                } else {
                    // tileSet is missing an element present in visibleTiles (shouldn't occur)
                    ++visTilesIter;
                }
            }
        }
    }

    if (m_loadPending < MAX_DOWNLOADS) {

        for (auto& item : m_loadTasks) {
            auto& id = *item.second;
            auto& tile = m_tileSet[id];

            setTileState(*tile, TileState::loading);

            for (auto& source : m_dataSources) {
                TileTask task = std::make_shared<TileTaskData>(tile, source.get());
                logMsg("[%d, %d, %d] Load\n", id.z, id.x, id.y);

                if (!source->loadTileData(task, m_dataCallback)) {
                    logMsg("ERROR: Loading failed for tile [%d, %d, %d]\n", id.z, id.x, id.y);
                }
            }
            if (m_loadPending == MAX_DOWNLOADS) {
                break;
            }
        }
    }
    m_loadTasks.clear();

    // logMsg("all:%d loading:%d processing:%d pending:%d\n",
    //        m_tileSet.size(), m_loadTasks.size(),
    //        m_queuedTiles.size(), m_loadPending);
}

void TileManager::addTile(const TileID& _tileID) {
    logMsg("[%d, %d, %d] Add\n", _tileID.z, _tileID.x, _tileID.y);

    std::shared_ptr<MapTile> tile(new MapTile(_tileID, m_view->getMapProjection()));

    //Add Proxy if corresponding proxy MapTile ready
    updateProxyTiles(*tile);

    m_tileSet[_tileID] = std::move(tile);
}

void TileManager::removeTile(std::map< TileID, std::shared_ptr<MapTile> >::iterator& _tileIter) {
    const TileID& id = _tileIter->first;
    auto& tile = _tileIter->second;

    logMsg("[%d, %d, %d] Remove\n", id.z, id.x, id.y);

    if (setTileState(*tile, TileState::canceled)) {

        // 1. Remove from Datasource
        // Make sure to cancel the network request associated with this tile.
        for(auto& dataSource : m_dataSources) {
            dataSource->cancelLoadingTile(id);
        }
    }

    cleanProxyTiles(*tile);

    // Remove tile from set
    _tileIter = m_tileSet.erase(_tileIter);

}

void TileManager::updateProxyTiles(MapTile& _tile) {
    const TileID& _tileID = _tile.getID();

    const auto& parentID = _tileID.getParent();
    const auto& parentTileIter = m_tileSet.find(parentID);
    if (parentTileIter != m_tileSet.end()) {
        auto& parent = parentTileIter->second;
        if (_tile.setProxy(MapTile::parent)) {
            parent->incProxyCounter();
        }
        return;
    }

    if (m_view->s_maxZoom > _tileID.z) {
        for(int i = 0; i < 4; i++) {
            const auto& childID = _tileID.getChild(i);
            const auto& childTileIter = m_tileSet.find(childID);
            if(childTileIter != m_tileSet.end()) {
                if (_tile.setProxy((MapTile::ProxyID)(1 << i))) {
                    childTileIter->second->incProxyCounter();
                }
            }
        }
    }
}

void TileManager::cleanProxyTiles(MapTile& _tile) {
    const TileID& _tileID = _tile.getID();

    // check if parent proxy is present
    if (_tile.hasProxy(MapTile::parent)) {
        const auto& parentID = _tileID.getParent();
        const auto& parentTileIter = m_tileSet.find(parentID);
        if (parentTileIter != m_tileSet.end()) {
            parentTileIter->second->decProxyCounter();
        }
    }

    // check if child proxies are present
    for(int i = 0; i < 4; i++) {
        if (_tile.hasProxy((MapTile::ProxyID)(1 << i))) {
            const auto& childID = _tileID.getChild(i);
            const auto& childTileIter = m_tileSet.find(childID);
            if (childTileIter != m_tileSet.end()) {
                childTileIter->second->decProxyCounter();
            }
        }
    }
    _tile.clearProxies();
}
<commit_msg>--warning<commit_after>#include "tileManager.h"

#include "data/dataSource.h"
#include "platform.h"
#include "scene/scene.h"
#include "tile/mapTile.h"
#include "view/view.h"

#include "glm/gtx/norm.hpp"

#include <chrono>
#include <algorithm>

TileManager::TileManager()
    : m_loadPending(0) {

    // Instantiate workers
    for (size_t i = 0; i < MAX_WORKERS; i++) {
        m_workers.push_back(std::unique_ptr<TileWorker>(new TileWorker(*this)));
    }
    m_dataCallback = std::bind(&TileManager::tileLoaded, this, std::placeholders::_1);
}

TileManager::~TileManager() {
    for (auto& worker : m_workers) {
        if (worker->isRunning()) {
            worker->abort();
            worker->drain();
        }
        // We stop all workers before we destroy the resources they use.
        // TODO: This will wait for any pending network requests to finish,
        // which could delay closing of the application.
    }
    m_dataSources.clear();
    m_tileSet.clear();
}

void TileManager::addDataSource(std::unique_ptr<DataSource> _source) {
    m_dataSources.push_back(std::move(_source));
}

void TileManager::tileLoaded(TileTask task) {
    std::lock_guard<std::mutex> lock(m_queueTileMutex);
    m_queuedTiles.push_back(std::move(task));
}

void TileManager::tileProcessed(TileTask task) {
    std::lock_guard<std::mutex> lock(m_readyTileMutex);
    m_readyTiles.push_back(std::move(task));
}

TileTask TileManager::pollProcessQueue() {
    std::lock_guard<std::mutex> lock(m_queueTileMutex);

    while (!m_queuedTiles.empty()) {
        auto task = std::move(m_queuedTiles.front());
        m_queuedTiles.pop_front();

        if (!setTileState(*(task->tile), TileState::processing)) {
            // Drop canceled task.
            continue;
        }
        return task;
    }

    return TileTask(nullptr);
}

bool TileManager::setTileState(MapTile& tile, TileState state) {
    std::lock_guard<std::mutex> lock(m_tileStateMutex);

    switch (tile.state()) {
    case TileState::none:
        if (state == TileState::loading) {
            tile.setState(state);
            m_loadPending++;
            return true;
        }
        break;

    case TileState::loading:
        if (state == TileState::processing) {
            tile.setState(state);
            m_loadPending--;
            return true;
        }
        if (state == TileState::canceled) {
            tile.setState(state);
            m_loadPending--;
            return true;
        }
        break;

    case TileState::processing:
        if (state == TileState::ready) {
            tile.setState(state);
            return true;
        }
        break;

    case TileState::canceled:
         return false;

    default:
        break;
    }

    if (state == TileState::canceled) {
        return false;
    }

    logMsg("Wrong state change %d -> %d<<<", tile.state(), state);
    assert(false);
    return false; // ...
}

void TileManager::enqueueLoadTask(const TileID& tileID, const glm::dvec2& viewCenter) {
    // Keep the items sorted by distance and limit list to MAX_DOWNLOADS

    auto tileCenter = m_view->getMapProjection().TileCenter(tileID);
    double distance = glm::length2(tileCenter - viewCenter);

    bool isFull = m_loadTasks.size() == MAX_DOWNLOADS;
    if (isFull && m_loadTasks.back().first < distance) {
        return;
    }

    auto iter = m_loadTasks.begin();
    while (iter != m_loadTasks.end()){
        if (iter->first > distance) {
            break;
        }
        ++iter;
    }

    if (!isFull || iter != m_loadTasks.end()) {
        m_loadTasks.insert(iter, { distance, &tileID });
    }
    if (isFull) {
        m_loadTasks.pop_back();
    }
}

void TileManager::updateTileSet() {

    m_tileSetChanged = false;

    // Check if any native worker needs to be dispatched.
    if (!m_queuedTiles.empty()) {

        for (auto& worker : m_workers) {
            if (worker->isRunning()) { continue; }

            worker->process(m_scene->getStyles());
        }
    }

    if (!m_readyTiles.empty()) {
        std::lock_guard<std::mutex> lock(m_readyTileMutex);
        auto it = m_readyTiles.begin();

        while (it != m_readyTiles.end()) {
            auto& task = *it;
            auto& tile = *(task->tile);

            if (setTileState(tile, TileState::ready)) {
                cleanProxyTiles(tile);
                m_tileSetChanged = true;
            }

            it = m_readyTiles.erase(it);
        }
    }

    if (m_view->changedOnLastUpdate() || m_tileSetChanged) {

        glm::dvec2 viewCenter(m_view->getPosition().x, -m_view->getPosition().y);

        const std::set<TileID>& visibleTiles = m_view->getVisibleTiles();

        // Loop over visibleTiles and add any needed tiles to tileSet
        {
            auto setTilesIter = m_tileSet.begin();
            auto visTilesIter = visibleTiles.begin();

            while (visTilesIter != visibleTiles.end()) {

                auto& visTileId = *visTilesIter;
                auto& curTileId = setTilesIter == m_tileSet.end() ? NOT_A_TILE : setTilesIter->first;

                if (visTileId == curTileId) {
                    if (setTilesIter->second->state() == TileState::none) {
                        //logMsg("[%d, %d, %d] - Enqueue\n", curTileId.z, curTileId.x, curTileId.y);
                        enqueueLoadTask(visTileId, viewCenter);
                    }

                    // tiles in both sets match
                    ++setTilesIter;
                    ++visTilesIter;

                } else if (curTileId == NOT_A_TILE || visTileId < curTileId) {
                    // tileSet is missing an element present in visibleTiles
                    addTile(visTileId);
                    enqueueLoadTask(visTileId, viewCenter);

                    ++visTilesIter;
                } else {
                    // visibleTiles is missing an element present in tileSet (handled below)
                    ++setTilesIter;
                }
            }
        }

        // Loop over tileSet and remove any tiles that are neither visible nor proxies
        {
            auto setTilesIter = m_tileSet.begin();
            auto visTilesIter = visibleTiles.begin();

            while (setTilesIter != m_tileSet.end()) {

                auto& visTileId = visTilesIter == visibleTiles.end() ? NOT_A_TILE : *visTilesIter;
                auto& curTileId = setTilesIter->first;

                if (visTileId == curTileId) {
                    // tiles in both sets match
                    ++setTilesIter;
                    ++visTilesIter;

                } else if (visTileId == NOT_A_TILE || curTileId < visTileId) {
                    // remove element from tileSet not present in visibleTiles
                    const auto& tile = setTilesIter->second;
                    if (tile->getProxyCounter() <= 0) {
                        removeTile(setTilesIter);
                    } else {
                        ++setTilesIter;
                    }
                } else {
                    // tileSet is missing an element present in visibleTiles (shouldn't occur)
                    ++visTilesIter;
                }
            }
        }
    }

    if (m_loadPending < MAX_DOWNLOADS) {

        for (auto& item : m_loadTasks) {
            auto& id = *item.second;
            auto& tile = m_tileSet[id];

            setTileState(*tile, TileState::loading);

            for (auto& source : m_dataSources) {
                TileTask task = std::make_shared<TileTaskData>(tile, source.get());
                logMsg("[%d, %d, %d] Load\n", id.z, id.x, id.y);

                if (!source->loadTileData(task, m_dataCallback)) {
                    logMsg("ERROR: Loading failed for tile [%d, %d, %d]\n", id.z, id.x, id.y);
                }
            }
            if (m_loadPending == MAX_DOWNLOADS) {
                break;
            }
        }
    }
    m_loadTasks.clear();

    // logMsg("all:%d loading:%d processing:%d pending:%d\n",
    //        m_tileSet.size(), m_loadTasks.size(),
    //        m_queuedTiles.size(), m_loadPending);
}

void TileManager::addTile(const TileID& _tileID) {
    logMsg("[%d, %d, %d] Add\n", _tileID.z, _tileID.x, _tileID.y);

    std::shared_ptr<MapTile> tile(new MapTile(_tileID, m_view->getMapProjection()));

    //Add Proxy if corresponding proxy MapTile ready
    updateProxyTiles(*tile);

    m_tileSet[_tileID] = std::move(tile);
}

void TileManager::removeTile(std::map< TileID, std::shared_ptr<MapTile> >::iterator& _tileIter) {
    const TileID& id = _tileIter->first;
    auto& tile = _tileIter->second;

    logMsg("[%d, %d, %d] Remove\n", id.z, id.x, id.y);

    if (setTileState(*tile, TileState::canceled)) {

        // 1. Remove from Datasource
        // Make sure to cancel the network request associated with this tile.
        for(auto& dataSource : m_dataSources) {
            dataSource->cancelLoadingTile(id);
        }
    }

    cleanProxyTiles(*tile);

    // Remove tile from set
    _tileIter = m_tileSet.erase(_tileIter);

}

void TileManager::updateProxyTiles(MapTile& _tile) {
    const TileID& _tileID = _tile.getID();

    const auto& parentID = _tileID.getParent();
    const auto& parentTileIter = m_tileSet.find(parentID);
    if (parentTileIter != m_tileSet.end()) {
        auto& parent = parentTileIter->second;
        if (_tile.setProxy(MapTile::parent)) {
            parent->incProxyCounter();
        }
        return;
    }

    if (m_view->s_maxZoom > _tileID.z) {
        for(int i = 0; i < 4; i++) {
            const auto& childID = _tileID.getChild(i);
            const auto& childTileIter = m_tileSet.find(childID);
            if(childTileIter != m_tileSet.end()) {
                if (_tile.setProxy((MapTile::ProxyID)(1 << i))) {
                    childTileIter->second->incProxyCounter();
                }
            }
        }
    }
}

void TileManager::cleanProxyTiles(MapTile& _tile) {
    const TileID& _tileID = _tile.getID();

    // check if parent proxy is present
    if (_tile.hasProxy(MapTile::parent)) {
        const auto& parentID = _tileID.getParent();
        const auto& parentTileIter = m_tileSet.find(parentID);
        if (parentTileIter != m_tileSet.end()) {
            parentTileIter->second->decProxyCounter();
        }
    }

    // check if child proxies are present
    for(int i = 0; i < 4; i++) {
        if (_tile.hasProxy((MapTile::ProxyID)(1 << i))) {
            const auto& childID = _tileID.getChild(i);
            const auto& childTileIter = m_tileSet.find(childID);
            if (childTileIter != m_tileSet.end()) {
                childTileIter->second->decProxyCounter();
            }
        }
    }
    _tile.clearProxies();
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2003-2005 Tommi Maekitalo
 *
 * 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.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <tnt/httpparser.h>
#include <tnt/httperror.h>
#include <tnt/httpheader.h>
#include <tnt/tntconfig.h>
#include <cxxtools/log.h>
#include <sstream>
#include <algorithm>

#define SET_STATE(new_state)  state = &Parser::new_state

namespace tnt
{
  namespace
  {
    std::string chartoprint(char ch)
    {
      const static char hex[] = "0123456789abcdef";
      if (std::isprint(ch))
        return std::string(1, '\'') + ch + '\'';
      else
        return std::string("'\\x") + hex[(ch >> 4) & 0xf] + hex[ch & 0xf] + '\'';
    }

    inline bool istokenchar(char ch)
    {
      static const char s[] = "\"(),/:;<=>?@[\\]{}";
      return std::isalpha(ch) || std::binary_search(s, s + sizeof(s) - 1, ch);
    }

    inline bool isHexDigit(char ch)
    {
      return (ch >= '0' && ch <= '9')
          || (ch >= 'A' && ch <= 'F')
          || (ch >= 'a' && ch <= 'f');
    }

    inline unsigned valueOfHexDigit(char ch)
    {
      return ch >= '0' && ch <= '9' ? ch - '0'
           : ch >= 'a' && ch <= 'z' ? ch - 'a' + 10
           : ch >= 'A' && ch <= 'Z' ? ch - 'A' + 10
           : 0;
    }
  }

  log_define("tntnet.httpmessage.parser")

  bool RequestSizeMonitor::post(bool ret)
  {
    if (++requestSize > TntConfig::it().maxRequestSize
      && TntConfig::it().maxRequestSize > 0)
    {
      requestSizeExceeded();
      return true;
    }
    return ret;
  }

  void RequestSizeMonitor::requestSizeExceeded()
  { }

  void HttpRequest::Parser::reset()
  {
    message.clear();
    SET_STATE(state_cmd0);
    httpCode = HTTP_OK;
    failedFlag = false;
    RequestSizeMonitor::reset();
    headerParser.reset();
  }

  bool HttpRequest::Parser::state_cmd0(char ch)
  {
    if (istokenchar(ch))
    {
      message.method[0] = ch;
      message.methodLen = 1;
      SET_STATE(state_cmd);
    }
    else if (ch != ' ' && ch != '\t')
    {
      log_warn("invalid character " << chartoprint(ch) << " in method");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_cmd(char ch)
  {
    if (istokenchar(ch))
    {
      if (message.methodLen >= sizeof(message.method) - 1)
      {
        log_debug("invalid method field; method=" << std::string(message.method, message.methodLen) << ", len=" << message.methodLen);
        throw HttpError(HTTP_BAD_REQUEST, "invalid method field");
      }
      message.method[message.methodLen++] = ch;
    }
    else if (ch == ' ')
    {
      message.method[message.methodLen] = '\0';
      log_debug("method=" << message.method);
      SET_STATE(state_url0);
    }
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in method");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_url0(char ch)
  {
    if (ch == ' ' || ch == '\t')
    {
    }
    else if (ch == '/')
    {
      message.url.clear();
      message.url.reserve(32);
      message.url += ch;
      SET_STATE(state_url);
    }
    else if (std::isalpha(ch))
    {
      SET_STATE(state_protocol);
    }
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }

    return failedFlag;
  }

  bool HttpRequest::Parser::state_protocol(char ch)
  {
    if (ch == ':')
      SET_STATE(state_protocol_slash1);
    else if (!std::isalpha(ch))
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }

    return failedFlag;
  }

  bool HttpRequest::Parser::state_protocol_slash1(char ch)
  {
    if (ch == '/')
      SET_STATE(state_protocol_slash2);
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }

    return failedFlag;
  }

  bool HttpRequest::Parser::state_protocol_slash2(char ch)
  {
    if (ch == '/')
      SET_STATE(state_protocol_host);
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }

    return failedFlag;
  }

  bool HttpRequest::Parser::state_protocol_host(char ch)
  {
    if (ch == '/')
    {
      message.url.clear();
      message.url.reserve(32);
      message.url += ch;
      SET_STATE(state_url);
    }
    else if (!std::isalpha(ch)
           && !std::isdigit(ch)
           && ch != '['
           && ch != ']'
           && ch != '.'
           && ch != ':')
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }

    return failedFlag;
  }

  bool HttpRequest::Parser::state_url(char ch)
  {
    if (ch == '?')
    {
      log_debug("url=" << message.url);
      SET_STATE(state_qparam);
    }
    else if (ch == '\r')
    {
      log_debug("url=" << message.url);
      SET_STATE(state_end0);
    }
    else if (ch == '\n')
    {
      log_debug("url=" << message.url);
      SET_STATE(state_header);
    }
    else if (ch == ' ' || ch == '\t')
    {
      log_debug("url=" << message.url);
      SET_STATE(state_version);
    }
    else if (ch == '%')
    {
      SET_STATE(state_urlesc);
      message.url += ch;
    }
    else if (ch > ' ')
      message.url += ch;
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_urlesc(char ch)
  {
    if (isHexDigit(ch))
    {
      if (message.url.size() >= 2 && message.url[message.url.size() - 2] == '%')
      {
        unsigned v = (valueOfHexDigit(message.url[message.url.size() - 1]) << 4) | valueOfHexDigit(ch);
        message.url[message.url.size() - 2] = static_cast<char>(v);
        message.url.resize(message.url.size() - 1);
        SET_STATE(state_url);
      }
      else
      {
        message.url += ch;
      }
      return false;
    }
    else
    {
      SET_STATE(state_url);
      return state_url(ch);
    }
  }

  bool HttpRequest::Parser::state_qparam(char ch)
  {
    if (ch == ' ' || ch == '\t')
    {
      log_debug("queryString=" << message.queryString);
      SET_STATE(state_version);
    }
    else
      message.queryString += ch;
    return false;
  }

  bool HttpRequest::Parser::state_version(char ch)
  {
    if (ch == '/')
    {
      message.setVersion(0, 0);
      skipWs(&Parser::state_version_major);
    }
    else if (ch == '\r')
    {
      log_warn("invalid character " << chartoprint(ch) << " in version");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_version_major(char ch)
  {
    if (ch == '.')
      SET_STATE(state_version_minor0);
    else if (std::isdigit(ch))
      message.setVersion(message.getMajorVersion() * 10 + (ch - '0'), message.getMinorVersion());
    else if (ch == ' ' || ch == '\t')
      SET_STATE(state_version_major_sp);
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in version-major");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_version_major_sp(char ch)
  {
    if (ch == '.')
      SET_STATE(state_version_minor0);
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in version-major");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_version_minor0(char ch)
  {
    return ch == ' ' || ch == '\t' ? failedFlag
                                   : state_version_minor(ch);
  }

  bool HttpRequest::Parser::state_version_minor(char ch)
  {
    if (ch == '\n')
      SET_STATE(state_header);
    else if (ch == ' ' || ch == '\t' || ch == '\r')
      SET_STATE(state_end0);
    else if (std::isdigit(ch))
      message.setVersion(message.getMajorVersion(), message.getMinorVersion() * 10 + (ch - '0'));
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in version-minor");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_end0(char ch)
  {
    if (ch == '\n')
      SET_STATE(state_header);
    else if (ch != ' ' && ch != '\t')
    {
      log_warn("invalid character " << chartoprint(ch) << " in end");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_header(char ch)
  {
    if (headerParser.parse(ch))
    {
      if (headerParser.failed())
      {
        httpCode = HTTP_BAD_REQUEST;
        failedFlag = true;
        return true;
      }

      const char* content_length_header = message.getHeader(httpheader::contentLength);
      if (*content_length_header)
      {
        bodySize = 0;
        for (const char* c = content_length_header; *c; ++c)
        {
          if (*c > '9' || *c < '0')
            throw HttpError(HTTP_BAD_REQUEST, "invalid Content-Length");
          bodySize = bodySize * 10 + *c - '0';
        }

        if (TntConfig::it().maxRequestSize > 0
          && getCurrentRequestSize() + bodySize > TntConfig::it().maxRequestSize)
        {
          requestSizeExceeded();
          return true;
        }

        message.contentSize = bodySize;
        if (bodySize == 0)
          return true;
        else
        {
          SET_STATE(state_body);
          message.body.reserve(bodySize);
          return false;
        }
      }

      return true;
    }

    return false;
  }

  bool HttpRequest::Parser::state_body(char ch)
  {
    message.body += ch;
    return --bodySize == 0;
  }

  void HttpRequest::Parser::requestSizeExceeded()
  {
    log_warn("max request size " << TntConfig::it().maxRequestSize << " exceeded");
    httpCode = HTTP_REQUEST_ENTITY_TOO_LARGE;
    failedFlag = true;
  }
}
<commit_msg>do not accept %00 in url since it is not processed correctly anyway<commit_after>/*
 * Copyright (C) 2003-2005 Tommi Maekitalo
 *
 * 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.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <tnt/httpparser.h>
#include <tnt/httperror.h>
#include <tnt/httpheader.h>
#include <tnt/tntconfig.h>
#include <cxxtools/log.h>
#include <sstream>
#include <algorithm>

#define SET_STATE(new_state)  state = &Parser::new_state

namespace tnt
{
  namespace
  {
    std::string chartoprint(char ch)
    {
      const static char hex[] = "0123456789abcdef";
      if (std::isprint(ch))
        return std::string(1, '\'') + ch + '\'';
      else
        return std::string("'\\x") + hex[(ch >> 4) & 0xf] + hex[ch & 0xf] + '\'';
    }

    inline bool istokenchar(char ch)
    {
      static const char s[] = "\"(),/:;<=>?@[\\]{}";
      return std::isalpha(ch) || std::binary_search(s, s + sizeof(s) - 1, ch);
    }

    inline bool isHexDigit(char ch)
    {
      return (ch >= '0' && ch <= '9')
          || (ch >= 'A' && ch <= 'F')
          || (ch >= 'a' && ch <= 'f');
    }

    inline unsigned valueOfHexDigit(char ch)
    {
      return ch >= '0' && ch <= '9' ? ch - '0'
           : ch >= 'a' && ch <= 'z' ? ch - 'a' + 10
           : ch >= 'A' && ch <= 'Z' ? ch - 'A' + 10
           : 0;
    }
  }

  log_define("tntnet.httpmessage.parser")

  bool RequestSizeMonitor::post(bool ret)
  {
    if (++requestSize > TntConfig::it().maxRequestSize
      && TntConfig::it().maxRequestSize > 0)
    {
      requestSizeExceeded();
      return true;
    }
    return ret;
  }

  void RequestSizeMonitor::requestSizeExceeded()
  { }

  void HttpRequest::Parser::reset()
  {
    message.clear();
    SET_STATE(state_cmd0);
    httpCode = HTTP_OK;
    failedFlag = false;
    RequestSizeMonitor::reset();
    headerParser.reset();
  }

  bool HttpRequest::Parser::state_cmd0(char ch)
  {
    if (istokenchar(ch))
    {
      message.method[0] = ch;
      message.methodLen = 1;
      SET_STATE(state_cmd);
    }
    else if (ch != ' ' && ch != '\t')
    {
      log_warn("invalid character " << chartoprint(ch) << " in method");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_cmd(char ch)
  {
    if (istokenchar(ch))
    {
      if (message.methodLen >= sizeof(message.method) - 1)
      {
        log_debug("invalid method field; method=" << std::string(message.method, message.methodLen) << ", len=" << message.methodLen);
        throw HttpError(HTTP_BAD_REQUEST, "invalid method field");
      }
      message.method[message.methodLen++] = ch;
    }
    else if (ch == ' ')
    {
      message.method[message.methodLen] = '\0';
      log_debug("method=" << message.method);
      SET_STATE(state_url0);
    }
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in method");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_url0(char ch)
  {
    if (ch == ' ' || ch == '\t')
    {
    }
    else if (ch == '/')
    {
      message.url.clear();
      message.url.reserve(32);
      message.url += ch;
      SET_STATE(state_url);
    }
    else if (std::isalpha(ch))
    {
      SET_STATE(state_protocol);
    }
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }

    return failedFlag;
  }

  bool HttpRequest::Parser::state_protocol(char ch)
  {
    if (ch == ':')
      SET_STATE(state_protocol_slash1);
    else if (!std::isalpha(ch))
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }

    return failedFlag;
  }

  bool HttpRequest::Parser::state_protocol_slash1(char ch)
  {
    if (ch == '/')
      SET_STATE(state_protocol_slash2);
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }

    return failedFlag;
  }

  bool HttpRequest::Parser::state_protocol_slash2(char ch)
  {
    if (ch == '/')
      SET_STATE(state_protocol_host);
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }

    return failedFlag;
  }

  bool HttpRequest::Parser::state_protocol_host(char ch)
  {
    if (ch == '/')
    {
      message.url.clear();
      message.url.reserve(32);
      message.url += ch;
      SET_STATE(state_url);
    }
    else if (!std::isalpha(ch)
           && !std::isdigit(ch)
           && ch != '['
           && ch != ']'
           && ch != '.'
           && ch != ':')
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }

    return failedFlag;
  }

  bool HttpRequest::Parser::state_url(char ch)
  {
    if (ch == '?')
    {
      log_debug("url=" << message.url);
      SET_STATE(state_qparam);
    }
    else if (ch == '\r')
    {
      log_debug("url=" << message.url);
      SET_STATE(state_end0);
    }
    else if (ch == '\n')
    {
      log_debug("url=" << message.url);
      SET_STATE(state_header);
    }
    else if (ch == ' ' || ch == '\t')
    {
      log_debug("url=" << message.url);
      SET_STATE(state_version);
    }
    else if (ch == '%')
    {
      SET_STATE(state_urlesc);
      message.url += ch;
    }
    else if (ch > ' ')
      message.url += ch;
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in url");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_urlesc(char ch)
  {
    if (isHexDigit(ch))
    {
      if (message.url.size() >= 2 && message.url[message.url.size() - 2] == '%')
      {
        unsigned v = (valueOfHexDigit(message.url[message.url.size() - 1]) << 4) | valueOfHexDigit(ch);
        if (v == 0)
          throw HttpError(HTTP_BAD_REQUEST, "invalid value in url");
        message.url[message.url.size() - 2] = static_cast<char>(v);
        message.url.resize(message.url.size() - 1);
        SET_STATE(state_url);
      }
      else
      {
        message.url += ch;
      }
      return false;
    }
    else
    {
      SET_STATE(state_url);
      return state_url(ch);
    }
  }

  bool HttpRequest::Parser::state_qparam(char ch)
  {
    if (ch == ' ' || ch == '\t')
    {
      log_debug("queryString=" << message.queryString);
      SET_STATE(state_version);
    }
    else
      message.queryString += ch;
    return false;
  }

  bool HttpRequest::Parser::state_version(char ch)
  {
    if (ch == '/')
    {
      message.setVersion(0, 0);
      skipWs(&Parser::state_version_major);
    }
    else if (ch == '\r')
    {
      log_warn("invalid character " << chartoprint(ch) << " in version");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_version_major(char ch)
  {
    if (ch == '.')
      SET_STATE(state_version_minor0);
    else if (std::isdigit(ch))
      message.setVersion(message.getMajorVersion() * 10 + (ch - '0'), message.getMinorVersion());
    else if (ch == ' ' || ch == '\t')
      SET_STATE(state_version_major_sp);
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in version-major");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_version_major_sp(char ch)
  {
    if (ch == '.')
      SET_STATE(state_version_minor0);
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in version-major");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_version_minor0(char ch)
  {
    return ch == ' ' || ch == '\t' ? failedFlag
                                   : state_version_minor(ch);
  }

  bool HttpRequest::Parser::state_version_minor(char ch)
  {
    if (ch == '\n')
      SET_STATE(state_header);
    else if (ch == ' ' || ch == '\t' || ch == '\r')
      SET_STATE(state_end0);
    else if (std::isdigit(ch))
      message.setVersion(message.getMajorVersion(), message.getMinorVersion() * 10 + (ch - '0'));
    else
    {
      log_warn("invalid character " << chartoprint(ch) << " in version-minor");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_end0(char ch)
  {
    if (ch == '\n')
      SET_STATE(state_header);
    else if (ch != ' ' && ch != '\t')
    {
      log_warn("invalid character " << chartoprint(ch) << " in end");
      httpCode = HTTP_BAD_REQUEST;
      failedFlag = true;
    }
    return failedFlag;
  }

  bool HttpRequest::Parser::state_header(char ch)
  {
    if (headerParser.parse(ch))
    {
      if (headerParser.failed())
      {
        httpCode = HTTP_BAD_REQUEST;
        failedFlag = true;
        return true;
      }

      const char* content_length_header = message.getHeader(httpheader::contentLength);
      if (*content_length_header)
      {
        bodySize = 0;
        for (const char* c = content_length_header; *c; ++c)
        {
          if (*c > '9' || *c < '0')
            throw HttpError(HTTP_BAD_REQUEST, "invalid Content-Length");
          bodySize = bodySize * 10 + *c - '0';
        }

        if (TntConfig::it().maxRequestSize > 0
          && getCurrentRequestSize() + bodySize > TntConfig::it().maxRequestSize)
        {
          requestSizeExceeded();
          return true;
        }

        message.contentSize = bodySize;
        if (bodySize == 0)
          return true;
        else
        {
          SET_STATE(state_body);
          message.body.reserve(bodySize);
          return false;
        }
      }

      return true;
    }

    return false;
  }

  bool HttpRequest::Parser::state_body(char ch)
  {
    message.body += ch;
    return --bodySize == 0;
  }

  void HttpRequest::Parser::requestSizeExceeded()
  {
    log_warn("max request size " << TntConfig::it().maxRequestSize << " exceeded");
    httpCode = HTTP_REQUEST_ENTITY_TOO_LARGE;
    failedFlag = true;
  }
}
<|endoftext|>