home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Programming / ICU / src / icu / source / i18n / choicfmt.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-19  |  17.4 KB  |  588 lines

  1. /*
  2. ********************************************************************************
  3. *                                                                              *
  4. * COPYRIGHT:                                                                   *
  5. *   (C) Copyright Taligent, Inc.,  1997                                        *
  6. *   (C) Copyright International Business Machines Corporation,  1997-1998      *
  7. *   Licensed Material - Program-Property of IBM - All Rights Reserved.         *
  8. *   US Government Users Restricted Rights - Use, duplication, or disclosure    *
  9. *   restricted by GSA ADP Schedule Contract with IBM Corp.                     *
  10. *                                                                              *
  11. ********************************************************************************
  12. *
  13. * File CHOICFMT.CPP
  14. *
  15. * Modification History:
  16. *
  17. *   Date        Name        Description
  18. *   02/19/97    aliu        Converted from java.
  19. *   03/20/97    helena      Finished first cut of implementation and got rid 
  20. *                           of nextDouble/previousDouble and replaced with
  21. *                           boolean array.
  22. *   4/10/97     aliu        Clean up.  Modified to work on AIX.
  23. *   06/04/97    helena      Fixed applyPattern(), toPattern() and not to include 
  24. *                           wchar.h.
  25. *   07/09/97    helena      Made ParsePosition into a class.
  26. *   08/06/97    nos         removed overloaded constructor, fixed 'format(array)'
  27. *    07/22/98    stephen        JDK 1.2 Sync - removed bool_t array (doubleFlags)
  28. *   02/22/99    stephen     Removed character literals for EBCDIC safety
  29. ********************************************************************************
  30. */
  31.  
  32. #include "cpputils.h"
  33. #include "choicfmt.h"
  34. #include "numfmt.h"
  35. #include "locid.h"
  36. #include "mutex.h" 
  37.  
  38. // *****************************************************************************
  39. // class ChoiceFormat
  40. // *****************************************************************************
  41.  
  42. char        ChoiceFormat::fgClassID = 0; // Value is irrelevant
  43.  
  44. NumberFormat* ChoiceFormat::fgNumberFormat = 0;
  45.  
  46. // -------------------------------------
  47. // Creates a ChoiceFormat instance based on the pattern.
  48.  
  49. ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern,
  50.                            UErrorCode& status)
  51. : fChoiceLimits(0),
  52.   fChoiceFormats(0),
  53.   fCount(0)
  54. {
  55.     applyPattern(newPattern, status);
  56. }
  57.  
  58. // -------------------------------------
  59. // Creates a ChoiceFormat instance with the limit array and 
  60. // format strings for each limit.
  61.  
  62. ChoiceFormat::ChoiceFormat(const double* limits, 
  63.                            const UnicodeString* formats, 
  64.                            int32_t cnt )
  65. : fChoiceLimits(0),
  66.   fChoiceFormats(0),
  67.   fCount(0)
  68. {
  69.     setChoices(limits, formats, cnt );
  70. }
  71.  
  72. // -------------------------------------
  73. // copy constructor
  74.  
  75. ChoiceFormat::ChoiceFormat(const    ChoiceFormat&   that) 
  76.     : fChoiceLimits(0),
  77.       fChoiceFormats(0)
  78. {
  79.     *this = that;
  80. }
  81.  
  82. // -------------------------------------
  83.  
  84. bool_t
  85. ChoiceFormat::operator==(const Format& that) const
  86. {
  87.     if (this == &that) return TRUE;
  88.     if (this->getDynamicClassID() != that.getDynamicClassID()) return FALSE;  // not the same class
  89.     if (!NumberFormat::operator==(that)) return FALSE;
  90.     ChoiceFormat& thatAlias = (ChoiceFormat&)that;
  91.     if (fCount != thatAlias.fCount) return FALSE;
  92.     // Checks the limits, the corresponding format string and LE or LT flags.
  93.     // LE means less than and equal to, LT means less than.
  94.     for (int32_t i = 0; i < fCount; i++) {
  95.         if ((fChoiceLimits[i] != thatAlias.fChoiceLimits[i]) ||
  96.             (fChoiceFormats[i] != thatAlias.fChoiceFormats[i]))
  97.             return FALSE;
  98.     }
  99.     return TRUE;
  100. }
  101.  
  102. // -------------------------------------
  103. // copy constructor
  104.  
  105. const ChoiceFormat&
  106. ChoiceFormat::operator=(const   ChoiceFormat& that)
  107. {
  108.     if (this != &that) {
  109.         NumberFormat::operator=(that);
  110.         fCount = that.fCount;
  111.         delete [] fChoiceLimits; fChoiceLimits = 0;
  112.         delete [] fChoiceFormats; fChoiceFormats = 0;
  113.         fChoiceLimits = new double[fCount];
  114.         fChoiceFormats = new UnicodeString[fCount];
  115.  
  116.         icu_arrayCopy(that.fChoiceLimits, fChoiceLimits, fCount);
  117.         icu_arrayCopy(that.fChoiceFormats, fChoiceFormats, fCount);
  118.     }
  119.     return *this;
  120. }
  121.  
  122. // -------------------------------------
  123.  
  124. ChoiceFormat::~ChoiceFormat()
  125. {
  126.     delete [] fChoiceLimits;
  127.     fChoiceLimits = 0;
  128.     delete [] fChoiceFormats;
  129.     fChoiceFormats = 0;
  130.     fCount = 0;
  131. }
  132.  
  133. // -------------------------------------
  134. // NumberFormat cache management
  135.  
  136. NumberFormat* 
  137. ChoiceFormat::getNumberFormat(UErrorCode &status)
  138. {
  139.     NumberFormat *theFormat = 0;
  140.  
  141.     if (fgNumberFormat != 0) // if there's something in the cache
  142.     {
  143.         Mutex lock;
  144.  
  145.         if (fgNumberFormat != 0) // Someone might have grabbed it.
  146.         {
  147.             theFormat = fgNumberFormat;
  148.             fgNumberFormat = 0; // We have exclusive right to this formatter.
  149.         }
  150.     }
  151.  
  152.     if(theFormat == 0) // If we weren't able to pull it out of the cache, then we have to create it.
  153.     {
  154.         theFormat = NumberFormat::createInstance(Locale::US, status);
  155.         if(U_FAILURE(status))
  156.             return 0;
  157.         theFormat->setMinimumFractionDigits(1);
  158.     }
  159.  
  160.     return theFormat;
  161. }
  162.  
  163. void          
  164. ChoiceFormat::releaseNumberFormat(NumberFormat *adopt)
  165. {
  166.     if(fgNumberFormat == 0) // If the cache is empty we must add it back.
  167.     {
  168.         Mutex lock;
  169.  
  170.         if(fgNumberFormat == 0)
  171.         {
  172.             fgNumberFormat = adopt;
  173.             adopt = 0;
  174.         }
  175.     }
  176.  
  177.     delete adopt;
  178. }
  179.  
  180. /**
  181.  * Convert a string to a double value using a default NumberFormat object
  182.  * which is static (shared by all ChoiceFormat instances).
  183.  */
  184. double
  185. ChoiceFormat::stod(const UnicodeString& string,
  186.                    UErrorCode& status)
  187. {
  188.     // Use a shared global number format to convert a double value to 
  189.     // or string or vice versa.
  190.     NumberFormat *myFormat = getNumberFormat(status);
  191.  
  192.     if(U_FAILURE(status))
  193.         return -1; // OK?
  194.  
  195.     Formattable result;
  196.     myFormat->parse(string, result, status);
  197.     releaseNumberFormat(myFormat);
  198.     double value = 0.0;
  199.     if (U_SUCCESS(status))
  200.     {
  201.         switch(result.getType())
  202.         {
  203.             case Formattable::kLong: value = result.getLong(); break;
  204.             case Formattable::kDouble: value = result.getDouble(); break;
  205.         }
  206.     }
  207.     return value;
  208. }
  209.  
  210. // -------------------------------------
  211.  
  212. /**
  213.  * Convert a double value to a string using a default NumberFormat object
  214.  * which is static (shared by all ChoiceFormat instances).
  215.  */
  216. UnicodeString&
  217. ChoiceFormat::dtos(double value,
  218.                    UnicodeString& string,
  219.                    UErrorCode& status)
  220. {
  221.     NumberFormat *myFormat = getNumberFormat(status);
  222.  
  223.     if (U_SUCCESS(status)) {
  224.         FieldPosition fieldPos(0);
  225.         myFormat->format(value, string, fieldPos);
  226.     }
  227.     releaseNumberFormat(myFormat);
  228.     return string;
  229. }
  230.  
  231. // -------------------------------------
  232. // Applies the pattern to this ChoiceFormat instance.
  233.  
  234. void
  235. ChoiceFormat::applyPattern(const UnicodeString& newPattern,
  236.                            UErrorCode& status)
  237. {
  238.     if (U_FAILURE(status))
  239.         return;
  240.  
  241.     UnicodeString segments[2];
  242.     double newChoiceLimits[30];  // current limit
  243.     UnicodeString newChoiceFormats[30];   // later, use Vectors
  244.     int32_t count = 0;
  245.     int32_t part = 0;
  246.     double startValue = 0;
  247.     double oldStartValue = icu_getNaN();
  248.     bool_t inQuote = FALSE;
  249.     for(int i = 0; i < newPattern.size(); ++i) {
  250.         UChar ch = newPattern[i];
  251.         if(ch == 0x0027 /*'\''*/) {
  252.             // Check for "''" indicating a literal quote
  253.             if((i+1) < newPattern.size() && newPattern[i+1] == ch) {
  254.                 segments[part] += ch;
  255.                 ++i;
  256.             }
  257.             else 
  258.                 inQuote = !inQuote;
  259.         }
  260.         else if (inQuote) {
  261.             segments[part] += ch;
  262.         }
  263.         else if (ch == 0x003C /*'<'*/ || ch == 0x0023 /*'#'*/ || ch == 0x2264) {
  264.             if (segments[0] == "") {
  265.                 status = U_ILLEGAL_ARGUMENT_ERROR;
  266.                 return;
  267.             }
  268.  
  269.             UnicodeString tempBuffer = segments[0];
  270.             tempBuffer.trim();
  271.             UChar posInf = 0x221E;
  272.             UChar negInf [] = {0x002D /*'-'*/, posInf };
  273.             if (tempBuffer == UnicodeString(&posInf, 1, 1)) {
  274.                 startValue = icu_getInfinity();
  275.             } 
  276.             else if (tempBuffer == UnicodeString(negInf, 2, 2)) {
  277.                 startValue = - icu_getInfinity();
  278.             } 
  279.             else {
  280.                 //segments[0].trim();
  281.                 startValue = stod(tempBuffer, status);
  282.                 if(U_FAILURE(status))
  283.                     return;
  284.             }
  285.  
  286.             if (ch == 0x003C /*'<'*/ && ! icu_isInfinite(startValue)) {
  287.                 startValue = nextDouble(startValue);
  288.             }
  289.             // {sfb} There is a bug in MSVC 5.0 sp3 -- 0.0 <= NaN ==> TRUE
  290.             //if (startValue <= oldStartValue) {
  291.             if (startValue <= oldStartValue && ! icu_isNaN(oldStartValue)) {
  292.                 status = U_ILLEGAL_ARGUMENT_ERROR;
  293.                 return;
  294.             }
  295.             segments[0].remove();
  296.             part = 1;
  297.         } else if (ch == 0x007C /*'|'*/) {
  298.             newChoiceLimits[count] = startValue;
  299.             newChoiceFormats[count] = segments[1];
  300.             ++count;
  301.             oldStartValue = startValue;
  302.             segments[1].remove();
  303.             part = 0;
  304.         } else {
  305.             segments[part] += ch;
  306.         }
  307.     }
  308.     // clean up last one
  309.     if (part == 1) {
  310.         newChoiceLimits[count] = startValue;
  311.         newChoiceFormats[count] = segments[1];
  312.         ++count;
  313.     }
  314.  
  315.  
  316.     delete [] fChoiceLimits; fChoiceLimits = 0;
  317.     delete [] fChoiceFormats; fChoiceFormats = 0;
  318.  
  319.     fCount = count;
  320.     fChoiceLimits    = new double[fCount];
  321.     fChoiceFormats    = new UnicodeString[fCount];
  322.     
  323.     icu_arrayCopy(newChoiceLimits, fChoiceLimits, fCount);
  324.     icu_arrayCopy(newChoiceFormats, fChoiceFormats, fCount);
  325. }
  326.  
  327. // -------------------------------------
  328. // Reconstruct the original input pattern.
  329.  
  330. UnicodeString&
  331. ChoiceFormat::toPattern(UnicodeString& result) const
  332. {
  333.     result.remove();
  334.     for (int32_t i = 0; i < fCount; ++i) {
  335.         if (i != 0) {
  336.             result += 0x007C /*'|'*/;
  337.         }
  338.         // choose based upon which has less precision
  339.         // approximate that by choosing the closest one to an integer.
  340.         // could do better, but it's not worth it.
  341.         double less = previousDouble(fChoiceLimits[i]);
  342.         double tryLessOrEqual = icu_fabs(icu_IEEEremainder(fChoiceLimits[i], 1.0));
  343.         double tryLess = icu_fabs(icu_IEEEremainder(less, 1.0));
  344.  
  345.         UErrorCode status = U_ZERO_ERROR;
  346.         UnicodeString buf;
  347.         // {sfb} hack to get this to work on MSVC - NaN doesn't behave as it should
  348.         if (tryLessOrEqual < tryLess && 
  349.             ! (icu_isNaN(tryLessOrEqual) || icu_isNaN(tryLess))) {
  350.             result += dtos(fChoiceLimits[i], buf, status);
  351.             result += 0x0023 /*'#'*/;
  352.         } 
  353.         else {
  354.             if (icu_isPositiveInfinity(fChoiceLimits[i])) {
  355.                 result += 0x221E;
  356.             } else if (icu_isNegativeInfinity(fChoiceLimits[i])) {
  357.                 result += 0x002D /*'-'*/;
  358.                 result += 0x221E;
  359.             } else {
  360.                 result += dtos(less, buf, status);
  361.             }
  362.             result += 0x003C /*'<'*/;
  363.         }
  364.         // Append fChoiceFormats[i], using quotes if there are special characters.
  365.         // Single quotes themselves must be escaped in either case.
  366.         UnicodeString text = fChoiceFormats[i];
  367.         bool_t needQuote = text.indexOf(0x003C /*'<'*/) >= 0
  368.             || text.indexOf(0x0023 /*'#'*/) >= 0
  369.             || text.indexOf(0x2264) >= 0
  370.             || text.indexOf(0x007C /*'|'*/) >= 0;
  371.         if (needQuote) 
  372.             result += 0x0027 /*'\''*/;
  373.         if (text.indexOf(0x0027 /*'\''*/) < 0) 
  374.             result += text;
  375.         else {
  376.             for (int j = 0; j < text.size(); ++j) {
  377.                 UChar c = text[j];
  378.                 result += c;
  379.                 if (c == 0x0027 /*'\''*/) 
  380.                     result += c;
  381.             }
  382.         }
  383.         if (needQuote) 
  384.             result += 0x0027 /*'\''*/;
  385.     }
  386.     
  387.     return result;
  388. }
  389.  
  390. // -------------------------------------
  391. // Adopts the limit and format arrays.
  392.  
  393. void
  394. ChoiceFormat::adoptChoices(double *limits, 
  395.                            UnicodeString *formats, 
  396.                            int32_t cnt )
  397. {
  398.     if(limits == 0 || formats == 0)
  399.         return;
  400.         
  401.     delete [] fChoiceLimits;
  402.     fChoiceLimits = 0;
  403.     delete [] fChoiceFormats;
  404.     fChoiceFormats = 0;
  405.     fChoiceLimits = limits;
  406.     fChoiceFormats = formats;
  407.     fCount = cnt;
  408. }
  409.  
  410. // -------------------------------------
  411. // Sets the limit and format arrays. 
  412. void
  413. ChoiceFormat::setChoices(  const double* limits, 
  414.                            const UnicodeString* formats, 
  415.                            int32_t cnt )
  416. {
  417.     if(limits == 0 || formats == 0)
  418.         return;
  419.  
  420.     delete [] fChoiceLimits; fChoiceLimits = 0;
  421.     delete [] fChoiceFormats; fChoiceFormats = 0;
  422.  
  423.     // Note that the old arrays are deleted and this owns
  424.     // the created array.
  425.     fCount = cnt;
  426.     fChoiceLimits = new double[fCount];
  427.     fChoiceFormats = new UnicodeString[fCount];
  428.  
  429.     icu_arrayCopy(limits, fChoiceLimits, fCount);
  430.     icu_arrayCopy(formats, fChoiceFormats, fCount);
  431. }
  432.  
  433. // -------------------------------------
  434. // Gets the limit array.
  435.  
  436. const double*
  437. ChoiceFormat::getLimits(int32_t& cnt) const 
  438. {
  439.     cnt = fCount;
  440.     return fChoiceLimits;
  441. }
  442.  
  443. // -------------------------------------
  444. // Gets the format array.
  445.  
  446. const UnicodeString*
  447. ChoiceFormat::getFormats(int32_t& cnt) const
  448. {
  449.     cnt = fCount;
  450.     return fChoiceFormats;
  451. }
  452.  
  453. // -------------------------------------
  454. // Formats a long number, it's actually formatted as
  455. // a double.  The returned format string may differ
  456. // from the input number because of this.
  457.  
  458. UnicodeString&
  459. ChoiceFormat::format(int32_t number, 
  460.                      UnicodeString& toAppendTo, 
  461.                      FieldPosition& status) const
  462. {
  463.     return format((double) number, toAppendTo, status);
  464. }
  465.  
  466. // -------------------------------------
  467. // Formats a double number.
  468.  
  469. UnicodeString&
  470. ChoiceFormat::format(double number, 
  471.                      UnicodeString& toAppendTo, 
  472.                      FieldPosition& status) const
  473. {
  474.     // find the number
  475.     int32_t i;
  476.     for (i = 0; i < fCount; ++i) {
  477.         if (!(number >= fChoiceLimits[i])) {
  478.             // same as number < fChoiceLimits, except catches NaN
  479.             break;
  480.         }
  481.     }
  482.     --i;
  483.     if (i < 0) 
  484.         i = 0;
  485.     // return either a formatted number, or a string
  486.     return (toAppendTo += fChoiceFormats[i]);
  487. }
  488.  
  489. // -------------------------------------
  490. // Formats an array of objects. Checks if the data type of the objects
  491. // to get the right value for formatting.  
  492.  
  493. UnicodeString&
  494. ChoiceFormat::format(const Formattable* objs,
  495.                      int32_t cnt,
  496.                      UnicodeString& toAppendTo,
  497.                      FieldPosition& pos,
  498.                      UErrorCode& status) const
  499. {
  500.     if(cnt < 0) {
  501.         status = U_ILLEGAL_ARGUMENT_ERROR;
  502.         return toAppendTo;
  503.     }
  504.     
  505.     UnicodeString buffer;
  506.     for (int32_t i = 0; i < cnt; i++) {
  507.         buffer.remove();
  508.         toAppendTo += format((objs[i].getType() == Formattable::kLong) ? objs[i].getLong() : objs[i].getDouble(), 
  509.                              buffer, pos);
  510.     }
  511.  
  512.     return toAppendTo;
  513. }
  514.  
  515. // -------------------------------------
  516. // Formats an array of objects. Checks if the data type of the objects
  517. // to get the right value for formatting.  
  518.  
  519. UnicodeString&
  520. ChoiceFormat::format(const Formattable& obj, 
  521.                      UnicodeString& toAppendTo, 
  522.                      FieldPosition& pos,
  523.                      UErrorCode& status) const
  524. {
  525.     return NumberFormat::format(obj, toAppendTo, pos, status); 
  526. }
  527. // -------------------------------------
  528.  
  529. void
  530. ChoiceFormat::parse(const UnicodeString& text, 
  531.                     Formattable& result,
  532.                     ParsePosition& status) const
  533. {
  534.     // find the best number (defined as the one with the longest parse)
  535.     int32_t start = status.getIndex();
  536.     int32_t furthest = start;
  537.     double bestNumber = icu_getNaN();
  538.     double tempNumber = 0.0;
  539.     for (int i = 0; i < fCount; ++i) {
  540.         UnicodeString tempString = fChoiceFormats[i];
  541.         if(text.compareBetween(start, tempString.size(), tempString, 0, tempString.size()) == 0) {
  542.             status.setIndex(start + tempString.size());
  543.             tempNumber = fChoiceLimits[i];
  544.             if (status.getIndex() > furthest) {
  545.                 furthest = status.getIndex();
  546.                 bestNumber = tempNumber;
  547.                 if (furthest == text.size()) 
  548.                     break;
  549.             }
  550.         }
  551.     }
  552.     status.setIndex(furthest);
  553.     if (status.getIndex() == start) {
  554.         status.setErrorIndex(furthest);
  555.     }
  556.     result.setDouble(bestNumber);
  557. }
  558.  
  559. // -------------------------------------
  560. // Parses the text and return the Formattable object.  
  561.  
  562. void
  563. ChoiceFormat::parse(const UnicodeString& text, 
  564.                     Formattable& result,
  565.                     UErrorCode& status) const
  566. {
  567.     NumberFormat::parse(text, result, status);
  568. }
  569.  
  570. // -------------------------------------
  571.  
  572. Format*
  573. ChoiceFormat::clone() const
  574. {
  575.     ChoiceFormat *aCopy = new ChoiceFormat(*this);
  576.     return aCopy;
  577. }
  578.  
  579. // -------------------------------------
  580.  
  581. double 
  582. ChoiceFormat::nextDouble( double d, bool_t positive )
  583. {
  584.     return icu_nextDouble( d, positive );
  585. }
  586.  
  587. //eof
  588.