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