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