45a6728cbe
X-SVN-Rev: 40491
713 lines
20 KiB
C++
713 lines
20 KiB
C++
// © 2016 and later: Unicode, Inc. and others.
|
|
// License & terms of use: http://www.unicode.org/copyright.html
|
|
/*
|
|
*******************************************************************************
|
|
*
|
|
* Copyright (C) 1999-2012, International Business Machines
|
|
* Corporation and others. All Rights Reserved.
|
|
*
|
|
*******************************************************************************
|
|
* file name: umsg.cpp
|
|
* encoding: UTF-8
|
|
* tab size: 8 (not used)
|
|
* indentation:4
|
|
*
|
|
* This is a C wrapper to MessageFormat C++ API.
|
|
*
|
|
* Change history:
|
|
*
|
|
* 08/5/2001 Ram Added C wrappers for C++ API. Changed implementation of old API's
|
|
* Removed pattern parser.
|
|
*
|
|
*/
|
|
|
|
#include "unicode/utypes.h"
|
|
|
|
#if !UCONFIG_NO_FORMATTING
|
|
|
|
#include "unicode/umsg.h"
|
|
#include "unicode/ustring.h"
|
|
#include "unicode/fmtable.h"
|
|
#include "unicode/msgfmt.h"
|
|
#include "unicode/unistr.h"
|
|
#include "cpputils.h"
|
|
#include "uassert.h"
|
|
#include "ustr_imp.h"
|
|
|
|
U_NAMESPACE_BEGIN
|
|
/**
|
|
* This class isolates our access to private internal methods of
|
|
* MessageFormat. It is never instantiated; it exists only for C++
|
|
* access management.
|
|
*/
|
|
class MessageFormatAdapter {
|
|
public:
|
|
static const Formattable::Type* getArgTypeList(const MessageFormat& m,
|
|
int32_t& count);
|
|
static UBool hasArgTypeConflicts(const MessageFormat& m) {
|
|
return m.hasArgTypeConflicts;
|
|
}
|
|
};
|
|
const Formattable::Type*
|
|
MessageFormatAdapter::getArgTypeList(const MessageFormat& m,
|
|
int32_t& count) {
|
|
return m.getArgTypeList(count);
|
|
}
|
|
U_NAMESPACE_END
|
|
|
|
U_NAMESPACE_USE
|
|
|
|
U_CAPI int32_t
|
|
u_formatMessage(const char *locale,
|
|
const UChar *pattern,
|
|
int32_t patternLength,
|
|
UChar *result,
|
|
int32_t resultLength,
|
|
UErrorCode *status,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
int32_t actLen;
|
|
//argument checking defered to subsequent method calls
|
|
// start vararg processing
|
|
va_start(ap, status);
|
|
|
|
actLen = u_vformatMessage(locale,pattern,patternLength,result,resultLength,ap,status);
|
|
// end vararg processing
|
|
va_end(ap);
|
|
|
|
return actLen;
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
|
//argument checking defered to subsequent method calls
|
|
UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,NULL,status);
|
|
int32_t retVal = umsg_vformat(fmt,result,resultLength,ap,status);
|
|
umsg_close(fmt);
|
|
return retVal;
|
|
}
|
|
|
|
U_CAPI int32_t
|
|
u_formatMessageWithError(const char *locale,
|
|
const UChar *pattern,
|
|
int32_t patternLength,
|
|
UChar *result,
|
|
int32_t resultLength,
|
|
UParseError *parseError,
|
|
UErrorCode *status,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
int32_t actLen;
|
|
//argument checking defered to subsequent method calls
|
|
// start vararg processing
|
|
va_start(ap, status);
|
|
|
|
actLen = u_vformatMessageWithError(locale,pattern,patternLength,result,resultLength,parseError,ap,status);
|
|
|
|
// end vararg processing
|
|
va_end(ap);
|
|
return actLen;
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
|
//argument checking defered to subsequent method calls
|
|
UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,parseError,status);
|
|
int32_t retVal = umsg_vformat(fmt,result,resultLength,ap,status);
|
|
umsg_close(fmt);
|
|
return retVal;
|
|
}
|
|
|
|
|
|
// For parse, do the reverse of format:
|
|
// 1. Call through to the C++ APIs
|
|
// 2. Just assume the user passed in enough arguments.
|
|
// 3. Iterate through each formattable returned, and assign to the arguments
|
|
U_CAPI void
|
|
u_parseMessage( const char *locale,
|
|
const UChar *pattern,
|
|
int32_t patternLength,
|
|
const UChar *source,
|
|
int32_t sourceLength,
|
|
UErrorCode *status,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
//argument checking defered to subsequent method calls
|
|
|
|
// start vararg processing
|
|
va_start(ap, status);
|
|
|
|
u_vparseMessage(locale,pattern,patternLength,source,sourceLength,ap,status);
|
|
// end vararg processing
|
|
va_end(ap);
|
|
}
|
|
|
|
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)
|
|
{
|
|
//argument checking defered to subsequent method calls
|
|
UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,NULL,status);
|
|
int32_t count = 0;
|
|
umsg_vparse(fmt,source,sourceLength,&count,ap,status);
|
|
umsg_close(fmt);
|
|
}
|
|
|
|
U_CAPI void
|
|
u_parseMessageWithError(const char *locale,
|
|
const UChar *pattern,
|
|
int32_t patternLength,
|
|
const UChar *source,
|
|
int32_t sourceLength,
|
|
UParseError *error,
|
|
UErrorCode *status,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
|
|
//argument checking defered to subsequent method calls
|
|
|
|
// start vararg processing
|
|
va_start(ap, status);
|
|
|
|
u_vparseMessageWithError(locale,pattern,patternLength,source,sourceLength,ap,error,status);
|
|
// end vararg processing
|
|
va_end(ap);
|
|
}
|
|
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)
|
|
{
|
|
//argument checking defered to subsequent method calls
|
|
UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,error,status);
|
|
int32_t count = 0;
|
|
umsg_vparse(fmt,source,sourceLength,&count,ap,status);
|
|
umsg_close(fmt);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Message format C API
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
U_CAPI UMessageFormat* U_EXPORT2
|
|
umsg_open( const UChar *pattern,
|
|
int32_t patternLength,
|
|
const char *locale,
|
|
UParseError *parseError,
|
|
UErrorCode *status)
|
|
{
|
|
//check arguments
|
|
if(status==NULL || U_FAILURE(*status))
|
|
{
|
|
return 0;
|
|
}
|
|
if(pattern==NULL||patternLength<-1){
|
|
*status=U_ILLEGAL_ARGUMENT_ERROR;
|
|
return 0;
|
|
}
|
|
|
|
UParseError tErr;
|
|
if(parseError==NULL)
|
|
{
|
|
parseError = &tErr;
|
|
}
|
|
|
|
int32_t len = (patternLength == -1 ? u_strlen(pattern) : patternLength);
|
|
UnicodeString patString(patternLength == -1, pattern, len);
|
|
|
|
MessageFormat* retVal = new MessageFormat(patString,Locale(locale),*parseError,*status);
|
|
if(retVal == NULL) {
|
|
*status = U_MEMORY_ALLOCATION_ERROR;
|
|
return NULL;
|
|
}
|
|
if (U_SUCCESS(*status) && MessageFormatAdapter::hasArgTypeConflicts(*retVal)) {
|
|
*status = U_ARGUMENT_TYPE_MISMATCH;
|
|
}
|
|
return (UMessageFormat*)retVal;
|
|
}
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umsg_close(UMessageFormat* format)
|
|
{
|
|
//check arguments
|
|
if(format==NULL){
|
|
return;
|
|
}
|
|
delete (MessageFormat*) format;
|
|
}
|
|
|
|
U_CAPI UMessageFormat U_EXPORT2
|
|
umsg_clone(const UMessageFormat *fmt,
|
|
UErrorCode *status)
|
|
{
|
|
//check arguments
|
|
if(status==NULL || U_FAILURE(*status)){
|
|
return NULL;
|
|
}
|
|
if(fmt==NULL){
|
|
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
return NULL;
|
|
}
|
|
UMessageFormat retVal = (UMessageFormat)((MessageFormat*)fmt)->clone();
|
|
if(retVal == 0) {
|
|
*status = U_MEMORY_ALLOCATION_ERROR;
|
|
return 0;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umsg_setLocale(UMessageFormat *fmt, const char* locale)
|
|
{
|
|
//check arguments
|
|
if(fmt==NULL){
|
|
return;
|
|
}
|
|
((MessageFormat*)fmt)->setLocale(Locale(locale));
|
|
}
|
|
|
|
U_CAPI const char* U_EXPORT2
|
|
umsg_getLocale(const UMessageFormat *fmt)
|
|
{
|
|
//check arguments
|
|
if(fmt==NULL){
|
|
return "";
|
|
}
|
|
return ((const MessageFormat*)fmt)->getLocale().getName();
|
|
}
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umsg_applyPattern(UMessageFormat *fmt,
|
|
const UChar* pattern,
|
|
int32_t patternLength,
|
|
UParseError* parseError,
|
|
UErrorCode* status)
|
|
{
|
|
//check arguments
|
|
UParseError tErr;
|
|
if(status ==NULL||U_FAILURE(*status)){
|
|
return ;
|
|
}
|
|
if(fmt==NULL || (pattern==NULL && patternLength!=0) || patternLength<-1) {
|
|
*status=U_ILLEGAL_ARGUMENT_ERROR;
|
|
return ;
|
|
}
|
|
|
|
if(parseError==NULL){
|
|
parseError = &tErr;
|
|
}
|
|
|
|
// UnicodeString(pattern, -1) calls u_strlen().
|
|
((MessageFormat*)fmt)->applyPattern(UnicodeString(pattern,patternLength),*parseError,*status);
|
|
}
|
|
|
|
U_CAPI int32_t U_EXPORT2
|
|
umsg_toPattern(const UMessageFormat *fmt,
|
|
UChar* result,
|
|
int32_t resultLength,
|
|
UErrorCode* status)
|
|
{
|
|
//check arguments
|
|
if(status ==NULL||U_FAILURE(*status)){
|
|
return -1;
|
|
}
|
|
if(fmt==NULL||resultLength<0 || (resultLength>0 && result==0)){
|
|
*status=U_ILLEGAL_ARGUMENT_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
|
|
UnicodeString res;
|
|
if(!(result==NULL && resultLength==0)) {
|
|
// NULL destination for pure preflighting: empty dummy string
|
|
// otherwise, alias the destination buffer
|
|
res.setTo(result, 0, resultLength);
|
|
}
|
|
((const MessageFormat*)fmt)->toPattern(res);
|
|
return res.extract(result, resultLength, *status);
|
|
}
|
|
|
|
U_CAPI int32_t
|
|
umsg_format( const UMessageFormat *fmt,
|
|
UChar *result,
|
|
int32_t resultLength,
|
|
UErrorCode *status,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
int32_t actLen;
|
|
//argument checking defered to last method call umsg_vformat which
|
|
//saves time when arguments are valid and we dont care when arguments are not
|
|
//since we return an error anyway
|
|
|
|
|
|
// start vararg processing
|
|
va_start(ap, status);
|
|
|
|
actLen = umsg_vformat(fmt,result,resultLength,ap,status);
|
|
|
|
// end vararg processing
|
|
va_end(ap);
|
|
|
|
return actLen;
|
|
}
|
|
|
|
U_CAPI int32_t U_EXPORT2
|
|
umsg_vformat( const UMessageFormat *fmt,
|
|
UChar *result,
|
|
int32_t resultLength,
|
|
va_list ap,
|
|
UErrorCode *status)
|
|
{
|
|
//check arguments
|
|
if(status==0 || U_FAILURE(*status))
|
|
{
|
|
return -1;
|
|
}
|
|
if(fmt==NULL||resultLength<0 || (resultLength>0 && result==0)) {
|
|
*status=U_ILLEGAL_ARGUMENT_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
int32_t count =0;
|
|
const Formattable::Type* argTypes =
|
|
MessageFormatAdapter::getArgTypeList(*(const MessageFormat*)fmt, count);
|
|
// Allocate at least one element. Allocating an array of length
|
|
// zero causes problems on some platforms (e.g. Win32).
|
|
Formattable* args = new Formattable[count ? count : 1];
|
|
|
|
// iterate through the vararg list, and get the arguments out
|
|
for(int32_t i = 0; i < count; ++i) {
|
|
|
|
UChar *stringVal;
|
|
double tDouble=0;
|
|
int32_t tInt =0;
|
|
int64_t tInt64 = 0;
|
|
UDate tempDate = 0;
|
|
switch(argTypes[i]) {
|
|
case Formattable::kDate:
|
|
tempDate = va_arg(ap, UDate);
|
|
args[i].setDate(tempDate);
|
|
break;
|
|
|
|
case Formattable::kDouble:
|
|
tDouble =va_arg(ap, double);
|
|
args[i].setDouble(tDouble);
|
|
break;
|
|
|
|
case Formattable::kLong:
|
|
tInt = va_arg(ap, int32_t);
|
|
args[i].setLong(tInt);
|
|
break;
|
|
|
|
case Formattable::kInt64:
|
|
tInt64 = va_arg(ap, int64_t);
|
|
args[i].setInt64(tInt64);
|
|
break;
|
|
|
|
case Formattable::kString:
|
|
// For some reason, a temporary is needed
|
|
stringVal = va_arg(ap, UChar*);
|
|
if(stringVal){
|
|
args[i].setString(UnicodeString(stringVal));
|
|
}else{
|
|
*status=U_ILLEGAL_ARGUMENT_ERROR;
|
|
}
|
|
break;
|
|
|
|
case Formattable::kArray:
|
|
// throw away this argument
|
|
// this is highly platform-dependent, and probably won't work
|
|
// so, if you try to skip arguments in the list (and not use them)
|
|
// you'll probably crash
|
|
va_arg(ap, int);
|
|
break;
|
|
|
|
case Formattable::kObject:
|
|
// Unused argument number. Read and ignore a pointer argument.
|
|
va_arg(ap, void*);
|
|
break;
|
|
|
|
default:
|
|
// Unknown/unsupported argument type.
|
|
U_ASSERT(FALSE);
|
|
*status=U_ILLEGAL_ARGUMENT_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
UnicodeString resultStr;
|
|
FieldPosition fieldPosition(FieldPosition::DONT_CARE);
|
|
|
|
/* format the message */
|
|
((const MessageFormat*)fmt)->format(args,count,resultStr,fieldPosition,*status);
|
|
|
|
delete[] args;
|
|
|
|
if(U_FAILURE(*status)){
|
|
return -1;
|
|
}
|
|
|
|
return resultStr.extract(result, resultLength, *status);
|
|
}
|
|
|
|
U_CAPI void
|
|
umsg_parse( const UMessageFormat *fmt,
|
|
const UChar *source,
|
|
int32_t sourceLength,
|
|
int32_t *count,
|
|
UErrorCode *status,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
//argument checking defered to last method call umsg_vparse which
|
|
//saves time when arguments are valid and we dont care when arguments are not
|
|
//since we return an error anyway
|
|
|
|
// start vararg processing
|
|
va_start(ap, status);
|
|
|
|
umsg_vparse(fmt,source,sourceLength,count,ap,status);
|
|
|
|
// end vararg processing
|
|
va_end(ap);
|
|
}
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umsg_vparse(const UMessageFormat *fmt,
|
|
const UChar *source,
|
|
int32_t sourceLength,
|
|
int32_t *count,
|
|
va_list ap,
|
|
UErrorCode *status)
|
|
{
|
|
//check arguments
|
|
if(status==NULL||U_FAILURE(*status))
|
|
{
|
|
return;
|
|
}
|
|
if(fmt==NULL||source==NULL || sourceLength<-1 || count==NULL){
|
|
*status=U_ILLEGAL_ARGUMENT_ERROR;
|
|
return;
|
|
}
|
|
if(sourceLength==-1){
|
|
sourceLength=u_strlen(source);
|
|
}
|
|
|
|
UnicodeString srcString(source,sourceLength);
|
|
Formattable *args = ((const MessageFormat*)fmt)->parse(srcString,*count,*status);
|
|
UDate *aDate;
|
|
double *aDouble;
|
|
UChar *aString;
|
|
int32_t* aInt;
|
|
int64_t* aInt64;
|
|
UnicodeString temp;
|
|
int len =0;
|
|
// assign formattables to varargs
|
|
for(int32_t i = 0; i < *count; i++) {
|
|
switch(args[i].getType()) {
|
|
|
|
case Formattable::kDate:
|
|
aDate = va_arg(ap, UDate*);
|
|
if(aDate){
|
|
*aDate = args[i].getDate();
|
|
}else{
|
|
*status=U_ILLEGAL_ARGUMENT_ERROR;
|
|
}
|
|
break;
|
|
|
|
case Formattable::kDouble:
|
|
aDouble = va_arg(ap, double*);
|
|
if(aDouble){
|
|
*aDouble = args[i].getDouble();
|
|
}else{
|
|
*status=U_ILLEGAL_ARGUMENT_ERROR;
|
|
}
|
|
break;
|
|
|
|
case Formattable::kLong:
|
|
aInt = va_arg(ap, int32_t*);
|
|
if(aInt){
|
|
*aInt = (int32_t) args[i].getLong();
|
|
}else{
|
|
*status=U_ILLEGAL_ARGUMENT_ERROR;
|
|
}
|
|
break;
|
|
|
|
case Formattable::kInt64:
|
|
aInt64 = va_arg(ap, int64_t*);
|
|
if(aInt64){
|
|
*aInt64 = args[i].getInt64();
|
|
}else{
|
|
*status=U_ILLEGAL_ARGUMENT_ERROR;
|
|
}
|
|
break;
|
|
|
|
case Formattable::kString:
|
|
aString = va_arg(ap, UChar*);
|
|
if(aString){
|
|
args[i].getString(temp);
|
|
len = temp.length();
|
|
temp.extract(0,len,aString);
|
|
aString[len]=0;
|
|
}else{
|
|
*status= U_ILLEGAL_ARGUMENT_ERROR;
|
|
}
|
|
break;
|
|
|
|
case Formattable::kObject:
|
|
// This will never happen because MessageFormat doesn't
|
|
// support kObject. When MessageFormat is changed to
|
|
// understand MeasureFormats, modify this code to do the
|
|
// right thing. [alan]
|
|
U_ASSERT(FALSE);
|
|
break;
|
|
|
|
// better not happen!
|
|
case Formattable::kArray:
|
|
U_ASSERT(FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// clean up
|
|
delete [] args;
|
|
}
|
|
|
|
#define SINGLE_QUOTE ((UChar)0x0027)
|
|
#define CURLY_BRACE_LEFT ((UChar)0x007B)
|
|
#define CURLY_BRACE_RIGHT ((UChar)0x007D)
|
|
|
|
#define STATE_INITIAL 0
|
|
#define STATE_SINGLE_QUOTE 1
|
|
#define STATE_IN_QUOTE 2
|
|
#define STATE_MSG_ELEMENT 3
|
|
|
|
#define MAppend(c) if (len < destCapacity) dest[len++] = c; else len++
|
|
|
|
int32_t umsg_autoQuoteApostrophe(const UChar* pattern,
|
|
int32_t patternLength,
|
|
UChar* dest,
|
|
int32_t destCapacity,
|
|
UErrorCode* ec)
|
|
{
|
|
int32_t state = STATE_INITIAL;
|
|
int32_t braceCount = 0;
|
|
int32_t len = 0;
|
|
|
|
if (ec == NULL || U_FAILURE(*ec)) {
|
|
return -1;
|
|
}
|
|
|
|
if (pattern == NULL || patternLength < -1 || (dest == NULL && destCapacity > 0)) {
|
|
*ec = U_ILLEGAL_ARGUMENT_ERROR;
|
|
return -1;
|
|
}
|
|
U_ASSERT(destCapacity >= 0);
|
|
|
|
if (patternLength == -1) {
|
|
patternLength = u_strlen(pattern);
|
|
}
|
|
|
|
for (int i = 0; i < patternLength; ++i) {
|
|
UChar c = pattern[i];
|
|
switch (state) {
|
|
case STATE_INITIAL:
|
|
switch (c) {
|
|
case SINGLE_QUOTE:
|
|
state = STATE_SINGLE_QUOTE;
|
|
break;
|
|
case CURLY_BRACE_LEFT:
|
|
state = STATE_MSG_ELEMENT;
|
|
++braceCount;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_SINGLE_QUOTE:
|
|
switch (c) {
|
|
case SINGLE_QUOTE:
|
|
state = STATE_INITIAL;
|
|
break;
|
|
case CURLY_BRACE_LEFT:
|
|
case CURLY_BRACE_RIGHT:
|
|
state = STATE_IN_QUOTE;
|
|
break;
|
|
default:
|
|
MAppend(SINGLE_QUOTE);
|
|
state = STATE_INITIAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_IN_QUOTE:
|
|
switch (c) {
|
|
case SINGLE_QUOTE:
|
|
state = STATE_INITIAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_MSG_ELEMENT:
|
|
switch (c) {
|
|
case CURLY_BRACE_LEFT:
|
|
++braceCount;
|
|
break;
|
|
case CURLY_BRACE_RIGHT:
|
|
if (--braceCount == 0) {
|
|
state = STATE_INITIAL;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default: // Never happens.
|
|
break;
|
|
}
|
|
|
|
U_ASSERT(len >= 0);
|
|
MAppend(c);
|
|
}
|
|
|
|
// End of scan
|
|
if (state == STATE_SINGLE_QUOTE || state == STATE_IN_QUOTE) {
|
|
MAppend(SINGLE_QUOTE);
|
|
}
|
|
|
|
return u_terminateUChars(dest, destCapacity, len, ec);
|
|
}
|
|
|
|
#endif /* #if !UCONFIG_NO_FORMATTING */
|