Home | History | Annotate | Download | only in i18n
      1 /*
      2 *******************************************************************************
      3 *
      4 *   Copyright (C) 1999-2011, International Business Machines
      5 *   Corporation and others.  All Rights Reserved.
      6 *
      7 *******************************************************************************
      8 *   file name:  umsg.cpp
      9 *   encoding:   US-ASCII
     10 *   tab size:   8 (not used)
     11 *   indentation:4
     12 *
     13 * This is a C wrapper to MessageFormat C++ API.
     14 *
     15 *   Change history:
     16 *
     17 *   08/5/2001  Ram         Added C wrappers for C++ API. Changed implementation of old API's
     18 *                          Removed pattern parser.
     19 *
     20 */
     21 
     22 #include "unicode/utypes.h"
     23 
     24 #if !UCONFIG_NO_FORMATTING
     25 
     26 #include "unicode/umsg.h"
     27 #include "unicode/ustring.h"
     28 #include "unicode/fmtable.h"
     29 #include "unicode/msgfmt.h"
     30 #include "unicode/unistr.h"
     31 #include "cpputils.h"
     32 #include "uassert.h"
     33 #include "ustr_imp.h"
     34 
     35 U_NAMESPACE_BEGIN
     36 /**
     37  * This class isolates our access to private internal methods of
     38  * MessageFormat.  It is never instantiated; it exists only for C++
     39  * access management.
     40  */
     41 class MessageFormatAdapter {
     42 public:
     43     static const Formattable::Type* getArgTypeList(const MessageFormat& m,
     44                                                    int32_t& count);
     45     static UBool hasArgTypeConflicts(const MessageFormat& m) {
     46         return m.hasArgTypeConflicts;
     47     }
     48 };
     49 const Formattable::Type*
     50 MessageFormatAdapter::getArgTypeList(const MessageFormat& m,
     51                                      int32_t& count) {
     52     return m.getArgTypeList(count);
     53 }
     54 U_NAMESPACE_END
     55 
     56 U_NAMESPACE_USE
     57 
     58 U_CAPI int32_t
     59 u_formatMessage(const char  *locale,
     60                 const UChar *pattern,
     61                 int32_t     patternLength,
     62                 UChar       *result,
     63                 int32_t     resultLength,
     64                 UErrorCode  *status,
     65                 ...)
     66 {
     67     va_list    ap;
     68     int32_t actLen;
     69     //argument checking defered to subsequent method calls
     70     // start vararg processing
     71     va_start(ap, status);
     72 
     73     actLen = u_vformatMessage(locale,pattern,patternLength,result,resultLength,ap,status);
     74     // end vararg processing
     75     va_end(ap);
     76 
     77     return actLen;
     78 }
     79 
     80 U_CAPI int32_t U_EXPORT2
     81 u_vformatMessage(   const char  *locale,
     82                     const UChar *pattern,
     83                     int32_t     patternLength,
     84                     UChar       *result,
     85                     int32_t     resultLength,
     86                     va_list     ap,
     87                     UErrorCode  *status)
     88 
     89 {
     90     //argument checking defered to subsequent method calls
     91     UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,NULL,status);
     92     int32_t retVal = umsg_vformat(fmt,result,resultLength,ap,status);
     93     umsg_close(fmt);
     94     return retVal;
     95 }
     96 
     97 U_CAPI int32_t
     98 u_formatMessageWithError(const char *locale,
     99                         const UChar *pattern,
    100                         int32_t     patternLength,
    101                         UChar       *result,
    102                         int32_t     resultLength,
    103                         UParseError *parseError,
    104                         UErrorCode  *status,
    105                         ...)
    106 {
    107     va_list    ap;
    108     int32_t actLen;
    109     //argument checking defered to subsequent method calls
    110     // start vararg processing
    111     va_start(ap, status);
    112 
    113     actLen = u_vformatMessageWithError(locale,pattern,patternLength,result,resultLength,parseError,ap,status);
    114 
    115     // end vararg processing
    116     va_end(ap);
    117     return actLen;
    118 }
    119 
    120 U_CAPI int32_t U_EXPORT2
    121 u_vformatMessageWithError(  const char  *locale,
    122                             const UChar *pattern,
    123                             int32_t     patternLength,
    124                             UChar       *result,
    125                             int32_t     resultLength,
    126                             UParseError *parseError,
    127                             va_list     ap,
    128                             UErrorCode  *status)
    129 
    130 {
    131     //argument checking defered to subsequent method calls
    132     UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,parseError,status);
    133     int32_t retVal = umsg_vformat(fmt,result,resultLength,ap,status);
    134     umsg_close(fmt);
    135     return retVal;
    136 }
    137 
    138 
    139 // For parse, do the reverse of format:
    140 //  1. Call through to the C++ APIs
    141 //  2. Just assume the user passed in enough arguments.
    142 //  3. Iterate through each formattable returned, and assign to the arguments
    143 U_CAPI void
    144 u_parseMessage( const char   *locale,
    145                 const UChar  *pattern,
    146                 int32_t      patternLength,
    147                 const UChar  *source,
    148                 int32_t      sourceLength,
    149                 UErrorCode   *status,
    150                 ...)
    151 {
    152     va_list    ap;
    153     //argument checking defered to subsequent method calls
    154 
    155     // start vararg processing
    156     va_start(ap, status);
    157 
    158     u_vparseMessage(locale,pattern,patternLength,source,sourceLength,ap,status);
    159     // end vararg processing
    160     va_end(ap);
    161 }
    162 
    163 U_CAPI void U_EXPORT2
    164 u_vparseMessage(const char  *locale,
    165                 const UChar *pattern,
    166                 int32_t     patternLength,
    167                 const UChar *source,
    168                 int32_t     sourceLength,
    169                 va_list     ap,
    170                 UErrorCode  *status)
    171 {
    172     //argument checking defered to subsequent method calls
    173     UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,NULL,status);
    174     int32_t count = 0;
    175     umsg_vparse(fmt,source,sourceLength,&count,ap,status);
    176     umsg_close(fmt);
    177 }
    178 
    179 U_CAPI void
    180 u_parseMessageWithError(const char  *locale,
    181                         const UChar *pattern,
    182                         int32_t     patternLength,
    183                         const UChar *source,
    184                         int32_t     sourceLength,
    185                         UParseError *error,
    186                         UErrorCode  *status,
    187                         ...)
    188 {
    189     va_list    ap;
    190 
    191     //argument checking defered to subsequent method calls
    192 
    193     // start vararg processing
    194     va_start(ap, status);
    195 
    196     u_vparseMessageWithError(locale,pattern,patternLength,source,sourceLength,ap,error,status);
    197     // end vararg processing
    198     va_end(ap);
    199 }
    200 U_CAPI void U_EXPORT2
    201 u_vparseMessageWithError(const char  *locale,
    202                          const UChar *pattern,
    203                          int32_t     patternLength,
    204                          const UChar *source,
    205                          int32_t     sourceLength,
    206                          va_list     ap,
    207                          UParseError *error,
    208                          UErrorCode* status)
    209 {
    210     //argument checking defered to subsequent method calls
    211     UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,error,status);
    212     int32_t count = 0;
    213     umsg_vparse(fmt,source,sourceLength,&count,ap,status);
    214     umsg_close(fmt);
    215 }
    216 //////////////////////////////////////////////////////////////////////////////////
    217 //
    218 //  Message format C API
    219 //
    220 /////////////////////////////////////////////////////////////////////////////////
    221 
    222 
    223 U_CAPI UMessageFormat* U_EXPORT2
    224 umsg_open(  const UChar     *pattern,
    225             int32_t         patternLength,
    226             const  char     *locale,
    227             UParseError     *parseError,
    228             UErrorCode      *status)
    229 {
    230     //check arguments
    231     if(status==NULL || U_FAILURE(*status))
    232     {
    233       return 0;
    234     }
    235     if(pattern==NULL||patternLength<-1){
    236         *status=U_ILLEGAL_ARGUMENT_ERROR;
    237         return 0;
    238     }
    239 
    240     UParseError tErr;
    241     if(parseError==NULL)
    242     {
    243         parseError = &tErr;
    244     }
    245 
    246     int32_t len = (patternLength == -1 ? u_strlen(pattern) : patternLength);
    247     UnicodeString patString(patternLength == -1, pattern, len);
    248 
    249     MessageFormat* retVal = new MessageFormat(patString,Locale(locale),*parseError,*status);
    250     if(retVal == NULL) {
    251         *status = U_MEMORY_ALLOCATION_ERROR;
    252         return NULL;
    253     }
    254     if (U_SUCCESS(*status) && MessageFormatAdapter::hasArgTypeConflicts(*retVal)) {
    255         *status = U_ARGUMENT_TYPE_MISMATCH;
    256     }
    257     return (UMessageFormat*)retVal;
    258 }
    259 
    260 U_CAPI void U_EXPORT2
    261 umsg_close(UMessageFormat* format)
    262 {
    263     //check arguments
    264     if(format==NULL){
    265         return;
    266     }
    267     delete (MessageFormat*) format;
    268 }
    269 
    270 U_CAPI UMessageFormat U_EXPORT2
    271 umsg_clone(const UMessageFormat *fmt,
    272            UErrorCode *status)
    273 {
    274     //check arguments
    275     if(status==NULL || U_FAILURE(*status)){
    276         return NULL;
    277     }
    278     if(fmt==NULL){
    279         *status = U_ILLEGAL_ARGUMENT_ERROR;
    280         return NULL;
    281     }
    282     UMessageFormat retVal = (UMessageFormat)((MessageFormat*)fmt)->clone();
    283     if(retVal == 0) {
    284         *status = U_MEMORY_ALLOCATION_ERROR;
    285         return 0;
    286     }
    287     return retVal;
    288 }
    289 
    290 U_CAPI void  U_EXPORT2
    291 umsg_setLocale(UMessageFormat *fmt, const char* locale)
    292 {
    293     //check arguments
    294     if(fmt==NULL){
    295         return;
    296     }
    297     ((MessageFormat*)fmt)->setLocale(Locale(locale));
    298 }
    299 
    300 U_CAPI const char*  U_EXPORT2
    301 umsg_getLocale(const UMessageFormat *fmt)
    302 {
    303     //check arguments
    304     if(fmt==NULL){
    305         return "";
    306     }
    307     return ((const MessageFormat*)fmt)->getLocale().getName();
    308 }
    309 
    310 U_CAPI void  U_EXPORT2
    311 umsg_applyPattern(UMessageFormat *fmt,
    312                            const UChar* pattern,
    313                            int32_t patternLength,
    314                            UParseError* parseError,
    315                            UErrorCode* status)
    316 {
    317     //check arguments
    318     UParseError tErr;
    319     if(status ==NULL||U_FAILURE(*status)){
    320         return ;
    321     }
    322     if(fmt==NULL||pattern==NULL||patternLength<-1){
    323         *status=U_ILLEGAL_ARGUMENT_ERROR;
    324         return ;
    325     }
    326 
    327     if(parseError==NULL){
    328       parseError = &tErr;
    329     }
    330     if(patternLength<-1){
    331         patternLength=u_strlen(pattern);
    332     }
    333 
    334     ((MessageFormat*)fmt)->applyPattern(UnicodeString(pattern,patternLength),*parseError,*status);
    335 }
    336 
    337 U_CAPI int32_t  U_EXPORT2
    338 umsg_toPattern(const UMessageFormat *fmt,
    339                UChar* result,
    340                int32_t resultLength,
    341                UErrorCode* status)
    342 {
    343     //check arguments
    344     if(status ==NULL||U_FAILURE(*status)){
    345         return -1;
    346     }
    347     if(fmt==NULL||resultLength<0 || (resultLength>0 && result==0)){
    348         *status=U_ILLEGAL_ARGUMENT_ERROR;
    349         return -1;
    350     }
    351 
    352 
    353     UnicodeString res;
    354     if(!(result==NULL && resultLength==0)) {
    355         // NULL destination for pure preflighting: empty dummy string
    356         // otherwise, alias the destination buffer
    357         res.setTo(result, 0, resultLength);
    358     }
    359     ((const MessageFormat*)fmt)->toPattern(res);
    360     return res.extract(result, resultLength, *status);
    361 }
    362 
    363 U_CAPI int32_t
    364 umsg_format(    const UMessageFormat *fmt,
    365                 UChar          *result,
    366                 int32_t        resultLength,
    367                 UErrorCode     *status,
    368                 ...)
    369 {
    370     va_list    ap;
    371     int32_t actLen;
    372     //argument checking defered to last method call umsg_vformat which
    373     //saves time when arguments are valid and we dont care when arguments are not
    374     //since we return an error anyway
    375 
    376 
    377     // start vararg processing
    378     va_start(ap, status);
    379 
    380     actLen = umsg_vformat(fmt,result,resultLength,ap,status);
    381 
    382     // end vararg processing
    383     va_end(ap);
    384 
    385     return actLen;
    386 }
    387 
    388 U_CAPI int32_t U_EXPORT2
    389 umsg_vformat(   const UMessageFormat *fmt,
    390                 UChar          *result,
    391                 int32_t        resultLength,
    392                 va_list        ap,
    393                 UErrorCode     *status)
    394 {
    395     //check arguments
    396     if(status==0 || U_FAILURE(*status))
    397     {
    398         return -1;
    399     }
    400     if(fmt==NULL||resultLength<0 || (resultLength>0 && result==0)) {
    401         *status=U_ILLEGAL_ARGUMENT_ERROR;
    402         return -1;
    403     }
    404 
    405     int32_t count =0;
    406     const Formattable::Type* argTypes =
    407         MessageFormatAdapter::getArgTypeList(*(const MessageFormat*)fmt, count);
    408     // Allocate at least one element.  Allocating an array of length
    409     // zero causes problems on some platforms (e.g. Win32).
    410     Formattable* args = new Formattable[count ? count : 1];
    411 
    412     // iterate through the vararg list, and get the arguments out
    413     for(int32_t i = 0; i < count; ++i) {
    414 
    415         UChar *stringVal;
    416         double tDouble=0;
    417         int32_t tInt =0;
    418         int64_t tInt64 = 0;
    419         UDate tempDate = 0;
    420         switch(argTypes[i]) {
    421         case Formattable::kDate:
    422             tempDate = va_arg(ap, UDate);
    423             args[i].setDate(tempDate);
    424             break;
    425 
    426         case Formattable::kDouble:
    427             tDouble =va_arg(ap, double);
    428             args[i].setDouble(tDouble);
    429             break;
    430 
    431         case Formattable::kLong:
    432             tInt = va_arg(ap, int32_t);
    433             args[i].setLong(tInt);
    434             break;
    435 
    436         case Formattable::kInt64:
    437             tInt64 = va_arg(ap, int64_t);
    438             args[i].setInt64(tInt64);
    439             break;
    440 
    441         case Formattable::kString:
    442             // For some reason, a temporary is needed
    443             stringVal = va_arg(ap, UChar*);
    444             if(stringVal){
    445                 args[i].setString(stringVal);
    446             }else{
    447                 *status=U_ILLEGAL_ARGUMENT_ERROR;
    448             }
    449             break;
    450 
    451         case Formattable::kArray:
    452             // throw away this argument
    453             // this is highly platform-dependent, and probably won't work
    454             // so, if you try to skip arguments in the list (and not use them)
    455             // you'll probably crash
    456             va_arg(ap, int);
    457             break;
    458 
    459         case Formattable::kObject:
    460         default:
    461             // This will never happen because MessageFormat doesn't
    462             // support kObject.  When MessageFormat is changed to
    463             // understand MeasureFormats, modify this code to do the
    464             // right thing. [alan]
    465             U_ASSERT(FALSE);
    466             *status=U_ILLEGAL_ARGUMENT_ERROR;
    467             break;
    468         }
    469     }
    470     UnicodeString resultStr;
    471     FieldPosition fieldPosition(0);
    472 
    473     /* format the message */
    474     ((const MessageFormat*)fmt)->format(args,count,resultStr,fieldPosition,*status);
    475 
    476     delete[] args;
    477 
    478     if(U_FAILURE(*status)){
    479         return -1;
    480     }
    481 
    482     return resultStr.extract(result, resultLength, *status);
    483 }
    484 
    485 U_CAPI void
    486 umsg_parse( const UMessageFormat *fmt,
    487             const UChar    *source,
    488             int32_t        sourceLength,
    489             int32_t        *count,
    490             UErrorCode     *status,
    491             ...)
    492 {
    493     va_list    ap;
    494     //argument checking defered to last method call umsg_vparse which
    495     //saves time when arguments are valid and we dont care when arguments are not
    496     //since we return an error anyway
    497 
    498     // start vararg processing
    499     va_start(ap, status);
    500 
    501     umsg_vparse(fmt,source,sourceLength,count,ap,status);
    502 
    503     // end vararg processing
    504     va_end(ap);
    505 }
    506 
    507 U_CAPI void U_EXPORT2
    508 umsg_vparse(const UMessageFormat *fmt,
    509             const UChar    *source,
    510             int32_t        sourceLength,
    511             int32_t        *count,
    512             va_list        ap,
    513             UErrorCode     *status)
    514 {
    515     //check arguments
    516     if(status==NULL||U_FAILURE(*status))
    517     {
    518         return;
    519     }
    520     if(fmt==NULL||source==NULL || sourceLength<-1 || count==NULL){
    521         *status=U_ILLEGAL_ARGUMENT_ERROR;
    522         return;
    523     }
    524     if(sourceLength==-1){
    525         sourceLength=u_strlen(source);
    526     }
    527 
    528     UnicodeString srcString(source,sourceLength);
    529     Formattable *args = ((const MessageFormat*)fmt)->parse(source,*count,*status);
    530     UDate *aDate;
    531     double *aDouble;
    532     UChar *aString;
    533     int32_t* aInt;
    534     int64_t* aInt64;
    535     UnicodeString temp;
    536     int len =0;
    537     // assign formattables to varargs
    538     for(int32_t i = 0; i < *count; i++) {
    539         switch(args[i].getType()) {
    540 
    541         case Formattable::kDate:
    542             aDate = va_arg(ap, UDate*);
    543             if(aDate){
    544                 *aDate = args[i].getDate();
    545             }else{
    546                 *status=U_ILLEGAL_ARGUMENT_ERROR;
    547             }
    548             break;
    549 
    550         case Formattable::kDouble:
    551             aDouble = va_arg(ap, double*);
    552             if(aDouble){
    553                 *aDouble = args[i].getDouble();
    554             }else{
    555                 *status=U_ILLEGAL_ARGUMENT_ERROR;
    556             }
    557             break;
    558 
    559         case Formattable::kLong:
    560             aInt = va_arg(ap, int32_t*);
    561             if(aInt){
    562                 *aInt = (int32_t) args[i].getLong();
    563             }else{
    564                 *status=U_ILLEGAL_ARGUMENT_ERROR;
    565             }
    566             break;
    567 
    568         case Formattable::kInt64:
    569             aInt64 = va_arg(ap, int64_t*);
    570             if(aInt64){
    571                 *aInt64 = args[i].getInt64();
    572             }else{
    573                 *status=U_ILLEGAL_ARGUMENT_ERROR;
    574             }
    575             break;
    576 
    577         case Formattable::kString:
    578             aString = va_arg(ap, UChar*);
    579             if(aString){
    580                 args[i].getString(temp);
    581                 len = temp.length();
    582                 temp.extract(0,len,aString);
    583                 aString[len]=0;
    584             }else{
    585                 *status= U_ILLEGAL_ARGUMENT_ERROR;
    586             }
    587             break;
    588 
    589         case Formattable::kObject:
    590             // This will never happen because MessageFormat doesn't
    591             // support kObject.  When MessageFormat is changed to
    592             // understand MeasureFormats, modify this code to do the
    593             // right thing. [alan]
    594             U_ASSERT(FALSE);
    595             break;
    596 
    597         // better not happen!
    598         case Formattable::kArray:
    599             U_ASSERT(FALSE);
    600             break;
    601         }
    602     }
    603 
    604     // clean up
    605     delete [] args;
    606 }
    607 
    608 #define SINGLE_QUOTE      ((UChar)0x0027)
    609 #define CURLY_BRACE_LEFT  ((UChar)0x007B)
    610 #define CURLY_BRACE_RIGHT ((UChar)0x007D)
    611 
    612 #define STATE_INITIAL 0
    613 #define STATE_SINGLE_QUOTE 1
    614 #define STATE_IN_QUOTE 2
    615 #define STATE_MSG_ELEMENT 3
    616 
    617 #define MAppend(c) if (len < destCapacity) dest[len++] = c; else len++
    618 
    619 int32_t umsg_autoQuoteApostrophe(const UChar* pattern,
    620                  int32_t patternLength,
    621                  UChar* dest,
    622                  int32_t destCapacity,
    623                  UErrorCode* ec)
    624 {
    625     int32_t state = STATE_INITIAL;
    626     int32_t braceCount = 0;
    627     int32_t len = 0;
    628 
    629     if (ec == NULL || U_FAILURE(*ec)) {
    630         return -1;
    631     }
    632 
    633     if (pattern == NULL || patternLength < -1 || (dest == NULL && destCapacity > 0)) {
    634         *ec = U_ILLEGAL_ARGUMENT_ERROR;
    635         return -1;
    636     }
    637 
    638     if (patternLength == -1) {
    639         patternLength = u_strlen(pattern);
    640     }
    641 
    642     for (int i = 0; i < patternLength; ++i) {
    643         UChar c = pattern[i];
    644         switch (state) {
    645         case STATE_INITIAL:
    646             switch (c) {
    647             case SINGLE_QUOTE:
    648                 state = STATE_SINGLE_QUOTE;
    649                 break;
    650             case CURLY_BRACE_LEFT:
    651                 state = STATE_MSG_ELEMENT;
    652                 ++braceCount;
    653                 break;
    654             }
    655             break;
    656 
    657         case STATE_SINGLE_QUOTE:
    658             switch (c) {
    659             case SINGLE_QUOTE:
    660                 state = STATE_INITIAL;
    661                 break;
    662             case CURLY_BRACE_LEFT:
    663             case CURLY_BRACE_RIGHT:
    664                 state = STATE_IN_QUOTE;
    665                 break;
    666             default:
    667                 MAppend(SINGLE_QUOTE);
    668                 state = STATE_INITIAL;
    669                 break;
    670             }
    671         break;
    672 
    673         case STATE_IN_QUOTE:
    674             switch (c) {
    675             case SINGLE_QUOTE:
    676                 state = STATE_INITIAL;
    677                 break;
    678             }
    679             break;
    680 
    681         case STATE_MSG_ELEMENT:
    682             switch (c) {
    683             case CURLY_BRACE_LEFT:
    684                 ++braceCount;
    685                 break;
    686             case CURLY_BRACE_RIGHT:
    687                 if (--braceCount == 0) {
    688                     state = STATE_INITIAL;
    689                 }
    690                 break;
    691             }
    692             break;
    693 
    694         default: // Never happens.
    695             break;
    696         }
    697 
    698         MAppend(c);
    699     }
    700 
    701     // End of scan
    702     if (state == STATE_SINGLE_QUOTE || state == STATE_IN_QUOTE) {
    703         MAppend(SINGLE_QUOTE);
    704     }
    705 
    706     return u_terminateUChars(dest, destCapacity, len, ec);
    707 }
    708 
    709 #endif /* #if !UCONFIG_NO_FORMATTING */
    710