/* ******************************************************************************* * Copyright (C) 1997-2001, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * * File CHOICFMT.CPP * * Modification History: * * Date Name Description * 02/19/97 aliu Converted from java. * 03/20/97 helena Finished first cut of implementation and got rid * of nextDouble/previousDouble and replaced with * boolean array. * 4/10/97 aliu Clean up. Modified to work on AIX. * 06/04/97 helena Fixed applyPattern(), toPattern() and not to include * wchar.h. * 07/09/97 helena Made ParsePosition into a class. * 08/06/97 nos removed overloaded constructor, fixed 'format(array)' * 07/22/98 stephen JDK 1.2 Sync - removed UBool array (doubleFlags) * 02/22/99 stephen Removed character literals for EBCDIC safety ******************************************************************************** */ #include "cpputils.h" #include "unicode/choicfmt.h" #include "unicode/numfmt.h" #include "unicode/locid.h" #include "mutex.h" // ***************************************************************************** // class ChoiceFormat // ***************************************************************************** char ChoiceFormat::fgClassID = 0; // Value is irrelevant //UMTX ChoiceFormat::fgMutex = NULL; // lock for fgNumberFormat //NumberFormat* ChoiceFormat::fgNumberFormat = 0; inline double _getDouble(const Formattable& f) { return (f.getType() == Formattable::kLong) ? ((double) f.getLong()) : f.getDouble(); } // Special characters used by ChoiceFormat. There are two characters // used interchangeably to indicate <=. Either is parsed, but only // LESS_EQUAL is generated by toPattern(). #define SINGLE_QUOTE ((UChar)0x0027) /*'*/ #define LESS_THAN ((UChar)0x003C) /*<*/ #define LESS_EQUAL ((UChar)0x0023) /*#*/ #define LESS_EQUAL2 ((UChar)0x2264) #define VERTICAL_BAR ((UChar)0x007C) /*|*/ #define MINUS ((UChar)0x002D) /*-*/ #define INFINITY ((UChar)0x221E) const UChar ChoiceFormat::fgPositiveInfinity[] = {INFINITY, 0}; const UChar ChoiceFormat::fgNegativeInfinity[] = {MINUS, INFINITY, 0}; #define POSITIVE_INF_STRLEN 1 #define NEGATIVE_INF_STRLEN 2 // ------------------------------------- // Creates a ChoiceFormat instance based on the pattern. ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern, UErrorCode& status) : fChoiceLimits(0), fClosures(0), fChoiceFormats(0), fCount(0) { applyPattern(newPattern, status); } // ------------------------------------- // Creates a ChoiceFormat instance with the limit array and // format strings for each limit. ChoiceFormat::ChoiceFormat(const double* limits, const UnicodeString* formats, int32_t cnt ) : fChoiceLimits(0), fClosures(0), fChoiceFormats(0), fCount(0) { setChoices(limits, formats, cnt ); } // ------------------------------------- ChoiceFormat::ChoiceFormat(const double* limits, const UBool* closures, const UnicodeString* formats, int32_t cnt ) : fChoiceLimits(0), fClosures(0), fChoiceFormats(0), fCount(0) { setChoices(limits, closures, formats, cnt ); } // ------------------------------------- // copy constructor ChoiceFormat::ChoiceFormat(const ChoiceFormat& that) : fChoiceLimits(0), fClosures(0), fChoiceFormats(0) { *this = that; } // ------------------------------------- // Private constructor that creates a // ChoiceFormat instance based on the // pattern and populates UParseError ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern, UParseError& parseError, UErrorCode& status) : fChoiceLimits(0), fClosures(0), fChoiceFormats(0), fCount(0) { applyPattern(newPattern,parseError, status); } // ------------------------------------- UBool ChoiceFormat::operator==(const Format& that) const { if (this == &that) return TRUE; if (this->getDynamicClassID() != that.getDynamicClassID()) return FALSE; // not the same class if (!NumberFormat::operator==(that)) return FALSE; ChoiceFormat& thatAlias = (ChoiceFormat&)that; if (fCount != thatAlias.fCount) return FALSE; // Checks the limits, the corresponding format string and LE or LT flags. // LE means less than and equal to, LT means less than. for (int32_t i = 0; i < fCount; i++) { if ((fChoiceLimits[i] != thatAlias.fChoiceLimits[i]) || (fClosures[i] != thatAlias.fClosures[i]) || (fChoiceFormats[i] != thatAlias.fChoiceFormats[i])) return FALSE; } return TRUE; } // ------------------------------------- // copy constructor const ChoiceFormat& ChoiceFormat::operator=(const ChoiceFormat& that) { if (this != &that) { NumberFormat::operator=(that); fCount = that.fCount; delete [] fChoiceLimits; fChoiceLimits = 0; delete [] fClosures; fClosures = 0; delete [] fChoiceFormats; fChoiceFormats = 0; fChoiceLimits = new double[fCount]; fClosures = new UBool[fCount]; fChoiceFormats = new UnicodeString[fCount]; uprv_arrayCopy(that.fChoiceLimits, fChoiceLimits, fCount); uprv_arrayCopy(that.fClosures, fClosures, fCount); uprv_arrayCopy(that.fChoiceFormats, fChoiceFormats, fCount); } return *this; } // ------------------------------------- ChoiceFormat::~ChoiceFormat() { delete [] fChoiceLimits; fChoiceLimits = 0; delete [] fClosures; fClosures = 0; delete [] fChoiceFormats; fChoiceFormats = 0; fCount = 0; } // ------------------------------------- // NumberFormat cache management /* NumberFormat* ChoiceFormat::getNumberFormat(UErrorCode &status) { NumberFormat *theFormat = 0; if (fgNumberFormat != 0) // if there's something in the cache { Mutex lock(&fgMutex); if (fgNumberFormat != 0) // Someone might have grabbed it. { theFormat = fgNumberFormat; fgNumberFormat = 0; // We have exclusive right to this formatter. } } if(theFormat == 0) // If we weren't able to pull it out of the cache, then we have to create it. { theFormat = NumberFormat::createInstance(Locale::US, status); if(U_FAILURE(status)) return 0; theFormat->setMinimumFractionDigits(1); } return theFormat; } */ /* void ChoiceFormat::releaseNumberFormat(NumberFormat *adopt) { if(fgNumberFormat == 0) // If the cache is empty we must add it back. { Mutex lock(&fgMutex); if(fgNumberFormat == 0) { fgNumberFormat = adopt; adopt = 0; } } delete adopt; } */ /** * Convert a string to a double value */ double ChoiceFormat::stod(const UnicodeString& string, UErrorCode& status) { char source[256] = { '\0' }; char* end; u_UCharsToChars(string.getUChars(),source,string.length()); return uprv_strtod(source,&end); } // ------------------------------------- /** * Convert a double value to a string */ UnicodeString& ChoiceFormat::dtos(double value, UnicodeString& string, UErrorCode& status) { char temp[256] = {'\0'}; uprv_dtostr(value,temp,3,TRUE); string = UnicodeString(temp); return string; } #if 0 // ------------------------------------- // Applies the pattern to this ChoiceFormat instance. void ChoiceFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) { if (U_FAILURE(status)) { return; } // Perform 2 passes. The first computes the number of limits in // this pattern (fCount), which is 1 more than the number of // literal VERTICAL_BAR characters. int32_t count = 1; int32_t i; for (i=0; i 0 && limit <= newLimits[k-1]) { // Each limit must be strictly > than the previous // limit. One exception: Two subsequent limits may be // == if the first closure is FALSE and the second // closure is TRUE. This places the limit value in // the second interval. if (!(limit == newLimits[k-1] && !newClosures[k-1] && newClosures[k])) { goto error; } } buf.truncate(0); } else if (c == VERTICAL_BAR) { if (inNumber) { goto error; } inNumber = TRUE; newFormats[k] = buf; ++k; buf.truncate(0); } else { buf += c; } } if (k != (count-1) || inNumber || inQuote) { goto error; } newFormats[k] = buf; // Don't modify this object until the parse succeeds delete[] fChoiceLimits; delete[] fClosures; delete[] fChoiceFormats; fCount = count; fChoiceLimits = newLimits; fClosures = newClosures; fChoiceFormats = newFormats; return; error: status = U_ILLEGAL_ARGUMENT_ERROR; delete[] newLimits; delete[] newClosures; delete[] newFormats; return; } #endif // ------------------------------------- // calls the overloaded applyPattern method. void ChoiceFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) { UParseError parseError; applyPattern(pattern,parseError,status); } // ------------------------------------- // Applies the pattern to this ChoiceFormat instance. void ChoiceFormat::applyPattern(const UnicodeString& pattern, UParseError& parseError, UErrorCode& status) { if (U_FAILURE(status)) { return; } // Clear error struct parseError.offset = 0; parseError.preContext[0] = parseError.postContext[0] = (UChar)0; // Perform 2 passes. The first computes the number of limits in // this pattern (fCount), which is 1 more than the number of // literal VERTICAL_BAR characters. int32_t count = 1; int32_t i; for (i=0; i 0 && limit <= newLimits[k-1]) { // Each limit must be strictly > than the previous // limit. One exception: Two subsequent limits may be // == if the first closure is FALSE and the second // closure is TRUE. This places the limit value in // the second interval. if (!(limit == newLimits[k-1] && !newClosures[k-1] && newClosures[k])) { goto error; } } buf.truncate(0); } else if (c == VERTICAL_BAR) { if (inNumber) { goto error; } inNumber = TRUE; newFormats[k] = buf; ++k; buf.truncate(0); } else { buf += c; } } if (k != (count-1) || inNumber || inQuote) { goto error; } newFormats[k] = buf; // Don't modify this object until the parse succeeds delete[] fChoiceLimits; delete[] fClosures; delete[] fChoiceFormats; fCount = count; fChoiceLimits = newLimits; fClosures = newClosures; fChoiceFormats = newFormats; return; error: //status = U_ILLEGAL_ARGUMENT_ERROR; syntaxError(pattern,i,parseError,status); delete[] newLimits; delete[] newClosures; delete[] newFormats; return; } // ------------------------------------- // Reconstruct the original input pattern. UnicodeString& ChoiceFormat::toPattern(UnicodeString& result) const { result.remove(); for (int32_t i = 0; i < fCount; ++i) { if (i != 0) { result += VERTICAL_BAR; } UErrorCode status = U_ZERO_ERROR; UnicodeString buf; if (uprv_isPositiveInfinity(fChoiceLimits[i])) { result += INFINITY; } else if (uprv_isNegativeInfinity(fChoiceLimits[i])) { result += MINUS; result += INFINITY; } else { result += dtos(fChoiceLimits[i], buf, status); } if (fClosures[i]) { result += LESS_THAN; } else { result += LESS_EQUAL; } // Append fChoiceFormats[i], using quotes if there are special // characters. Single quotes themselves must be escaped in // either case. const UnicodeString& text = fChoiceFormats[i]; UBool needQuote = text.indexOf(LESS_THAN) >= 0 || text.indexOf(LESS_EQUAL) >= 0 || text.indexOf(LESS_EQUAL2) >= 0 || text.indexOf(VERTICAL_BAR) >= 0; if (needQuote) { result += SINGLE_QUOTE; } if (text.indexOf(SINGLE_QUOTE) < 0) { result += text; } else { for (int32_t j = 0; j < text.length(); ++j) { UChar c = text[j]; result += c; if (c == SINGLE_QUOTE) { result += c; } } } if (needQuote) { result += SINGLE_QUOTE; } } return result; } // ------------------------------------- // Adopts the limit and format arrays. void ChoiceFormat::adoptChoices(double *limits, UnicodeString *formats, int32_t cnt ) { adoptChoices(limits, (UBool *)0, formats, cnt); } // ------------------------------------- // Adopts the limit and format arrays. void ChoiceFormat::adoptChoices(double *limits, UBool *closures, UnicodeString *formats, int32_t cnt ) { if(limits == 0 || formats == 0) return; delete [] fChoiceLimits; delete [] fClosures; delete [] fChoiceFormats; fChoiceLimits = limits; fClosures = closures; fChoiceFormats = formats; fCount = cnt; if (fClosures == 0) { fClosures = new UBool[fCount]; int32_t i; for (i=0; i fChoiceLimits[i])) { // same as number <= fChoiceLimits, except catches NaN break; } } else if (!(number >= fChoiceLimits[i])) { // same as number < fChoiceLimits, except catches NaN break; } } --i; if (i < 0) { i = 0; } // return either a formatted number, or a string toAppendTo += fChoiceFormats[i]; return toAppendTo; } // ------------------------------------- // Formats an array of objects. Checks if the data type of the objects // to get the right value for formatting. UnicodeString& ChoiceFormat::format(const Formattable* objs, int32_t cnt, UnicodeString& toAppendTo, FieldPosition& pos, UErrorCode& status) const { if(cnt < 0) { status = U_ILLEGAL_ARGUMENT_ERROR; return toAppendTo; } UnicodeString buffer; for (int32_t i = 0; i < cnt; i++) { buffer.remove(); toAppendTo += format(_getDouble(objs[i]), buffer, pos); } return toAppendTo; } // ------------------------------------- // Formats an array of objects. Checks if the data type of the objects // to get the right value for formatting. UnicodeString& ChoiceFormat::format(const Formattable& obj, UnicodeString& toAppendTo, FieldPosition& pos, UErrorCode& status) const { return NumberFormat::format(obj, toAppendTo, pos, status); } // ------------------------------------- void ChoiceFormat::parse(const UnicodeString& text, Formattable& result, ParsePosition& status) const { // find the best number (defined as the one with the longest parse) int32_t start = status.getIndex(); int32_t furthest = start; double bestNumber = uprv_getNaN(); double tempNumber = 0.0; for (int i = 0; i < fCount; ++i) { UnicodeString tempString = fChoiceFormats[i]; if(text.compareBetween(start, tempString.length(), tempString, 0, tempString.length()) == 0) { status.setIndex(start + tempString.length()); tempNumber = fChoiceLimits[i]; if (status.getIndex() > furthest) { furthest = status.getIndex(); bestNumber = tempNumber; if (furthest == text.length()) break; } } } status.setIndex(furthest); if (status.getIndex() == start) { status.setErrorIndex(furthest); } result.setDouble(bestNumber); } // ------------------------------------- // Parses the text and return the Formattable object. void ChoiceFormat::parse(const UnicodeString& text, Formattable& result, UErrorCode& status) const { NumberFormat::parse(text, result, status); } // ------------------------------------- Format* ChoiceFormat::clone() const { ChoiceFormat *aCopy = new ChoiceFormat(*this); return aCopy; } // ------------------------------------- double ChoiceFormat::nextDouble( double d, UBool positive ) { return uprv_nextDouble( d, positive ); } //eof