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