| /* |
| * 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; |
| } |