2005/5/9

     
 

DecimalFormat.cpp

artefaktur
// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*- 
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// 
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the 
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
// 
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
// 
// $Header: /cvsroot/acdk/acdk/acdk_text/src/acdk/text/DecimalFormat.cpp,v 1.15 2005/04/19 13:49:44 kommer Exp $
#include <acdk.h>
#include <acdk/lang/System.h>
#include <acdk/lang/Long.h>
#include <acdk/lang/Double.h>
#include <acdk/lang/IllegalArgumentException.h>
#include <acdk/io/IOException.h>
#include "DecimalFormat.h"
#include "DecimalFormatSymbols.h"
#include "FieldPosition.h"

#if defined(__BORLANDC__)

namespace {
//  is needed to resolves a linker/template bug
void foo()
{
  try { 
    THROW1_FQ(::acdk::io::, IOException, "asdf");
  } catch (::acdk::io::RIOException ex){
  }
}

}



#endif
/FONT>

namespace acdk {
namespace text {

  /*
  class DigitNumber
: public ::acdk::lang::Object
{
public:
  int _decimalIdx;
  int _size;
  byteArray _bytes;
  enum {
    MaxLongDigits = 19
  };

  DigitNumber()
  : acdk::lang::Object()
  , _decimalIdx(0)
  , _size(0)
  , _bytes(MaxLongDigits)
  {
  }
  void set(jlong v, int maxl);
  bool needRoundUp(int maxl);
  void round(int maxl);
};
*/

namespace {

/** @internal */
  int findTokenPos(IN(RString) pattern, char ch)
{
  const char* ptr = pattern->c_str();
  while (*ptr != 0) {
    if (*ptr == '\'')
      ++ptr;
    else if (*ptr == ';')
      return ptr - pattern->c_str();
    ++ptr;
  }
  return -1;
}

/** @internal */
  RString getPattern(bool isNeg, IN(RString) pattern)
{
  int negpatternidx = findTokenPos(pattern, ';');
  if (negpatternidx == -1)
    return pattern;
  if (isNeg == true) 
    return pattern->substr(negpatternidx + 1);
  else  
    return pattern->substr(0, negpatternidx);
}



} //anon namespace

/*
void 
DigitNumber::set(jlong v, int maxl)
{
  int left = MaxLongDigits;
  int right = 0;
  while (v > 0) 
  {
    _bytes[--left] = (byte) (((jlong) '0') + (v % 10));
    v /= 10;
  }
  _decimalIdx = MaxLongDigits - left;
  for (right = MaxLongDigits - 1; _bytes[right] == (byte) '0'; --right) 
  {
    //nothing
  }
  _size = right - left + 1;
  System::arraycopy(RbyteArray(&_bytes), left, RbyteArray(&_bytes), 0, _size);
  if (maxl > 0) 
    round(maxl);
}

bool
DigitNumber::needRoundUp(int maxl) 
{
  bool doIncrement = false;
  if (_bytes[maxl] > '5') 
    return true;
  if (_bytes[maxl] != '5' ) 
    return false;
  
  for (int i = maxl + 1; i < _size; ++i) {
    if (_bytes[i] != '0') {
      return true;
    }
  }
  if (maxl == 0)
    return true;
  return (_bytes[maxl - 1] % 2 != 0);
}

void 
DigitNumber::round(int maxl)
{
  if (maxl <= 0 || maxl >= _size) 
    return;
  if (needRoundUp(maxl)) {
    while (true)  {
      --maxl;
      if (maxl < 0) {
        _bytes[0] = (byte) '1';
        ++_decimalIdx;
        maxl = 0; 
        break;
      }
      ++_bytes[maxl];
      if (_bytes[maxl] <= '9') 
        break;
      
    }
    ++maxl; // Increment for use as _size
  }
  _size = maxl;
}
  

*/

DecimalFormat::DecimalFormat()
: _dateFormatSymbols(new DecimalFormatSymbols())
, _positiv(new DecimalSubpatternProperties(""))
, _negativ(new DecimalSubpatternProperties(""))
{
  
}

DecimalFormat::DecimalFormat(IN(RDecimalFormatSymbols) symbols)
: _dateFormatSymbols(symbols)
, _positiv(new DecimalSubpatternProperties(""))
, _negativ(new DecimalSubpatternProperties(""))
{
}

void 
DecimalFormat::applyLocalizedPattern(IN(RString) pattern)
{
}

void 
DecimalFormat::applyPattern(IN(RString) pattern)
{
  _pattern = pattern;
  if (pattern->indexOf(';') != -1) {
    _negativ = new DecimalSubpatternProperties(getPattern(true, pattern));
    _positiv = new DecimalSubpatternProperties(getPattern(false, pattern));
  } else
    _negativ = _positiv = new DecimalSubpatternProperties(_pattern);
}

acdk::lang::Object 
DecimalFormat::clone()
{
  return Object::serialized_clone(false);
}

bool 
DecimalFormat::equals(IN(acdk::lang::Object) obj)
{
  return Object::serialized_equals(obj, false);
}

/** @internal */
void checkGroup(const char* pattern, const char* pptr, const char* grouppos, int& groupsize)
{
  if (grouppos == 0)
    return;
  int diff = pptr - grouppos;
  if (groupsize != 0 && groupsize != diff)
    THROW1(IllegalArgumentException, RString("in sub pattern [") 
                    + pattern + "] grouping size must me always the same");
  groupsize = diff;
}

//foreign 
void 
DecimalSubpatternProperties::_analysePattern()
{
  const char* pptr = _pattern->c_str();

  const char* grouppos = 0;

  StringBuffer suffix("");
  StringBuffer prefix("");
  enum State 
  {
    Prefix = 0,
    Integer,
    Fraction,
    Suffix
  };
  State state = Prefix;
  while (*pptr) {
    switch (*pptr) {
    case '0':
      if (state == Prefix)
        state = Integer;
      if (state == Integer)
        ++_minimumIntegerDigits;
      else
        ++_minimumFractionDigits;
      break;
    case '#':
      if (state == Prefix)
        state = Integer;
      if (state == Integer)
        ++_maximumIntegerDigits;
      else
        ++_maximumFractionDigits;
      break;
    case ',' :
      if (grouppos != 0) {
        int diff = pptr - grouppos;
        if (_groupingSize != 0 && diff != _groupingSize)
          THROW1(IllegalArgumentException, RString("in sub pattern [") 
                    + _pattern + "] grouping size must me always the same");
        _groupingSize = diff;
      } else
        grouppos = pptr;
      break;
    case '-' :
      if (state == Fraction)
        state = Suffix;
      if (state == Prefix)
        prefix.append('-');
      else if (state == Suffix)
        suffix.append('-');
      break;
    case '\'':
      if (state > Prefix) {
        checkGroup(_pattern->c_str(), pptr, grouppos, _groupingSize);
        state = Suffix;
      }
      ++pptr;
      if (*pptr == '\'') {
        if (state == Prefix)
          prefix.append(*pptr);
        else
          suffix.append(*pptr);
        break;
      }
      while (*pptr != 0 && *pptr != '\'')
      {
        if (state == Prefix)
          prefix.append(*pptr);
        else
          suffix.append(*pptr);
        ++pptr;
      }
      break;
    case '.':
      if (state > Integer)
        THROW1(IllegalArgumentException, RString("in sub pattern [") 
                    + _pattern + "] only one '.' allowed");
      checkGroup(_pattern->c_str(), pptr, grouppos, _groupingSize);
      state = Fraction;
      break;
    case 'E':
      ++pptr;
      if (*pptr != 0) 
        THROW1(IllegalArgumentException, RString("in sub pattern [") 
                    + _pattern + "]: only exponent 0 is supported");
      _exponent = 0;
      break;
    case '%' :
      if (state == Fraction)
        state = Suffix;
      if (state == Prefix)
        _leftPercent = true; 
      else if (state == Suffix)
        _leftPercent = false; 
      else
        THROW1(IllegalArgumentException, RString("in sub pattern [") 
                    + _pattern + "]: '%' can only be in suffix or prefix");
      _multiplier = 100;
      break;
    default:
      THROW1(IllegalArgumentException, RString("in sub pattern [") 
                    + _pattern + "]: '" + *pptr + "' unknown pattern character");
      break;
    }
    ++pptr;
  }
  _suffix = suffix.toString();
  _prefix = prefix.toString();
  
}

/*
//foreign 
void 
DecimalFormat::format(DigitNumber& dn, 
                      StringBuffer& result, 
                      FieldPosition& fieldPosition,
                      bool is_int, bool is_negativ)
{
  
  //result.append(is_negativ ? _negativePrefix : _positivePrefix);

}
*/


/** @internal */
RString renderInteger(jlong num, int mind, int maxd, int radix)
{
  RString erg = Long::toString(num, radix);
  if (num < 0) // first char is '-'
    erg = erg->substr(1);
  int diff = mind - erg->length();
  while (diff-- > 0) 
  {
    erg = "0" + erg;
    
  }
  return erg;
}


/** @internal */
RString renderFraction(double num, int mind, int maxd)
{
  jlong lp = jlong(num);
  double fp = num - lp;
  RString erg = Double::toString(fp);
  RString fract = erg->substr(erg->indexOf('.') + 1);
  while (fract->length() < mind) {
    fract = fract + "0";
  }
  if (maxd > 0) {
    if (fract->length() > maxd) {
      fract = fract->substr(0, maxd);
    }
  } else {
    if (fract->length() > mind) {
      fract = fract->substr(0, mind);
    }
  }
  return fract;
}

/** @internal */
RString 
applyGrouping(IN(RString) text, int groupsize, char groupsymbol)
{
  if (groupsize == 0)
    return text;
  if (text->length() <= groupsize)
    return text;
  StringBuffer sb;
  const char* textptr = text->c_str();
  const char* eptr = textptr + text->length() - 1;
  for (int i = 0; eptr >= textptr; --eptr, ++i) 
  {
    if (i == groupsize - 1) {
      sb.insert(0, groupsymbol);
      i = 0;
    }
    sb.insert(0, *eptr);
  }
  return sb.toString();
}


RStringBuffer 
DecimalFormat::format(jlong number, IN(RStringBuffer) result, IN(RFieldPosition) fieldPosition)
{
  RDecimalSubpatternProperties props = _positiv;
  if (number < 0)
    props = _negativ;
  result->append(props->_prefix);
  if (number < 0)
    result->append(_dateFormatSymbols->getMinusSign());
  if (props->_multiplier != 1 && props->_leftPercent == true)
    result->append(props->_multiplier == 100 
                    ? _dateFormatSymbols->getPercent()
                    : _dateFormatSymbols->getPerMill());

  RString integ = renderInteger(number, props->_minimumIntegerDigits,  
                               props->_maximumIntegerDigits, 
                               props->_exponent == -1 ? 10 : props->_exponent);
  integ = applyGrouping(integ, props->_groupingSize, 
                        _dateFormatSymbols->getGroupingSeparator());
  result->append(integ);
  
  RString fract = renderFraction(0, props->_minimumFractionDigits,  
                                   props->_maximumFractionDigits);
  if (fract->length() > 0) {
    result->append(_dateFormatSymbols->getDecimalSeparator());
    result->append(fract);
  }
  if (props->_multiplier != 1 && props->_leftPercent == false)
    result->append(props->_multiplier == 100 
                    ? _dateFormatSymbols->getPercent()
                    : _dateFormatSymbols->getPerMill());
  result->append(props->_suffix);


  return result;
}


RStringBuffer 
DecimalFormat::format(double number, IN(RStringBuffer) result, IN(RFieldPosition) fieldPosition)
{
  RDecimalSubpatternProperties props = _positiv;
  if (number < 0)
    props = _negativ;
  result->append(props->_prefix);
  if (Double::isNaN(number) == true) {
    result->append(_dateFormatSymbols->getNaN());
  } else if (Double::isInfinite(number) == true) {
    result->append(_dateFormatSymbols->getInfinity());
  } else {
    if (number < 0)
      result->append(_dateFormatSymbols->getMinusSign());
    if (props->_multiplier != 1 && props->_leftPercent == true)
      result->append(props->_multiplier == 100 
                    ? _dateFormatSymbols->getPercent()
                    : _dateFormatSymbols->getPerMill());

    number = number * props->_multiplier;
    RString integ = renderInteger((jlong)number, props->_minimumIntegerDigits,  
                                  props->_maximumIntegerDigits, 
                                  props->_exponent == -1 ? 10 : props->_exponent);
    integ = applyGrouping(integ, props->_groupingSize, 
                        _dateFormatSymbols->getGroupingSeparator());
    result->append(integ);
    RString fract = renderFraction(number, props->_minimumFractionDigits,  
                                   props->_maximumFractionDigits);
    if (fract->length() > 0) {
      result->append(_dateFormatSymbols->getDecimalSeparator());
      result->append(fract);
    }
  }
  if (props->_multiplier != 1 && props->_leftPercent == false)
    result->append(props->_multiplier == 100 
                    ? _dateFormatSymbols->getPercent()
                    : _dateFormatSymbols->getPerMill());
  result->append(props->_suffix);
  return result;
}


RDecimalFormatSymbols 
DecimalFormat::getDecimalFormatSymbols()
{
  return _dateFormatSymbols; 
}
 
RStringBuffer 
DecimalFormat::format(IN(acdk::lang::Object) obj, IN(RStringBuffer) sb, IN(RFieldPosition) pos)
{
  return Nil;
}

int 
DecimalFormat::hashCode()
{
  return serialized_hashCode(false);
}

RNumber 
DecimalFormat::parse(IN(RString) text, IN(RParsePosition) parsePosition)
{
  //THROW1(
  return Nil; 
}


/*
bool 
DecimalFormat::isDecimalSeparatorAlwaysShown()
{
  return false; // fixme
}


void 
DecimalFormat::setDecimalFormatSymbols(RDecimalFormatSymbols newSymbols)
{
}

void 
DecimalFormat::setDecimalSeparatorAlwaysShown(bool newValue)
{
}
*/



/*
RString 
DecimalFormat::toLocalizedPattern()
{
  return Nil; //fixme
}

RString 
DecimalFormat::toPattern()
{
  return _pattern ; 
}

*/
} // text
} // acdk