Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1633 lines
47 KiB

#include "private.h"
#include "sapilayr.h"
#include "citn.h"
#include "xstring.h"
#include "winnls.h"
#include "wchar.h"
#define MILLION ((LONGLONG) 1000000)
#define BILLION ((LONGLONG) 1000000000)
#define MILLION_STR (L"million")
#define BILLION_STR (L"billion")
#define MANN ((LONGLONG) 10000)
#define OKU ((LONGLONG) 100000000)
#define CHUU ((LONGLONG) 1000000000000)
#define MANN_STR (L"\x4E07")
#define OKU_STR (L"\x5104")
#define CHUU_STR (L"\x5146")
HRESULT CSimpleITN::InterpretNumberEn
(
const SPPHRASEPROPERTY *pProperties,
const bool fCardinal,
DOUBLE *pdblVal,
WCHAR *pszVal,
UINT cSize,
const bool fFinalDisplayFmt
)
{
if ( !pdblVal || !pszVal || !pProperties)
{
return E_POINTER;
}
*pszVal = 0;
int iPositive = 1;
const SPPHRASEPROPERTY *pFirstProp = pProperties;
// Handle negatives
if ( NEGATIVE == pFirstProp->ulId )
{
// There's no such thing as a negative ordinal
SPDBG_ASSERT( fCardinal );
// There had better be more stuff following
SPDBG_ASSERT( pFirstProp->pNextSibling );
iPositive = -1;
pFirstProp = pFirstProp->pNextSibling;
}
if ( GRID_INTEGER_STANDALONE == pFirstProp->ulId )
{
// This is interger number
TraceMsg(TF_GENERAL, "English Interger Number");
SPDBG_ASSERT( pFirstProp->pFirstChild);
pFirstProp = pFirstProp->pFirstChild;
// Handle the "2.6 million" case, in which case the number
// has already been formatted
if ( GRID_INTEGER_MILLBILL == pFirstProp->ulId )
{
if ( pFirstProp->pszValue == NULL
|| cSize < (wcslen( pFirstProp->pszValue ) + 1) )
{
return E_INVALIDARG;
}
*pdblVal = pFirstProp->vValue.dblVal * iPositive;
if ( iPositive < 0 )
{
StringCchCopyW( pszVal, cSize, m_pwszNeg );
}
StringCchCatW( pszVal, cSize, pFirstProp->pszValue );
return S_OK;
}
else
{
BOOL fNegative;
fNegative = (iPositive == -1);
return InterpretIntegerEn(pFirstProp,
fCardinal,
pdblVal,
pszVal,
cSize,
fFinalDisplayFmt,
fNegative);
}
}
else if ( GRID_FP_NUMBER == pFirstProp->ulId )
{
// This is decimal number
TraceMsg(TF_GENERAL, "English Floating point (decimal) Number");
SPDBG_ASSERT( pFirstProp->pFirstChild);
pFirstProp = pFirstProp->pFirstChild;
// todo for decimal number handling.
return InterpretDecimalEn(pFirstProp,
fCardinal,
pdblVal,
pszVal,
cSize,
fFinalDisplayFmt,
(iPositive == -1) );
}
return S_OK;
}
HRESULT CSimpleITN::InterpretIntegerEn
( const SPPHRASEPROPERTY *pProperties,
const bool fCardinal,
DOUBLE *pdblVal,
WCHAR *pszVal,
UINT cSize,
const bool fFinalDisplayFmt,
BOOL fNegative)
{
HRESULT hr=S_OK;
LONGLONG llValue = 0;
if ( !pdblVal || !pszVal || !pProperties)
{
return E_POINTER;
}
*pszVal = 0;
const SPPHRASEPROPERTY *pFirstProp = pProperties;
// Handle the digit-by-digit case
if ( GRID_DIGIT_NUMBER == pFirstProp->ulId )
{
const SPPHRASEPROPERTY * pProp;
int iNumDigit = 0;
// iNumDigit is 1 means the current property is ONEDIGIT
// iNumDigit is 2 means the current property is TWODIGIT.
// llValue = llValue * ( 10 ^ iNumDigit ) + Value in current property
for (pProp = pFirstProp->pFirstChild; pProp; pProp ? pProp = pProp->pNextSibling : NULL)
{
LONGLONG llValCurrent; // current property's value
const SPPHRASEPROPERTY *pPropValue;
switch ( pProp->ulId)
{
case ONEDIGIT:
{
ASSERT( pProp->pFirstChild );
pPropValue = pProp->pFirstChild;
ASSERT( VT_UI4 == pPropValue->vValue.vt );
llValCurrent = pPropValue->vValue.ulVal;
iNumDigit = 1;
TraceMsg(TF_GENERAL, "ONEDIGIT: %d", llValCurrent);
break;
}
case TWODIGIT :
{
ASSERT( pProp->pFirstChild );
pPropValue = pProp->pFirstChild;
TraceMsg(TF_GENERAL, "TWODIGIT:");
if ( pPropValue->ulId == TENS )
{
const SPPHRASEPROPERTY *pPropOnes;
ASSERT(pPropValue->pFirstChild);
ASSERT( VT_UI4 == pPropValue->pFirstChild->vValue.vt );
llValCurrent = pPropValue->vValue.ulVal * pPropValue->pFirstChild->vValue.ulVal;
TraceMsg(TF_GENERAL, "TENS: %d", llValCurrent);
pPropOnes = pPropValue->pNextSibling;
if ( pPropOnes )
{
ASSERT(pPropOnes->pFirstChild);
ASSERT( VT_UI4 == pPropOnes->pFirstChild->vValue.vt );
llValCurrent += pPropOnes->pFirstChild->vValue.ulVal;
TraceMsg(TF_GENERAL, "TENS: Second: %d", pPropOnes->pFirstChild->vValue.ulVal);
}
}
else if ( pPropValue->ulId == TEENS )
{
ASSERT( pPropValue->pFirstChild );
ASSERT( VT_UI4 == pPropValue->pFirstChild->vValue.vt );
llValCurrent = pPropValue->pFirstChild->vValue.ulVal;
TraceMsg(TF_GENERAL, "One Teens, %d", llValCurrent);
}
else
{
llValCurrent = 0;
TraceMsg(TF_GENERAL, "Wrong ulIds");
ASSERT(false);
}
iNumDigit = 2;
break;
}
default :
{
iNumDigit = 0;
llValCurrent = 0;
TraceMsg(TF_GENERAL, "ulId error!");
ASSERT(false);
}
}
for (int i=0; i<iNumDigit; i++)
llValue = llValue * 10;
llValue += llValCurrent;
TraceMsg(TF_GENERAL, "llValue=%d", llValue);
}
}
else
{ for (const SPPHRASEPROPERTY * pProp = pFirstProp; pProp; pProp ? pProp = pProp->pNextSibling : NULL)
{
switch(pProp->ulId)
{
case ONES:
{
SPDBG_ASSERT(pProp->pFirstChild);
llValue += ComputeNum999En( pProp->pFirstChild );
}
break;
case THOUSANDS:
{
llValue += ComputeNum999En( pProp->pFirstChild ) * 1000;
}
break;
case MILLIONS:
{
SPDBG_ASSERT(pProp->pFirstChild);
llValue += ComputeNum999En( pProp->pFirstChild ) * (LONGLONG) 1e6;
}
break;
case BILLIONS:
{
SPDBG_ASSERT(pProp->pFirstChild);
llValue += ComputeNum999En( pProp->pFirstChild ) * (LONGLONG) 1e9;
}
break;
case HUNDREDS:
{
SPDBG_ASSERT( pProp->pFirstChild );
llValue += ComputeNum999En( pProp->pFirstChild ) * 100;
}
break;
case TENS:
default:
SPDBG_ASSERT(false);
}
}
}
if ( fNegative )
llValue *= (-1);
*pdblVal = (DOUBLE) llValue;
DWORD dwDisplayFlags = (fCardinal ? 0 : DF_ORDINAL)
| (fFinalDisplayFmt ? DF_MILLIONBILLION : 0 );
hr = MakeDisplayNumber( *pdblVal, dwDisplayFlags, 0, 0, pszVal, cSize );
return hr;
}
HRESULT CSimpleITN::InterpretDecimalEn
( const SPPHRASEPROPERTY *pProperties,
const bool fCardinal,
DOUBLE *pdblVal,
WCHAR *pszVal,
UINT cSize,
const bool fFinalDisplayFmt,
BOOL fNegative)
{
HRESULT hr = S_OK;
const SPPHRASEPROPERTY *pPropertiesFpPart = NULL;
const SPPHRASEPROPERTY *pPropertiesPointZero = NULL;
const SPPHRASEPROPERTY *pPropertiesOnes = NULL;
const SPPHRASEPROPERTY *pPropertiesZero = NULL;
const SPPHRASEPROPERTY *pPropertiesPtr;
if ( !pdblVal || !pszVal || !pProperties)
{
return E_POINTER;
}
*pszVal = 0;
for(pPropertiesPtr=pProperties; pPropertiesPtr; pPropertiesPtr=pPropertiesPtr->pNextSibling)
{
if (POINT_ZERO == pPropertiesPtr->ulId )
pPropertiesPointZero = pPropertiesPtr;
else if ( FP_PART == pPropertiesPtr->ulId )
pPropertiesFpPart = pPropertiesPtr;
else if (ONES == pPropertiesPtr->ulId )
pPropertiesOnes = pPropertiesPtr;
else if (ZERO == pPropertiesPtr->ulId )
pPropertiesZero = pPropertiesPtr;
}
// Look for optional ONES (optional because you can say
// "point five"
if ( pPropertiesOnes )
{
SPDBG_ASSERT(pPropertiesOnes->pFirstChild);
hr = InterpretIntegerEn( pPropertiesOnes->pFirstChild,
fCardinal,
pdblVal,
pszVal,
cSize,
fFinalDisplayFmt,
FALSE);
}
else if (pPropertiesZero || m_nmfmtDefault.LeadingZero )
{
// There should be a leading zero
StringCchCopyW( pszVal, cSize, L"0" );
}
SPDBG_ASSERT(pPropertiesFpPart || pPropertiesPointZero);
// Put in a decimal separator
// Set m_nmfmtDefault.lpDecimalSep as L'.'
if ( m_nmfmtDefault.lpDecimalSep )
{
if ( (cSize - wcslen( pszVal )) < (wcslen(m_nmfmtDefault.lpDecimalSep) + 1) )
{
return E_INVALIDARG;
}
StringCchCatW( pszVal, cSize, m_nmfmtDefault.lpDecimalSep);
}
if ( pPropertiesFpPart )
{
// Deal with the FP part, which will also have been ITNed correctly
INT ulSize = cSize - wcslen(pszVal);
if ( ulSize < 0 )
return E_FAIL;
WCHAR *pwszFpValue = new WCHAR[ulSize+1];
DOUBLE dblFPPart;
if (pwszFpValue)
{
hr = InterpretIntegerEn( pPropertiesFpPart->pFirstChild,
fCardinal,
&dblFPPart,
pwszFpValue,
ulSize,
fFinalDisplayFmt,
FALSE);
if ( hr == S_OK )
{
StringCchCatW( pszVal, cSize, pwszFpValue);
for ( UINT ui=0; ui < wcslen(pwszFpValue); ui++ )
{
dblFPPart /= (DOUBLE) 10;
}
*pdblVal += dblFPPart;
}
delete[] pwszFpValue;
}
else
hr = E_OUTOFMEMORY;
}
else
{
// "point oh": The DOUBLE is already right, just add a "0"
if ( (cSize - wcslen( pszVal )) < 2 )
{
return E_INVALIDARG;
}
StringCchCatW( pszVal, cSize, L"0" );
}
// Handle the negative sign
if ( (hr == S_OK) && fNegative)
{
*pdblVal = -*pdblVal;
if ( (cSize = wcslen( pszVal )) < 2 )
{
return E_INVALIDARG;
}
hr = MakeNumberNegative( pszVal, cSize );
}
return hr;
}
HRESULT CSimpleITN::MakeNumberNegative( WCHAR *pwszNumber, UINT cSize )
{
// Create a temporary buffer with the non-negated number in it
if ( (pwszNumber == NULL) || (cSize == 0) )
return E_POINTER;
WCHAR *pwszTemp = _wcsdup( pwszNumber );
if ( !pwszTemp )
{
return E_OUTOFMEMORY;
}
switch ( m_nmfmtDefault.NegativeOrder )
{
case 0:
// (1.1)
StringCchCopyW( pwszNumber, cSize, L"(" );
StringCchCatW( pwszNumber, cSize, pwszTemp );
StringCchCatW( pwszNumber, cSize, L")" );
break;
case 1: case 2:
// 1: -1.1 2: - 1.1
StringCchCopyW( pwszNumber, cSize, m_pwszNeg );
if ( 2 == m_nmfmtDefault.NegativeOrder )
{
StringCchCatW( pwszNumber, cSize, L" " );
}
StringCchCatW( pwszNumber, cSize, pwszTemp );
break;
case 3: case 4:
// 3: 1.1- 4: 1.1 -
StringCchCopyW( pwszNumber, cSize, pwszTemp );
if ( 4 == m_nmfmtDefault.NegativeOrder )
{
StringCchCatW( pwszNumber, cSize, L" " );
}
StringCchCatW( pwszNumber, cSize, m_pwszNeg );
break;
default:
SPDBG_ASSERT( false );
break;
}
free( pwszTemp );
return S_OK;
} /* CTestITN::MakeNumberNegative */
/***********************************************************************
* _EnsureNumberFormatDefaults
*
* Description:
* This finds all of the defaults for formatting numbers for
* this user.
*************************************************************************/
HRESULT CSimpleITN::_EnsureNumberFormatDefaults()
{
LCID lcid = MAKELCID(m_langid, SORT_DEFAULT);
if (m_pwszNeg != NULL) return S_OK;
//
// we use ansi version so we can run on win9x too
//
CHAR szLocaleData[16];
int iRet = GetLocaleInfoA( lcid, LOCALE_IDIGITS, szLocaleData, ARRAYSIZE(szLocaleData) );
if ( !iRet )
{
return E_FAIL;
}
m_nmfmtDefault.NumDigits = atoi( szLocaleData );
iRet = GetLocaleInfoA( lcid, LOCALE_ILZERO, szLocaleData, ARRAYSIZE(szLocaleData) );
if ( !iRet )
{
return E_FAIL;
}
// It's always either 0 or 1
m_nmfmtDefault.LeadingZero = atoi( szLocaleData );
iRet = GetLocaleInfoA( lcid, LOCALE_SGROUPING, szLocaleData, ARRAYSIZE(szLocaleData) );
if ( !iRet )
{
return E_FAIL;
}
// It will look like single_digit;0, or else it will look like
// 3;2;0
UINT uiGrouping = *szLocaleData - '0';
if ( (3 == uiGrouping) && (';' == szLocaleData[1]) && ('2' == szLocaleData[2]) )
{
uiGrouping = 32;
}
m_nmfmtDefault.Grouping = uiGrouping;
iRet = GetLocaleInfoA( lcid, LOCALE_INEGNUMBER, szLocaleData, ARRAYSIZE(szLocaleData) );
if ( !iRet )
{
return E_FAIL;
}
m_nmfmtDefault.NegativeOrder = atoi( szLocaleData );
// Get the negative sign
iRet = GetLocaleInfoA( lcid, LOCALE_SNEGATIVESIGN, NULL, 0);
if ( !iRet )
{
return E_FAIL;
}
CHAR szNeg[16];
Assert(iRet < 8);
int iLenNeg = iRet + 1;
m_pwszNeg = new WCHAR[ iLenNeg ];
if ( m_pwszNeg == NULL )
{
return E_OUTOFMEMORY;
}
iRet = GetLocaleInfoA( lcid, LOCALE_SNEGATIVESIGN, szNeg, iRet );
StringCchCopyW(m_pwszNeg, iLenNeg, AtoW(szNeg));
iRet = GetLocaleInfoA( lcid, LOCALE_SDECIMAL, NULL, 0);
if ( !iRet )
return E_FAIL;
Assert(iRet < 16);
if ( m_nmfmtDefault.lpDecimalSep )
{
delete[] m_nmfmtDefault.lpDecimalSep;
m_nmfmtDefault.lpDecimalSep = NULL;
}
int iDecSepLen = iRet + 1;
m_nmfmtDefault.lpDecimalSep = new WCHAR[ iDecSepLen ];
if ( m_nmfmtDefault.lpDecimalSep == NULL )
{
return E_OUTOFMEMORY;
}
iRet = GetLocaleInfoA( lcid, LOCALE_SDECIMAL, szNeg, iRet );
StringCchCopyW(m_nmfmtDefault.lpDecimalSep, iDecSepLen, AtoW(szNeg));
iRet = GetLocaleInfoA( lcid, LOCALE_STHOUSAND, NULL, 0);
if ( !iRet )
return E_FAIL;
Assert(iRet < 16);
if ( m_nmfmtDefault.lpThousandSep )
{
delete[] m_nmfmtDefault.lpThousandSep;
m_nmfmtDefault.lpThousandSep = NULL;
}
int iThousSepLen = iRet + 1;
m_nmfmtDefault.lpThousandSep = new WCHAR[ iThousSepLen ];
if ( m_nmfmtDefault.lpThousandSep == NULL )
{
return E_OUTOFMEMORY;
}
iRet = GetLocaleInfoA( lcid, LOCALE_STHOUSAND, szNeg, iRet );
StringCchCopyW(m_nmfmtDefault.lpThousandSep, iThousSepLen, AtoW(szNeg));
return iRet ? S_OK : E_FAIL;
}
/***********************************************************************
* MakeDisplayNumber
*
* Description:
* Converts a DOUBLE into a displayable
* number in the range -999,999,999,999 to +999,999,999,999.
* cSize is the number of chars for which pwszNum has space
* allocated.
* If DF_UNFORMATTED is set, all other flags are ignored,
* and the number is passed back as an optional negative
* followed by a string of digits
* If DF_ORDINAL is set in dwDisplayFlags, displays an
* ordinal number (i.e. tacks on "th" or the appropriate suffix.
* If DF_WHOLENUMBER is set, lops off the decimal separator
* and everything after it. If DF_WHOLENUMBER is not set,
* then uses the uiDecimalPlaces parameter to determine
* how many decimal places to display
* If DF_FIXEDWIDTH is set, will display at least uiFixedWidth
* digits; otherwise uiFixedWidth is ignored.
* If DF_NOTHOUSANDSGROUP is set, will not do thousands
* grouping (commas)
*************************************************************************/
HRESULT CSimpleITN::MakeDisplayNumber(DOUBLE dblNum,
DWORD dwDisplayFlags,
UINT uiFixedWidth,
UINT uiDecimalPlaces,
WCHAR *pwszNum,
UINT cSize )
{
SPDBG_ASSERT( pwszNum );
SPDBG_ASSERT( !SPIsBadWritePtr( pwszNum, cSize ) );
*pwszNum = 0;
// Get the default number formatting.
// Note that this gets called every time, since otherwise there
// is no way to pick up changes that the user has made since
// this process has started.
HRESULT hr = _EnsureNumberFormatDefaults();
if ( FAILED( hr ) )
{
return hr;
}
// check for straight millions and straight billions
// This is a workaround for the fact that we can't resolve the ambiguity
// and get "two million" to go through GRID_INTEGER_MILLBILL
if ( m_langid != 0x0411 )
{
if (( dwDisplayFlags & DF_WHOLENUMBER ) && ( dwDisplayFlags & DF_MILLIONBILLION ) && (dblNum > 0))
{
if ( 0 == (( ((LONGLONG) dblNum) % BILLION )) )
{
// e.g. for "five billion" get the "5" and then
// tack on " billion"
hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) BILLION) ),
dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize );
if ( SUCCEEDED( hr ) )
{
StringCchCatW( pwszNum, cSize, L" " );
StringCchCatW( pwszNum, cSize, BILLION_STR );
}
return hr;
}
else if (( ((LONGLONG) dblNum) < BILLION ) &&
( 0 == (( ((LONGLONG) dblNum) % MILLION )) ))
{
hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) MILLION) ),
dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize );
if ( SUCCEEDED( hr ) )
{
StringCchCatW( pwszNum, cSize, L" " );
StringCchCatW( pwszNum, cSize, MILLION_STR );
}
return hr;
}
}
}
else
{
if (( dwDisplayFlags & DF_WHOLENUMBER ) && (dblNum > 0))
{
if ( 0 == (( ((LONGLONG) dblNum) % CHUU )) )
{
hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) CHUU) ),
dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize);
if ( SUCCEEDED( hr ) )
{
StringCchCatW( pwszNum, cSize, CHUU_STR );
}
return hr;
}
else if (( ((LONGLONG) dblNum) < CHUU ) &&
( 0 == (( ((LONGLONG) dblNum) % OKU )) ))
{
hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) OKU) ),
dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize);
if ( SUCCEEDED( hr ) )
{
StringCchCatW( pwszNum, cSize, OKU_STR );
}
return hr;
}
else if (( ((LONGLONG) dblNum) < OKU ) &&
( 0 == (( ((LONGLONG) dblNum) % MANN )) ))
{
hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) MANN) ),
dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize);
if ( SUCCEEDED( hr ) )
{
StringCchCatW( pwszNum, cSize, MANN_STR );
}
return hr;
}
}
}
// Put in the negative sign if necessary
if ( dblNum < 0 )
{
StringCchCatW( pwszNum, cSize, L"-" );
// From now on we want to deal with the magnitude of the number
dblNum *= -1;
}
SPDBG_ASSERT(m_langid == 0x411 ? dblNum < 1e16 : dblNum < 1e12 );
WCHAR *pwszTemp = new WCHAR[ cSize ];
if ( !pwszTemp )
{
return E_OUTOFMEMORY;
}
*pwszTemp = 0;
LONGLONG llIntPart = (LONGLONG) dblNum;
UINT64 uiDigitsLeftOfDecimal;
if ( dwDisplayFlags & DF_WHOLENUMBER )
{
StringCchPrintfW( pwszTemp, cSize, L"%I64d", llIntPart );
uiDigitsLeftOfDecimal = wcslen( pwszTemp );
}
else
{
StringCchPrintfW( pwszTemp, cSize, L"%.*f", uiDecimalPlaces, dblNum );
WCHAR *pwc = wcschr( pwszTemp, L'.' );
uiDigitsLeftOfDecimal = pwc - pwszTemp;
}
// The following handles the case where the user said something
// like "zero zero zero three" and wants to see "0,003"
BOOL fChangedFirstDigit = false;
const WCHAR wcFakeFirstDigit = L'1';
if ( !(dwDisplayFlags & DF_UNFORMATTED) &&
(dwDisplayFlags & DF_FIXEDWIDTH) && (uiDigitsLeftOfDecimal < uiFixedWidth) )
{
// The following handles the case where the user wants leading
// zeroes displayed
// Need to pad the front with zeroes
for ( UINT ui = 0; ui < (uiFixedWidth - uiDigitsLeftOfDecimal); ui++ )
{
StringCchCatW( pwszNum, cSize, L"0" );
}
// HACK
// In order to force something like "zero zero zero three"
// into the form "0,003", we need to make GetNumberFormat()
// think that the first digit is 1.
WCHAR *pwc = wcschr( pwszNum, L'0' );
SPDBG_ASSERT( pwc );
*pwc = wcFakeFirstDigit;
fChangedFirstDigit = true;
}
// Copy over the unformatted number after the possible negative sign
StringCchCatW( pwszNum, cSize, pwszTemp );
delete[] pwszTemp;
// If we do not want to format the number, then bail here
if ( dwDisplayFlags & DF_UNFORMATTED )
{
return S_OK;
}
// Make a copy so that we can change some fields according to the
// flags param
NUMBERFMTW nmfmt = m_nmfmtDefault;
// How many decimal places to display?
if ( dwDisplayFlags & DF_WHOLENUMBER )
{
nmfmt.NumDigits = 0;
}
else
{
// Use the uiDecimalPlaces value to determine how
// many to display
nmfmt.NumDigits = uiDecimalPlaces;
}
// Leading zeroes?
nmfmt.LeadingZero = (dwDisplayFlags & DF_LEADINGZERO) ? 1 : 0;
// Thousands grouping?
if ( dwDisplayFlags & DF_NOTHOUSANDSGROUP )
{
nmfmt.Grouping = 0;
}
#if 0
if ( m_langid == 0x411)
{
// Format the number string
WCHAR *pwszFormattedNum = new WCHAR[ cSize ];
if ( !pwszFormattedNum )
{
return E_OUTOFMEMORY;
}
*pwszFormattedNum = 0;
int iRet;
do
{
iRet = GetNumberFormatW( m_langid, 0, pwszNum, &nmfmt, pwszFormattedNum, cSize );
if ( !iRet && nmfmt.NumDigits )
{
// Try displaying fewer digits
nmfmt.NumDigits--;
}
} while ( !iRet && nmfmt.NumDigits );
SPDBG_ASSERT( iRet );
// Copy the formatted number into pwszNum
StringCchCopyW( pwszNum, cSize, pwszFormattedNum );
delete[] pwszFormattedNum;
}
#endif
// This undoes the hack of changing the first digit
if ( fChangedFirstDigit )
{
// We need to find the first digit and change it back to zero
WCHAR *pwc = wcschr( pwszNum, wcFakeFirstDigit );
SPDBG_ASSERT( pwc );
*pwc = L'0';
}
if ( dwDisplayFlags & DF_ORDINAL )
{
SPDBG_ASSERT( dwDisplayFlags & DF_WHOLENUMBER ); // sanity
// This is an ordinal number, tack on the appropriate suffix
// The "st", "nd", "rd" endings only happen when you
// don't have something like "twelfth"
if ( ((llIntPart % 100) < 10) || ((llIntPart % 100) > 20) )
{
switch ( llIntPart % 10 )
{
case 1:
StringCchCatW( pwszNum, cSize, L"st" );
break;
case 2:
StringCchCatW( pwszNum, cSize, L"nd" );
break;
case 3:
StringCchCatW( pwszNum, cSize, L"rd" );
break;
default:
StringCchCatW( pwszNum, cSize, L"th" );
break;
}
}
else
{
StringCchCatW( pwszNum, cSize, L"th" );
}
}
return S_OK;
} /* MakeDisplayNumber */
/***********************************************************************
* ComputeNum999
*
* Description:
* Converts a set of SPPHRASEPROPERTYs into a number in
* [-999, 999].
* The way these properties is structured is that the top-level
* properties contain the place of the number (100s, 10s, 1s)
* by having the value 100, 10, or 1.
* The child has the appropriate number value.
* Return:
* Value of the number
*************************************************************************/
ULONG CSimpleITN::ComputeNum999En(const SPPHRASEPROPERTY *pProperties )
{
ULONG ulVal = 0;
if ( pProperties == NULL )
return ulVal;
for (const SPPHRASEPROPERTY * pProp = pProperties; pProp; pProp = pProp->pNextSibling)
{
if ( ZERO != pProp->ulId )
{
SPDBG_ASSERT( pProp->pFirstChild );
SPDBG_ASSERT( VT_UI4 == pProp->vValue.vt );
SPDBG_ASSERT( VT_UI4 == pProp->pFirstChild->vValue.vt );
ulVal += pProp->pFirstChild->vValue.ulVal * pProp->vValue.ulVal;
}
}
return ulVal;
}
// assume that we have only THOUSANDS(qian), HUNDREDS(bai), TENS(shi), and ONES(ge) here!!
ULONG CSimpleITN::ComputeNum9999Ch(const SPPHRASEPROPERTY *pProperties)
{
ULONG ulVal = 0;
if ( !pProperties )
return ulVal;
if (pProperties->pFirstChild)
{
for (const SPPHRASEPROPERTY * pProp = pProperties; pProp; pProp = pProp->pNextSibling)
{
if ( 0 != pProp->ulId )
{
SPDBG_ASSERT( pProp->pFirstChild );
SPDBG_ASSERT( VT_UI4 == pProp->vValue.vt );
SPDBG_ASSERT( VT_UI4 == pProp->pFirstChild->vValue.vt );
ulVal += pProp->pFirstChild->vValue.ulVal * pProp->vValue.ulVal;
}
}
}
return ulVal;
}
ULONG CSimpleITN::ComputeNum10000Ch(const SPPHRASEPROPERTY *pProperties)
{
ULONG ulVal = 0;
WCHAR * pszStopped;
if ( !pProperties )
return ulVal;
ulVal = wcstol(pProperties->pszValue, &pszStopped, 10);
return ulVal;
}
HRESULT CSimpleITN::InterpretNumberCh(const SPPHRASEPROPERTY *pProperties,
const bool fCardinal,
DOUBLE *pdblVal,
WCHAR *pszVal,
UINT cSize,
const bool fFinalDisplayFmt)
{
HRESULT hr = S_OK;
if ( !pdblVal || !pszVal || !pProperties)
{
return E_POINTER;
}
*pszVal = 0;
BOOL fNegative = FALSE;
const SPPHRASEPROPERTY *pFirstProp = pProperties;
// Handle negatives
if ( CHS_NEGATIVE == pFirstProp->ulId )
{
// There had better be more stuff following
SPDBG_ASSERT( pFirstProp->pNextSibling );
fNegative = TRUE;
pFirstProp = pFirstProp->pNextSibling;
TraceMsg(TF_GENERAL, "This is a minus number");
}
if ( pFirstProp->ulId == CHS_GRID_NUMBER )
{
TraceMsg(TF_GENERAL, "Number is interger");
SPDBG_ASSERT(pFirstProp->pFirstChild);
pFirstProp = pFirstProp->pFirstChild;
hr = InterpretIntegerCh(pFirstProp,
fCardinal,
pdblVal,
pszVal,
cSize,
fFinalDisplayFmt,
fNegative);
}
else if ( pFirstProp->ulId == CHS_GRID_DECIMAL )
{
TraceMsg(TF_GENERAL, "Number is floating pointer decimal");
SPDBG_ASSERT(pFirstProp->pFirstChild);
pFirstProp = pFirstProp->pFirstChild;
hr = InterpretDecimalCh(pFirstProp,
fCardinal,
pdblVal,
pszVal,
cSize,
fFinalDisplayFmt,
fNegative);
}
return hr;
}
HRESULT CSimpleITN::InterpretIntegerCh
( const SPPHRASEPROPERTY *pProperties,
const bool fCardinal,
DOUBLE *pdblVal,
WCHAR *pszVal,
UINT cSize,
const bool fFinalDisplayFmt,
BOOL fNegative)
{
__int64 ulValue = 0;
ULONG ulLength = 0;
HRESULT hr = S_OK;
if ( !pdblVal || !pszVal || !pProperties)
{
return E_POINTER;
}
*pszVal = 0;
const SPPHRASEPROPERTY *pFirstProp = pProperties;
if ( pFirstProp->ulId == CHS_DIGITS )
{
// This must be digit-by-digit case, specially handle it here.
for(const SPPHRASEPROPERTY * pProp=pFirstProp; pProp; pProp=pProp->pNextSibling)
{
ASSERT( pProp->ulId == CHS_DIGITS );
ASSERT( VT_UI4 == pProp->vValue.vt );
ulValue = ulValue * 10 + pProp->vValue.ulVal;
}
}
else
{
for (const SPPHRASEPROPERTY * pProp = pFirstProp; pProp; pProp ? pProp = pProp->pNextSibling : NULL)
{
switch(pProp->ulId)
{
case CHS_TENTHOUSANDS_:
{
__int64 v1 = 0;
_ASSERT(pProp);
SPDBG_ASSERT(pProp);
v1 = (__int64) ComputeNum10000Ch(pProp);
ulValue += v1 * 10000;
}
break;
case CHS_TENTHOUSANDS:
{
__int64 v1 = 0;
_ASSERT(pProp->pFirstChild);
SPDBG_ASSERT(pProp->pFirstChild);
v1 = (__int64) ComputeNum9999Ch(pProp->pFirstChild);
ulValue += v1 * 10000;
}
break;
case CHS_HUNDREDMILLIONS:
{
__int64 v1 = 0;
_ASSERT(pProp->pFirstChild);
SPDBG_ASSERT(pProp->pFirstChild);
v1 = (__int64) ComputeNum9999Ch(pProp->pFirstChild);
ulValue += v1 * 100000000;
}
break;
case CHS_ONES:
{
__int64 v1 = 0;
SPDBG_ASSERT(pProp->pFirstChild);
v1 = (__int64) ComputeNum9999Ch(pProp->pFirstChild);
ulValue += v1;
pProp = NULL;
}
break;
case CHS_ONES_THOUSANDS:
{
SPDBG_ASSERT(pProp->pFirstChild);
ulValue += pProp->pFirstChild->vValue.ulVal;
pProp = NULL;
}
break;
case CHS_THOUSANDS:
case CHS_HUNDREDS:
default:
_ASSERT(false);
SPDBG_ASSERT(false);
}
}
}
if ( fNegative )
ulValue *= (-1);
*pdblVal = (DOUBLE) ulValue;
DWORD dwDisplayFlags = (fCardinal ? 0 : DF_ORDINAL);
return MakeDisplayNumber( *pdblVal, dwDisplayFlags, 0, 0, pszVal, cSize );
}
HRESULT CSimpleITN::InterpretDecimalCh
( const SPPHRASEPROPERTY *pProperties,
const bool fCardinal,
DOUBLE *pdblVal,
WCHAR *pszVal,
UINT cSize,
const bool fFinalDisplayFmt,
BOOL fNegative)
{
HRESULT hr = S_OK;
const SPPHRASEPROPERTY *pPropertiesInteger = NULL;
const SPPHRASEPROPERTY *pPropertiesPtr;
if ( !pdblVal || !pszVal || !pProperties)
{
return E_POINTER;
}
*pszVal = 0;
pPropertiesPtr = pProperties;
SPDBG_ASSERT( pPropertiesPtr->ulId == CHS_INTEGER);
pPropertiesInteger = pPropertiesPtr;
SPDBG_ASSERT(pPropertiesInteger->pFirstChild);
hr = InterpretIntegerCh( pPropertiesInteger->pFirstChild,
fCardinal,
pdblVal,
pszVal,
cSize,
fFinalDisplayFmt,
FALSE);
if ( hr == S_OK )
{
// Put in a decimal separator
if ( m_nmfmtDefault.lpDecimalSep )
{
if ( (cSize - wcslen( pszVal )) < (wcslen(m_nmfmtDefault.lpDecimalSep) + 1) )
{
return E_INVALIDARG;
}
StringCchCatW( pszVal, cSize, m_nmfmtDefault.lpDecimalSep);
}
// Deal with the FP part, which will also have been ITNed correctly
INT ulSize = cSize - wcslen(pszVal);
if ( ulSize < 0 )
return E_FAIL;
WCHAR *pwszFpValue = new WCHAR[ulSize+1];
if ( pwszFpValue )
{
DOUBLE dblFPPart = 0;
for(pPropertiesPtr=pPropertiesPtr->pNextSibling; pPropertiesPtr; pPropertiesPtr=pPropertiesPtr->pNextSibling)
{
if ( pPropertiesPtr->ulId == CHS_DIGITS )
{
SPDBG_ASSERT( VT_UI4 == pPropertiesPtr->vValue.vt );
dblFPPart = dblFPPart * 10 + pPropertiesPtr->vValue.ulVal;
StringCchCatW(pwszFpValue, ulSize + 1, pPropertiesPtr->pszValue);
}
}
StringCchCatW( pszVal, cSize, pwszFpValue);
for ( UINT ui=0; ui < wcslen(pwszFpValue); ui++ )
{
dblFPPart /= (DOUBLE) 10;
}
*pdblVal += dblFPPart;
delete[] pwszFpValue;
}
else
hr = E_OUTOFMEMORY;
}
// Handle the negative sign
if ( (hr == S_OK) && fNegative)
{
*pdblVal = -*pdblVal;
if ( (cSize = wcslen( pszVal )) < 2 )
{
return E_INVALIDARG;
}
hr = MakeNumberNegative( pszVal, cSize );
}
return hr;
}
// For Japanese.
/***********************************************************************
* CSimpleITN::InterpretNumberJp *
*-----------------------------*
* Description:
* Interprets a number in the range -999,999,999,999 to
* +999,999,999,999 and sends the properties and
* replacements to the CFGInterpreterSite as appropriate.
* The property will be added and the pszValue will be a string
* with the correct display number.
* If fCardinal is set, makes the display a cardinal number;
* otherwise makes it an ordinal number.
* The number will be formatted only if it was a properly-formed
* number (not given digit by digit).
* Result:
*************************************************************************/
HRESULT CSimpleITN::InterpretNumberJp(const SPPHRASEPROPERTY *pProperties,
const bool fCardinal,
DOUBLE *pdblVal,
WCHAR *pszVal,
UINT cSize,
const bool fFinalDisplayFmt)
{
HRESULT hr = S_OK;
if ( !pdblVal || !pszVal || !pProperties)
{
return E_POINTER;
}
*pszVal = 0;
BOOL fNegative = FALSE;
const SPPHRASEPROPERTY *pFirstProp = pProperties;
// Handle negatives
if ( JPN_NEGATIVE == pFirstProp->ulId )
{
// There's no such thing as a negative ordinal
SPDBG_ASSERT( fCardinal );
// There had better be more stuff following
SPDBG_ASSERT( pFirstProp->pNextSibling );
fNegative = TRUE;
pFirstProp = pFirstProp->pNextSibling;
}
if ( pFirstProp->ulId == JPN_GRID_INTEGER_STANDALONE )
{
TraceMsg(TF_GENERAL, "Number is Japanese Interger");
SPDBG_ASSERT(pFirstProp->pFirstChild);
pFirstProp = pFirstProp->pFirstChild;
hr = InterpretIntegerJp(pFirstProp,
fCardinal,
pdblVal,
pszVal,
cSize,
fFinalDisplayFmt,
fNegative);
}
else
{
TraceMsg(TF_GENERAL, "Number is Japanese Floating pointer number");
SPDBG_ASSERT(pFirstProp->pFirstChild);
pFirstProp = pFirstProp->pFirstChild;
hr = InterpretDecimalJp(pFirstProp,
fCardinal,
pdblVal,
pszVal,
cSize,
fFinalDisplayFmt,
fNegative);
}
return hr;
} /* CSimpleITN::InterpretNumberJp */
/***********************************************************************
* ComputeNum9999Jp *
*----------------*
* Description:
* Converts a set of SPPHRASEPROPERTYs into a number in
* [-9999, 9999].
* The way these properties is structured is that the top-level
* properties contain the place of the number (100s, 10s, 1s)
* by having the value 100, 10, or 1.
* The child has the appropriate number value.
* Return:
* Value of the number
*************************************************************************/
ULONG CSimpleITN::ComputeNum9999Jp(const SPPHRASEPROPERTY *pProperties )//, ULONG *pVal)
{
ULONG ulVal = 0;
if ( !pProperties )
return ulVal;
for (const SPPHRASEPROPERTY * pProp = pProperties; pProp; pProp = pProp->pNextSibling)
{
if ( JPN_ZERO != pProp->ulId )
{
SPDBG_ASSERT( VT_UI4 == pProp->vValue.vt );
ulVal += pProp->vValue.ulVal;
}
}
return ulVal;
} /* ComputeNum9999Jp */
HRESULT CSimpleITN::InterpretIntegerJp
( const SPPHRASEPROPERTY *pProperties,
const bool fCardinal,
DOUBLE *pdblVal,
WCHAR *pszVal,
UINT cSize,
const bool fFinalDisplayFmt,
BOOL fNegative)
{
HRESULT hr = S_OK;
if ( !pdblVal || !pszVal || !pProperties)
{
return E_POINTER;
}
*pszVal = 0;
LONGLONG llValue = 0;
const SPPHRASEPROPERTY *pFirstProp = pProperties;
// Handle the digit-by-digit case
if ( JPN_GRID_DIGIT_NUMBER == pFirstProp->ulId )
{
UINT uiFixedWidth = 0;
DOUBLE dblVal = 0;
for(const SPPHRASEPROPERTY * pPropertiesPtr=pFirstProp->pFirstChild; pPropertiesPtr; pPropertiesPtr=pPropertiesPtr->pNextSibling)
{
if ( pPropertiesPtr->ulId == JPN_DIGIT )
{
SPDBG_ASSERT( VT_UI4 == pPropertiesPtr->vValue.vt );
dblVal = dblVal * 10 + pPropertiesPtr->vValue.ulVal;
uiFixedWidth ++;
}
}
if ( fNegative )
dblVal *= (-1);
*pdblVal = dblVal;
DWORD dwDisplayFlags = DF_WHOLENUMBER | DF_FIXEDWIDTH | DF_NOTHOUSANDSGROUP;
return MakeDisplayNumber( *pdblVal, dwDisplayFlags,
uiFixedWidth, 0, pszVal, MAX_PATH);
}
for (const SPPHRASEPROPERTY * pProp = pFirstProp; pProp; pProp ? pProp = pProp->pNextSibling : NULL)
{
switch(pProp->ulId)
{
case JPN_ICHIs:
{
SPDBG_ASSERT(pProp->pFirstChild);
llValue += ComputeNum9999Jp( pProp->pFirstChild );
}
break;
case JPN_MANNs:
{
llValue += ComputeNum9999Jp( pProp->pFirstChild ) * 10000;
}
break;
case JPN_OKUs:
{
SPDBG_ASSERT(pProp->pFirstChild);
llValue += ComputeNum9999Jp( pProp->pFirstChild ) * (LONGLONG) 1e8;
}
break;
case JPN_CHOOs:
{
SPDBG_ASSERT(pProp->pFirstChild);
llValue += ComputeNum9999Jp( pProp->pFirstChild ) * (LONGLONG) 1e12;
}
break;
default:
SPDBG_ASSERT(false);
}
}
if ( fNegative )
llValue *= (-1);
*pdblVal = (DOUBLE) llValue;
DWORD dwDisplayFlags = (fCardinal ? 0 : DF_ORDINAL);
//Special code to handle minus 0.
if ((fNegative) && (*pdblVal == 0.0f))
{
*pszVal = L'-';
*(pszVal+1) = 0;
hr = MakeDisplayNumber( *pdblVal, dwDisplayFlags, 0, 0, pszVal+1, cSize-1);
}
else
{
hr = MakeDisplayNumber( *pdblVal, dwDisplayFlags, 0, 0, pszVal, cSize);
}
return hr;
}
HRESULT CSimpleITN::InterpretDecimalJp
( const SPPHRASEPROPERTY *pProperties,
const bool fCardinal,
DOUBLE *pdblVal,
WCHAR *pszVal,
UINT cSize,
const bool fFinalDisplayFmt,
BOOL fNegative)
{
HRESULT hr = S_OK;
UINT uiFixedWidth = 0;
DWORD dwDisplayFlags = 0;
UINT uiDecimalPlaces = 0;
BOOL bOverWriteNOTHOUSANDSGROUP = FALSE;
const SPPHRASEPROPERTY *pPropertiesInteger = NULL;
const SPPHRASEPROPERTY *pPropertiesPtr;
if ( !pdblVal || !pszVal || !pProperties)
{
return E_POINTER;
}
*pszVal = 0;
pPropertiesPtr = pProperties;
*pdblVal = 0;
if (m_nmfmtDefault.LeadingZero)
{
dwDisplayFlags |= DF_LEADINGZERO;
}
if ( JPN_ICHIs == pPropertiesPtr->ulId )
{
pPropertiesInteger = pPropertiesPtr;
SPDBG_ASSERT(pPropertiesInteger->pFirstChild);
hr = InterpretIntegerJp( pPropertiesInteger->pFirstChild,
fCardinal,
pdblVal,
pszVal,
cSize,
fFinalDisplayFmt,
FALSE);
if ( hr == S_OK )
{
dwDisplayFlags |= DF_FIXEDWIDTH;
const WCHAR *pwc;
for ( uiFixedWidth = 0, pwc = pszVal; *pwc; pwc++ )
{
if ( iswdigit( *pwc ) )
{
uiFixedWidth++;
}
}
if (!iswdigit( pszVal[wcslen(pszVal) - 1] ))
{
//Ends with Mann, Choo,..
bOverWriteNOTHOUSANDSGROUP = TRUE;
}
// This needs to be here in case the user said "zero"
dwDisplayFlags |= DF_LEADINGZERO;
// If there is no thousands separator in its string value,
// then leave out the thousands separator in the result
if (m_nmfmtDefault.lpThousandSep && (NULL == wcsstr(pszVal, m_nmfmtDefault.lpThousandSep)) && !bOverWriteNOTHOUSANDSGROUP)
{
dwDisplayFlags |= DF_NOTHOUSANDSGROUP;
}
pPropertiesPtr = pPropertiesPtr->pNextSibling;
}
}
if ( hr == S_OK )
{
// Deal with the FP part, which will also have been ITNed correctly
if ( pPropertiesPtr && (JPN_FP_PART == pPropertiesPtr->ulId) ){
DOUBLE dblFPPart = 0;
uiDecimalPlaces = 0;
for(pPropertiesPtr=pPropertiesPtr->pFirstChild; pPropertiesPtr; pPropertiesPtr=pPropertiesPtr->pNextSibling)
{
if ( pPropertiesPtr->ulId == JPN_DIGIT )
{
SPDBG_ASSERT( VT_UI4 == pPropertiesPtr->vValue.vt );
dblFPPart = dblFPPart * 10 + pPropertiesPtr->vValue.ulVal;
uiDecimalPlaces ++;
}
}
for ( UINT ui=0; ui < uiDecimalPlaces; ui++ )
{
dblFPPart /= (DOUBLE) 10;
}
*pdblVal += dblFPPart;
}
else if ( pPropertiesPtr && (JPN_FP_PART_D == pPropertiesPtr->ulId) ){
// The user said "point" and one digit
SPDBG_ASSERT( VT_UI4 == pPropertiesPtr->pFirstChild->vValue.vt );
uiDecimalPlaces = 1;
if ( *pdblVal >= 0 )
{
*pdblVal += pPropertiesPtr->pFirstChild->vValue.iVal / 10.0;
}
else
{
*pdblVal -= pPropertiesPtr->pFirstChild->vValue.iVal / 10.0;
}
}
}
// Handle the negative sign
if ( (hr == S_OK) && fNegative)
{
*pdblVal = -*pdblVal;
}
hr = MakeDisplayNumber( *pdblVal, dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pszVal, cSize);
return hr;
}