home *** CD-ROM | disk | FTP | other *** search
- /*
- *******************************************************************************
- * *
- * COPYRIGHT: *
- * (C) Copyright Taligent, Inc., 1996 *
- * (C) Copyright International Business Machines Corporation, 1998-1999 *
- * Licensed Material - Program-Property of IBM - All Rights Reserved. *
- * US Government Users Restricted Rights - Use, duplication, or disclosure *
- * restricted by GSA ADP Schedule Contract with IBM Corp. *
- * *
- *******************************************************************************
- */
-
- #include "umsg.h"
- #include "mutex.h"
- #include "uloc.h"
- #include "ustring.h"
- #include "fmtable.h"
- #include "cpputils.h"
- #include "msgfmt.h"
- #include "unistr.h"
- #include "numfmt.h"
-
-
- // MessageFormat Type List Number, Date, Time or Choice
- const UnicodeString fgTypeList[] = {
- "", "", "number", "", "date", "", "time", "", "choice"
- };
-
- // NumberFormat modifier list, default, currency, percent or integer
- const UnicodeString fgModifierList[] = {
- "", "", "currency", "", "percent", "", "integer", "", ""
- };
-
- // DateFormat modifier list, default, short, medium, long or full
- const UnicodeString fgDateModifierList[] = {
- "", "", "short", "", "medium", "", "long", "", "full"
- };
-
- // Number of items in the lists
- const int32_t fgListLength = 9;
-
- // Determine if a keyword belongs to a list of keywords
- int32_t
- findKeyword(const UnicodeString& s,
- const UnicodeString *list,
- int32_t& kwLen)
- {
- UnicodeString buffer = s;
-
- // Trims the space characters and turns all characters
- // in s to lower case.
- buffer.trim().toLower();
- for(int32_t i = 0; i < fgListLength; ++i) {
-
- // Determine if there is a ','
- // If so, the string contains a modifier, and we only want to
- // parse the type
- int32_t commaPos = buffer.indexOf(0x002C);
- commaPos = (commaPos == -1 ? buffer.size() : commaPos);
- buffer.truncate(commaPos);
- if(buffer == list[i]) {
- kwLen = list[i].size();
- return i;
- }
- }
-
- kwLen = 0;
- return - 1;
- }
-
- // Match the type of argument in a message format pattern
- // The type consists of a type indicator and an optional modifier
- // Possible types : number, date, time, choice
- // Possible modifiers : currency, percent, integer, full, long, short
- // We only worry about parsing the types and the "integer" modifier
- Formattable::Type
- matchType(const UChar *pat,
- int32_t openBrace,
- int32_t closeBrace)
- {
- int32_t len = (closeBrace - openBrace) - 1;
- Formattable::Type result = Formattable::kString;
-
- // Strings like "{0}" are strings
- if(len == 1) {
- result = Formattable::kString;
- return result;
- }
- // Assume the input is well-formed
- else {
- UnicodeString type((UChar*)pat + openBrace + 1 + 2, len - 2, len - 2);
- int32_t matchLen, kw;
-
- kw = findKeyword(type, fgTypeList, matchLen);
-
- // there is a modifier if type contains a ','
- bool_t hasModifier = (type.indexOf(0x002C) != -1);
-
- switch(kw) {
-
- // number
- case 1: case 2:
-
- if(hasModifier) {
- UnicodeString modifier((UChar*)pat + openBrace + 1 + 1 + 2 + matchLen,
- len - 2 - matchLen - 1,
- len - 2 - matchLen - 1);
-
- switch(findKeyword(modifier, fgModifierList, matchLen)) {
-
- // default
- case 0:
- // currency
- case 1: case 2:
- // percent
- case 3: case 4:
- result = Formattable::kDouble;
- break;
-
- // integer
- case 5: case 6:
- result = Formattable::kLong;
- break;
- }
- }
- else {
- result = Formattable::kDouble;
- }
- break;
-
- // date
- case 3: case 4:
- // time
- case 5: case 6:
- result = Formattable::kDate;
- break;
-
- // choice
- case 7: case 8:
- result = Formattable::kDouble;
- break;
- }
- }
-
- return result;
- }
-
-
- // ==========
- // This code section is entirely bogus. I just need an eeasy way to
- // convert from string to an int, and I can't use the standard library
-
- static NumberFormat *fgNumberFormat = 0;
-
- NumberFormat*
- umsg_getNumberFormat(UErrorCode& status)
- {
- NumberFormat *theFormat = 0;
-
- if(fgNumberFormat != 0) {
- Mutex lock;
-
- if(fgNumberFormat != 0) {
- theFormat = fgNumberFormat;
- fgNumberFormat = 0; // We have exclusive right to this formatter.
- }
- }
-
- if(theFormat == 0) {
- theFormat = NumberFormat::createInstance(Locale::US, status);
- if(U_FAILURE(status))
- return 0;
- theFormat->setParseIntegerOnly(TRUE);
- }
-
- return theFormat;
- }
-
- void
- umsg_releaseNumberFormat(NumberFormat *adopt)
- {
- if(fgNumberFormat == 0) {
- Mutex lock;
-
- if(fgNumberFormat == 0) {
- fgNumberFormat = adopt;
- adopt = 0;
- }
- }
-
- delete adopt;
- }
-
- int32_t
- umsg_stoi(const UnicodeString& string,
- UErrorCode& status)
- {
- NumberFormat *myFormat = umsg_getNumberFormat(status);
-
- if(U_FAILURE(status))
- return -1; // OK?
-
- Formattable result;
- // Uses the global number formatter to parse the string.
- // Note: We assume here that parse() is thread-safe.
- myFormat->parse(string, result, status);
- umsg_releaseNumberFormat(myFormat);
-
- int32_t value = 0;
- if(U_SUCCESS(status) && result.getType() == Formattable::kLong)
- value = result.getLong();
-
- return value;
- }
-
- UnicodeString&
- umsg_itos(int32_t i,
- UnicodeString& string)
- {
- UErrorCode status = U_ZERO_ERROR;
- NumberFormat *myFormat = umsg_getNumberFormat(status);
-
- if(U_FAILURE(status))
- return (string = "<ERROR>");
-
- myFormat->format(i, string);
- umsg_releaseNumberFormat(myFormat);
-
- return string;
- }
-
- // ==========
-
- #define MAX_ARGS 10
-
- // Eventually, message format should be rewritten natively in C.
- // For now, this is a hack that should work:
- // 1. Parse the pattern, determining the argument types
- // 2. Create a Formattable array with the varargs
- // 3. Call through to the existing C++ code
- //
- // Right now this imposes the same limit as MessageFormat in C++
- // Namely, only MAX_ARGS arguments are supported
- U_CAPI int32_t
- u_formatMessage( const char *locale,
- const UChar *pattern,
- int32_t patternLength,
- UChar *result,
- int32_t resultLength,
- UErrorCode *status,
- ...)
- {
- if(U_FAILURE(*status)) return -1;
-
- int32_t patLen = (patternLength == -1 ? u_strlen(pattern) : patternLength);
- int32_t actLen;
-
- // ========================================
- // Begin pseudo-parser
-
- // This is a simplified version of the C++ pattern parser
- // All it does is look for an unquoted '{' and read the type
-
- va_list ap;
- int32_t part = 0;
- bool_t inQuote = FALSE;
- int32_t braceStack = 0;
- const UChar *pat = pattern;
- const UChar *patLimit = pattern + patLen;
- int32_t bracePos = 0;
- int32_t count = 0;
- Formattable args [ MAX_ARGS ];
- Formattable::Type argTypes [ MAX_ARGS ];
-
-
- // set the types to a bogus value initially (no such type as kArray from C)
- for(int32_t j = 0; j < MAX_ARGS; ++j)
- argTypes[j] = Formattable::kArray;
-
- // pseudo-parse the pattern
- while(pat < patLimit) {
- if(part == 0) {
- if(*pat == 0x0027 /*'\''*/) {
- // handle double quotes
- if( (pat + 1) < patLimit && *(pat + 1) == 0x0027 /*'\''*/)
- *pat++;
- else
- inQuote = ! inQuote;
- }
- else if(*pat == 0x007B /*'{'*/ && ! inQuote) {
- part = 1;
- bracePos = (pat - pattern);
- }
- }
- else if(inQuote) { // just copy quotes in parts
- if(*pat == 0x0027 /*'\''*/)
- inQuote = FALSE;
- }
- else {
- switch (*pat) {
-
- case 0x002C /*','*/:
- if(part < 3)
- part += 1;
- break;
-
- case 0x007B /*'{'*/:
- ++braceStack;
- break;
-
- case 0x007D /*'}'*/:
- if(braceStack == 0) {
- part = 0;
- // found a close brace, determine the argument type enclosed
- // and the numeric ID of the argument
- Formattable::Type type =
- matchType(pattern, bracePos, (pat - pattern));
-
- // the numeric ID is important, because if the pattern has a
- // section like "{0} {0} {0}" we only want to get one argument
- // from the variable argument list, despite the fact that
- // it is in the pattern three times
- int32_t argNum = umsg_stoi(pattern + bracePos + 1, *status);
-
- if(argNum >= MAX_ARGS) {
- *status = U_INTERNAL_PROGRAM_ERROR;
- return -1;
- }
-
- // register the type of this argument in our list
- argTypes[argNum] = type;
-
- // adjust argument count
- count = ( (argNum + 1) > count ? (argNum + 1) : count);
- }
- else
- --braceStack;
- break;
-
- case 0x0027 /*'\''*/:
- inQuote = TRUE;
- break;
- }
- }
-
- // increment position in pattern
- *pat++;
- }
-
- // detect any unmatched braces in the pattern
- if(braceStack == 0 && part != 0) {
- *status = U_INVALID_FORMAT_ERROR;
- return -1;
- }
-
- // start vararg processing
- va_start(ap, status);
-
- // iterate through the vararg list, and get the arguments out
- for(int32_t i = 0; i < count; ++i) {
-
- UChar *stringVal;
-
- switch(argTypes[i]) {
- case Formattable::kDate:
- args[i].setDate(va_arg(ap, UDate));
- break;
-
- case Formattable::kDouble:
- args[i].setDouble(va_arg(ap, double));
- break;
-
- case Formattable::kLong:
- args[i].setLong(va_arg(ap, int32_t));
- break;
-
- case Formattable::kString:
- // For some reason, a temporary is needed
- stringVal = va_arg(ap, UChar*);
- args[i].setString(stringVal);
- break;
-
- case Formattable::kArray:
- // throw away this argument
- // this is highly platform-dependent, and probably won't work
- // so, if you try to skip arguments in the list (and not use them)
- // you'll probably crash
- va_arg(ap, int);
- break;
- }
- }
-
- // end vararg processing
- va_end(ap);
-
- // End pseudo-parser
- // ========================================
-
- // just call through to the C++ implementation
- UnicodeString patString((UChar*)pattern, patLen, patLen);
- MessageFormat fmt(patString, Locale().init(locale), *status);
- UnicodeString res(result, 0, resultLength);
- FieldPosition fp;
- fmt.format(args, count, res, fp, *status);
-
- T_fillOutputParams(&res, result, resultLength, &actLen, status);
- return actLen;
- }
-
-
- // For parse, do the reverse of format:
- // 1. Call through to the C++ APIs
- // 2. Just assume the user passed in enough arguments.
- // 3. Iterate through each formattable returned, and assign to the arguments
- U_CAPI void
- u_parseMessage( const char *locale,
- const UChar *pattern,
- int32_t patternLength,
- const UChar *source,
- int32_t sourceLength,
- UErrorCode *status,
- ...)
- {
- if(U_FAILURE(*status)) return;
-
- int32_t patLen = (patternLength == -1 ? u_strlen(pattern) : patternLength);
- int32_t srcLen = (sourceLength == -1 ? u_strlen(source) : sourceLength);
-
- UnicodeString patString((UChar*)pattern, patLen, patLen);
- MessageFormat fmt(patString, Locale().init(locale), *status);
- UnicodeString srcString((UChar*)source, srcLen, srcLen);
- int32_t count = 0;
- Formattable *args = fmt.parse(srcString, count, *status);
-
- // start vararg processing
- va_list ap;
- va_start(ap, status);
-
- UDate *aDate;
- double *aDouble;
- UChar *aString;
- UnicodeString temp;
-
- // assign formattables to varargs
- for(int32_t i = 0; i < count; i++) {
- switch(args[i].getType()) {
-
- case Formattable::kDate:
- aDate = va_arg(ap, UDate*);
- *aDate = args[i].getDate();
- break;
-
- case Formattable::kDouble:
- aDouble = va_arg(ap, double*);
- *aDouble = args[i].getDouble();
- break;
-
- case Formattable::kLong:
- // always assume doubles for parsing
- aDouble = va_arg(ap, double*);
- *aDouble = (double) args[i].getLong();
- break;
-
- case Formattable::kString:
- aString = va_arg(ap, UChar*);
- args[i].getString(temp);
- u_strcpy(aString, temp.getUChars());
- break;
-
- // better not happen!
- case Formattable::kArray:
- // DIE
- break;
- }
- }
-
- // end vararg processing
- va_end(ap);
-
- // clean up
- delete [] args;
- }
-