2016-06-15 18:58:17 +00:00
|
|
|
// Copyright (C) 2016 and later: Unicode, Inc. and others.
|
|
|
|
// License & terms of use: http://www.unicode.org/copyright.html
|
2015-08-25 23:31:01 +00:00
|
|
|
/*
|
2016-05-31 21:45:07 +00:00
|
|
|
* Copyright (C) 2015, International Business Machines
|
|
|
|
* Corporation and others. All Rights Reserved.
|
2015-08-25 23:31:01 +00:00
|
|
|
*
|
|
|
|
* file name: affixpatternparser.cpp
|
|
|
|
*/
|
|
|
|
|
2015-09-22 16:24:52 +00:00
|
|
|
#include "unicode/utypes.h"
|
|
|
|
|
|
|
|
#if !UCONFIG_NO_FORMATTING
|
2015-08-25 23:31:01 +00:00
|
|
|
|
|
|
|
#include "unicode/dcfmtsym.h"
|
|
|
|
#include "unicode/plurrule.h"
|
|
|
|
#include "unicode/ucurr.h"
|
|
|
|
#include "affixpatternparser.h"
|
|
|
|
#include "charstr.h"
|
|
|
|
#include "precision.h"
|
|
|
|
#include "uassert.h"
|
|
|
|
#include "unistrappender.h"
|
|
|
|
|
|
|
|
static UChar gDefaultSymbols[] = {0xa4, 0xa4, 0xa4};
|
|
|
|
|
|
|
|
static UChar gPercent = 0x25;
|
|
|
|
static UChar gPerMill = 0x2030;
|
|
|
|
static UChar gNegative = 0x2D;
|
|
|
|
static UChar gPositive = 0x2B;
|
|
|
|
|
|
|
|
#define PACK_TOKEN_AND_LENGTH(t, l) ((UChar) (((t) << 8) | (l & 0xFF)))
|
|
|
|
|
|
|
|
#define UNPACK_TOKEN(c) ((AffixPattern::ETokenType) (((c) >> 8) & 0x7F))
|
|
|
|
|
|
|
|
#define UNPACK_LONG(c) (((c) >> 8) & 0x80)
|
|
|
|
|
|
|
|
#define UNPACK_LENGTH(c) ((c) & 0xFF)
|
|
|
|
|
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
|
|
|
|
static int32_t
|
|
|
|
nextToken(const UChar *buffer, int32_t idx, int32_t len, UChar *token) {
|
|
|
|
if (buffer[idx] != 0x27 || idx + 1 == len) {
|
|
|
|
*token = buffer[idx];
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
*token = buffer[idx + 1];
|
|
|
|
if (buffer[idx + 1] == 0xA4) {
|
|
|
|
int32_t i = 2;
|
2016-09-20 17:36:32 +00:00
|
|
|
for (; idx + i < len && i < 4 && buffer[idx + i] == buffer[idx + 1]; ++i)
|
|
|
|
;
|
2015-08-25 23:31:01 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t
|
|
|
|
nextUserToken(const UChar *buffer, int32_t idx, int32_t len, UChar *token) {
|
|
|
|
*token = buffer[idx];
|
|
|
|
int32_t max;
|
|
|
|
switch (buffer[idx]) {
|
|
|
|
case 0x27:
|
|
|
|
max = 2;
|
|
|
|
break;
|
|
|
|
case 0xA4:
|
|
|
|
max = 3;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
max = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
int32_t i = 1;
|
2016-09-20 17:36:32 +00:00
|
|
|
for (; idx + i < len && i < max && buffer[idx + i] == buffer[idx]; ++i)
|
|
|
|
;
|
2015-08-25 23:31:01 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
CurrencyAffixInfo::CurrencyAffixInfo()
|
|
|
|
: fSymbol(gDefaultSymbols, 1),
|
|
|
|
fISO(gDefaultSymbols, 2),
|
|
|
|
fLong(DigitAffix(gDefaultSymbols, 3)),
|
|
|
|
fIsDefault(TRUE) {
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CurrencyAffixInfo::set(
|
|
|
|
const char *locale,
|
|
|
|
const PluralRules *rules,
|
|
|
|
const UChar *currency,
|
|
|
|
UErrorCode &status) {
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fIsDefault = FALSE;
|
|
|
|
if (currency == NULL) {
|
|
|
|
fSymbol.setTo(gDefaultSymbols, 1);
|
|
|
|
fISO.setTo(gDefaultSymbols, 2);
|
|
|
|
fLong.remove();
|
|
|
|
fLong.append(gDefaultSymbols, 3);
|
|
|
|
fIsDefault = TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int32_t len;
|
|
|
|
UBool unusedIsChoice;
|
|
|
|
const UChar *symbol = ucurr_getName(
|
|
|
|
currency, locale, UCURR_SYMBOL_NAME, &unusedIsChoice,
|
|
|
|
&len, &status);
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fSymbol.setTo(symbol, len);
|
|
|
|
fISO.setTo(currency, u_strlen(currency));
|
|
|
|
fLong.remove();
|
|
|
|
StringEnumeration* keywords = rules->getKeywords(status);
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const UnicodeString* pluralCount;
|
|
|
|
while ((pluralCount = keywords->snext(status)) != NULL) {
|
|
|
|
CharString pCount;
|
|
|
|
pCount.appendInvariantChars(*pluralCount, status);
|
|
|
|
const UChar *pluralName = ucurr_getPluralName(
|
|
|
|
currency, locale, &unusedIsChoice, pCount.data(),
|
|
|
|
&len, &status);
|
|
|
|
fLong.setVariant(pCount.data(), UnicodeString(pluralName, len), status);
|
|
|
|
}
|
|
|
|
delete keywords;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CurrencyAffixInfo::adjustPrecision(
|
|
|
|
const UChar *currency, const UCurrencyUsage usage,
|
|
|
|
FixedPrecision &precision, UErrorCode &status) {
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t digitCount = ucurr_getDefaultFractionDigitsForUsage(
|
|
|
|
currency, usage, &status);
|
|
|
|
precision.fMin.setFracDigitCount(digitCount);
|
|
|
|
precision.fMax.setFracDigitCount(digitCount);
|
|
|
|
double increment = ucurr_getRoundingIncrementForUsage(
|
|
|
|
currency, usage, &status);
|
|
|
|
if (increment == 0.0) {
|
|
|
|
precision.fRoundingIncrement.clear();
|
|
|
|
} else {
|
|
|
|
precision.fRoundingIncrement.set(increment);
|
|
|
|
// guard against round-off error
|
|
|
|
precision.fRoundingIncrement.round(6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AffixPattern::addLiteral(
|
|
|
|
const UChar *literal, int32_t start, int32_t len) {
|
|
|
|
char32Count += u_countChar32(literal + start, len);
|
|
|
|
literals.append(literal, start, len);
|
|
|
|
int32_t tlen = tokens.length();
|
|
|
|
// Takes 4 UChars to encode maximum literal length.
|
|
|
|
UChar *tokenChars = tokens.getBuffer(tlen + 4);
|
|
|
|
|
|
|
|
// find start of literal size. May be tlen if there is no literal.
|
|
|
|
// While finding start of literal size, compute literal length
|
|
|
|
int32_t literalLength = 0;
|
|
|
|
int32_t tLiteralStart = tlen;
|
|
|
|
while (tLiteralStart > 0 && UNPACK_TOKEN(tokenChars[tLiteralStart - 1]) == kLiteral) {
|
|
|
|
tLiteralStart--;
|
|
|
|
literalLength <<= 8;
|
|
|
|
literalLength |= UNPACK_LENGTH(tokenChars[tLiteralStart]);
|
|
|
|
}
|
|
|
|
// Add number of chars we just added to literal
|
|
|
|
literalLength += len;
|
|
|
|
|
|
|
|
// Now encode the new length starting at tLiteralStart
|
|
|
|
tlen = tLiteralStart;
|
|
|
|
tokenChars[tlen++] = PACK_TOKEN_AND_LENGTH(kLiteral, literalLength & 0xFF);
|
|
|
|
literalLength >>= 8;
|
|
|
|
while (literalLength) {
|
|
|
|
tokenChars[tlen++] = PACK_TOKEN_AND_LENGTH(kLiteral | 0x80, literalLength & 0xFF);
|
|
|
|
literalLength >>= 8;
|
|
|
|
}
|
|
|
|
tokens.releaseBuffer(tlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AffixPattern::add(ETokenType t) {
|
|
|
|
add(t, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AffixPattern::addCurrency(uint8_t count) {
|
|
|
|
add(kCurrency, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AffixPattern::add(ETokenType t, uint8_t count) {
|
|
|
|
U_ASSERT(t != kLiteral);
|
|
|
|
char32Count += count;
|
|
|
|
switch (t) {
|
|
|
|
case kCurrency:
|
|
|
|
hasCurrencyToken = TRUE;
|
|
|
|
break;
|
|
|
|
case kPercent:
|
|
|
|
hasPercentToken = TRUE;
|
|
|
|
break;
|
|
|
|
case kPerMill:
|
|
|
|
hasPermillToken = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Do nothing
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tokens.append(PACK_TOKEN_AND_LENGTH(t, count));
|
|
|
|
}
|
|
|
|
|
|
|
|
AffixPattern &
|
|
|
|
AffixPattern::append(const AffixPattern &other) {
|
|
|
|
AffixPatternIterator iter;
|
|
|
|
other.iterator(iter);
|
|
|
|
UnicodeString literal;
|
|
|
|
while (iter.nextToken()) {
|
|
|
|
switch (iter.getTokenType()) {
|
|
|
|
case kLiteral:
|
|
|
|
iter.getLiteral(literal);
|
|
|
|
addLiteral(literal.getBuffer(), 0, literal.length());
|
|
|
|
break;
|
|
|
|
case kCurrency:
|
|
|
|
addCurrency(iter.getTokenLength());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
add(iter.getTokenType());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AffixPattern::remove() {
|
|
|
|
tokens.remove();
|
|
|
|
literals.remove();
|
|
|
|
hasCurrencyToken = FALSE;
|
|
|
|
hasPercentToken = FALSE;
|
|
|
|
hasPermillToken = FALSE;
|
|
|
|
char32Count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// escapes literals for strings where special characters are NOT escaped
|
|
|
|
// except for apostrophe.
|
|
|
|
static void escapeApostropheInLiteral(
|
|
|
|
const UnicodeString &literal, UnicodeStringAppender &appender) {
|
|
|
|
int32_t len = literal.length();
|
|
|
|
const UChar *buffer = literal.getBuffer();
|
|
|
|
for (int32_t i = 0; i < len; ++i) {
|
|
|
|
UChar ch = buffer[i];
|
|
|
|
switch (ch) {
|
|
|
|
case 0x27:
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
appender.append(ch);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// escapes literals for user strings where special characters in literals
|
|
|
|
// are escaped with apostrophe.
|
|
|
|
static void escapeLiteral(
|
|
|
|
const UnicodeString &literal, UnicodeStringAppender &appender) {
|
|
|
|
int32_t len = literal.length();
|
|
|
|
const UChar *buffer = literal.getBuffer();
|
|
|
|
for (int32_t i = 0; i < len; ++i) {
|
|
|
|
UChar ch = buffer[i];
|
|
|
|
switch (ch) {
|
|
|
|
case 0x27:
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
break;
|
|
|
|
case 0x25:
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
appender.append((UChar) 0x25);
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
break;
|
|
|
|
case 0x2030:
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
appender.append((UChar) 0x2030);
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
break;
|
|
|
|
case 0xA4:
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
appender.append((UChar) 0xA4);
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
break;
|
|
|
|
case 0x2D:
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
appender.append((UChar) 0x2D);
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
break;
|
|
|
|
case 0x2B:
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
appender.append((UChar) 0x2B);
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
appender.append(ch);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UnicodeString &
|
|
|
|
AffixPattern::toString(UnicodeString &appendTo) const {
|
|
|
|
AffixPatternIterator iter;
|
|
|
|
iterator(iter);
|
|
|
|
UnicodeStringAppender appender(appendTo);
|
|
|
|
UnicodeString literal;
|
|
|
|
while (iter.nextToken()) {
|
|
|
|
switch (iter.getTokenType()) {
|
|
|
|
case kLiteral:
|
|
|
|
escapeApostropheInLiteral(iter.getLiteral(literal), appender);
|
|
|
|
break;
|
|
|
|
case kPercent:
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
appender.append((UChar) 0x25);
|
|
|
|
break;
|
|
|
|
case kPerMill:
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
appender.append((UChar) 0x2030);
|
|
|
|
break;
|
|
|
|
case kCurrency:
|
|
|
|
{
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
int32_t cl = iter.getTokenLength();
|
|
|
|
for (int32_t i = 0; i < cl; ++i) {
|
|
|
|
appender.append((UChar) 0xA4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kNegative:
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
appender.append((UChar) 0x2D);
|
|
|
|
break;
|
|
|
|
case kPositive:
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
appender.append((UChar) 0x2B);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
U_ASSERT(FALSE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
|
|
|
|
UnicodeString &
|
|
|
|
AffixPattern::toUserString(UnicodeString &appendTo) const {
|
|
|
|
AffixPatternIterator iter;
|
|
|
|
iterator(iter);
|
|
|
|
UnicodeStringAppender appender(appendTo);
|
|
|
|
UnicodeString literal;
|
|
|
|
while (iter.nextToken()) {
|
|
|
|
switch (iter.getTokenType()) {
|
|
|
|
case kLiteral:
|
|
|
|
escapeLiteral(iter.getLiteral(literal), appender);
|
|
|
|
break;
|
|
|
|
case kPercent:
|
|
|
|
appender.append((UChar) 0x25);
|
|
|
|
break;
|
|
|
|
case kPerMill:
|
|
|
|
appender.append((UChar) 0x2030);
|
|
|
|
break;
|
|
|
|
case kCurrency:
|
|
|
|
{
|
|
|
|
int32_t cl = iter.getTokenLength();
|
|
|
|
for (int32_t i = 0; i < cl; ++i) {
|
|
|
|
appender.append((UChar) 0xA4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kNegative:
|
|
|
|
appender.append((UChar) 0x2D);
|
|
|
|
break;
|
|
|
|
case kPositive:
|
|
|
|
appender.append((UChar) 0x2B);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
U_ASSERT(FALSE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
|
|
|
|
class AffixPatternAppender : public UMemory {
|
|
|
|
public:
|
|
|
|
AffixPatternAppender(AffixPattern &dest) : fDest(&dest), fIdx(0) { }
|
|
|
|
|
|
|
|
inline void append(UChar x) {
|
|
|
|
if (fIdx == UPRV_LENGTHOF(fBuffer)) {
|
|
|
|
fDest->addLiteral(fBuffer, 0, fIdx);
|
|
|
|
fIdx = 0;
|
|
|
|
}
|
|
|
|
fBuffer[fIdx++] = x;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void append(UChar32 x) {
|
|
|
|
if (fIdx >= UPRV_LENGTHOF(fBuffer) - 1) {
|
|
|
|
fDest->addLiteral(fBuffer, 0, fIdx);
|
|
|
|
fIdx = 0;
|
|
|
|
}
|
|
|
|
U16_APPEND_UNSAFE(fBuffer, fIdx, x);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void flush() {
|
|
|
|
if (fIdx) {
|
|
|
|
fDest->addLiteral(fBuffer, 0, fIdx);
|
|
|
|
}
|
|
|
|
fIdx = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* flush the buffer when we go out of scope.
|
|
|
|
*/
|
|
|
|
~AffixPatternAppender() {
|
|
|
|
flush();
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
AffixPattern *fDest;
|
|
|
|
int32_t fIdx;
|
|
|
|
UChar fBuffer[32];
|
|
|
|
AffixPatternAppender(const AffixPatternAppender &other);
|
|
|
|
AffixPatternAppender &operator=(const AffixPatternAppender &other);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
AffixPattern &
|
|
|
|
AffixPattern::parseUserAffixString(
|
|
|
|
const UnicodeString &affixStr,
|
|
|
|
AffixPattern &appendTo,
|
|
|
|
UErrorCode &status) {
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
int32_t len = affixStr.length();
|
|
|
|
const UChar *buffer = affixStr.getBuffer();
|
|
|
|
// 0 = not quoted; 1 = quoted.
|
|
|
|
int32_t state = 0;
|
|
|
|
AffixPatternAppender appender(appendTo);
|
|
|
|
for (int32_t i = 0; i < len; ) {
|
|
|
|
UChar token;
|
|
|
|
int32_t tokenSize = nextUserToken(buffer, i, len, &token);
|
|
|
|
i += tokenSize;
|
|
|
|
if (token == 0x27 && tokenSize == 1) { // quote
|
|
|
|
state = 1 - state;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (state == 0) {
|
|
|
|
switch (token) {
|
|
|
|
case 0x25:
|
|
|
|
appender.flush();
|
|
|
|
appendTo.add(kPercent, 1);
|
|
|
|
break;
|
|
|
|
case 0x27: // double quote
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
break;
|
|
|
|
case 0x2030:
|
|
|
|
appender.flush();
|
|
|
|
appendTo.add(kPerMill, 1);
|
|
|
|
break;
|
|
|
|
case 0x2D:
|
|
|
|
appender.flush();
|
|
|
|
appendTo.add(kNegative, 1);
|
|
|
|
break;
|
|
|
|
case 0x2B:
|
|
|
|
appender.flush();
|
|
|
|
appendTo.add(kPositive, 1);
|
|
|
|
break;
|
|
|
|
case 0xA4:
|
|
|
|
appender.flush();
|
|
|
|
appendTo.add(kCurrency, tokenSize);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
appender.append(token);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (token) {
|
|
|
|
case 0x27: // double quote
|
|
|
|
appender.append((UChar) 0x27);
|
|
|
|
break;
|
|
|
|
case 0xA4: // included b/c tokenSize can be > 1
|
|
|
|
for (int32_t j = 0; j < tokenSize; ++j) {
|
|
|
|
appender.append((UChar) 0xA4);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
appender.append(token);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
|
|
|
|
AffixPattern &
|
|
|
|
AffixPattern::parseAffixString(
|
|
|
|
const UnicodeString &affixStr,
|
|
|
|
AffixPattern &appendTo,
|
|
|
|
UErrorCode &status) {
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
int32_t len = affixStr.length();
|
|
|
|
const UChar *buffer = affixStr.getBuffer();
|
|
|
|
for (int32_t i = 0; i < len; ) {
|
|
|
|
UChar token;
|
|
|
|
int32_t tokenSize = nextToken(buffer, i, len, &token);
|
|
|
|
if (tokenSize == 1) {
|
|
|
|
int32_t literalStart = i;
|
|
|
|
++i;
|
|
|
|
while (i < len && (tokenSize = nextToken(buffer, i, len, &token)) == 1) {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
appendTo.addLiteral(buffer, literalStart, i - literalStart);
|
|
|
|
|
|
|
|
// If we reached end of string, we are done
|
|
|
|
if (i == len) {
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i += tokenSize;
|
|
|
|
switch (token) {
|
|
|
|
case 0x25:
|
|
|
|
appendTo.add(kPercent, 1);
|
|
|
|
break;
|
|
|
|
case 0x2030:
|
|
|
|
appendTo.add(kPerMill, 1);
|
|
|
|
break;
|
|
|
|
case 0x2D:
|
|
|
|
appendTo.add(kNegative, 1);
|
|
|
|
break;
|
|
|
|
case 0x2B:
|
|
|
|
appendTo.add(kPositive, 1);
|
|
|
|
break;
|
|
|
|
case 0xA4:
|
|
|
|
{
|
|
|
|
if (tokenSize - 1 > 3) {
|
|
|
|
status = U_PARSE_ERROR;
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
appendTo.add(kCurrency, tokenSize - 1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
appendTo.addLiteral(&token, 0, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
|
|
|
|
AffixPatternIterator &
|
|
|
|
AffixPattern::iterator(AffixPatternIterator &result) const {
|
|
|
|
result.nextLiteralIndex = 0;
|
|
|
|
result.lastLiteralLength = 0;
|
|
|
|
result.nextTokenIndex = 0;
|
|
|
|
result.tokens = &tokens;
|
|
|
|
result.literals = &literals;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
UBool
|
|
|
|
AffixPatternIterator::nextToken() {
|
|
|
|
int32_t tlen = tokens->length();
|
|
|
|
if (nextTokenIndex == tlen) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
++nextTokenIndex;
|
|
|
|
const UChar *tokenBuffer = tokens->getBuffer();
|
|
|
|
if (UNPACK_TOKEN(tokenBuffer[nextTokenIndex - 1]) ==
|
|
|
|
AffixPattern::kLiteral) {
|
|
|
|
while (nextTokenIndex < tlen &&
|
|
|
|
UNPACK_LONG(tokenBuffer[nextTokenIndex])) {
|
|
|
|
++nextTokenIndex;
|
|
|
|
}
|
|
|
|
lastLiteralLength = 0;
|
|
|
|
int32_t i = nextTokenIndex - 1;
|
|
|
|
for (; UNPACK_LONG(tokenBuffer[i]); --i) {
|
|
|
|
lastLiteralLength <<= 8;
|
|
|
|
lastLiteralLength |= UNPACK_LENGTH(tokenBuffer[i]);
|
|
|
|
}
|
|
|
|
lastLiteralLength <<= 8;
|
|
|
|
lastLiteralLength |= UNPACK_LENGTH(tokenBuffer[i]);
|
|
|
|
nextLiteralIndex += lastLiteralLength;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
AffixPattern::ETokenType
|
|
|
|
AffixPatternIterator::getTokenType() const {
|
|
|
|
return UNPACK_TOKEN(tokens->charAt(nextTokenIndex - 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
UnicodeString &
|
|
|
|
AffixPatternIterator::getLiteral(UnicodeString &result) const {
|
|
|
|
const UChar *buffer = literals->getBuffer();
|
|
|
|
result.setTo(buffer + (nextLiteralIndex - lastLiteralLength), lastLiteralLength);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t
|
|
|
|
AffixPatternIterator::getTokenLength() const {
|
|
|
|
const UChar *tokenBuffer = tokens->getBuffer();
|
|
|
|
AffixPattern::ETokenType type = UNPACK_TOKEN(tokenBuffer[nextTokenIndex - 1]);
|
|
|
|
return type == AffixPattern::kLiteral ? lastLiteralLength : UNPACK_LENGTH(tokenBuffer[nextTokenIndex - 1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
AffixPatternParser::AffixPatternParser()
|
|
|
|
: fPercent(gPercent), fPermill(gPerMill), fNegative(gNegative), fPositive(gPositive) {
|
|
|
|
}
|
|
|
|
|
|
|
|
AffixPatternParser::AffixPatternParser(
|
|
|
|
const DecimalFormatSymbols &symbols) {
|
|
|
|
setDecimalFormatSymbols(symbols);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AffixPatternParser::setDecimalFormatSymbols(
|
|
|
|
const DecimalFormatSymbols &symbols) {
|
|
|
|
fPercent = symbols.getConstSymbol(DecimalFormatSymbols::kPercentSymbol);
|
|
|
|
fPermill = symbols.getConstSymbol(DecimalFormatSymbols::kPerMillSymbol);
|
|
|
|
fNegative = symbols.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
|
|
|
|
fPositive = symbols.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol);
|
|
|
|
}
|
|
|
|
|
|
|
|
PluralAffix &
|
|
|
|
AffixPatternParser::parse(
|
|
|
|
const AffixPattern &affixPattern,
|
|
|
|
const CurrencyAffixInfo ¤cyAffixInfo,
|
|
|
|
PluralAffix &appendTo,
|
|
|
|
UErrorCode &status) const {
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
AffixPatternIterator iter;
|
|
|
|
affixPattern.iterator(iter);
|
|
|
|
UnicodeString literal;
|
|
|
|
while (iter.nextToken()) {
|
|
|
|
switch (iter.getTokenType()) {
|
|
|
|
case AffixPattern::kPercent:
|
|
|
|
appendTo.append(fPercent, UNUM_PERCENT_FIELD);
|
|
|
|
break;
|
|
|
|
case AffixPattern::kPerMill:
|
|
|
|
appendTo.append(fPermill, UNUM_PERMILL_FIELD);
|
|
|
|
break;
|
|
|
|
case AffixPattern::kNegative:
|
|
|
|
appendTo.append(fNegative, UNUM_SIGN_FIELD);
|
|
|
|
break;
|
|
|
|
case AffixPattern::kPositive:
|
|
|
|
appendTo.append(fPositive, UNUM_SIGN_FIELD);
|
|
|
|
break;
|
|
|
|
case AffixPattern::kCurrency:
|
|
|
|
switch (iter.getTokenLength()) {
|
|
|
|
case 1:
|
|
|
|
appendTo.append(
|
|
|
|
currencyAffixInfo.getSymbol(), UNUM_CURRENCY_FIELD);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
appendTo.append(
|
|
|
|
currencyAffixInfo.getISO(), UNUM_CURRENCY_FIELD);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
appendTo.append(
|
|
|
|
currencyAffixInfo.getLong(), UNUM_CURRENCY_FIELD, status);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
U_ASSERT(FALSE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AffixPattern::kLiteral:
|
|
|
|
appendTo.append(iter.getLiteral(literal));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
U_ASSERT(FALSE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
U_NAMESPACE_END
|
2015-09-22 16:24:52 +00:00
|
|
|
#endif /* #if !UCONFIG_NO_FORMATTING */
|