ceki        2004/12/21 07:21:56

  Added:       src/java/org/apache/log4j/helpers CachedDateFormat.java
               tests/src/java/org/apache/log4j/helpers
                        CachedDateFormatTestCase.java
  Log:
  CachedDataFormat is really good stuff.
  
  Revision  Changes    Path
  1.1                  
logging-log4j/src/java/org/apache/log4j/helpers/CachedDateFormat.java
  
  Index: CachedDateFormat.java
  ===================================================================
  /*
   * Copyright 1999,2004 The Apache Software Foundation.
   *
   * Licensed 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.
   */
  
  package org.apache.log4j.helpers;
  
  import java.util.Date;
  import java.text.FieldPosition;
  import java.text.ParsePosition;
  import java.text.DateFormat;
  import java.text.NumberFormat;
  import java.util.TimeZone;
  
  /**
   * Caches the results of a DateFormat.
   *  @author Curt Arnold
   *  @since 1.3
   */
  public final class CachedDateFormat
      extends DateFormat {
  
    private DateFormat formatter;
    private int millisecondStart;
    private StringBuffer cache = new StringBuffer();
    private long previousTime;
    private NumberFormat numberFormat;
    private static final int UNRECOGNIZED_MILLISECOND_PATTERN = -2;
    private static final int NO_MILLISECOND_PATTERN = -1;
  
    public CachedDateFormat(final DateFormat formatter) {
      if (formatter == null) {
        throw new NullPointerException("formatter");
      }
      this.formatter = formatter;
      numberFormat = formatter.getNumberFormat();
      if (numberFormat == null) {
        throw new NullPointerException("numberFormat");
      }
      Date now = new Date();
      long nowTime = now.getTime();
      previousTime = (nowTime / 1000L) * 1000L;
      //
      //    if now is before 1970 and previousTime was truncated forward
      //       set cached time back one second
      if (nowTime - previousTime < 0) {
        previousTime -= 1000;
      }
      Date lastSecond = new Date(previousTime);
      String formatted = formatter.format(lastSecond);
      cache.append(formatted);
      millisecondStart = findMillisecondStart(previousTime, formatted,
                                              formatter);
    }
  
    /**
     * Finds start of millisecond field in formatted time.
     * @param time long time, must be integral number of seconds
     * @param formatted String corresponding formatted string
     * @param formatter DateFormat date format
     * @return int position in string of first digit of milliseconds,
     *    -1 indicates no millisecond field, -2 indicates unrecognized
     *    field (likely RelativeTimeDateFormat)
     */
    private static int findMillisecondStart(final long time,
                                            final String formatted,
                                            final DateFormat formatter) {
        String plus987 = formatter.format(new Date(time + 987));
        //
        //    find first difference between values
        //
        for (int i = 0; i < formatted.length(); i++) {
          if (formatted.charAt(i) != plus987.charAt(i)) {
            //
            //   if one string has "000" and the other "987"
            //      we have found the millisecond field
            //
            if (i + 3 <= formatted.length() 
                && formatted.substring(i, i + 3) == "000" 
                && plus987.substring(i, i + 3) == "987") {
              return i;
            } else {
              return UNRECOGNIZED_MILLISECOND_PATTERN;
            }
          }
        }
      return NO_MILLISECOND_PATTERN;
  }
  
  
    /**
     * Converts a Date utilizing a previously converted
     * value if possible.
  
       @param date the date to format
       @param sbuf the string buffer to write to
       @param fieldPosition remains untouched
     */
    public
        StringBuffer format(Date date, StringBuffer sbuf,
                            FieldPosition fieldPosition) {
  
      if (millisecondStart == UNRECOGNIZED_MILLISECOND_PATTERN) {
        return formatter.format(date, sbuf, fieldPosition);
      }
      long now = date.getTime();
      if (now < previousTime + 1000L && now >= previousTime) {
        if (millisecondStart >= 0) {
          cache.delete(millisecondStart, millisecondStart + 3);
          int millis = (int) (now - previousTime);
          int cacheLength = cache.length();
          //
          //   append milliseconds to the end of the cache
          numberFormat.format(millis, cache, fieldPosition);
          int milliLength = cache.length() - cacheLength;
          //
          //   if it didn't belong at the end, then move it
          if (cacheLength != millisecondStart) {
            String milli = cache.substring(cacheLength);
            cache.setLength(cacheLength);
            cache.insert(millisecondStart, milli);
          }
          for (int i = milliLength; i < 3; i++) {
            cache.insert(millisecondStart, "0");
          }
        }
      } else {
        previousTime = (now / 1000L) * 1000L;
        //
        //   if earlier than 1970 and rounded toward 1970
        //      then move back one second
        if (now - previousTime < 0) {
          previousTime -= 1000;
        }
        int prevLength = cache.length();
        cache.setLength(0);
        formatter.format(date, cache, fieldPosition);
        //
        //   if the length changed then
        //      recalculate the millisecond position
        if (cache.length() != prevLength) {
          //
          //    format the previous integral second
          StringBuffer tempBuffer = new StringBuffer(cache.length());
          formatter.format(new Date(previousTime), tempBuffer, fieldPosition);
          //
          //    detect the start of the millisecond field
          millisecondStart = findMillisecondStart(previousTime,
                                                  tempBuffer.toString(),
                                                  formatter);
        }
      }
      sbuf.append(cache);
      return sbuf;
    }
  
  
    /**
     * Set timezone.
     *
     * @remarks Setting the timezone using getCalendar().setTimeZone()
     * will likely cause caching to misbehave.
     * @param timeZone TimeZone new timezone
     */
    public void setTimeZone(final TimeZone timeZone) {
      formatter.setTimeZone(timeZone);
      int prevLength = cache.length();
      cache.setLength(0);
      cache.append(formatter.format(new Date(previousTime)));
      //
      //   if the length changed then
      //      recalculate the millisecond position
      if (cache.length() != prevLength) {
        millisecondStart = findMillisecondStart(previousTime,
                                                cache.toString(),
                                                formatter);
      }
    }
  
    /**
       This method is delegated to the formatter which most
       likely returns null.
     */
    public
        Date parse(String s, ParsePosition pos) {
      return formatter.parse(s, pos);
    }
  
    /**
     * Gets number formatter.
     *
     * @return NumberFormat number formatter
     */
    public NumberFormat getNumberFormat() {
      return formatter.getNumberFormat();
    }
  }
  
  
  
  1.1                  
