|
|
// TestITN_J.cpp : Implementation of CTestITN_J
#include "stdafx.h"
#include <winnls.h>
#include "Itngram_J.h"
#include "TestITN_J.h"
#include "sphelper.h"
#include "spddkhlp.h"
#include "test_j.h"
#define MAX_SIG_FIGS 12
#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")
const WCHAR s_pwszDegree[] = { 0xb0, 0 }; const WCHAR s_pwszMinute[] = { 0x2032, 0 }; const WCHAR s_pwszSecond[] = { 0x2033, 0 };
#define DAYOFWEEK_STR_ABBR ("ddd")
#define DAYOFWEEK_STR ("dddd")
/////////////////////////////////////////////////////////////////////////////
// CTestITN_J
/****************************************************************************
* CTestITN_J::InitGrammar * *-------------------------* * Description: * * Returns: * ********************************************************************* RAL ***/
STDMETHODIMP CTestITN_J::InitGrammar(const WCHAR * pszGrammarName, const void ** pvGrammarData) { HRESULT hr = S_OK; HRSRC hResInfo = ::FindResource(_Module.GetModuleInstance(), _T("TEST"), _T("ITNGRAMMAR")); if (hResInfo) { HGLOBAL hData = ::LoadResource(_Module.GetModuleInstance(), hResInfo); if (hData) { *pvGrammarData = ::LockResource(hData); if (*pvGrammarData == NULL) { hr = HRESULT_FROM_WIN32(::GetLastError()); } } else { hr = HRESULT_FROM_WIN32(::GetLastError()); } } else { hr = HRESULT_FROM_WIN32(::GetLastError()); }
return hr; }
/****************************************************************************
* CTestITN_J::Interpret * *-----------------------* * Description: * * Returns: * ********************************************************************* RAL ***/
STDMETHODIMP CTestITN_J::Interpret(ISpPhraseBuilder * pPhrase, const ULONG ulFirstElement, const ULONG ulCountOfElements, ISpCFGInterpreterSite * pSite) { HRESULT hr = S_OK; ULONG ulRuleId = 0; CSpPhrasePtr cpPhrase; hr = pPhrase->GetPhrase(&cpPhrase);
m_pSite = pSite;
//Just use ulFirstElement & ulCountOfElements
// Get the minimum and maximum positions
ULONG ulMinPos; ULONG ulMaxPos; //GetMinAndMaxPos( cpPhrase->pProperties, &ulMinPos, &ulMaxPos );
ulMinPos = ulFirstElement; ulMaxPos = ulMinPos + ulCountOfElements;
if (SUCCEEDED(hr)) { hr = S_FALSE;
WCHAR pszValueBuff[ MAX_PATH ]; // No ITN result should be longer than this
DOUBLE dblValue; // All ITN results will have a 64-bit value
pszValueBuff[0] = 0;
switch (cpPhrase->Rule.ulId) { case GRID_INTEGER_STANDALONE: // Fired as a toplevel rule
hr = InterpretNumber( cpPhrase->pProperties, true, &dblValue, pszValueBuff, MAX_PATH ); if (SUCCEEDED( hr ) && ( dblValue >= 0 ) && ( dblValue <= 20 ) && ( GRID_DIGIT_NUMBER != cpPhrase->pProperties->ulId )) { // Throw this one out because numbers like "three"
// shouldn't be ITNed by themselves
hr = S_FALSE; goto Cleanup; // no replacement
} break; case GRID_INTEGER: case GRID_INTEGER_9999: case GRID_ORDINAL:// Number
hr = InterpretNumber( cpPhrase->pProperties, true, &dblValue, pszValueBuff, MAX_PATH ); break;
case GRID_DIGIT_NUMBER: // Number "spelled out" digit by digit
hr = InterpretDigitNumber( cpPhrase->pProperties, &dblValue, pszValueBuff, MAX_PATH ); break;
case GRID_FP_NUMBER: hr = InterpretFPNumber( cpPhrase->pProperties, &dblValue, pszValueBuff, MAX_PATH ); break;
case GRID_DENOMINATOR: hr = InterpretNumber( cpPhrase->pProperties, true, &dblValue, pszValueBuff, MAX_PATH ); break;
case GRID_FRACTION: hr = InterpretFraction( cpPhrase->pProperties, &dblValue, pszValueBuff, MAX_PATH ); break;
case GRID_DATE: hr = InterpretDate( cpPhrase->pProperties, &dblValue, pszValueBuff, MAX_PATH ); break;
case GRID_CURRENCY: // Currency
hr = InterpretCurrency( cpPhrase->pProperties, &dblValue, pszValueBuff, MAX_PATH ); break; case GRID_TIME: hr = InterpretTime( cpPhrase->pProperties, &dblValue, pszValueBuff, MAX_PATH ); break; case GRID_DEGREES: hr = InterpretDegrees( cpPhrase->pProperties, &dblValue, pszValueBuff, MAX_PATH ); break;
case GRID_MEASUREMENT: hr = InterpretMeasurement( cpPhrase->pProperties, &dblValue, pszValueBuff, MAX_PATH ); break; default: _ASSERT(FALSE); break; }
if ( SUCCEEDED( hr ) ) { hr = AddPropertyAndReplacement( pszValueBuff, dblValue, ulMinPos, ulMaxPos, ulMinPos, ulMaxPos - ulMinPos );//ulFirstElement, ulCountOfElements );
return hr; }
}
Cleanup:
return S_FALSE; }
/***********************************************************************
* CTestITN_J::InterpretNumber * *-----------------------------* * 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 CTestITN_J::InterpretNumber(const SPPHRASEPROPERTY *pProperties, const bool fCardinal, DOUBLE *pdblVal, WCHAR *pszVal, UINT cSize) { if ( !pdblVal || !pszVal ) { return E_POINTER; }
LONGLONG llValue = 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; }
// Handle the digit-by-digit case
if ( GRID_DIGIT_NUMBER == pFirstProp->ulId ) { // There had better be nothing following this
SPDBG_ASSERT( !pFirstProp->pNextSibling ); SPDBG_ASSERT( VT_R8 == pFirstProp->vValue.vt ); SPDBG_ASSERT( pFirstProp->pszValue );
DOUBLE dblVal = pFirstProp->vValue.dblVal; UINT uiFixedWidth = wcslen( pFirstProp->pszValue );
*pdblVal = dblVal * iPositive;
DWORD dwDisplayFlags = DF_WHOLENUMBER | DF_FIXEDWIDTH | DF_NOTHOUSANDSGROUP; return MakeDisplayNumber( *pdblVal, dwDisplayFlags, uiFixedWidth, 0, pszVal, MAX_PATH, FALSE ); }
for (const SPPHRASEPROPERTY * pProp = pFirstProp; pProp; pProp ? pProp = pProp->pNextSibling : NULL) { switch(pProp->ulId) { case ICHIs: { SPDBG_ASSERT(pProp->pFirstChild); llValue += ComputeNum9999( pProp->pFirstChild ); } break; case MANNs: { llValue += ComputeNum9999( pProp->pFirstChild ) * 10000; } break; case OKUs: { SPDBG_ASSERT(pProp->pFirstChild); llValue += ComputeNum9999( pProp->pFirstChild ) * (LONGLONG) 1e8; } break; case CHOOs: { SPDBG_ASSERT(pProp->pFirstChild); llValue += ComputeNum9999( pProp->pFirstChild ) * (LONGLONG) 1e12; } break; default: SPDBG_ASSERT(false); } }
llValue *= iPositive;
*pdblVal = (DOUBLE) llValue;
#if 0
if ( !pProperties->pNextSibling && ( (BILLIONS == pProperties->ulId) || (MILLIONS == pProperties->ulId) ) ) { // This is something like "3 billion", which should be displayed that way
return E_NOTIMPL; } else #endif
{ DWORD dwDisplayFlags = DF_WHOLENUMBER | (fCardinal ? 0 : DF_ORDINAL); //Special code to handle minus 0.
if ((iPositive == -1) && (*pdblVal == 0.0f)) { *pszVal = L'-'; *(pszVal+1) = 0; return MakeDisplayNumber( *pdblVal, dwDisplayFlags, 0, 0, pszVal+1, cSize-1, FALSE ); } else { return MakeDisplayNumber( *pdblVal, dwDisplayFlags, 0, 0, pszVal, cSize, FALSE ); } }
} /* CTestITN_J::InterpretNumber */
/***********************************************************************
* CTestITN_J::InterpretDigitNumber * *----------------------------------* * Description: * Interprets an integer in (-INF, +INF) that has been spelled * out digit by digit. * Result: *************************************************************************/ HRESULT CTestITN_J::InterpretDigitNumber( const SPPHRASEPROPERTY *pProperties, DOUBLE *pdblVal, WCHAR *pszVal, UINT cSize) { if ( !pdblVal || !pszVal ) { return E_POINTER; }
BOOL fPositive = TRUE; ULONG ulLength = 0; *pszVal = 0; WCHAR *pwc = pszVal; UINT cLen = 0; for (const SPPHRASEPROPERTY * pProp = pProperties; pProp && (cLen < cSize); pProp ? pProp = pProp->pNextSibling : NULL) { switch(pProp->ulId) { case NEGATIVE: { SPDBG_ASSERT( pProp == pProperties );
fPositive = FALSE; *pwc++ = L'-'; cLen++; break; } case DIGIT: { *pwc++ = L'0' + pProp->vValue.iVal; cLen++; break; } default: SPDBG_ASSERT(false); } } *pwc = 0; SPDBG_ASSERT( cLen <= MAX_SIG_FIGS );
*pdblVal = (DOUBLE) _wtoi64( pszVal );
return S_OK; } /* CTestITN_J::InterpretDigitNumber */
/***********************************************************************
* CTestITN_J::InterpretFPNumber * *-------------------------------* * Description: * Interprets a floating-point number of up to MAX_SIG_FIGS sig * figs. Truncates the floating-point part as necessary * The way the grammar is structured, there will be an optional * ONES property, whose value will already have been interpreted, * as well as a mandatory FP_PART property, whose value will * be divided by the appropriate multiple of 10. * Result: * Return value of CTestITN_J::AddPropertyAndReplacement() *************************************************************************/ HRESULT CTestITN_J::InterpretFPNumber( const SPPHRASEPROPERTY *pProperties, DOUBLE *pdblVal, WCHAR *pszVal, UINT cSize) { if ( !pdblVal || !pszVal ) { return E_POINTER; } SPDBG_ASSERT( pProperties );
UINT uiSigFigs = 0; *pdblVal = 0; *pszVal = 0; DWORD dwDisplayFlags = 0; BOOL bOverWriteNOTHOUSANDSGROUP = FALSE;
const SPPHRASEPROPERTY *pProp = pProperties; UINT uiFixedWidth = 0;
TCHAR pwszLocaleData[ MAX_LOCALE_DATA ]; int iRet = ::GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_ILZERO, pwszLocaleData, MAX_LOCALE_DATA ); if ( !iRet ) { return E_FAIL; } if (atoi( pwszLocaleData )) { dwDisplayFlags |= DF_LEADINGZERO; } // Minus sign?
if (NEGATIVE == pProp->ulId ) { uiSigFigs = 1; // Go to the next property
pProp = pProp->pNextSibling; } // ONES is optional since "point five" should be ".5" if the user perfers
if ( ICHIs == pProp->ulId ) { // Get the value
SPDBG_ASSERT( VT_R8 == pProp->vValue.vt ); *pdblVal = pProp->vValue.dblVal; uiSigFigs = (pProp->pszValue[0] == L'-'); // Have to take care of the case of -0.05
if (uiSigFigs) { *pdblVal = -*pdblVal; } // Count up the width of the number and set the fixed width flag
dwDisplayFlags |= DF_FIXEDWIDTH; const WCHAR *pwc; for ( uiFixedWidth = 0, pwc = pProp->pszValue; *pwc; pwc++ ) { if ( iswdigit( *pwc ) ) { uiFixedWidth++; } } if (!iswdigit( pProp->pszValue[wcslen(pProp->pszValue) - 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
USES_CONVERSION; TCHAR pszThousandSep[ MAX_LOCALE_DATA ]; ::GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousandSep, MAX_LOCALE_DATA ); if ( NULL == wcsstr( pProp->pszValue, T2W( pszThousandSep ) ) && !bOverWriteNOTHOUSANDSGROUP) { dwDisplayFlags |= DF_NOTHOUSANDSGROUP; }
// Go to the next property
pProp = pProp->pNextSibling; } else if ( ZERO == pProp->ulId ) { // "oh point..."
// This will force a leading zero
dwDisplayFlags |= DF_LEADINGZERO; pProp = pProp->pNextSibling; }
UINT uiDecimalPlaces = 0; if ( pProp && (FP_PART == pProp->ulId) ) { // Deal with the stuff to the right of the decimal point
// Count up the number of decimal places, and for each
// decimal place divide the value by 10
// (e.g. 83 gets divided by 100).
SPDBG_ASSERT( VT_R8 == pProp->vValue.vt ); DOUBLE dblRightOfDecimal = pProp->vValue.dblVal; const WCHAR *pwc; for ( uiDecimalPlaces = 0, pwc = pProp->pszValue; *pwc; pwc++ ) { if ( iswdigit( *pwc ) ) { dblRightOfDecimal /= (DOUBLE) 10; uiDecimalPlaces++; } }
*pdblVal += dblRightOfDecimal;
} else if ( pProp && (FP_PART_D == pProp->ulId) ) { // The user said "point" and one digit
SPDBG_ASSERT( VT_UI4 == pProp->pFirstChild->vValue.vt ); uiDecimalPlaces = 1; if ( *pdblVal >= 0 ) { *pdblVal += pProp->pFirstChild->vValue.iVal / 10.0; } else { *pdblVal -= pProp->pFirstChild->vValue.iVal / 10.0; } }
if (uiSigFigs) { *pdblVal = -*pdblVal; } MakeDisplayNumber( *pdblVal, dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pszVal, cSize, FALSE );
return S_OK; } /* CTestITN_J::InterpretFPNumber */
/***********************************************************************
* CTestITN_J::InterpretFraction * *-------------------------------* * Description: * Interprets a fraction. * The DENOMINATOR property should be present. * If the NUMERATOR property is absent, it is assumed to be 1. * Divides the NUMERATOR by the DENOMINATOR and sets the value * accordingly. *************************************************************************/ HRESULT CTestITN_J::InterpretFraction( const SPPHRASEPROPERTY *pProperties, DOUBLE *pdblVal, WCHAR *pszVal, UINT cSize) { if ( !pdblVal || !pszVal ) { return E_POINTER; } SPDBG_ASSERT( pProperties ); DOUBLE dblWholeValue = 0; DOUBLE dblFracValue = 1; BOOL bNegativeDenominator = FALSE; WCHAR wszBuffer[MAX_PATH];
const SPPHRASEPROPERTY *pProp = pProperties; *pszVal = 0;
// Space to store whatever characters follow the digits
// in the numerator (like ")")
WCHAR pszTemp[ 10 ]; // will never need this much
pszTemp[0] = 0;
// Whole part is optional, otherwise assumed to be 0
if ( WHOLE == pProp->ulId ) { SPDBG_ASSERT( VT_R8 == pProp->vValue.vt ); dblWholeValue = pProp->vValue.dblVal; wcscpy( pszVal, pProp->pszValue );
// Do we need to re-generate the numbers?
if (!iswdigit( pszVal[wcslen(pszVal) - 1] )) { MakeDisplayNumber( dblWholeValue, DF_WHOLENUMBER, 0, 0, pszVal, MAX_PATH, TRUE ); } // Add a space between the whole part and the fractional part
wcscat( pszVal, L" " );
SPDBG_ASSERT( pProp->pNextSibling ); pProp = pProp->pNextSibling; }
// Nothing is optional in Japanese, However, the order is different from English
SPDBG_ASSERT( DENOMINATOR == pProp->ulId ); // Look ahead to see if it is a nagative number
bNegativeDenominator = (pProp->vValue.dblVal < 0);
for( pProp = pProperties; NUMERATOR != pProp->ulId; pProp = pProp->pNextSibling ); if( NUMERATOR == pProp->ulId) { SPDBG_ASSERT( VT_R8 == pProp->vValue.vt ); dblFracValue = pProp->vValue.dblVal; if (bNegativeDenominator && (pProp->vValue.dblVal >= 0)) { //put the minus sign here.
wcscat( pszVal, L"-"); bNegativeDenominator = FALSE; } // Do we need to re-generate the numbers?
if (!iswdigit( pProp->pszValue[wcslen(pProp->pszValue) - 1] )) { MakeDisplayNumber( dblFracValue, DF_WHOLENUMBER, 0, 0, wszBuffer, MAX_PATH, TRUE ); wcscat( pszVal, wszBuffer ); } else { wcscat( pszVal, pProp->pszValue ); }
} else { // No numerator, assume 1
wcscat( pszVal, L"1" ); }
wcscat( pszVal, L"/" );
for( pProp = pProperties; DENOMINATOR != pProp->ulId; pProp = pProp->pNextSibling );
SPDBG_ASSERT( DENOMINATOR == pProp->ulId ); SPDBG_ASSERT( VT_R8 == pProp->vValue.vt ); if ( 0 == pProp->vValue.dblVal ) { // Will not ITN a fraction with a zero denominator
return E_FAIL; }
dblFracValue /= pProp->vValue.dblVal; if (!bNegativeDenominator && (pProp->vValue.dblVal<0)) { // Do we need to re-generate the numbers?
if (!iswdigit( pProp->pszValue[wcslen(pProp->pszValue) - 1] )) { MakeDisplayNumber( -pProp->vValue.dblVal, DF_WHOLENUMBER, 0, 0, wszBuffer, MAX_PATH, TRUE ); wcscat( pszVal, wszBuffer ); } else { wcscat( pszVal, pProp->pszValue+1 ); } } else { // Do we need to re-generate the numbers?
if (!iswdigit( pProp->pszValue[wcslen(pProp->pszValue) - 1] )) { MakeDisplayNumber( pProp->vValue.dblVal, DF_WHOLENUMBER, 0, 0, wszBuffer, MAX_PATH, TRUE ); wcscat( pszVal, wszBuffer ); } else { wcscat( pszVal, pProp->pszValue ); }
}
// Tack on the ")" or "-" from the end of the numerator
wcscat( pszVal, pszTemp );
*pdblVal = dblWholeValue + dblFracValue; return S_OK; } /* CTestITN_J::InterpretFraction */
/***********************************************************************
* CTestITN_J::InterpretDate * *---------------------------* * Description: * Interprets a date. * Converts the date into a VT_DATE format, even though it * gets stored as a VT_R8 (both are 64-bit quantities). * The Japanese Grammar will not accept invalid date. *************************************************************************/ HRESULT CTestITN_J::InterpretDate( const SPPHRASEPROPERTY *pProperties, DOUBLE *pdblVal, WCHAR *pszVal, UINT cSize) { if ( !pdblVal || !pszVal ) { return E_POINTER; }
*pszVal = 0;
// Get the date formatting string to be used right now
if ( 0 == ::GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SLONGDATE, m_pszLongDateFormat, MAX_DATE_FORMAT ) ) { return E_FAIL; } SYSTEMTIME stDate; memset( (void *) &stDate, 0, sizeof( stDate ) );
// Arguments for ::GetDateFormat()
TCHAR *pszFormatArg = NULL; TCHAR pszFormat[ MAX_DATE_FORMAT ]; const SPPHRASEPROPERTY *pProp; const SPPHRASEPROPERTY *pPropChild; const WCHAR* pwszEmperor; // Get the month
for ( pProp = pProperties; pProp && ( GATSU != pProp->ulId ); pProp = pProp->pNextSibling ) ; SPDBG_ASSERT( pProp ); //There should be a month, and the grammar is forcing it
SPDBG_ASSERT( pProp->pFirstChild ); pPropChild = pProp->pFirstChild; SPDBG_ASSERT( VT_UI4 == pPropChild->vValue.vt ); SPDBG_ASSERT( (1 <= pPropChild->vValue.ulVal) && (12 >= pPropChild->vValue.ulVal) ); if ( (pPropChild->vValue.ulVal < 1) || (pPropChild->vValue.ulVal > 12) ) { return E_INVALIDARG; } stDate.wMonth = (WORD) pPropChild->vValue.ulVal;
// Get the emperor's name
for ( pProp = pProperties; pProp && ( IMPERIAL != pProp->ulId ); pProp = pProp->pNextSibling ) ; if ( pProp ) { SPDBG_ASSERT( pProp ); pPropChild = pProp->pFirstChild; pwszEmperor = pPropChild->pszValue; } else { pwszEmperor = 0; }
// Get the year
for ( pProp = pProperties; pProp && ( NENN != pProp->ulId ); pProp = pProp->pNextSibling ) ; const SPPHRASEPROPERTY * const pPropYear = pProp; if ( pProp ) { SPDBG_ASSERT( pProp ); // There should be a year
SPDBG_ASSERT( VT_R8 == pProp->vValue.vt ); stDate.wYear = (WORD) pProp->vValue.dblVal; }
// Attempt to get the day of month
for ( pProp = pProperties; pProp && ( NICHI != pProp->ulId ); pProp = pProp->pNextSibling ) ; const SPPHRASEPROPERTY * const pPropDayOfMonth = pProp; if ( pProp ) { pPropChild = pProp->pFirstChild; SPDBG_ASSERT( VT_UI4 == pPropChild->vValue.vt ); SPDBG_ASSERT( (1 <= pPropChild->vValue.ulVal) && (31 >= pPropChild->vValue.ulVal) ); if ( (pPropChild->vValue.ulVal < 1) || (pPropChild->vValue.ulVal > 31) ) { return E_INVALIDARG; } stDate.wDay = (WORD) pPropChild->vValue.ulVal;
// Look for a day of week
for ( pProp = pProperties; pProp && ( YOUBI != pProp->ulId ); pProp = pProp->pNextSibling ) ; if ( pProp ) { // Day of week present
pPropChild = pProp->pFirstChild; SPDBG_ASSERT( VT_UI4 == pPropChild->vValue.vt ); SPDBG_ASSERT( 6 >= pPropChild->vValue.ulVal ); if ( pPropChild->vValue.ulVal > 6 ) { return E_INVALIDARG; } stDate.wDayOfWeek = (WORD) pPropChild->vValue.ulVal;
// Use the full long date format
pszFormatArg = m_pszLongDateFormat; // If the user did say the day of week but the format string does
// not called for the day of week being displayed anywhere,
// Write out the day of week at the END of the output.
if ( !_tcsstr( m_pszLongDateFormat, DAYOFWEEK_STR_ABBR ) ) { _tcscat( m_pszLongDateFormat, " dddd" ); } } else { TCHAR pszDayOfWeekStr[ MAX_LOCALE_DATA];
// Remove the day of week from the current date format string
TCHAR *pc = _tcsstr( m_pszLongDateFormat, DAYOFWEEK_STR ); if ( pc ) { _tcscpy( pszDayOfWeekStr, DAYOFWEEK_STR ); } else if ( NULL != (pc = _tcsstr( m_pszLongDateFormat, DAYOFWEEK_STR_ABBR )) ) { _tcscpy( pszDayOfWeekStr, DAYOFWEEK_STR_ABBR ); }
if ( pc ) { // Copy over everything until this character info the format string
_tcsncpy( pszFormat, m_pszLongDateFormat, (pc - m_pszLongDateFormat) ); pszFormat[(pc - m_pszLongDateFormat)] = 0; // Skip over the day of week until the next symbol
// (the way date format strings work, this is the first
// alphabetical symbol
pc += _tcslen( pszDayOfWeekStr ); while ( *pc && !_istalpha( *pc ) ) { pc++; }
// Copy over everything from here on out
_tcscat( pszFormat, pc );
//dwFlags = 0;
pszFormatArg = pszFormat; } else // We don't have DAY_OF_WEEK in either the display format nor the results.
{ pszFormatArg = m_pszLongDateFormat; } } } else { _tcscpy( pszFormat, "MMMM, yyyy" ); pszFormatArg = pszFormat; }
// Get the date in VariantTime form so we can set it as a semantic value
int iRet = ::SystemTimeToVariantTime( &stDate, pdblVal ); if ( 0 == iRet ) { // Not serious, just the semantic value will be wrong
*pdblVal = 0; }
// Do the formatting
iRet = FormatDate( stDate, pszFormatArg, pszVal, cSize, pwszEmperor ); if ( 0 == iRet ) { return E_FAIL; }
return S_OK; } /* CTestITN_J::InterpretDate */
/***********************************************************************
* CTestITN_J::InterpretTime * *---------------------------* * Description: * Interprets time, which can be of the following forms: * * Hour with qualifier ("half past three"), time marker optional * * Hour with minutes, time marker mandatory * * Military time "hundred hours" * * Hour with "o'clock", time marker optional * * Number of hours and number of minutes and optional number * of seconds * Return: * S_OK * E_POINTER if !pdblVal or !pszVal *************************************************************************/ HRESULT CTestITN_J::InterpretTime( const SPPHRASEPROPERTY *pProperties, DOUBLE *pdblVal, WCHAR *pszVal, UINT cSize ) { if ( !pdblVal || !pszVal ) { return E_POINTER; } // Time marker and seconds should not be shown unless some
// component of the time specifically requires it
DWORD dwFlags = TIME_NOSECONDS | TIME_NOTIMEMARKER; SYSTEMTIME stTime; ::memset( (void *) &stTime, 0, sizeof( SYSTEMTIME ) );
bool fPM = false; bool fClockTime = true; bool fMinuteMinus = false;
const SPPHRASEPROPERTY *pProp; for ( pProp = pProperties; pProp; pProp = pProp->pNextSibling ) { #if 1
switch ( pProp->ulId ) { case GOZENN: // If it is PM, add the hour by 12
{ if (pProp->pszValue[0] == L'P') { fPM = TRUE; } dwFlags &= ~TIME_NOTIMEMARKER; break; } case JI: { UINT uiHour = pProp->pFirstChild->vValue.uiVal; stTime.wHour = (WORD) uiHour; if (fPM && (stTime.wHour < 12)) { stTime.wHour += 12; }
break; } case HOUR_COUNT: { // Just take the hour for what it is
stTime.wHour = (WORD) pProp->vValue.dblVal;
// This is not a clock time
fClockTime = false; break; } case MINUTE: { // Minutes are evaluted as numbers, so their values
// are stored as doubles
stTime.wMinute += (WORD) pProp->pFirstChild->vValue.uiVal; break; } case HUNN: { // Special case for :30 (��)
stTime.wMinute = 30; break; } case SECOND: { stTime.wSecond += (WORD) pProp->pFirstChild->vValue.uiVal; dwFlags &= ~TIME_NOSECONDS; break; } case MINUTE_TAG: { // We only need to deal with the case of ���O
if( pProp->pszValue[0] == L'-' ) { fMinuteMinus = true; }
break; } default: SPDBG_ASSERT( false );
} #endif
} if (fMinuteMinus) { stTime.wMinute = 60 - stTime.wMinute; stTime.wHour--; } HRESULT hr = S_OK; if ( fClockTime ) { // Get the time in VariantTime form so we can set it as a semantic value
if ( 0 == ::SystemTimeToVariantTime( &stTime, pdblVal ) ) { // Not serious, just the semantic value will be wrong
*pdblVal = 0; }
TCHAR *pszTime = new TCHAR[ cSize ]; if ( !pszTime ) { return E_OUTOFMEMORY; } if (stTime.wHour >= 24) { stTime.wHour -= 24; //To avoid problems in GetTimeFormat
} if (stTime.wHour >= 12) // Enable the TimeMarker if the time is in the afternoon
{ dwFlags &= ~TIME_NOTIMEMARKER; } int iRet = ::GetTimeFormat( LOCALE_USER_DEFAULT, dwFlags, &stTime, NULL, pszTime, cSize ); USES_CONVERSION; wcscpy( pszVal, T2W(pszTime) ); delete[] pszTime;
// NB: GetTimeFormat() will put an extra space at the end of the
// time if the default format has AM or PM but TIME_NOTIMEMARKER is
// set
if ( iRet && (TIME_NOTIMEMARKER & dwFlags) ) { WCHAR *pwc = pszVal + wcslen( pszVal ) - 1; while ( iswspace( *pwc ) ) { *pwc-- = 0; } } hr = iRet ? S_OK : E_FAIL; } else { // No need to go through the system's time formatter
if ( cSize < 10 ) // Space for xxx:xx:xx\0
{ return E_INVALIDARG; }
if ( dwFlags & TIME_NOSECONDS ) { swprintf( pszVal, L"%d:%02d", stTime.wHour, stTime.wMinute ); } else { swprintf( pszVal, L"%d:%02d:%02d", stTime.wHour, stTime.wMinute, stTime.wSecond ); } }
return hr; } /* CTestITN_J::InterpretTime */
/***********************************************************************
* CTestITN_J::InterpretDegrees * *------------------------------* * Description: * Interprets degrees as a angle-measurement or direction * Return: * S_OK * E_POINTER * E_INVALIDARG *************************************************************************/ HRESULT CTestITN_J::InterpretDegrees( const SPPHRASEPROPERTY *pProperties, DOUBLE *pdblVal, WCHAR *pszVal, UINT cSize ) { if ( !pProperties || !pdblVal || !pszVal ) { return E_POINTER; } WCHAR *pwchDirection = 0; *pszVal = 0;
//Do we have those direction tags
if (DIRECTION_TAG == pProperties->ulId) { pwchDirection = (WCHAR*) pProperties->pszValue; pProperties = pProperties->pNextSibling; } // Get the number
*pdblVal = pProperties->vValue.dblVal; wcscat( pszVal, pProperties->pszValue ); wcscat( pszVal, s_pwszDegree ); pProperties = pProperties->pNextSibling;
if ( pProperties && (MINUTE == pProperties->ulId ) ) { SPDBG_ASSERT( *pdblVal >= 0 ); DOUBLE dblMin = pProperties->vValue.dblVal; *pdblVal += dblMin / (DOUBLE) 60; wcscat( pszVal, pProperties->pszValue ); wcscat( pszVal, s_pwszMinute ); pProperties = pProperties->pNextSibling; }
if ( pProperties && (SECOND == pProperties->ulId) ) { DOUBLE dblSec = pProperties->vValue.dblVal; *pdblVal += dblSec / (DOUBLE) 3600; wcscat( pszVal, pProperties->pszValue ); wcscat( pszVal, s_pwszSecond ); pProperties = pProperties->pNextSibling; }
if (pwchDirection) { wcscat( pszVal, pwchDirection ); } SPDBG_ASSERT( !pProperties );
return S_OK; } /* CTestITN_J::InterpretDegrees */
/***********************************************************************
* CTestITN_J::InterpretMeasurement * *----------------------------------* * Description: * Interprets measurements, which is a number followed * by a units name * Return: * S_OK * E_POINTER * E_INVALIDARG *************************************************************************/ HRESULT CTestITN_J::InterpretMeasurement( const SPPHRASEPROPERTY *pProperties, DOUBLE *pdblVal, WCHAR *pszVal, UINT cSize ) { if ( !pProperties || !pdblVal || !pszVal ) { return E_POINTER; }
const SPPHRASEPROPERTY *pPropNumber = NULL; const SPPHRASEPROPERTY *pPropUnits = NULL; const SPPHRASEPROPERTY *pProp; for(pProp= pProperties; pProp; pProp = pProp->pNextSibling) { if (NUMBER == pProp->ulId ) pPropNumber = pProp; else if ( UNITS == pProp->ulId ) pPropUnits = pProp; }
if (!pPropUnits || !pPropNumber ) { SPDBG_ASSERT( FALSE ); return E_INVALIDARG; }
if ( cSize < (wcslen(pPropNumber->pszValue) + wcslen(pPropUnits->pszValue) + 1) ) { // Not enough space
return E_INVALIDARG; }
// Do we need to re-generate the numbers?
if (!iswdigit( pPropNumber->pszValue[wcslen(pPropNumber->pszValue) - 1] )) { MakeDisplayNumber( pPropNumber->vValue.dblVal, DF_WHOLENUMBER, 0, 0, pszVal, MAX_PATH, TRUE ); } else { wcscpy(pszVal, pPropNumber->pszValue ); } wcscat( pszVal, pPropUnits->pszValue );
*pdblVal = pPropNumber->vValue.dblVal;
return S_OK; } /* CTestITN_J::InterpretMeasurement */
/***********************************************************************
* CTestITN_J::InterpretCurrency * *-------------------------------* * Description: * Interprets currency. * Return: * S_OK * E_POINTER if !pdblVal or !pszVal * E_INVALIDARG if the number of cents is not between 0 and 99 * inclusive *************************************************************************/ HRESULT CTestITN_J::InterpretCurrency( const SPPHRASEPROPERTY *pProperties, DOUBLE *pdblVal, WCHAR *pszVal, UINT cSize) { if ( !pdblVal || !pszVal ) { return E_POINTER; }
// Find the dollars and cents properties
const SPPHRASEPROPERTY *pPropDollars;
for ( pPropDollars = pProperties; pPropDollars && ( YENs != pPropDollars->ulId ); pPropDollars = pPropDollars->pNextSibling ) ;
SPDBG_ASSERT( pPropDollars ); const WCHAR *pszDollars = NULL; DOUBLE dblDollars = 0; if ( pPropDollars ) { SPDBG_ASSERT( VT_R8 == pPropDollars->vValue.vt ); pszDollars = pPropDollars->pszValue; dblDollars = pPropDollars->vValue.dblVal; }
if (pPropDollars) { //Japanese people don't like \1Million, for the case of whole numbers like MANN, OKU,
//Simply write out the YEN at the end.
if (iswdigit( pszDollars[wcslen(pszDollars) - 1] )) { wcscpy(pszVal + 1, pszDollars); pszVal[0] = L'\\'; } else { wcscpy(pszVal, pszDollars); wcscat(pszVal, L"\x5186"); } }
*pdblVal = dblDollars;
return S_OK; } /* CTestITN_J::InterpretCurrency */
/***********************************************************************
* CTestITN_J::AddPropertyAndReplacement * *---------------------------------------* * Description: * Takes all of the info that we want to pass into the * engine site, forms the SPPHRASEPROPERTY and * SPPHRASERREPLACEMENT, and adds them to the engine site * Return: * Return values of ISpCFGInterpreterSite::AddProperty() * and ISpCFGInterpreterSite::AddTextReplacement() *************************************************************************/ HRESULT CTestITN_J::AddPropertyAndReplacement( const WCHAR *szBuff, const DOUBLE dblValue, const ULONG ulMinPos, const ULONG ulMaxPos, const ULONG ulFirstElement, const ULONG ulCountOfElements ) { // Add the property
SPPHRASEPROPERTY prop; memset(&prop,0,sizeof(prop)); prop.pszValue = szBuff; prop.vValue.vt = VT_R8; prop.vValue.dblVal = dblValue; prop.ulFirstElement = ulMinPos; prop.ulCountOfElements = ulMaxPos - ulMinPos; HRESULT hr = m_pSite->AddProperty(&prop);
if (SUCCEEDED(hr)) { SPPHRASEREPLACEMENT repl; memset(&repl,0, sizeof(repl)); repl.bDisplayAttributes = SPAF_ONE_TRAILING_SPACE; repl.pszReplacementText = szBuff; repl.ulFirstElement = ulFirstElement; repl.ulCountOfElements = ulCountOfElements; hr = m_pSite->AddTextReplacement(&repl); }
return hr; } /* CTestITN_J::AddPropertyAndReplacement */
// Helper functions
/***********************************************************************
* CTestITN_J::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 CTestITN_J::MakeDisplayNumber( DOUBLE dblNum, DWORD dwDisplayFlags, UINT uiFixedWidth, UINT uiDecimalPlaces, WCHAR *pwszNum, UINT cSize, BOOL bForced) { SPDBG_ASSERT( pwszNum ); SPDBG_ASSERT( !SPIsBadWritePtr( pwszNum, cSize ) ); *pwszNum = 0;
// check for straight millions and straight billions
if (( dwDisplayFlags & DF_WHOLENUMBER ) && (dblNum > 0) && !bForced) { HRESULT hr; if ( 0 == (( ((LONGLONG) dblNum) % CHUU )) ) { // e.g. for "five billion" get the "5" and then
// tack on " billion"
hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) CHUU) ), dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize, FALSE ); if ( SUCCEEDED( hr ) ) { //wcscat( pwszNum, L" " );
wcscat( pwszNum, CHUU_STR ); } return hr; } else if (( ((LONGLONG) dblNum) < CHUU ) && ( 0 == (( ((LONGLONG) dblNum) % OKU )) )) { hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) OKU) ), dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize, FALSE ); if ( SUCCEEDED( hr ) ) { //wcscat( pwszNum, L" " );
wcscat( pwszNum, OKU_STR ); } return hr; } else if (( ((LONGLONG) dblNum) < OKU ) && ( 0 == (( ((LONGLONG) dblNum) % MANN )) )) { hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) MANN) ), dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize, FALSE ); if ( SUCCEEDED( hr ) ) { //wcscat( pwszNum, L" " );
wcscat( pwszNum, MANN_STR ); } return hr; } }
// Put in the negative sign if necessary
if ( dblNum < 0 ) { wcscat( pwszNum, L"-" );
// From now on we want to deal with the magnitude of the number
dblNum *= -1; } SPDBG_ASSERT( dblNum < 1e16 );
WCHAR *pwszTemp = new WCHAR[ cSize ]; if ( !pwszTemp ) { return E_OUTOFMEMORY; } *pwszTemp = 0;
LONGLONG llIntPart = (LONGLONG) dblNum; UINT uiDigitsLeftOfDecimal; if ( dwDisplayFlags & DF_WHOLENUMBER ) { swprintf( pwszTemp, L"%I64d", llIntPart ); uiDigitsLeftOfDecimal = wcslen( pwszTemp ); } else { swprintf( pwszTemp, 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++ ) { wcscat( pwszNum, 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
wcscat( pwszNum, pwszTemp ); delete[] pwszTemp;
// If we do not want to format the number, then bail here
if ( dwDisplayFlags & DF_UNFORMATTED ) { return S_OK; }
// 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.
GetNumberFormatDefaults(); // Make a copy so that we can change some fields according to the
// flags param
NUMBERFMT 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; }
// Format the number string
TCHAR *pszFormattedNum = new TCHAR[ cSize ]; if ( !pszFormattedNum ) { return E_OUTOFMEMORY; } *pszFormattedNum = 0; USES_CONVERSION; int iRet; do { iRet = ::GetNumberFormat( LOCALE_USER_DEFAULT, 0, W2T( pwszNum ), &nmfmt, pszFormattedNum, cSize ); if ( !iRet && nmfmt.NumDigits ) { // Try displaying fewer digits
nmfmt.NumDigits--; } } while ( !iRet && nmfmt.NumDigits ); SPDBG_ASSERT( iRet );
// Copy the formatted number into pwszNum
wcscpy( pwszNum, T2W(pszFormattedNum) ); delete[] pszFormattedNum;
// 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: wcscat( pwszNum, L"st" ); break; case 2: wcscat( pwszNum, L"nd" ); break; case 3: wcscat( pwszNum, L"rd" ); break; default: wcscat( pwszNum, L"th" ); break; } } else { wcscat( pwszNum, L"th" ); } }
return S_OK;
} /* CTestITN_J::MakeDisplayNumber */
/***********************************************************************
* CTestITN_J::GetNumberFormatDefaults * *-------------------------------------* * Description: * This finds all of the defaults for formatting numbers for * this user. *************************************************************************/ void CTestITN_J::GetNumberFormatDefaults() { LCID lcid = ::GetUserDefaultLCID(); TCHAR pszLocaleData[ MAX_LOCALE_DATA ]; ::GetLocaleInfo( lcid, LOCALE_IDIGITS, pszLocaleData, MAX_LOCALE_DATA ); m_nmfmtDefault.NumDigits = _ttoi( pszLocaleData ); ::GetLocaleInfo( lcid, LOCALE_ILZERO, pszLocaleData, MAX_LOCALE_DATA ); // It's always either 0 or 1
m_nmfmtDefault.LeadingZero = _ttoi( pszLocaleData );
::GetLocaleInfo( lcid, LOCALE_SGROUPING, pszLocaleData, MAX_LOCALE_DATA ); // It will look like single_digit;0, or else it will look like
// 3;2;0
UINT uiGrouping = *pszLocaleData - _T('0'); if ( (3 == uiGrouping) && (_T(';') == pszLocaleData[1]) && (_T('2') == pszLocaleData[2]) ) { uiGrouping = 32; } m_nmfmtDefault.Grouping = uiGrouping;
::GetLocaleInfo( lcid, LOCALE_SDECIMAL, m_pszDecimalSep, MAX_LOCALE_DATA ); m_nmfmtDefault.lpDecimalSep = m_pszDecimalSep;
::GetLocaleInfo( lcid, LOCALE_STHOUSAND, m_pszThousandSep, MAX_LOCALE_DATA ); m_nmfmtDefault.lpThousandSep = m_pszThousandSep;
::GetLocaleInfo( lcid, LOCALE_INEGNUMBER, pszLocaleData, MAX_LOCALE_DATA ); m_nmfmtDefault.NegativeOrder = _ttoi( pszLocaleData ); } /* CTestITN_J::GetNumberFormatDefaults
/***********************************************************************
* HandleDigitsAfterDecimal * *--------------------------* * Description: * If pwszRightOfDecimal is NULL, then cuts off all of the numbers * following the decimal separator. * Otherwise, copies pwszRightOfDecimal right after the decimal * separator in pwszFormattedNum. * Preserves the stuff after the digits in the pwszFormattedNum * (e.g. if pwszFormattedNum starts out "(3.00)" and * pwszRightOfDecimal is NULL, then pwszFormattedNum will end * up as "(3)" *************************************************************************/ void HandleDigitsAfterDecimal( WCHAR *pwszFormattedNum, UINT cSizeOfFormattedNum, const WCHAR *pwszRightOfDecimal ) { SPDBG_ASSERT( pwszFormattedNum ); // First need to find what the decimal string is
LCID lcid = ::GetUserDefaultLCID(); TCHAR pszDecimalString[ 5 ]; // Guaranteed to be no longer than 4 long
int iRet = ::GetLocaleInfo( lcid, LOCALE_SDECIMAL, pszDecimalString, 4 ); SPDBG_ASSERT( iRet );
USES_CONVERSION; WCHAR *pwcDecimal = wcsstr( pwszFormattedNum, T2W(pszDecimalString) ); SPDBG_ASSERT( pwcDecimal );
// pwcAfterDecimal points to the first character after the decimal separator
WCHAR *pwcAfterDecimal = pwcDecimal + _tcslen( pszDecimalString );
// Remember what originally followed the digits
WCHAR *pwszTemp = new WCHAR[ cSizeOfFormattedNum ]; WCHAR *pwcAfterDigits; // points to the first character after the end of the digits
for ( pwcAfterDigits = pwcAfterDecimal; *pwcAfterDigits && iswdigit( *pwcAfterDigits ); pwcAfterDigits++ ) ; wcscpy( pwszTemp, pwcAfterDigits ); // OK if *pwcAfterDigits == 0
if ( pwszRightOfDecimal ) { // This means that the caller wants the digits in pwszRightOfDecimal
// copied after the decimal separator
// Copy the decimal string after the decimal separater
wcscpy( pwcAfterDecimal, pwszRightOfDecimal );
} else { // This means that the caller wanted the decimal separator
// and all text following it stripped off
*pwcDecimal = 0; }
// Add on the extra after-digit characters
wcscat( pwszFormattedNum, pwszTemp );
delete[] pwszTemp;
} /* HandleDigitsAfterDecimal */
/***********************************************************************
* ComputeNum9999 * *----------------* * 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 ComputeNum9999(const SPPHRASEPROPERTY *pProperties )//, ULONG *pVal)
{ ULONG ulVal = 0;
for (const SPPHRASEPROPERTY * pProp = pProperties; pProp; pProp = pProp->pNextSibling) { if ( ZERO != pProp->ulId ) { SPDBG_ASSERT( VT_UI4 == pProp->vValue.vt );
ulVal += pProp->vValue.ulVal; } } return ulVal; } /* ComputeNum9999 */
/***********************************************************************
* GetMinAndMaxPos * *-----------------* * Description: * Gets the minimum and maximum elements spanned by the * set of properties *************************************************************************/ void GetMinAndMaxPos( const SPPHRASEPROPERTY *pProperties, ULONG *pulMinPos, ULONG *pulMaxPos ) { if ( !pulMinPos || !pulMaxPos ) { return; } ULONG ulMin = 9999999; ULONG ulMax = 0;
for ( const SPPHRASEPROPERTY *pProp = pProperties; pProp; pProp = pProp->pNextSibling ) { ulMin = __min( ulMin, pProp->ulFirstElement ); ulMax = __max( ulMax, pProp->ulFirstElement + pProp->ulCountOfElements ); } *pulMinPos = ulMin; *pulMaxPos = ulMax; } /* GetMinAndMaxPos */
/***********************************************************************
* GetMonthName * *--------------* * Description: * Gets the name of the month, abbreviated if desired * Return: * Number of characters written to pszMonth, 0 if failed *************************************************************************/ int GetMonthName( int iMonth, WCHAR *pwszMonth, int cSize, bool fAbbrev ) { LCTYPE lctype; switch ( iMonth ) { case 1: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1; break; case 2: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME2 : LOCALE_SMONTHNAME2; break; case 3: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME3 : LOCALE_SMONTHNAME3; break; case 4: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME4 : LOCALE_SMONTHNAME4; break; case 5: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME5 : LOCALE_SMONTHNAME5; break; case 6: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME6 : LOCALE_SMONTHNAME6; break; case 7: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME7 : LOCALE_SMONTHNAME7; break; case 8: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME8 : LOCALE_SMONTHNAME8; break; case 9: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME9 : LOCALE_SMONTHNAME9; break; case 10: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME10 : LOCALE_SMONTHNAME10; break; case 11: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME11 : LOCALE_SMONTHNAME11; break; case 12: lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME12 : LOCALE_SMONTHNAME12; break; default: return 0; }
TCHAR *pszMonth = new TCHAR[ cSize ]; if ( !pszMonth ) { return 0; } int iRet = ::GetLocaleInfo( LOCALE_USER_DEFAULT, lctype, pszMonth, cSize ); iRet = _mbslen((const unsigned char*) pszMonth); //Jpn needs chars, not bytes
USES_CONVERSION; wcscpy( pwszMonth, T2W(pszMonth) ); delete[] pszMonth;
return iRet; } /* GetMonthName */
/***********************************************************************
* GetDayOfWeekName * *------------------* * Description: * Gets the name of the day of week, abbreviated if desired * Return: * Number of characters written to pszDayOfWeek, 0 if failed *************************************************************************/ int GetDayOfWeekName( int iDayOfWeek, WCHAR *pwszDayOfWeek, int cSize, bool fAbbrev ) { LCTYPE lctype; switch ( iDayOfWeek ) { case 0: // Sunday is day 7
lctype = fAbbrev ? LOCALE_SABBREVDAYNAME7 : LOCALE_SDAYNAME7; break; case 1: lctype = fAbbrev ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1; break; case 2: lctype = fAbbrev ? LOCALE_SABBREVDAYNAME2 : LOCALE_SDAYNAME2; break; case 3: lctype = fAbbrev ? LOCALE_SABBREVDAYNAME3 : LOCALE_SDAYNAME3; break; case 4: lctype = fAbbrev ? LOCALE_SABBREVDAYNAME4 : LOCALE_SDAYNAME4; break; case 5: lctype = fAbbrev ? LOCALE_SABBREVDAYNAME5 : LOCALE_SDAYNAME5; break; case 6: lctype = fAbbrev ? LOCALE_SABBREVDAYNAME6 : LOCALE_SDAYNAME6; break; default: return 0; }
TCHAR *pszDayOfWeek = new TCHAR[ cSize ]; if ( !pszDayOfWeek ) { return 0; } int iRet = ::GetLocaleInfo( LOCALE_USER_DEFAULT, lctype, pszDayOfWeek, cSize ); USES_CONVERSION; wcscpy( pwszDayOfWeek, T2W(pszDayOfWeek) ); iRet = wcslen(pwszDayOfWeek); delete[] pszDayOfWeek;
return iRet; } /* GetMonthName */
/***********************************************************************
* FormatDate * *------------* * Description: * Uses the format string to format a SYSTEMTIME date. * We are using this instead of GetDateFormat() since * we also want to format bogus dates and dates with * years like 1492 that are not accepted by GetDateFormat() * Return: * Number of characters written to pszDate (including * null terminating character), 0 if failed *************************************************************************/ int FormatDate( const SYSTEMTIME &stDate, TCHAR *pszFormat, WCHAR *pwszDate, int cSize, const WCHAR *pwszEmperor) { if ( !pszFormat || !pwszDate ) { SPDBG_ASSERT( FALSE ); return 0; }
WCHAR * const pwszDateStart = pwszDate;
// Convert the format string to unicode
WCHAR pwszFormat[ MAX_PATH ]; USES_CONVERSION; wcscpy( pwszFormat, T2W(pszFormat) );
WCHAR *pwc = pwszFormat; //Modify the format string to drop the fileds we don't have (Year, gg)
while ( *pwc ) { switch( *pwc ) { case L'y': if (!stDate.wYear) { do { *pwc++ = L'\''; } while ( *pwc && (L'M' != *pwc) && (L'd' != *pwc)); } else { pwc++; } break; case L'g': *pwc++ = L'\''; break; default: pwc ++; break; } } pwc = pwszFormat;
// output the Emperor's name if there is one
if (pwszEmperor) { wcscpy(pwszDate,pwszEmperor); pwszDate += wcslen(pwszEmperor); } // Copy the format string to the date string character by
// character, replacing the string like "dddd" as appropriate
while ( *pwc ) { switch( *pwc ) { case L'\'': pwc++; // Don't need '
break; case L'd': { // Count the number of d's
int cNumDs = 0; int iRet; do { pwc++; cNumDs++; } while ( L'd' == *pwc ); switch ( cNumDs ) { case 1: // Day with no leading zeroes
swprintf( pwszDate, L"%d", stDate.wDay ); iRet = wcslen( pwszDate ); break; case 2: // Day with one fixed width of 2
swprintf( pwszDate, L"%02d", stDate.wDay ); iRet = wcslen( pwszDate ); break; case 3: // Abbreviated day of week
iRet = GetDayOfWeekName( stDate.wDayOfWeek, pwszDate, cSize, true ); break; default: // More than 4? Treat it as 4
// Day of week
iRet = GetDayOfWeekName( stDate.wDayOfWeek, pwszDate, cSize, false ); break; }
if ( iRet <= 0 ) { return 0; } else { pwszDate += iRet; } break; }
case L'M': { // Count the number of M's
int cNumMs = 0; int iRet; do { pwc++; cNumMs++; } while ( L'M' == *pwc ); switch ( cNumMs ) { case 1: // Day with no leading zeroes
swprintf( pwszDate, L"%d", stDate.wMonth ); iRet = wcslen( pwszDate ); break; case 2: // Day with one fixed width of 2
swprintf( pwszDate, L"%02d", stDate.wMonth ); iRet = wcslen( pwszDate ); break; case 3: // Abbreviated month name
iRet = GetMonthName( stDate.wMonth, pwszDate, cSize, true ); break; default: // More than 4? Treat it as 4
// Month
iRet = GetMonthName( stDate.wMonth, pwszDate, cSize, false ); break; }
if ( iRet < 0 ) { return 0; } else { pwszDate += iRet; } break; } case L'y': { // Count the number of y's
int cNumYs = 0; do { pwc++; cNumYs++; } while ( L'y' == *pwc ); switch ( cNumYs ) { case 1: // Last two digits of year, width of 2
if (stDate.wYear % 100 > 9) { swprintf( pwszDate, L"%02d", stDate.wYear % 100 ); pwszDate += 2; } else { swprintf( pwszDate, L"%01d", stDate.wYear % 100 ); pwszDate += 1; } break; case 2: // Last two digits of year, width of 2
{ swprintf( pwszDate, L"%02d", stDate.wYear % 100 ); pwszDate += 2; } break; default: // All four digits of year, width of 4
// Last two digits of year, width of 2
swprintf( pwszDate, L"%04d", stDate.wYear % 10000 ); pwszDate += 4; break; } break; }
case L'g': { // NB: GetCalendarInfo is supported on Win98 or Win2K, but not on NT4
/*
if ( L'g' == *(pwc + 1) ) { // Get the era string
TCHAR pszCalEra[ MAX_LOCALE_DATA ]; if ( 0 == GetCalendarInfo( LOCALE_USER_DEFAULT, CAL_GREGORIAN, CAL_SERASTRING, pszCalEra, MAX_LOCALE_DATA ) ) { return 0; } USES_CONVERSION; wcscpy( pwszDate, T2W(pszCalEra) ); pwc += 2; } else { // It's just a 'g'
*pwszDate++ = *pwc++; } */ *pwszDate++ = *pwc++; break; } default: *pwszDate++ = *pwc++; } } *pwszDate++ = 0;
return (pwszDate - pwszDateStart); } /* FormatDate */
|