blob: e3f39d2046dca32121655571cbaf044538a8173d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <log4cxx/logstring.h>
#include <log4cxx/helpers/simpledateformat.h>
#include <apr_time.h>
#include <apr_strings.h>
#include <sstream>
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/helpers/stringhelper.h>
#include <assert.h>
#if !defined(LOG4CXX)
#define LOG4CXX 1
#endif
#include <log4cxx/private/log4cxx_private.h>
#include <log4cxx/helpers/pool.h>
using namespace log4cxx;
using namespace log4cxx::helpers;
using namespace std;
#if LOG4CXX_HAS_STD_LOCALE
#include <locale>
#endif
#if defined(_MSC_VER) && _MSC_VER < 1300
#define HAS_FACET(locale, type) _HAS(locale, type)
#define USE_FACET(locale, type) _USE(locale, type)
#define PUT_FACET(facet, os, time, spec) facet.put(os, os, time, spec)
#else
#if defined(_RWSTD_NO_TEMPLATE_ON_RETURN_TYPE)
#define HAS_FACET(locale, type) std::has_facet(locale, (type*) 0)
#define USE_FACET(locale, type) std::use_facet(locale, (type*) 0)
#else
#define HAS_FACET(locale, type) std::has_facet < type >(locale)
#define USE_FACET(locale, type) std::use_facet < type >(locale)
#endif
#define PUT_FACET(facet, os, time, spec) facet.put(os, os, os.fill(), time, spec)
#endif
namespace log4cxx
{
namespace helpers
{
namespace SimpleDateFormatImpl
{
typedef void (*incrementFunction)(tm& time, apr_time_exp_t& apr_time);
/**
* Abstract inner class representing one format token
* (one or more instances of a character).
*/
class PatternToken
{
public:
PatternToken()
{
}
virtual ~PatternToken()
{
}
/**
* Sets the time zone.
* @param zone new time zone.
*/
virtual void setTimeZone(const TimeZonePtr& zone)
{
}
/**
* Appends the formatted content to the string.
* @param s string to which format contribution is appended.
* @param date exploded date/time.
* @param p memory pool.
*/
virtual void format(LogString& s,
const apr_time_exp_t& date,
log4cxx::helpers::Pool& p) const = 0;
protected:
static void incrementMonth(tm& time, apr_time_exp_t& aprtime)
{
time.tm_mon++;
aprtime.tm_mon++;
}
static void incrementDay(tm& time, apr_time_exp_t& aprtime)
{
time.tm_wday++;
aprtime.tm_wday++;
}
static void incrementHalfDay(tm& time, apr_time_exp_t& aprtime)
{
time.tm_hour += 12;
aprtime.tm_hour += 12;
}
static void renderFacet(const std::locale* locale,
incrementFunction inc,
char spec,
unsigned int wspec,
const char* aprspec,
std::vector<LogString>& values)
{
std::vector<LogString>::iterator valueIter = values.begin();
tm time;
memset(&time, 0, sizeof(time));
apr_time_exp_t aprtime;
memset(&aprtime, 0, sizeof(aprtime));
#if LOG4CXX_HAS_STD_LOCALE
if (locale != NULL)
{
#if LOG4CXX_WCHAR_T_API
if (HAS_FACET(*locale, std::time_put<wchar_t>))
{
const std::time_put<wchar_t>& facet = USE_FACET(*locale, std::time_put<wchar_t>);
size_t start = 0;
std::basic_ostringstream<wchar_t> os;
for (; valueIter != values.end(); valueIter++)
{
PUT_FACET(facet, os, &time, (wchar_t) wspec);
Transcoder::decode(os.str().substr(start), *valueIter);
start = os.str().length();
(*inc)(time, aprtime);
}
}
else
#endif
if (HAS_FACET(*locale, std::time_put<char>))
{
const std::time_put<char>& facet = USE_FACET(*locale, std::time_put<char> );
size_t start = 0;
std::ostringstream os;
for (; valueIter != values.end(); valueIter++)
{
PUT_FACET(facet, os, &time, spec);
Transcoder::decode(os.str().substr(start), *valueIter);
start = os.str().length();
(*inc)(time, aprtime);
}
}
}
#endif
const size_t BUFSIZE = 256;
char buf[BUFSIZE];
memset(buf, 0, BUFSIZE);
apr_size_t retsize = 0;
for (; valueIter != values.end(); valueIter++)
{
apr_status_t stat = apr_strftime(buf, &retsize, BUFSIZE, aprspec, &aprtime);
(*inc)(time, aprtime);
if (stat == APR_SUCCESS)
{
Transcoder::decode(std::string(buf, retsize), *valueIter);
}
else
{
valueIter->append(1, (logchar) 0x3F);
}
}
}
private:
/**
* Private copy constructor.
*/
PatternToken(const PatternToken&);
/**
* Private assignment operator.
*/
PatternToken& operator=(const PatternToken&);
};
class LiteralToken : public PatternToken
{
public:
LiteralToken( logchar ch1, int count1 ) : ch( ch1 ), count( count1 )
{
}
void format( LogString& s, const apr_time_exp_t&, Pool& /* p */ ) const
{
s.append( count, ch );
}
private:
logchar ch;
int count;
};
class EraToken : public PatternToken
{
public:
EraToken( int /* count */, const std::locale* /* locale */ )
{
}
void format(LogString& s, const apr_time_exp_t& /* tm */, Pool& /* p */ ) const
{
s.append(1, (logchar) 0x41 /* 'A' */);
s.append(1, (logchar) 0x44 /* 'D' */);
}
};
class NumericToken : public PatternToken
{
public:
NumericToken( size_t width1 ) : width( width1 )
{
}
virtual int getField( const apr_time_exp_t& tm ) const = 0;
void format( LogString& s, const apr_time_exp_t& tm, Pool& p ) const
{
size_t initialLength = s.length();
StringHelper::toString( getField( tm ), p, s );
size_t finalLength = s.length();
if ( initialLength + width > finalLength )
{
s.insert( initialLength, ( initialLength + width ) - finalLength, (logchar) 0x30 /* '0' */);
}
}
private:
size_t width;
};
class YearToken : public NumericToken
{
public:
YearToken( int width1 ) : NumericToken( width1 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return 1900 + tm.tm_year;
}
};
class MonthToken : public NumericToken
{
public:
MonthToken( int width1 ) : NumericToken( width1 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return tm.tm_mon + 1;
}
};
class AbbreviatedMonthNameToken : public PatternToken
{
public:
AbbreviatedMonthNameToken(int, const std::locale* locale) : names( 12 )
{
renderFacet(locale, PatternToken::incrementMonth, 'b', 0x62, "%b", names);
}
void format(LogString& s, const apr_time_exp_t& tm, Pool& /* p */ ) const
{
s.append( names[tm.tm_mon] );
}
private:
std::vector < LogString > names;
};
class FullMonthNameToken : public PatternToken
{
public:
FullMonthNameToken( int width, const std::locale* locale) : names( 12 )
{
renderFacet(locale, PatternToken::incrementMonth, 'B', 0x42, "%B", names);
}
void format( LogString& s, const apr_time_exp_t& tm, Pool& /* p */ ) const
{
s.append( names[tm.tm_mon] );
}
private:
std::vector < LogString > names;
};
class WeekInYearToken : public NumericToken
{
public:
WeekInYearToken( int width1 ) : NumericToken( width1 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return tm.tm_yday / 7;
}
};
class WeekInMonthToken : public NumericToken
{
public:
WeekInMonthToken( int width1 ) : NumericToken( width1 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return tm.tm_mday / 7;
}
};
class DayInMonthToken : public NumericToken
{
public:
DayInMonthToken( int width1 ) : NumericToken( width1 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return tm.tm_mday;
}
};
class DayInYearToken : public NumericToken
{
public:
DayInYearToken( int width1 ) : NumericToken( width1 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return tm.tm_yday;
}
};
class DayOfWeekInMonthToken : public NumericToken
{
public:
DayOfWeekInMonthToken( int width1 ) : NumericToken( width1 )
{
}
int getField( const apr_time_exp_t& /* tm */ ) const
{
return -1;
}
};
class AbbreviatedDayNameToken : public PatternToken
{
public:
AbbreviatedDayNameToken( int width, const std::locale* locale) : names( 7 )
{
renderFacet(locale, PatternToken::incrementDay, 'a', 0x61, "%a", names);
}
void format( LogString& s, const apr_time_exp_t& tm, Pool& /* p */ ) const
{
s.append( names[tm.tm_wday] );
}
private:
std::vector < LogString > names;
};
class FullDayNameToken : public PatternToken
{
public:
FullDayNameToken( int width, const std::locale* locale) : names( 7 )
{
renderFacet(locale, PatternToken::incrementDay, 'A', 0x41, "%A", names);
}
void format( LogString& s, const apr_time_exp_t& tm, Pool& /* p */ ) const
{
s.append( names[tm.tm_wday] );
}
private:
std::vector < LogString > names;
};
class MilitaryHourToken : public NumericToken
{
public:
MilitaryHourToken( int width1, int offset1 ) : NumericToken( width1 ), offset( offset1 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return tm.tm_hour + offset;
}
private:
int offset;
};
class HourToken : public NumericToken
{
public:
HourToken( int width1, int /* offset1 */ ) : NumericToken( width1 ), offset( 0 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return ( ( tm.tm_hour + 12 - offset ) % 12 ) + offset;
}
private:
int offset;
};
class MinuteToken : public NumericToken
{
public:
MinuteToken( int width1 ) : NumericToken( width1 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return tm.tm_min;
}
};
class SecondToken : public NumericToken
{
public:
SecondToken( int width1 ) : NumericToken( width1 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return tm.tm_sec;
}
};
class MillisecondToken : public NumericToken
{
public:
MillisecondToken( int width1 ) : NumericToken( width1 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return tm.tm_usec / 1000;
}
};
class MicrosecondToken : public NumericToken
{
public:
MicrosecondToken( int width1 ) : NumericToken( width1 )
{
}
int getField( const apr_time_exp_t& tm ) const
{
return tm.tm_usec;
}
};
class AMPMToken : public PatternToken
{
public:
AMPMToken( int width, const std::locale* locale) : names( 2 )
{
renderFacet(locale, PatternToken::incrementHalfDay, 'p', 0x70, "%p", names);
}
void format( LogString& s, const apr_time_exp_t& tm, Pool& /* p */ ) const
{
s.append( names[tm.tm_hour / 12] );
}
private:
std::vector < LogString > names;
};
class GeneralTimeZoneToken : public PatternToken
{
public:
GeneralTimeZoneToken( int /* width */ )
{
}
void format( LogString& s, const apr_time_exp_t&, Pool& /* p */ ) const
{
s.append(timeZone->getID());
}
void setTimeZone( const TimeZonePtr& zone )
{
timeZone = zone;
}
private:
TimeZonePtr timeZone;
};
class RFC822TimeZoneToken : public PatternToken
{
public:
RFC822TimeZoneToken( int /* width */ )
{
}
void format( LogString& s, const apr_time_exp_t& tm, Pool& p ) const
{
if ( tm.tm_gmtoff == 0 )
{
s.append( 1, (logchar) 0x5A /* 'Z' */ );
}
else
{
apr_int32_t off = tm.tm_gmtoff;
size_t basePos = s.length();
s.append( LOG4CXX_STR( "+0000" ) );
if ( off < 0 )
{
s[basePos] = 0x2D; // '-'
off = -off;
}
LogString hours;
StringHelper::toString( off / 3600, p, hours );
size_t hourPos = basePos + 2;
//
// assumes that point values for 0-9 are same between char and wchar_t
//
for ( size_t i = hours.length(); i-- > 0; )
{
s[hourPos--] = hours[i];
}
LogString min;
StringHelper::toString( ( off % 3600 ) / 60, p, min );
size_t minPos = basePos + 4;
//
// assumes that point values for 0-9 are same between char and wchar_t
//
for ( size_t j = min.length(); j-- > 0; )
{
s[minPos--] = min[j];
}
}
}
};
}
}
}
using namespace log4cxx::helpers::SimpleDateFormatImpl;
void SimpleDateFormat::addToken(const logchar spec, const int repeat, const std::locale* locale,
std::vector < PatternToken* >& pattern )
{
PatternToken* token = NULL;
switch ( spec )
{
case 0x47: // 'G'
token = ( new EraToken( repeat, locale ) );
break;
case 0x79: // 'y'
token = ( new YearToken( repeat ) );
break;
case 0x4D: // 'M'
if ( repeat <= 2 )
{
token = ( new MonthToken( repeat ) );
}
else if ( repeat <= 3 )
{
token = ( new AbbreviatedMonthNameToken( repeat, locale ) );
}
else
{
token = ( new FullMonthNameToken( repeat, locale ) );
}
break;
case 0x77: // 'w'
token = ( new WeekInYearToken( repeat ) );
break;
case 0x57: // 'W'
token = ( new WeekInMonthToken( repeat ) );
break;
case 0x44: // 'D'
token = ( new DayInYearToken( repeat ) );
break;
case 0x64: // 'd'
token = ( new DayInMonthToken( repeat ) );
break;
case 0x46: // 'F'
token = ( new DayOfWeekInMonthToken( repeat ) );
break;
case 0x45: // 'E'
if ( repeat <= 3 )
{
token = ( new AbbreviatedDayNameToken( repeat, locale ) );
}
else
{
token = ( new FullDayNameToken( repeat, locale ) );
}
break;
case 0x61: // 'a'
token = ( new AMPMToken( repeat, locale ) );
break;
case 0x48: // 'H'
token = ( new MilitaryHourToken( repeat, 0 ) );
break;
case 0x6B: // 'k'
token = ( new MilitaryHourToken( repeat, 1 ) );
break;
case 0x4B: // 'K'
token = ( new HourToken( repeat, 0 ) );
break;
case 0x68: // 'h'
token = ( new HourToken( repeat, 1 ) );
break;
case 0x6D: // 'm'
token = ( new MinuteToken( repeat ) );
break;
case 0x73: // 's'
token = ( new SecondToken( repeat ) );
break;
case 0x53: // 'S'
if ( repeat == 6 )
{
token = ( new MicrosecondToken( repeat ) );
}
else
{
// It would be nice to support patterns with arbitrary
// subsecond precision (like "s.S" or "s.SSSS"), but we
// don't; so this is a back-compatible default.
token = ( new MillisecondToken( repeat ) );
}
break;
case 0x7A: // 'z'
token = ( new GeneralTimeZoneToken( repeat ) );
break;
case 0x5A: // 'Z'
token = ( new RFC822TimeZoneToken( repeat ) );
break;
default:
token = ( new LiteralToken( spec, repeat ) );
}
assert( token != NULL );
pattern.push_back( token );
}
void SimpleDateFormat::parsePattern( const LogString& fmt, const std::locale* locale,
std::vector < PatternToken* >& pattern )
{
if ( !fmt.empty() )
{
LogString::const_iterator iter = fmt.begin();
int repeat = 1;
logchar prevChar = * iter;
for ( iter++; iter != fmt.end(); iter++ )
{
if ( * iter == prevChar )
{
repeat++;
}
else
{
addToken( prevChar, repeat, locale, pattern );
prevChar = * iter;
repeat = 1;
}
}
addToken( prevChar, repeat, locale, pattern );
}
}
SimpleDateFormat::SimpleDateFormat( const LogString& fmt ) : timeZone( TimeZone::getDefault() )
{
#if LOG4CXX_HAS_STD_LOCALE
std::locale defaultLocale;
parsePattern( fmt, & defaultLocale, pattern );
#else
parsePattern( fmt, NULL, pattern );
#endif
for ( PatternTokenList::iterator iter = pattern.begin(); iter != pattern.end(); iter++ )
{
( * iter )->setTimeZone( timeZone );
}
}
SimpleDateFormat::SimpleDateFormat( const LogString& fmt, const std::locale* locale ) : timeZone( TimeZone::getDefault() )
{
parsePattern( fmt, locale, pattern );
for ( PatternTokenList::iterator iter = pattern.begin(); iter != pattern.end(); iter++ )
{
( * iter )->setTimeZone( timeZone );
}
}
SimpleDateFormat::~SimpleDateFormat()
{
for ( PatternTokenList::iterator iter = pattern.begin(); iter != pattern.end(); iter++ )
{
delete * iter;
}
}
void SimpleDateFormat::format( LogString& s, log4cxx_time_t time, Pool& p ) const
{
apr_time_exp_t exploded;
apr_status_t stat = timeZone->explode( & exploded, time );
if ( stat == APR_SUCCESS )
{
for ( PatternTokenList::const_iterator iter = pattern.begin(); iter != pattern.end(); iter++ )
{
( * iter )->format( s, exploded, p );
}
}
}
void SimpleDateFormat::setTimeZone( const TimeZonePtr& zone )
{
timeZone = zone;
}