logging-log4j/tests/src/java/org/apache/log4j/helpers/CachedDateFormatTestCase.java
  
  Index: CachedDateFormatTestCase.java
  ===================================================================
  /*
   * Copyright 2004 The Apache Software Foundation.
   *
   * Licensed 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.
   */
  
  package org.apache.log4j.helpers;
  
  import junit.framework.TestCase;
  import org.apache.log4j.helpers.AbsoluteTimeDateFormat;
  import java.text.DateFormat;
  import java.util.TimeZone;
  import java.util.Date;
  import java.text.SimpleDateFormat;
  import java.util.Locale;
  import java.util.Calendar;
  
  /**
     Unit test [EMAIL PROTECTED] AbsoluteTimeDateFormat}.
     @author Curt Arnold
     @since 1.3.0 */
  public final class CachedDateFormatTestCase
      extends TestCase {
  
    /**
     * Test constructor
     * @param name String test name
     */
    public CachedDateFormatTestCase(String name) {
      super(name);
    }
  
    /**
     * Asserts that formatting the provided date results
     * in the expected string.
     *
     * @param date Date date
     * @param timeZone TimeZone timezone for conversion
     * @param expected String expected string
     */
    private final void assertFormattedTime(Date date,
                                           TimeZone timeZone,
                                           String expected) {
      DateFormat formatter = new AbsoluteTimeDateFormat(timeZone);
      String actual = formatter.format(date);
      assertEquals(expected, actual);
    }
  
    /**
     * Timezone representing GMT.
     */
    private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
  
    /**
     * Timezone for Chicago, Ill. USA.
     */
    private static final TimeZone CHICAGO = TimeZone.getTimeZone(
        "America/Chicago");
  
    /**
     * Test multiple calls in close intervals.
     */
    public void test1() {
      //   subsequent calls within one minute
      //     are optimized to reuse previous formatted value
      //     make a couple of nearly spaced calls
      DateFormat gmtFormat = new CachedDateFormat(new 
AbsoluteTimeDateFormat(GMT));
      long ticks = 12601L * 86400000L;
      Date jul1 = new Date(ticks);
      assertEquals("00:00:00,000", gmtFormat.format(jul1));
      Date plus8ms = new Date(ticks + 8);
      assertEquals("00:00:00,008", gmtFormat.format(plus8ms));
      Date plus17ms = new Date(ticks + 17);
      assertEquals("00:00:00,017", gmtFormat.format(plus17ms));
      Date plus237ms = new Date(ticks + 237);
      assertEquals("00:00:00,237", gmtFormat.format(plus237ms));
      Date plus1415ms = new Date(ticks + 1415);
      assertEquals("00:00:01,415", gmtFormat.format(plus1415ms));
    }
  
    /**
     *  Check for interaction between caches.
     */
  
    public void test2() {
        Date jul2 = new Date(12602L * 86400000L);
        DateFormat gmtFormat = new CachedDateFormat(new 
AbsoluteTimeDateFormat(GMT));
        DateFormat chicagoFormat = new CachedDateFormat(new 
AbsoluteTimeDateFormat(CHICAGO));
        assertEquals("00:00:00,000", gmtFormat.format(jul2));
        assertEquals("19:00:00,000", chicagoFormat.format(jul2));
        assertEquals("00:00:00,000", gmtFormat.format(jul2));
    }
  
    /**
     * Test multiple calls in close intervals prior to 1 Jan 1970.
     */
    public void test3() {
      //   subsequent calls within one minute
      //     are optimized to reuse previous formatted value
      //     make a couple of nearly spaced calls
      DateFormat gmtFormat = new CachedDateFormat(
         new AbsoluteTimeDateFormat(GMT));
      long ticks = -7L * 86400000L;
      Date jul1 = new Date(ticks);
      assertEquals("00:00:00,000", gmtFormat.format(jul1));
      Date plus8ms = new Date(ticks + 8);
      assertEquals("00:00:00,008", gmtFormat.format(plus8ms));
      Date plus17ms = new Date(ticks + 17);
      assertEquals("00:00:00,017", gmtFormat.format(plus17ms));
      Date plus237ms = new Date(ticks + 237);
      assertEquals("00:00:00,237", gmtFormat.format(plus237ms));
      Date plus1415ms = new Date(ticks + 1415);
      assertEquals("00:00:01,415", gmtFormat.format(plus1415ms));
    }
  
    public void test4() {
      //   subsequent calls within one minute
      //     are optimized to reuse previous formatted value
      //     make a couple of nearly spaced calls
      SimpleDateFormat baseFormat =
           new SimpleDateFormat("EEE, MMM dd, HH:mm:ss.SSS Z", Locale.ENGLISH);
      DateFormat cachedFormat = new CachedDateFormat(baseFormat);
      //
      //   use a date in 2000 to attempt to confuse the millisecond locator
      long ticks = 11141L * 86400000L;
      Date jul1 = new Date(ticks);
      assertEquals(baseFormat.format(jul1), cachedFormat.format(jul1));
      Date plus8ms = new Date(ticks + 8);
      String base = baseFormat.format(plus8ms);
      String cached = cachedFormat.format(plus8ms);
      assertEquals(baseFormat.format(plus8ms), cachedFormat.format(plus8ms));
      Date plus17ms = new Date(ticks + 17);
      assertEquals(baseFormat.format(plus17ms), cachedFormat.format(plus17ms));
      Date plus237ms = new Date(ticks + 237);
      assertEquals(baseFormat.format(plus237ms), 
cachedFormat.format(plus237ms));
      Date plus1415ms = new Date(ticks + 1415);
      assertEquals(baseFormat.format(plus1415ms), 
cachedFormat.format(plus1415ms));
    }
  
    public void test5() {
      //   subsequent calls within one minute
      //     are optimized to reuse previous formatted value
      //     make a couple of nearly spaced calls
      Locale thai = new Locale("th");
      SimpleDateFormat baseFormat =
           new SimpleDateFormat("EEE, MMM dd, HH:mm:ss.SSS Z", thai);
      DateFormat cachedFormat = new CachedDateFormat(baseFormat);
      //
      //   use a date in 2000 to attempt to confuse the millisecond locator
      long ticks = 11141L * 86400000L;
      Date jul1 = new Date(ticks);
      assertEquals(baseFormat.format(jul1), cachedFormat.format(jul1));
      Date plus8ms = new Date(ticks + 8);
      assertEquals(baseFormat.format(plus8ms), cachedFormat.format(plus8ms));
      Date plus17ms = new Date(ticks + 17);
      assertEquals(baseFormat.format(plus17ms), cachedFormat.format(plus17ms));
      Date plus237ms = new Date(ticks + 237);
      assertEquals(baseFormat.format(plus237ms), 
cachedFormat.format(plus237ms));
      Date plus1415ms = new Date(ticks + 1415);
      assertEquals(baseFormat.format(plus1415ms), 
cachedFormat.format(plus1415ms));
    }
  
    /**
     * Checks that getNumberFormat does not return null.
     */
    public void test6() {
      assertNotNull(new CachedDateFormat(new 
SimpleDateFormat()).getNumberFormat());
    }
  
    /**
     * Attempt to cache a RelativeTimeDateFormat which isn't compatible
     * with caching.  Should just delegate to the RelativeTimeDateFormat.
     */
    public void test7() {
      DateFormat baseFormat = new RelativeTimeDateFormat();
      DateFormat cachedFormat = new CachedDateFormat(baseFormat);
      long ticks = 12603L * 86400000L;
      Date jul3 = new Date(ticks);
      assertEquals(baseFormat.format(jul3), cachedFormat.format(jul3));
      Date plus8ms = new Date(ticks + 8);
      assertEquals(baseFormat.format(plus8ms), cachedFormat.format(plus8ms));
      Date plus17ms = new Date(ticks + 17);
      assertEquals(baseFormat.format(plus17ms), cachedFormat.format(plus17ms));
      Date plus237ms = new Date(ticks + 237);
      assertEquals(baseFormat.format(plus237ms), 
cachedFormat.format(plus237ms));
      Date plus1415ms = new Date(ticks + 1415);
      assertEquals(baseFormat.format(plus1415ms), 
cachedFormat.format(plus1415ms));
    }
  
    /**
     * Set time zone on cached and check that it is effective.
     */
    public void test8() {
      DateFormat baseFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
      DateFormat cachedFormat = new CachedDateFormat(baseFormat);
      cachedFormat.setTimeZone(TimeZone.getTimeZone("GMT-6"));
      Date jul4 = new Date(12603L * 86400000L);
      assertEquals("2004-07-03 18:00:00,000", cachedFormat.format(jul4));
    }
  
  
    /**
     * Test of caching when less than three millisecond digits are specified.
     */
    public void test9() {
      DateFormat baseFormat = new SimpleDateFormat("yyyy-MMMM-dd HH:mm:ss,SS 
Z", Locale.US);
      DateFormat cachedFormat = new CachedDateFormat(baseFormat);
      TimeZone cet = TimeZone.getTimeZone("GMT+1");
      cachedFormat.setTimeZone(cet);
      
      Calendar c = Calendar.getInstance();
      c.set(2004, Calendar.DECEMBER, 12, 20, 0);
      c.set(Calendar.SECOND, 37);
      c.set(Calendar.MILLISECOND, 23);
      c.setTimeZone(cet);
  
      String s = cachedFormat.format(c.getTime());
      assertEquals("2004-December-12 20:00:37,23 +0100", s);
  
      c.set(2005, Calendar.JANUARY, 1, 0, 0);
      c.set(Calendar.SECOND, 13);
      c.set(Calendar.MILLISECOND, 905);
  
      s = cachedFormat.format(c.getTime());
      assertEquals("2005-January-01 00:00:13,905 +0100", s);
    }
    
  
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to