|
|
#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;
}
|