• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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);
hasArgTypeConflicts(const MessageFormat & m)45     static UBool hasArgTypeConflicts(const MessageFormat& m) {
46         return m.hasArgTypeConflicts;
47     }
48 };
49 const Formattable::Type*
getArgTypeList(const MessageFormat & m,int32_t & count)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
u_formatMessage(const char * locale,const UChar * pattern,int32_t patternLength,UChar * result,int32_t resultLength,UErrorCode * status,...)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
u_vformatMessage(const char * locale,const UChar * pattern,int32_t patternLength,UChar * result,int32_t resultLength,va_list ap,UErrorCode * status)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
u_formatMessageWithError(const char * locale,const UChar * pattern,int32_t patternLength,UChar * result,int32_t resultLength,UParseError * parseError,UErrorCode * status,...)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
u_vformatMessageWithError(const char * locale,const UChar * pattern,int32_t patternLength,UChar * result,int32_t resultLength,UParseError * parseError,va_list ap,UErrorCode * status)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
u_parseMessage(const char * locale,const UChar * pattern,int32_t patternLength,const UChar * source,int32_t sourceLength,UErrorCode * status,...)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
u_vparseMessage(const char * locale,const UChar * pattern,int32_t patternLength,const UChar * source,int32_t sourceLength,va_list ap,UErrorCode * status)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
u_parseMessageWithError(const char * locale,const UChar * pattern,int32_t patternLength,const UChar * source,int32_t sourceLength,UParseError * error,UErrorCode * status,...)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
u_vparseMessageWithError(const char * locale,const UChar * pattern,int32_t patternLength,const UChar * source,int32_t sourceLength,va_list ap,UParseError * error,UErrorCode * status)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
umsg_open(const UChar * pattern,int32_t patternLength,const char * locale,UParseError * parseError,UErrorCode * status)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
umsg_close(UMessageFormat * format)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
umsg_clone(const UMessageFormat * fmt,UErrorCode * status)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
umsg_setLocale(UMessageFormat * fmt,const char * locale)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
umsg_getLocale(const UMessageFormat * fmt)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
umsg_applyPattern(UMessageFormat * fmt,const UChar * pattern,int32_t patternLength,UParseError * parseError,UErrorCode * status)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
umsg_toPattern(const UMessageFormat * fmt,UChar * result,int32_t resultLength,UErrorCode * status)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
umsg_format(const UMessageFormat * fmt,UChar * result,int32_t resultLength,UErrorCode * status,...)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
umsg_vformat(const UMessageFormat * fmt,UChar * result,int32_t resultLength,va_list ap,UErrorCode * status)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
umsg_parse(const UMessageFormat * fmt,const UChar * source,int32_t sourceLength,int32_t * count,UErrorCode * status,...)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
umsg_vparse(const UMessageFormat * fmt,const UChar * source,int32_t sourceLength,int32_t * count,va_list ap,UErrorCode * status)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 
umsg_autoQuoteApostrophe(const UChar * pattern,int32_t patternLength,UChar * dest,int32_t destCapacity,UErrorCode * ec)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