2005/5/9

     
 

SimpleDateFormat.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/SimpleDateFormat.cpp,v 1.22 2005/04/08 10:53:21 kommer Exp $


/** 
 the headers have to be included in this order.
 maybe they have to be moved to SimpleDateFormat.h 
 for distribution. (jb)
*/

#include "NumberFormat.h"
#include "ParsePosition.h"
#include "FieldPosition.h"
#include "ParseException.h"
#include "SimpleDateFormat.h"
#include "FieldPosition.h"
#include "DateFormatSymbols.h"

#include <acdk/lang/UnsupportedOperationException.h>
#include <acdk/lang/Integer.h>
#include <acdk/lang/Character.h>
#include <acdk/lang/Byte.h>
#include <acdk/util/ResourceBundle.h>
#include <acdk/util/GregorianCalendar.h>
#include <acdk/util/SysDate.h>

#include <stdio.h>

namespace acdk {
namespace text {

using namespace acdk::util;
using namespace acdk::lang;
using namespace acdk::io;

SimpleDateFormat::SimpleDateFormat()
: DateFormat(),
  _pattern(Nil),
  _format(Nil),
  _loc(::acdk::util::Locale::getDefault())
{
   _format = new DateFormatSymbols(_loc);
}

SimpleDateFormat::SimpleDateFormat(IN(RString) pattern)
: DateFormat(),
  _pattern(pattern),
  _format(Nil),
  _loc(::acdk::util::Locale::getDefault())
{
  _format = new DateFormatSymbols(_loc);
}

SimpleDateFormat::SimpleDateFormat(IN(RString) pattern, IN(RDateFormatSymbols) formatData)
: DateFormat(),
  _pattern(pattern),
  _format(formatData),
  _loc(::acdk::util::Locale::getDefault())
{
  
}

SimpleDateFormat::SimpleDateFormat(IN(RString) pattern, IN(RLocale) loc)
: DateFormat(),
  _pattern(pattern),
  _format(Nil),
  _loc(loc)
{
  _format = new DateFormatSymbols(_loc);
}

//virtual 
void 
SimpleDateFormat::applyLocalizedPattern(IN(RString) pattern)
{
  _pattern = pattern;
}
 
//virtual 
void 
SimpleDateFormat::applyPattern(IN(RString) pattern)
{
  _pattern = pattern;
}

//virtual 
acdk::lang::Object 
SimpleDateFormat::clone(sys::Allocator* alc)
{
  SimpleDateFormat* sdf = new (alc) SimpleDateFormat(_pattern, _loc);
  sdf->setDateFormatSymbols(_format);
  return sdf;
}


bool _equals(IN(acdk::lang::Object) f, IN(acdk::lang::Object) s)
{
  if (f == Nil && s == Nil)
    return true;
  if (f == Nil || s == Nil)
    return false;
  if (f == s)
    return true;
  return f->equals(s);
}
//virtual 
bool 
SimpleDateFormat::equals(IN(acdk::lang::Object) obj)
{
  if (instanceof(obj, SimpleDateFormat) == false)
    return false;
  RSimpleDateFormat other = (RSimpleDateFormat)obj;
  if (_equals((acdk::lang::Object)_pattern, (acdk::lang::Object)other->toPattern()) == false)
    return false;
  if (_equals((acdk::lang::Object)_format, (acdk::lang::Object)other->getDateFormatSymbols()) == false)
    return false;
  return true;
}


namespace {

/** @internal */
  inline int getTokenCount(String::iterator& fit, String::iterator fend)
{
  int tkcount = 1;
  while ((fit + 1) != fend && *(fit + 1) == *fit) 
  {
      ++tkcount;
      ++fit;
  }
  return tkcount;
}

/** @internal */
  bool scanQuotedText(String::iterator& fid,  String::iterator fend,
                    String::iterator& tit, String::iterator tend)
{
   //const char* startptr = ptr;
  while (fid != fend && tit != tend) 
  {
    if (*fid == '\'') 
    {
      if ((fid + 1) != fend && *(fid + 1) == '\'') 
      {
        if (*tit != '\'')
          return false;
        ++fid;
      } 
      else 
      {
        return true;
      }
    } 
    ++tit;
    ++fid;
  }
  return false;
}


/** @internal */
  bool scanText(IN(RString) text, String::iterator& tit, OUT(RString) erg)
{
  String::iterator start = tit;
  String::iterator tend = text->end();
  while (tit != tend) 
  {
    if (isalnum(*tit) == false) 
    {
      int istart = start - text->begin();
      int iend = istart + (tit - start);
      erg = text->substr(istart, iend);
      return true;
    }
    ++tit;
  }
  erg = text->substr(start - text->begin(), start - tit);
  return true;
}


/**
   if a given year has only 2 digits, 
   the century will be calculated
   80 after current year, or 20 before
   current year.
*/
/** @internal */
  int getWidenYear(int nowYear, int shortYear)
{
   int lowYear = (nowYear - 20) / 100;
   int highYear = (nowYear + 80)  / 100;
   lowYear = (lowYear * 100) + shortYear;
   highYear = (highYear * 100) + shortYear;
   if (nowYear - lowYear <= 20)
      return lowYear;
   else
      return highYear;
}

/** @internal */
  bool parseInt(int tcount, int& terg, IN(RString) text, String::iterator& tit)
{
  String::iterator startptr = tit;
  String::iterator tend = text->end();
  while (tit != tend) 
  {
    if (Character::isDigit(*tit) == false)
      return false;
    --tcount;
    if (tcount == 0) 
    {
      int starti = startptr - text->begin();
      int leni = tit - startptr + 1;
      RString tstr = text->substr(starti, starti + leni);
      terg = Integer::parseInt(tstr);
      ++tit;
      return true;
    }
    ++tit;
  }
  RString tstr = text->substr(tit - text->begin(), tit - startptr);
  terg = atoi(tstr->c_str());
  return false;
}

/** @internal */
  int mapMonth(IN(RString) month, DateFormatSymbols& dfs)
{
  StringArray& sa = *dfs.getMonths();
  for (int i = 0; i < sa.length(); ++i)
  {
    if (sa[i]->equals(month) == true)
      return i + 1;
  }
  return 0;
}

/** @internal */
  int getAmPm(IN(RString) str, DateFormatSymbols& dfs)
{
  StringArray& sa = *dfs.getAmPmStrings();
  for (int i = 0; i < sa.length(); ++i)
  {
    if (sa[i]->equals(str) == true)
      return i;
  }
  return 0;
  
}

/** @internal */
#define WARN(msg) sys::coreout << msg << sys::eofl


/** @internal */
  RString formatInt(int c, int i)
{
   char ibuf[128];
  ::sprintf(ibuf, "%i", i);
  if (c == (int)strlen(ibuf))
      return SCS(ibuf);
  if (c < (int)strlen(ibuf))
    return SCS(ibuf + strlen(ibuf) - c);
  char buffer[128];
  memset(buffer, 0, 128);
  memset(buffer, '0', c);
  strcpy(buffer + c - strlen(ibuf), ibuf);
  return SCS(buffer);
}

/** @internal */
  bool parseDate(IN(RString) format, IN(RString) text, Calendar& gc, DateFormatSymbols& dfs)
{
  String::iterator fit = format->begin();
  String::iterator fend = format->end();
  String::iterator tit = text->begin();
  String::iterator tend = text->end();
      
  RString pt = "";
   int terg;
   bool success = true;
   
   while (fit != fend) 
   {
     int tkcount = 1;
     ucchar cchar = *fit;
     switch (cchar) 
     {
      case 'G' : 
      {
        tkcount = getTokenCount(fit, fend);
        if (scanText(text, tit, pt) == false)
          return false;
        acdk::util::RMap eramap = dfs.getEras();
        acdk::util::RIterator it = eramap->entrySet()->iterator();
        while (it->hasNext() == true)
        {
          acdk::util::RMapEntry me(it->next());
          if (me->getValue()->equals(&pt) == true)
          {
            int i = Integer::parseInt(RString(me->getKey()));
            gc.set(Calendar::ERA, i);
            break;  
          }
        }
        /* old code 
        RStringArray eras = dfs.getEras();
        
        for (int i = 0; i < eras.length(); ++i) 
        {
          if (eras[i]->equals(pt) == true) 
          {
            gc.set(Calendar::ERA, i);
            break;
          }
        }
        */
        break;
      }
      case 'y' :
         tkcount = getTokenCount(fit, fend);
         if (parseInt(tkcount, terg, text, tit) == false) 
            return false;            
         
         if (tkcount < 3) {
            gc.set(Calendar::YEAR, getWidenYear(SysDate().getYear(), terg));
         } else
            gc.set(Calendar::YEAR, terg);
         break;
      case 'M' :
         tkcount = getTokenCount(fit, fend);
         if (tkcount >= 3) {
            if (scanText(text, tit, pt) == false)
               return false;
            gc.set(Calendar::MONTH, mapMonth(pt, dfs));
         } else {
            if (parseInt(tkcount, terg, text, tit) == false) 
               return false;            
            gc.set(Calendar::MONTH, terg - 1);
         }
         break;
      case 'd':
         tkcount = getTokenCount(fit, fend);
         if (parseInt(tkcount, terg, text, tit) == false) 
            return false;            
         gc.set(Calendar::DAY_OF_MONTH, terg);
         break;
      case 'k' : // no break;
      case 'h' : // 12 hour
      case 'K': // no break
      case 'H':
        tkcount = getTokenCount(fit, fend);
        if (parseInt(tkcount, terg, text, tit) == false) 
           return false;
        if (cchar == 'k')
          gc.set(Calendar::HOUR_OF_DAY, terg - 1);
        else if (cchar == 'h')
          gc.set(Calendar::HOUR, terg - 1);
        else if (cchar == 'K')
           gc.set(Calendar::HOUR, terg);
        else if (cchar == 'H')
           gc.set(Calendar::HOUR_OF_DAY, terg);
        break; 
      case 'm' :
         tkcount = getTokenCount(fit, fend);
         if (parseInt(tkcount, terg, text, tit) == false) 
            return false;
         gc.set(Calendar::MINUTE, terg);
         break;
      case 's' :
         tkcount = getTokenCount(fit, fend);
         if (parseInt(tkcount, terg, text, tit) == false) 
            return false;
         gc.set(Calendar::SECOND, terg);
         break;
      case 'S' :
         tkcount = getTokenCount(fit, fend);
         if (parseInt(tkcount, terg, text, tit) == false) 
            return false;
         gc.set(Calendar::MILLISECOND, terg);
         break;
      case 'E' : //day in week             (Text)              
         tkcount = getTokenCount(fit, fend);
         /* not supported
         if (parseInt(tkcount, terg, text, tit) == false) 
            return false;
         //???
         */
         break;
      case 'D' : // day in year             
         tkcount = getTokenCount(fit, fend);
         /* not supported
         if (parseInt(tkcount, terg, text, tit) == false) 
            return false;
         //???
         */
         break;
      case 'F' : //F        day of week in month    (Number)            
         tkcount = getTokenCount(fit, fend);
         /* not supported

         if (parseInt(tkcount, terg, text, tit) == false) 
            return false;
         //???
         */
         break;
      case 'w' : //        week in year            (Number)            
         tkcount = getTokenCount(fit, fend);
         /* not supported

         if (parseInt(tkcount, terg, text, tit) == false) 
            return false;
         //???
         */
         break;
      case 'W' : //        week in month           (Number)            2
         tkcount = getTokenCount(fit, fend);
         /* not supported
         if (parseInt(tkcount, terg, text, tit) == false) 
            return false;
         //???
         */
         break;
      case 'a' :
         tkcount = getTokenCount(fit, fend);
         if (scanText(text, tit, pt) == false)
            return false;
          gc.set(Calendar::AM_PM, getAmPm(pt, dfs));
         
         break;
      case 'z' :
         tkcount = getTokenCount(fit, fend);
         if (tkcount >= 4) {
            if (scanText(text, tit, pt) == false)
               return false;
            if (scanText(text, tit, pt) == false)
               return false;
            if (scanText(text, tit, pt) == false)
               return false;
         } else {
            if (scanText(text, tit, pt) == false)
               return false;
         }  
         break;
      case '\'':
         if (scanQuotedText(++fit, fend, tit, tend) == false)
            return false;
         
         break;
      default :
         if ((*fit < 'a' || *fit > 'z') &&  (*fit < 'A' || *fit > 'Z')) 
         {
            if (*fit != *tit)
               return false;
            ++tit;
         } 
         else
            WARN("Unknown Token: " << *fit);
         break;

      }
      ++fit;
   }
   return success;
}






/** @internal */
void 
formatDate(String::iterator fit, String::iterator fend, StringBuffer& sb
               , Calendar& gc, DateFormatSymbols& dfs)
{
  String::iterator fstart = fit;
  int terg = 0;
  while (fit != fend) 
  {
    int tkcount = 1;
    char cchar = *fit;
    switch (cchar) 
    {
    case 'G' :
      tkcount = getTokenCount(fit, fend);
      if (gc.get(Calendar::ERA) == 1)
        sb.append(dfs.getEras()->get(&RString("1")));
      else
        sb.append(dfs.getEras()->get(&RString("0")));
      break;
    case 'y' :
      tkcount = getTokenCount(fit, fend);
      if (tkcount == 2)
        sb.append(formatInt(tkcount, gc.get(Calendar::YEAR) % 100));
      else if (tkcount == 4)
        sb.append(formatInt(tkcount, gc.get(Calendar::YEAR)));
      else
        WARN("Format of Year can have 2 or 4 digits");
      break;
    case 'M' :
      tkcount = getTokenCount(fit, fend);
      if (tkcount >= 3) 
      {
        int m = gc.get(Calendar::MONTH);
        sb.append(dfs.getMonths()[m]);
      } else
        sb.append(formatInt(tkcount, gc.get(Calendar::MONTH) + 1));
      break;
    case 'd':
      tkcount = getTokenCount(fit, fend);
      sb.append(formatInt(tkcount, gc.get(Calendar::DAY_OF_MONTH)));
      break;
      
    case 'k' : // no break;
    case 'h' : // 12 hour
    case 'K': // no break
    case 'H':
      tkcount = getTokenCount(fit, fend);
      if (cchar == 'k')
        terg = gc.get(Calendar::HOUR_OF_DAY) + 1;
      else if (cchar == 'h')
        terg = gc.get(Calendar::HOUR) + 1;
      else if (cchar == 'K')
        terg = gc.get(Calendar::HOUR);
      else if (cchar == 'H')
        terg = gc.get(Calendar::HOUR_OF_DAY);
      sb.append(formatInt(tkcount, terg));
      break; 
    case 'm' :
      tkcount = getTokenCount(fit, fend);
      sb.append(formatInt(tkcount, gc.get(Calendar::MINUTE)));
      break;
    case 's' :
      tkcount = getTokenCount(fit, fend);
      sb.append(formatInt(tkcount, gc.get(Calendar::SECOND)));
      break;
    case 'S' :
      tkcount = getTokenCount(fit, fend);
      sb.append(formatInt(tkcount, gc.get(Calendar::MILLISECOND)));
      break;
    case 'E' : //day in week             (Text)              
               /*
               tkcount = getTokenCount(fit, fend);
               os << "<WeekDay>"; // ###FIXME
      */
      break;
    case 'D' : // day in year             
               /* not supported
               tkcount = getTokenCount(fit, fend);
               os << std::setw(tkcount) << std::setfill('D') 
               << 0;
      */
      break;
    case 'F' : //F        day of week in month    (Number)            
               /* not supported
               tkcount = getTokenCount(fit, fend);
               os << std::setw(tkcount) << std::setfill('F') 
               << 0;
      */
      break;
    case 'w' : //        week in year            (Number)            
               /* not supported
               tkcount = getTokenCount(fit, fend);
               os << std::setw(tkcount) << std::setfill('w') 
               << 0;
      */
      break;
    case 'W' : //        week in month           (Number)            2
               /* not supported
               tkcount = getTokenCount(fit, fend);
               os << std::setw(tkcount) << std::setfill('W') 
               << 0;
      */
      break;
    case 'a' :
      tkcount = getTokenCount(fit, fend);
      if (gc.get(Calendar::HOUR) > 11) 
        sb.append(dfs.getAmPmStrings()[1]);
      else
        sb.append(dfs.getAmPmStrings()[0]);
      break;
    case 'z' :
    /* not supported
    tkcount = getTokenCount(fit, fend);
    if (tkcount >= 4)
    os << "Central European Time";
    else
    os << "CET";
      */
      break;
    case '\'':
      if (fit + 1 != fend && *(fit + 1) == '\'') 
      {
        sb.append('\'');
        ++fit;
      } 
      else 
      {
        ++fit;
        while (fit != fend) 
        {
          if (*fit == '\'') 
          {
            if ((fit + 1) != fend && *(fit + 1) == '\'') 
            {
              sb.append('\'');
              ++fit;
            } 
            else 
            {
              break;
            }
          } 
          else 
          {
            sb.append(*fit);
            ++fit;
          }
        }
      }
      break;
    default :
      if ((*fit < 'a' || *fit > 'z') &&  (*fit < 'A' || *fit > 'Z'))
        sb.append(*fit);
      else
        WARN("Unknown Token: " << *fit);
      break;
      
      }
      ++fit;
   }
}


} // anon namespace




//virtual 
RStringBuffer 
SimpleDateFormat::format(IN(RDate) date, IN(RStringBuffer) toAppendTo, IN(RFieldPosition) pos)
{
  
  GregorianCalendar gc;
  gc.setTime(date);

  formatDate(_pattern->begin(), _pattern->end(), *toAppendTo, gc, *_format);
  return toAppendTo;
}

//virtual 
RDate 
SimpleDateFormat::get2DigitYearStart()
{
  THROW0(UnsupportedOperationException);
  return Nil;
}

//virtual 
RDateFormatSymbols 
SimpleDateFormat::getDateFormatSymbols()
{
  return _format;
}
 
//virtual 
int 
SimpleDateFormat::hashCode()
{
  int result = DateFormat::hashCode();
  result = 31 * result + (_pattern == Nil ? 0 : _pattern->hashCode());
  result = 31 * result + (_format == Nil ? 0 : _format->hashCode());  
  //result = 31 * result + (_loc == Nil ? 0 : _loc->hashCode());  
  //result = 31 * result + (_fields == Nil ? 0 : _fields->hashCode());  
  return result;
}

RDate
SimpleDateFormat::parse(IN(RString) text)
{
  GregorianCalendar gc;
  parseDate(_pattern, text, gc, *_format);
  return gc.getTime();
}


//virtual 
RDate 
SimpleDateFormat::parse(IN(RString) text, IN(RParsePosition) pos)
{
  GregorianCalendar gc;
  int ipos = 0;
  if (pos != Nil)
    ipos = pos->getIndex();
  parseDate(_pattern, text->substring(ipos), gc, *_format);
  return gc.getTime();
}

//virtual 
void 
SimpleDateFormat::set2DigitYearStart(IN(RDate) startDate)
{
  THROW0(UnsupportedOperationException);
}

//virtual 
void 
SimpleDateFormat::setDateFormatSymbols(IN(RDateFormatSymbols) newFormatSymbols)
{
  _format = newFormatSymbols;
}

//virtual 
RString 
SimpleDateFormat::toLocalizedPattern()
{
  return _pattern;
}

//virtual 
RString 
SimpleDateFormat::toPattern()
{
  return _pattern;
}

} // text
} // acdk