home *** CD-ROM | disk | FTP | other *** search
- /*
- ********************************************************************************
- * *
- * COPYRIGHT: *
- * (C) Copyright Taligent, Inc., 1997 *
- * (C) Copyright International Business Machines Corporation, 1997-1998 *
- * Licensed Material - Program-Property of IBM - All Rights Reserved. *
- * US Government Users Restricted Rights - Use, duplication, or disclosure *
- * restricted by GSA ADP Schedule Contract with IBM Corp. *
- * *
- ********************************************************************************
- *
- * 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 bool_t array (doubleFlags)
- * 02/22/99 stephen Removed character literals for EBCDIC safety
- ********************************************************************************
- */
-
- #include "cpputils.h"
- #include "choicfmt.h"
- #include "numfmt.h"
- #include "locid.h"
- #include "mutex.h"
-
- // *****************************************************************************
- // class ChoiceFormat
- // *****************************************************************************
-
- char ChoiceFormat::fgClassID = 0; // Value is irrelevant
-
- NumberFormat* ChoiceFormat::fgNumberFormat = 0;
-
- // -------------------------------------
- // Creates a ChoiceFormat instance based on the pattern.
-
- ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern,
- UErrorCode& status)
- : fChoiceLimits(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),
- fChoiceFormats(0),
- fCount(0)
- {
- setChoices(limits, formats, cnt );
- }
-
- // -------------------------------------
- // copy constructor
-
- ChoiceFormat::ChoiceFormat(const ChoiceFormat& that)
- : fChoiceLimits(0),
- fChoiceFormats(0)
- {
- *this = that;
- }
-
- // -------------------------------------
-
- bool_t
- 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]) ||
- (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 [] fChoiceFormats; fChoiceFormats = 0;
- fChoiceLimits = new double[fCount];
- fChoiceFormats = new UnicodeString[fCount];
-
- icu_arrayCopy(that.fChoiceLimits, fChoiceLimits, fCount);
- icu_arrayCopy(that.fChoiceFormats, fChoiceFormats, fCount);
- }
- return *this;
- }
-
- // -------------------------------------
-
- ChoiceFormat::~ChoiceFormat()
- {
- delete [] fChoiceLimits;
- fChoiceLimits = 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;
-
- 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;
-
- if(fgNumberFormat == 0)
- {
- fgNumberFormat = adopt;
- adopt = 0;
- }
- }
-
- delete adopt;
- }
-
- /**
- * Convert a string to a double value using a default NumberFormat object
- * which is static (shared by all ChoiceFormat instances).
- */
- double
- ChoiceFormat::stod(const UnicodeString& string,
- UErrorCode& status)
- {
- // Use a shared global number format to convert a double value to
- // or string or vice versa.
- NumberFormat *myFormat = getNumberFormat(status);
-
- if(U_FAILURE(status))
- return -1; // OK?
-
- Formattable result;
- myFormat->parse(string, result, status);
- releaseNumberFormat(myFormat);
- double value = 0.0;
- if (U_SUCCESS(status))
- {
- switch(result.getType())
- {
- case Formattable::kLong: value = result.getLong(); break;
- case Formattable::kDouble: value = result.getDouble(); break;
- }
- }
- return value;
- }
-
- // -------------------------------------
-
- /**
- * Convert a double value to a string using a default NumberFormat object
- * which is static (shared by all ChoiceFormat instances).
- */
- UnicodeString&
- ChoiceFormat::dtos(double value,
- UnicodeString& string,
- UErrorCode& status)
- {
- NumberFormat *myFormat = getNumberFormat(status);
-
- if (U_SUCCESS(status)) {
- FieldPosition fieldPos(0);
- myFormat->format(value, string, fieldPos);
- }
- releaseNumberFormat(myFormat);
- return string;
- }
-
- // -------------------------------------
- // Applies the pattern to this ChoiceFormat instance.
-
- void
- ChoiceFormat::applyPattern(const UnicodeString& newPattern,
- UErrorCode& status)
- {
- if (U_FAILURE(status))
- return;
-
- UnicodeString segments[2];
- double newChoiceLimits[30]; // current limit
- UnicodeString newChoiceFormats[30]; // later, use Vectors
- int32_t count = 0;
- int32_t part = 0;
- double startValue = 0;
- double oldStartValue = icu_getNaN();
- bool_t inQuote = FALSE;
- for(int i = 0; i < newPattern.size(); ++i) {
- UChar ch = newPattern[i];
- if(ch == 0x0027 /*'\''*/) {
- // Check for "''" indicating a literal quote
- if((i+1) < newPattern.size() && newPattern[i+1] == ch) {
- segments[part] += ch;
- ++i;
- }
- else
- inQuote = !inQuote;
- }
- else if (inQuote) {
- segments[part] += ch;
- }
- else if (ch == 0x003C /*'<'*/ || ch == 0x0023 /*'#'*/ || ch == 0x2264) {
- if (segments[0] == "") {
- status = U_ILLEGAL_ARGUMENT_ERROR;
- return;
- }
-
- UnicodeString tempBuffer = segments[0];
- tempBuffer.trim();
- UChar posInf = 0x221E;
- UChar negInf [] = {0x002D /*'-'*/, posInf };
- if (tempBuffer == UnicodeString(&posInf, 1, 1)) {
- startValue = icu_getInfinity();
- }
- else if (tempBuffer == UnicodeString(negInf, 2, 2)) {
- startValue = - icu_getInfinity();
- }
- else {
- //segments[0].trim();
- startValue = stod(tempBuffer, status);
- if(U_FAILURE(status))
- return;
- }
-
- if (ch == 0x003C /*'<'*/ && ! icu_isInfinite(startValue)) {
- startValue = nextDouble(startValue);
- }
- // {sfb} There is a bug in MSVC 5.0 sp3 -- 0.0 <= NaN ==> TRUE
- //if (startValue <= oldStartValue) {
- if (startValue <= oldStartValue && ! icu_isNaN(oldStartValue)) {
- status = U_ILLEGAL_ARGUMENT_ERROR;
- return;
- }
- segments[0].remove();
- part = 1;
- } else if (ch == 0x007C /*'|'*/) {
- newChoiceLimits[count] = startValue;
- newChoiceFormats[count] = segments[1];
- ++count;
- oldStartValue = startValue;
- segments[1].remove();
- part = 0;
- } else {
- segments[part] += ch;
- }
- }
- // clean up last one
- if (part == 1) {
- newChoiceLimits[count] = startValue;
- newChoiceFormats[count] = segments[1];
- ++count;
- }
-
-
- delete [] fChoiceLimits; fChoiceLimits = 0;
- delete [] fChoiceFormats; fChoiceFormats = 0;
-
- fCount = count;
- fChoiceLimits = new double[fCount];
- fChoiceFormats = new UnicodeString[fCount];
-
- icu_arrayCopy(newChoiceLimits, fChoiceLimits, fCount);
- icu_arrayCopy(newChoiceFormats, fChoiceFormats, fCount);
- }
-
- // -------------------------------------
- // 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 += 0x007C /*'|'*/;
- }
- // choose based upon which has less precision
- // approximate that by choosing the closest one to an integer.
- // could do better, but it's not worth it.
- double less = previousDouble(fChoiceLimits[i]);
- double tryLessOrEqual = icu_fabs(icu_IEEEremainder(fChoiceLimits[i], 1.0));
- double tryLess = icu_fabs(icu_IEEEremainder(less, 1.0));
-
- UErrorCode status = U_ZERO_ERROR;
- UnicodeString buf;
- // {sfb} hack to get this to work on MSVC - NaN doesn't behave as it should
- if (tryLessOrEqual < tryLess &&
- ! (icu_isNaN(tryLessOrEqual) || icu_isNaN(tryLess))) {
- result += dtos(fChoiceLimits[i], buf, status);
- result += 0x0023 /*'#'*/;
- }
- else {
- if (icu_isPositiveInfinity(fChoiceLimits[i])) {
- result += 0x221E;
- } else if (icu_isNegativeInfinity(fChoiceLimits[i])) {
- result += 0x002D /*'-'*/;
- result += 0x221E;
- } else {
- result += dtos(less, buf, status);
- }
- result += 0x003C /*'<'*/;
- }
- // Append fChoiceFormats[i], using quotes if there are special characters.
- // Single quotes themselves must be escaped in either case.
- UnicodeString text = fChoiceFormats[i];
- bool_t needQuote = text.indexOf(0x003C /*'<'*/) >= 0
- || text.indexOf(0x0023 /*'#'*/) >= 0
- || text.indexOf(0x2264) >= 0
- || text.indexOf(0x007C /*'|'*/) >= 0;
- if (needQuote)
- result += 0x0027 /*'\''*/;
- if (text.indexOf(0x0027 /*'\''*/) < 0)
- result += text;
- else {
- for (int j = 0; j < text.size(); ++j) {
- UChar c = text[j];
- result += c;
- if (c == 0x0027 /*'\''*/)
- result += c;
- }
- }
- if (needQuote)
- result += 0x0027 /*'\''*/;
- }
-
- return result;
- }
-
- // -------------------------------------
- // Adopts the limit and format arrays.
-
- void
- ChoiceFormat::adoptChoices(double *limits,
- UnicodeString *formats,
- int32_t cnt )
- {
- if(limits == 0 || formats == 0)
- return;
-
- delete [] fChoiceLimits;
- fChoiceLimits = 0;
- delete [] fChoiceFormats;
- fChoiceFormats = 0;
- fChoiceLimits = limits;
- fChoiceFormats = formats;
- fCount = cnt;
- }
-
- // -------------------------------------
- // Sets the limit and format arrays.
- void
- ChoiceFormat::setChoices( const double* limits,
- const UnicodeString* formats,
- int32_t cnt )
- {
- if(limits == 0 || formats == 0)
- return;
-
- delete [] fChoiceLimits; fChoiceLimits = 0;
- delete [] fChoiceFormats; fChoiceFormats = 0;
-
- // Note that the old arrays are deleted and this owns
- // the created array.
- fCount = cnt;
- fChoiceLimits = new double[fCount];
- fChoiceFormats = new UnicodeString[fCount];
-
- icu_arrayCopy(limits, fChoiceLimits, fCount);
- icu_arrayCopy(formats, fChoiceFormats, fCount);
- }
-
- // -------------------------------------
- // Gets the limit array.
-
- const double*
- ChoiceFormat::getLimits(int32_t& cnt) const
- {
- cnt = fCount;
- return fChoiceLimits;
- }
-
- // -------------------------------------
- // Gets the format array.
-
- const UnicodeString*
- ChoiceFormat::getFormats(int32_t& cnt) const
- {
- cnt = fCount;
- return fChoiceFormats;
- }
-
- // -------------------------------------
- // Formats a long number, it's actually formatted as
- // a double. The returned format string may differ
- // from the input number because of this.
-
- UnicodeString&
- ChoiceFormat::format(int32_t number,
- UnicodeString& toAppendTo,
- FieldPosition& status) const
- {
- return format((double) number, toAppendTo, status);
- }
-
- // -------------------------------------
- // Formats a double number.
-
- UnicodeString&
- ChoiceFormat::format(double number,
- UnicodeString& toAppendTo,
- FieldPosition& status) const
- {
- // find the number
- int32_t i;
- for (i = 0; i < fCount; ++i) {
- 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
- return (toAppendTo += fChoiceFormats[i]);
- }
-
- // -------------------------------------
- // 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((objs[i].getType() == Formattable::kLong) ? objs[i].getLong() : objs[i].getDouble(),
- 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 = icu_getNaN();
- double tempNumber = 0.0;
- for (int i = 0; i < fCount; ++i) {
- UnicodeString tempString = fChoiceFormats[i];
- if(text.compareBetween(start, tempString.size(), tempString, 0, tempString.size()) == 0) {
- status.setIndex(start + tempString.size());
- tempNumber = fChoiceLimits[i];
- if (status.getIndex() > furthest) {
- furthest = status.getIndex();
- bestNumber = tempNumber;
- if (furthest == text.size())
- 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, bool_t positive )
- {
- return icu_nextDouble( d, positive );
- }
-
- //eof
-