/////////////////////////////////////////////////////////////////////////////////////////
// Project: SP Data Formatting Library 1.5
//
// File: IntegerFormatter.cpp
//
// Developer(s): Sergei Pavlovsky
// sergei_vp@hotmail.com
// Copyright (c) 2004-2005
//
// Description:
//
// Platforms: Win32, ATL
//
// This code may be used in compiled form in any way you desire. This file may be
// redistributed unmodified by any means PROVIDING it is not sold for profit without
// the authors written consent, and providing that this notice and the authors name
// is included. If the source code in this file is used in any commercial application
// then acknowledgement must be made to the author of this file (in whatever form
// you wish).
//
// This file is provided "as is" with no expressed or implied warranty. The author
// accepts no liability for any damage/loss of business that this product may cause.
/////////////////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "../resource.h" // ???
#include "IntegerFormatter.h"
namespace SP
{
/////////////////////////////////////////////////////////////////////////////////////////
// CIntegerSegment class
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Operations: Common
UINT CIntegerSegment::_GetGeneralOperationErrorReason(EnumGeneralOperations enOp) const
{
const UINT rgIDs[] =
{
IDS_ERDF_COMPOSEINTSEGMENT,
IDS_ERDF_GENERATEINTSEGMENT,
IDS_ERDF_TRANSLATEINTSEGMENT,
IDS_ERDF_TRANSLATEINTSEGMENT,
0,
0,
0,
0
};
ATLASSERT( enOp < sizeof(rgIDs)/sizeof(UINT) );
return rgIDs[enOp];
}
/////////////////////////////////////////////////////////////////////////////////////////
// Helpers: Expression
UINT_PTR CIntegerSegment::ComposeTraitsToken(CNumericCompositionCtx& ctx,
CNumericFormatTraits::TokensEnum enToken,
LPTSTR& lpBuf, UINT_PTR& cchBuf) const
{
ATLASSERT( enToken );
ATLASSERT( !::IsBadWritePtr(lpBuf, cchBuf * sizeof(TCHAR)) );
if ( enToken == CNumericFormatTraits::TK_VALUE )
{
CIntegerCompositionCtx& ctxIEC = static_cast<CIntegerCompositionCtx&>(ctx);
UINT_PTR cchResult = 0;
LPTSTR lpBufPos = lpBuf + cchBuf;
// Add first entry
UINT_PTR cMinDigits = ctxIEC.GetMinIntegerDigits();
// Add groups
for ( UINT_PTR i = 0; i < ctxIEC.GetGroupsCount(); )
{
const BYTE cGroupDigits = ctxIEC.GetGroupSizeAt(i);
const bool bFirstGroup = cchResult == 0;
TCHAR chSrndBack;
TCHAR chSrndFore;
if ( bFirstGroup || cMinDigits )
{
chSrndBack = chSrndFore = _T('\x0');
}
else
{
if ( ctxIEC.IsLastGroupRepeated() && i == ctxIEC.GetGroupsCount() - 1 )
{
chSrndBack = TN_CLOSEREPEAT;
chSrndFore = TN_OPENREPEAT;
}
else
{
chSrndBack = TN_CLOSEOPTIONAL;
chSrndFore = TN_OPENOPTIONAL;
}
}
// Write symbol opening group
if ( chSrndFore )
cchResult += CFormatter::WriteTokenBwd(ctx, lpBufPos, cchBuf, chSrndFore);
if ( cGroupDigits )
{
// Write group separator
if ( !bFirstGroup )
cchResult += CFormatter::WriteTokenBwd(ctx, lpBufPos, cchBuf,
TN_THOUSANDSSEPARATOR);
// Write digit symbols
for ( BYTE j = 0; j < cGroupDigits; j++ )
{
if ( cMinDigits )
{
cchResult += CFormatter::WriteTokenBwd(ctx, lpBufPos, cchBuf, TN_ZERO);
cMinDigits--;
}
else
{
cchResult += CFormatter::WriteTokenBwd(ctx, lpBufPos, cchBuf, TN_DIGIT);
}
}
}
else
{
// Write digit symbols
if ( cMinDigits )
{
cchResult += CFormatter::WriteTokenBwd(ctx, lpBufPos, cchBuf, TN_ZERO);
cMinDigits--;
}
else
{
cchResult += CFormatter::WriteTokenBwd(ctx, lpBufPos, cchBuf, TN_DIGIT);
}
}
// Write symbol closing group
if ( chSrndBack )
cchResult += CFormatter::WriteTokenBwd(ctx, lpBufPos, cchBuf, chSrndBack);
if ( i + 1 < ctxIEC.GetGroupsCount() || !ctxIEC.IsLastGroupRepeated() ||
chSrndBack == TN_CLOSEREPEAT )
{
i++;
}
}
// Write the rest of non-optional digit symbols
while ( cMinDigits )
{
cchResult += CFormatter::WriteTokenBwd(ctx, lpBufPos, cchBuf, TN_ZERO);
cMinDigits--;
}
// If last group is not continued and number of digits is unlimited.
// we should add final digit to be repeated.
if ( ctxIEC.GetMaxIntegerDigits() == DC_UNLIMITED && !ctxIEC.IsLastGroupRepeated() )
{
// Write symbol opening group
cchResult += CFormatter::WriteTokenBwd(ctx, lpBufPos, cchBuf, TN_OPENREPEAT);
// Write digit symbols
cchResult += CFormatter::WriteTokenBwd(ctx, lpBufPos, cchBuf, TN_DIGIT);
// Write symbol closing group
cchResult += CFormatter::WriteTokenBwd(ctx, lpBufPos, cchBuf, TN_CLOSEREPEAT);
}
if ( lpBuf )
{
::MoveMemory(lpBuf, lpBufPos, cchResult * sizeof(TCHAR));
lpBuf += cchResult;
}
return cchResult;
}
return CNumericDigitalRTLSegment::ComposeTraitsToken(ctx, enToken, lpBuf, cchBuf);
}
/////////////////////////////////////////////////////////////////////////////////////////
// Operations: Expression
UINT_PTR CIntegerSegment::ComposeExpression(CCompositionCtx& ctx,
LPTSTR& lpBuf, UINT_PTR& cchBuf) const
{
ATLASSERT( !::IsBadReadPtr(&ctx, sizeof(CIntegerCompositionCtx)) );
ATLASSERT( !::IsBadWritePtr(lpBuf, cchBuf * sizeof(TCHAR)) );
// Update context
CIntegerCompositionCtx& ctxC = static_cast<CIntegerCompositionCtx&>(ctx);
ctxC.ChangeSegment(this);
UINT_PTR cchResult;
__try
{
cchResult = ComposeTraitsToken(ctxC, CNumericFormatTraits::TK_VALUE,
lpBuf, cchBuf);
}
__finally
{
// Restore previous context
ctx.DiscardSegment();
}
return cchResult;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Operations: Formatting & scanning
UINT_PTR CIntegerSegment::Format(CNumericDataCtx& ctx,
LPCTSTR lpcValue, UINT_PTR cchValue,
LPTSTR& lpBuffer, UINT_PTR& cchBuffer) const
{
ATLASSERT( !::IsBadReadPtr(lpcValue, cchValue * sizeof(TCHAR)) );
ATLASSERT( !::IsBadWritePtr(lpBuffer, cchBuffer * sizeof(TCHAR)) );
CIntegerDataCtx& ctxD = static_cast<CIntegerDataCtx&>(ctx);
ctxD.ChangeSegment(this);
UINT_PTR cchResult;
__try
{
cchResult = _Format(ctx, lpcValue + cchValue, cchValue,
lpBuffer + cchBuffer, cchBuffer);
if ( lpBuffer )
{
MoveMemory(lpBuffer, lpBuffer + cchBuffer - cchResult,
cchResult * sizeof(TCHAR));
lpBuffer += cchResult;
cchBuffer -= cchResult;
}
}
__finally
{
ctx.DiscardSegment();
}
return cchResult;
}
UINT_PTR CIntegerSegment::Scan(CNumericDataCtx& ctx, LPCTSTR lpcText, UINT_PTR cchText,
LPTSTR& lpBuffer, UINT_PTR& cchBuffer) const
{
CIntegerDataCtx& ctxD = static_cast<CIntegerDataCtx&>(ctx);
ctxD.ChangeSegment(this);
UINT_PTR cchResult = 0;
__try
{
LPTSTR lpBuf = lpBuffer;
UINT_PTR cchBuf = cchBuffer;
// Write sign
if ( ctxD.GetSign() < 0 )
{
const TCHAR cchSign = SV_MINUS;
cchResult += CFormatter::WriteEntryFwd(ctxD, lpBuf, cchBuf, &cchSign, 1);
}
// Write digits
UINT_PTR cchWritten = _Scan(ctxD, lpcText + cchText, cchText,
lpBuf + cchBuf, cchBuf);
if ( cchWritten == STATUS_FALSE ) // ??? Error
{
cchResult = STATUS_FALSE;
}
else
{
cchResult += cchWritten;
if ( lpBuffer )
{
::MoveMemory(lpBuf, lpBuf + cchBuf - cchWritten,
cchWritten * sizeof(TCHAR));
lpBuffer += cchResult;
cchBuffer -= cchResult;
}
}
}
__finally
{
ctx.DiscardSegment();
}
return cchResult;
}
/////////////////////////////////////////////////////////////////////////////////////////
// CIntegerLiteralSegment class
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Operations: Common
UINT CIntegerLiteralSegment::_GetGeneralOperationErrorReason(
EnumGeneralOperations enOp) const
{
const UINT rgIDs[] =
{
IDS_ERDF_COMPOSESEGMENT,
IDS_ERDF_GENERATESEGMENT,
IDS_ERDF_TRANSLATESEGMENT,
IDS_ERDF_TRANSLATESEGMENT,
0,
0,
0,
0
};
ATLASSERT( enOp < sizeof(rgIDs)/sizeof(UINT) );
return rgIDs[enOp];
}
/////////////////////////////////////////////////////////////////////////////////////////
// CIntegerDigitalPattern class implementation
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Constants
const UINT CIntegerDigitalPattern::ms_aDigitalSegmentIDs[] =
{
SG_INTEGER
};
#ifdef _DEBUG
/////////////////////////////////////////////////////////////////////////////////////////
// Debug
CTString CIntegerDigitalPattern::DumpContent() const
{
CTString csText;
/* bool bResult = csText.Allocate(64 + 32 * m_vecIntegerCommands.size());
ATLASSERT( bResult );
_stprintf(csText, _T("Commands: Capacity: %u\r\n"),
GetIntegerDigitCapacity());
for ( UINT_PTR i = 0; i < m_vecIntegerCommands.size(); i++ )
{
const CToken& cmd = m_vecIntegerCommands[i];
CTString csNumber;
csNumber.Allocate(16);
_stprintf(csNumber, _T("%03u: "), i);
csText += (csNumber + cmd.DumpContent() + _T("\r\n"));
}
*/
return csText;
}
#endif // _DEBUG
/////////////////////////////////////////////////////////////////////////////////////////
// Helpers: Common
UINT_PTR CIntegerDigitalPattern::GetDigitalSegments(
const CNumericDigitalSegment** prgSegments) const
{
const UINT_PTR cSegments = sizeof(ms_aDigitalSegmentIDs)/sizeof(UINT);
ATLASSERT( !prgSegments || !::IsBadWritePtr(prgSegments,
cSegments * sizeof(CNumericDigitalSegment*)) );
if ( prgSegments )
{
prgSegments[0] = &m_segInteger;
}
return cSegments;
}
UINT_PTR CIntegerDigitalPattern::GetDigitalSegments(
CNumericDigitalSegment** prgSegments)
{
const UINT_PTR cSegments = sizeof(ms_aDigitalSegmentIDs)/sizeof(UINT);
ATLASSERT( !prgSegments || !::IsBadWritePtr(prgSegments,
cSegments * sizeof(CNumericDigitalSegment*)) );
if ( prgSegments )
{
prgSegments[0] = &m_segInteger;
}
return cSegments;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Helpers: Expression
bool CIntegerDigitalPattern::ParseIExpression(CExpressionCtx& ctx, LPCTSTR lpcSrc,
UINT_PTR cchSrc, IExpression* pExpr)
{
ATLASSERT( !::IsBadReadPtr(lpcSrc, cchSrc * sizeof(TCHAR)) );
ATLASSERT( !::IsBadReadPtr(pExpr, sizeof(IExpression)) );
bool bResult = ParseVExpression(ctx, lpcSrc, cchSrc, pExpr);
ATLASSERT( bResult );
if ( !bResult )
return false;
LPCTSTR lpVBegin = pExpr->lpcPrefix
? pExpr->lpcPrefix + pExpr->cchPrefix + 1
: lpcSrc;
LPCTSTR lpVEnd = pExpr->lpcSuffix
? pExpr->lpcSuffix - 1
: lpcSrc + cchSrc;
pExpr->lpcInteger = lpVBegin;
pExpr->cchInteger = lpVEnd - lpVBegin;
pExpr->cIntegerBaseLevel = 0;
return true;
}
bool CIntegerDigitalPattern::ParseExpression(CExpressionCtx& ctx,
LPCTSTR lpcSrc, UINT_PTR cchSrc,
SExpression* prgSExpressions) const
{
ATLASSERT( !::IsBadReadPtr(lpcSrc, cchSrc * sizeof(TCHAR)) );
ATLASSERT( !::IsBadReadPtr(prgSExpressions,
(GetDigitalSegments(NULL) + 2) * sizeof(SExpression)) );
// Parse expression segments
IExpression iexp;
bool bResult = ParseIExpression(ctx, lpcSrc, cchSrc, &iexp);
ATLASSERT( bResult );
if ( !bResult )
{
const UINT eReasonID = _GetGeneralOperationErrorReason(GO_PARSEEXPRESSION);
SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPCONVERSION, 2,
eReasonID);
return false;
}
// Add segments information to the vector
prgSExpressions[0].lpcSergment = iexp.lpcPrefix;
prgSExpressions[0].cchSergment = iexp.cchPrefix;
prgSExpressions[0].cBaseLevel = 0;
prgSExpressions[1].lpcSergment = iexp.lpcInteger;
prgSExpressions[1].cchSergment = iexp.cchInteger;
prgSExpressions[1].cBaseLevel = iexp.cIntegerBaseLevel;
prgSExpressions[2].lpcSergment = iexp.lpcSuffix;
prgSExpressions[2].cchSergment = iexp.cchSuffix;
prgSExpressions[2].cBaseLevel = 0;
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Operations: Expression
UINT_PTR CIntegerDigitalPattern::ComposeExpression(CCompositionCtx& ctx, LPTSTR& lpBuf,
UINT_PTR& cchBuf) const
{
ATLASSERT( !::IsBadWritePtr(lpBuf, cchBuf * sizeof(TCHAR)) );
CIntegerCompositionCtx& ctxC = static_cast<CIntegerCompositionCtx&>(ctx);
ctxC.ChangePattern(this);
UINT_PTR cchResult;
__try
{
cchResult = Compose(ctxC, lpBuf, cchBuf);
}
__finally
{
ctx.DiscardPattern();
}
return cchResult;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Helpers: Formatting & scanning
void CIntegerDigitalPattern::ParseStdTextIValue(CIntegerDataCtx& ctx,
LPCTSTR lpcValue, UINT_PTR cchValue,
IValueParts* pVP) const
{
ATLASSERT( !::IsBadReadPtr(lpcValue, cchValue * sizeof(TCHAR)) );
ATLASSERT( !::IsBadWritePtr(pVP, sizeof(IValueParts)) );
pVP->lpcInteger = lpcValue;
pVP->cchInteger = cchValue;
// Define sign
if ( pVP->cchInteger )
{
switch ( *pVP->lpcInteger )
{
case SV_MINUS:
pVP->nSign = -1;
pVP->lpcInteger++;
pVP->cchInteger--;
break;
case SV_PLUS:
pVP->nSign = 1;
pVP->lpcInteger++;
pVP->cchInteger--;
break;
default:
pVP->nSign = 0;
}
}
else
{
pVP->nSign = 0;
}
// Skip sign and spaces in the beginning
while ( *pVP->lpcInteger == SV_ZERO )
{
pVP->lpcInteger++;
pVP->cchInteger--;
}
}
void CIntegerDigitalPattern::ParseTextIValue(CIntegerDataCtx& ctx,
LPCTSTR lpcText, UINT_PTR cchText,
IValueParts* pVP) const
{
ATLASSERT( !::IsBadReadPtr(lpcText, cchText * sizeof(TCHAR)) );
ATLASSERT( !::IsBadWritePtr(pVP, sizeof(IValueParts)) );
pVP->lpcInteger = lpcText;
pVP->cchInteger = cchText;
}
UINT_PTR CIntegerDigitalPattern::_Format(CIntegerDataCtx& ctx,
LPCTSTR lpcValue, UINT_PTR cchValue,
LPTSTR lpBuffer, UINT_PTR cchBuffer) const
{
ATLASSERT( !::IsBadReadPtr(lpcValue, cchValue * sizeof(TCHAR)) );
ATLASSERT( !::IsBadWritePtr(lpBuffer, cchBuffer * sizeof(TCHAR)) );
IValueParts vp;
ParseStdTextValue(ctx, lpcValue, cchValue, &vp);
vp.lpcInteger = vp.lpcPrefix + vp.cchPrefix;
vp.cchInteger = vp.lpcSuffix - vp.lpcInteger;
ParseStdTextIValue(ctx, vp.lpcInteger, vp.cchInteger, &vp);
UINT_PTR cchResult = 0;
// Format prefix part
cchResult += FormatPrefix(ctx, lpBuffer, cchBuffer);
// Format integer part
cchResult += FormatInteger(ctx, vp.nSign, vp.lpcInteger, vp.cchInteger,
lpBuffer, cchBuffer);
// Format suffix part
cchResult += FormatSufix(ctx, lpBuffer, cchBuffer);
return cchResult;
}
UINT_PTR CIntegerDigitalPattern::_Scan(CIntegerDataCtx& ctx,
LPCTSTR lpcText, UINT_PTR cchText,
LPTSTR lpBuffer, UINT_PTR cchBuffer) const
{
ATLASSERT( !::IsBadReadPtr(lpcText, cchText * sizeof(TCHAR)) );
ATLASSERT( !::IsBadWritePtr(lpBuffer, cchBuffer * sizeof(TCHAR)) );
// Parse value text
IValueParts vp;
if ( !ParseTextValue(ctx, lpcText, cchText, &vp) )
return STATUS_FALSE;
vp.lpcInteger = vp.lpcPrefix + vp.cchPrefix;
vp.cchInteger = vp.lpcSuffix - vp.lpcInteger;
ParseTextIValue(ctx, vp.lpcInteger, vp.cchInteger, &vp);
// Text with empty numeric parts cannot define a number. Check it.
if ( !vp.cchInteger )
return STATUS_FALSE;
// Scan all digital segments
return ScanInteger(ctx, vp.lpcInteger, vp.cchInteger, lpBuffer, cchBuffer);
}
/////////////////////////////////////////////////////////////////////////////////////////
// CIntegerLiteralPattern class
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// CIntegerNullPattern class implementation
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Operarions: Common
UINT CIntegerNullPattern::_GetGeneralOperationErrorReason(
EnumGeneralOperations enOp) const
{
const UINT rgIDs[] =
{
IDS_ERDF_COMPOSENULLPATTERN,
IDS_ERDF_GENERATENULLPATTERN,
IDS_ERDF_PARSENULLPATTERN,
IDS_ERDF_TRANSLATENULLPATTERN,
0,
0,
0,
0
};
ATLASSERT( enOp < sizeof(rgIDs)/sizeof(UINT) );
return rgIDs[enOp];
}
/////////////////////////////////////////////////////////////////////////////////////////
// Operations: Formatting & scanning
UINT_PTR CIntegerNullPattern::Format(CDataCtx& ctx, const void* lpcValue,
LPTSTR lpBuffer, UINT_PTR cchBuffer) const
{
ATLASSERT( !::IsBadWritePtr(lpBuffer, cchBuffer * sizeof(TCHAR)) );
CIntegerDataCtx& ctxD = static_cast<CIntegerDataCtx&>(ctx);
ctxD.ChangePattern(this);
UINT_PTR cchResult;
__try
{
cchResult = m_segment.Format(ctxD, lpBuffer, cchBuffer);
}
__finally
{
ctx.DiscardPattern();
}
return cchResult;
}
UINT_PTR CIntegerNullPattern::Matches(CDataCtx& ctx, LPCTSTR lpcText,
UINT_PTR cchText) const
{
ATLASSERT( !::IsBadReadPtr(lpcText, cchText * sizeof(TCHAR)) );
CIntegerDataCtx& ctxD = static_cast<CIntegerDataCtx&>(ctx);
ctxD.ChangePattern(this);
UINT_PTR cchResult;
__try
{
cchResult = m_segment.Check(ctxD, lpcText, cchText)
? STATUS_TRUE : STATUS_FALSE;
}
__finally
{
ctx.DiscardPattern();
}
return cchResult;
}
UINT_PTR CIntegerNullPattern::Scan(CDataCtx& ctx, LPCTSTR lpcText, UINT_PTR cchText,
void* lpValue, bool* pbNotNull) const
{
ATLASSERT( !::IsBadReadPtr(lpcText, cchText * sizeof(TCHAR)) );
ATLASSERT( !::IsBadWritePtr(pbNotNull, sizeof(bool)) );
CIntegerDataCtx& ctxD = static_cast<CIntegerDataCtx&>(ctx);
ctxD.ChangePattern(this);
UINT_PTR cchResult;
__try
{
if ( m_segment.Check(ctxD, lpcText, cchText) )
{
*pbNotNull = false;
cchResult = STATUS_TRUE;
}
else
{
cchResult = STATUS_FALSE;
}
}
__finally
{
ctx.DiscardPattern();
}
return cchResult;
}
/////////////////////////////////////////////////////////////////////////////////////////
// CIntegerFormatter class implementation
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Operations: Formatting & scanning
UINT_PTR CIntegerFormatter::Format(const void* lpcValue,
LPTSTR lpBuffer, UINT_PTR cchBuffer) const
{
ATLASSERT( !::IsBadWritePtr(lpBuffer, cchBuffer * sizeof(TCHAR)) );
// Create context and format the value.
return _Format(CIntegerDataCtx(this), lpcValue, lpBuffer, cchBuffer);
}
UINT_PTR CIntegerFormatter::Matches(LPCTSTR lpcText, UINT_PTR cchText) const
{
ATLASSERT( !::IsBadReadPtr(lpcText, cchText * sizeof(TCHAR)) );
// Create context and check if the value matches to one of the patterns
return _Matches(CIntegerDataCtx(this), lpcText, cchText);
}
UINT_PTR CIntegerFormatter::Scan(LPCTSTR lpcText, UINT_PTR cchText,
void* lpValue, bool* pbNotNull) const
{
ATLASSERT( !::IsBadReadPtr(lpcText, sizeof(TCHAR) * cchText) );
// Create context and try to scan the value using one of the patterns
return _Scan(CIntegerDataCtx(this), lpcText, cchText, lpValue, pbNotNull);
}
}; // namespace SP