home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Programming / ICU / src / icu / source / i18n / umsg.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-19  |  12.5 KB  |  484 lines

  1. /*
  2. *******************************************************************************
  3. *                                                                             *
  4. * COPYRIGHT:                                                                  *
  5. *   (C) Copyright Taligent, Inc.,  1996                                       *
  6. *   (C) Copyright International Business Machines Corporation,  1998-1999     *
  7. *   Licensed Material - Program-Property of IBM - All Rights Reserved.        *
  8. *   US Government Users Restricted Rights - Use, duplication, or disclosure   *
  9. *   restricted by GSA ADP Schedule Contract with IBM Corp.                    *
  10. *                                                                             *
  11. *******************************************************************************
  12. */
  13.  
  14. #include "umsg.h"
  15. #include "mutex.h"
  16. #include "uloc.h"
  17. #include "ustring.h"
  18. #include "fmtable.h"
  19. #include "cpputils.h"
  20. #include "msgfmt.h"
  21. #include "unistr.h"
  22. #include "numfmt.h"
  23.  
  24.  
  25. // MessageFormat Type List  Number, Date, Time or Choice
  26. const UnicodeString fgTypeList[] = {
  27.   "", "", "number", "", "date", "", "time", "", "choice"
  28. };
  29.  
  30. // NumberFormat modifier list, default, currency, percent or integer
  31. const UnicodeString fgModifierList[] = {
  32.   "", "", "currency", "", "percent", "", "integer", "", ""
  33. };
  34.  
  35. // DateFormat modifier list, default, short, medium, long or full
  36. const UnicodeString fgDateModifierList[] = {
  37.   "", "", "short", "", "medium", "", "long", "", "full"
  38. };
  39.  
  40. // Number of items in the lists
  41. const int32_t fgListLength = 9;
  42.  
  43. // Determine if a keyword belongs to a list of keywords
  44. int32_t
  45. findKeyword(const     UnicodeString&     s, 
  46.         const     UnicodeString    *list,
  47.                 int32_t&    kwLen)
  48. {
  49.   UnicodeString buffer = s;
  50.  
  51.   // Trims the space characters and turns all characters
  52.   // in s to lower case.
  53.   buffer.trim().toLower();
  54.   for(int32_t i = 0; i < fgListLength; ++i) {
  55.  
  56.     // Determine if there is a ','
  57.     // If so, the string contains a modifier, and we only want to 
  58.     // parse the type
  59.     int32_t commaPos = buffer.indexOf(0x002C);
  60.     commaPos = (commaPos == -1 ? buffer.size() : commaPos);
  61.     buffer.truncate(commaPos);
  62.     if(buffer == list[i]) {
  63.       kwLen = list[i].size();
  64.       return i;
  65.     }
  66.   }
  67.   
  68.   kwLen = 0;
  69.   return - 1;
  70. }
  71.  
  72. // Match the type of argument in a message format pattern
  73. // The type consists of a type indicator and an optional modifier
  74. // Possible types : number, date, time, choice
  75. // Possible modifiers : currency, percent, integer, full, long, short
  76. // We only worry about parsing the types and the "integer" modifier
  77. Formattable::Type
  78. matchType(const UChar         *pat,
  79.           int32_t     openBrace,
  80.           int32_t     closeBrace)
  81. {
  82.   int32_t len = (closeBrace - openBrace) - 1;
  83.   Formattable::Type result = Formattable::kString;
  84.  
  85.   // Strings like "{0}" are strings
  86.   if(len == 1) {
  87.     result = Formattable::kString;
  88.     return result;
  89.   }
  90.   // Assume the input is well-formed
  91.   else {
  92.     UnicodeString type((UChar*)pat + openBrace + 1 + 2, len - 2, len - 2);
  93.     int32_t matchLen, kw;
  94.  
  95.     kw = findKeyword(type, fgTypeList, matchLen);
  96.  
  97.     // there is a modifier if type contains a ','
  98.     bool_t hasModifier = (type.indexOf(0x002C) != -1);
  99.     
  100.     switch(kw) {
  101.  
  102.       // number
  103.     case 1: case 2:
  104.  
  105.       if(hasModifier) {
  106.     UnicodeString modifier((UChar*)pat + openBrace + 1 + 1 + 2 + matchLen, 
  107.                    len - 2 - matchLen - 1, 
  108.                    len - 2 - matchLen - 1);
  109.     
  110.     switch(findKeyword(modifier, fgModifierList, matchLen)) {
  111.     
  112.       // default
  113.     case 0:
  114.       // currency
  115.     case 1: case 2:
  116.       // percent
  117.     case 3: case 4:
  118.       result = Formattable::kDouble;
  119.       break;
  120.       
  121.       // integer
  122.     case 5: case 6:
  123.       result = Formattable::kLong;
  124.       break;
  125.     }
  126.       }
  127.       else {
  128.     result = Formattable::kDouble;
  129.       }
  130.       break;
  131.       
  132.       // date
  133.     case 3: case 4:
  134.       // time
  135.     case 5: case 6:
  136.       result = Formattable::kDate;
  137.       break;
  138.  
  139.       // choice      
  140.     case 7: case 8:
  141.       result = Formattable::kDouble;
  142.       break;
  143.     }
  144.   }
  145.   
  146.   return result;
  147. }
  148.  
  149.   
  150. // ==========
  151. // This code section is entirely bogus.  I just need an eeasy way to
  152. // convert from string to an int, and I can't use the standard library
  153.  
  154. static NumberFormat *fgNumberFormat = 0;
  155.  
  156. NumberFormat* 
  157. umsg_getNumberFormat(UErrorCode& status)
  158. {
  159.   NumberFormat *theFormat = 0;
  160.   
  161.   if(fgNumberFormat != 0) {
  162.     Mutex lock;
  163.     
  164.     if(fgNumberFormat != 0) {
  165.       theFormat = fgNumberFormat;
  166.       fgNumberFormat = 0; // We have exclusive right to this formatter.
  167.     }
  168.   }
  169.   
  170.   if(theFormat == 0) {
  171.     theFormat = NumberFormat::createInstance(Locale::US, status);
  172.     if(U_FAILURE(status))
  173.       return 0;
  174.     theFormat->setParseIntegerOnly(TRUE);
  175.   }
  176.   
  177.   return theFormat;
  178. }
  179.  
  180. void          
  181. umsg_releaseNumberFormat(NumberFormat *adopt)
  182. {
  183.   if(fgNumberFormat == 0) {
  184.     Mutex lock;
  185.     
  186.     if(fgNumberFormat == 0) {
  187.       fgNumberFormat = adopt;
  188.       adopt = 0;
  189.     }
  190.   }
  191.   
  192.   delete adopt;
  193. }
  194.  
  195. int32_t
  196. umsg_stoi(const UnicodeString& string,
  197.       UErrorCode& status)
  198. {
  199.   NumberFormat *myFormat = umsg_getNumberFormat(status);
  200.   
  201.   if(U_FAILURE(status))
  202.     return -1; // OK?
  203.   
  204.   Formattable result;
  205.   // Uses the global number formatter to parse the string.
  206.   // Note: We assume here that parse() is thread-safe.
  207.   myFormat->parse(string, result, status);
  208.   umsg_releaseNumberFormat(myFormat);
  209.   
  210.   int32_t value = 0;
  211.   if(U_SUCCESS(status) && result.getType() == Formattable::kLong)
  212.     value = result.getLong();
  213.   
  214.   return value;
  215. }
  216.  
  217. UnicodeString&
  218. umsg_itos(int32_t i,
  219.       UnicodeString& string)
  220. {
  221.   UErrorCode status = U_ZERO_ERROR;
  222.   NumberFormat *myFormat = umsg_getNumberFormat(status);
  223.   
  224.   if(U_FAILURE(status))
  225.     return (string = "<ERROR>");
  226.   
  227.   myFormat->format(i, string);
  228.   umsg_releaseNumberFormat(myFormat);
  229.   
  230.   return string;
  231. }
  232.  
  233. // ==========
  234.  
  235. #define MAX_ARGS 10
  236.  
  237. // Eventually, message format should be rewritten natively in C.
  238. // For now, this is a hack that should work:
  239. //  1. Parse the pattern, determining the argument types
  240. //  2. Create a Formattable array with the varargs
  241. //  3. Call through to the existing C++ code
  242. //
  243. // Right now this imposes the same limit as MessageFormat in C++
  244. // Namely, only MAX_ARGS arguments are supported
  245. U_CAPI int32_t
  246. u_formatMessage(    const    char        *locale,
  247.             const    UChar        *pattern,
  248.                 int32_t        patternLength,
  249.                 UChar        *result,
  250.                 int32_t        resultLength,
  251.                 UErrorCode    *status,
  252.                 ...)
  253. {
  254.   if(U_FAILURE(*status)) return -1;
  255.  
  256.   int32_t patLen = (patternLength == -1 ? u_strlen(pattern) : patternLength);
  257.   int32_t actLen;
  258.  
  259.   // ========================================
  260.   // Begin pseudo-parser
  261.  
  262.   // This is a simplified version of the C++ pattern parser
  263.   // All it does is look for an unquoted '{' and read the type
  264.  
  265.   va_list    ap;
  266.   int32_t     part         = 0;
  267.   bool_t     inQuote     = FALSE;
  268.   int32_t     braceStack     = 0;
  269.   const UChar     *pat         = pattern;
  270.   const UChar     *patLimit     = pattern + patLen;
  271.   int32_t    bracePos    = 0;
  272.   int32_t    count        = 0;
  273.   Formattable    args        [ MAX_ARGS ];
  274.   Formattable::Type argTypes     [ MAX_ARGS ];
  275.  
  276.  
  277.   // set the types to a bogus value initially (no such type as kArray from C)
  278.   for(int32_t j = 0; j < MAX_ARGS; ++j)
  279.     argTypes[j] = Formattable::kArray;
  280.  
  281.   // pseudo-parse the pattern
  282.   while(pat < patLimit) {
  283.     if(part == 0) {
  284.       if(*pat == 0x0027 /*'\''*/) {
  285.     // handle double quotes
  286.     if( (pat + 1) < patLimit && *(pat + 1) == 0x0027 /*'\''*/)
  287.       *pat++;
  288.     else
  289.       inQuote = ! inQuote;
  290.       }  
  291.       else if(*pat == 0x007B /*'{'*/ && ! inQuote) {
  292.     part = 1;
  293.     bracePos = (pat - pattern);
  294.       }
  295.     }
  296.     else if(inQuote) {              // just copy quotes in parts
  297.       if(*pat == 0x0027 /*'\''*/)
  298.     inQuote = FALSE;
  299.     } 
  300.     else {
  301.       switch (*pat) {
  302.     
  303.       case 0x002C /*','*/:
  304.     if(part < 3)
  305.       part += 1;
  306.     break;
  307.  
  308.       case 0x007B /*'{'*/:
  309.     ++braceStack;
  310.     break;
  311.  
  312.       case 0x007D /*'}'*/:
  313.     if(braceStack == 0) {
  314.       part = 0;
  315.       // found a close brace, determine the argument type enclosed
  316.       // and the numeric ID of the argument
  317.       Formattable::Type type = 
  318.         matchType(pattern, bracePos, (pat - pattern));
  319.  
  320.       // the numeric ID is important, because if the pattern has a 
  321.       // section like "{0} {0} {0}" we only want to get one argument
  322.       // from the variable argument list, despite the fact that
  323.       // it is in the pattern three times
  324.       int32_t argNum = umsg_stoi(pattern + bracePos + 1, *status);
  325.  
  326.       if(argNum >= MAX_ARGS) {
  327.         *status = U_INTERNAL_PROGRAM_ERROR;
  328.         return -1;
  329.       }
  330.       
  331.       // register the type of this argument in our list
  332.       argTypes[argNum] = type;
  333.       
  334.       // adjust argument count
  335.       count = ( (argNum + 1) > count ? (argNum + 1) : count);
  336.     }
  337.     else
  338.       --braceStack;
  339.     break;
  340.     
  341.       case 0x0027 /*'\''*/:
  342.     inQuote = TRUE;
  343.     break;
  344.       }
  345.     }
  346.     
  347.     // increment position in pattern
  348.     *pat++;
  349.   } 
  350.  
  351.   // detect any unmatched braces in the pattern
  352.   if(braceStack == 0 && part != 0) {
  353.     *status = U_INVALID_FORMAT_ERROR;
  354.     return -1;
  355.   }
  356.  
  357.   // start vararg processing
  358.   va_start(ap, status);
  359.  
  360.   // iterate through the vararg list, and get the arguments out
  361.   for(int32_t i = 0; i < count; ++i) {
  362.     
  363.     UChar *stringVal;
  364.     
  365.     switch(argTypes[i]) {
  366.     case Formattable::kDate:
  367.       args[i].setDate(va_arg(ap, UDate));
  368.       break;
  369.       
  370.     case Formattable::kDouble:
  371.       args[i].setDouble(va_arg(ap, double));
  372.       break;
  373.       
  374.     case Formattable::kLong:
  375.       args[i].setLong(va_arg(ap, int32_t));
  376.       break;
  377.       
  378.     case Formattable::kString:
  379.       // For some reason, a temporary is needed
  380.       stringVal = va_arg(ap, UChar*);
  381.       args[i].setString(stringVal);
  382.       break;
  383.       
  384.     case Formattable::kArray:
  385.       // throw away this argument
  386.       // this is highly platform-dependent, and probably won't work
  387.       // so, if you try to skip arguments in the list (and not use them)
  388.       // you'll probably crash
  389.       va_arg(ap, int);
  390.       break;
  391.     }
  392.   }
  393.   
  394.   // end vararg processing
  395.   va_end(ap);
  396.  
  397.   // End pseudo-parser
  398.   // ========================================
  399.  
  400.   // just call through to the C++ implementation
  401.   UnicodeString patString((UChar*)pattern, patLen, patLen);
  402.   MessageFormat fmt(patString, Locale().init(locale), *status);
  403.   UnicodeString res(result, 0, resultLength);
  404.   FieldPosition fp;
  405.   fmt.format(args, count, res, fp, *status);
  406.  
  407.   T_fillOutputParams(&res, result, resultLength, &actLen, status);
  408.   return actLen;
  409. }
  410.  
  411.  
  412. // For parse, do the reverse of format:
  413. //  1. Call through to the C++ APIs
  414. //  2. Just assume the user passed in enough arguments.
  415. //  3. Iterate through each formattable returned, and assign to the arguments
  416. U_CAPI void 
  417. u_parseMessage(    const    char        *locale,
  418.         const    UChar        *pattern,
  419.             int32_t        patternLength,
  420.         const    UChar        *source,
  421.             int32_t        sourceLength,
  422.             UErrorCode    *status,
  423.             ...)
  424. {
  425.   if(U_FAILURE(*status)) return;
  426.  
  427.   int32_t patLen = (patternLength == -1 ? u_strlen(pattern) : patternLength);
  428.   int32_t srcLen = (sourceLength == -1 ? u_strlen(source) : sourceLength);
  429.   
  430.   UnicodeString patString((UChar*)pattern, patLen, patLen);
  431.   MessageFormat fmt(patString, Locale().init(locale), *status);
  432.   UnicodeString srcString((UChar*)source, srcLen, srcLen);
  433.   int32_t count = 0;
  434.   Formattable *args = fmt.parse(srcString, count, *status);
  435.  
  436.   // start vararg processing
  437.   va_list ap;
  438.   va_start(ap, status);
  439.  
  440.   UDate *aDate;
  441.   double *aDouble;
  442.   UChar *aString;
  443.   UnicodeString temp;
  444.  
  445.   // assign formattables to varargs
  446.   for(int32_t i = 0; i < count; i++) {
  447.     switch(args[i].getType()) {
  448.       
  449.     case Formattable::kDate:
  450.       aDate = va_arg(ap, UDate*);
  451.       *aDate = args[i].getDate();
  452.       break;
  453.       
  454.     case Formattable::kDouble:
  455.       aDouble = va_arg(ap, double*);
  456.       *aDouble = args[i].getDouble();
  457.       break;
  458.       
  459.     case Formattable::kLong:
  460.       // always assume doubles for parsing
  461.       aDouble = va_arg(ap, double*);
  462.       *aDouble = (double) args[i].getLong();
  463.       break;
  464.       
  465.     case Formattable::kString:
  466.       aString = va_arg(ap, UChar*);
  467.       args[i].getString(temp);
  468.       u_strcpy(aString, temp.getUChars());
  469.       break;
  470.       
  471.       // better not happen!
  472.     case Formattable::kArray:
  473.       // DIE
  474.       break;
  475.     }
  476.   }
  477.   
  478.   // end vararg processing
  479.   va_end(ap);
  480.  
  481.   // clean up
  482.   delete [] args;
  483. }
  484.