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]