You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

709 lines
20 KiB

// © 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.
UPRV_UNREACHABLE;
}
}
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]
UPRV_UNREACHABLE;
// better not happen!
case Formattable::kArray:
UPRV_UNREACHABLE;
}
}
// 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 */