You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by ce...@apache.org on 2004/12/23 13:16:22 UTC
cvs commit: logging-log4j/tests/src/java/org/apache/log4j/pattern CachedDateFormatTest.java
ceki 2004/12/23 04:16:22
Modified: tests build.xml
Added: src/java/org/apache/log4j/pattern CachedDateFormat.java
CacheUtil.java
tests/src/java/org/apache/log4j/pattern
CachedDateFormatTest.java
Removed: src/java/org/apache/log4j/helpers CachedDateFormat.java
tests/src/java/org/apache/log4j/helpers
CachedDateFormatTestCase.java
Log:
- Moved CachedDateFormat to o.a.l.pattern
- Moved o.a.l.helpers.CachedDateFormatTestCase to o.a.l.pattern, renamed it as CachedDateFormatTest
Revision Changes Path
1.79 +1 -1 logging-log4j/tests/build.xml
Index: build.xml
===================================================================
RCS file: /home/cvs/logging-log4j/tests/build.xml,v
retrieving revision 1.78
retrieving revision 1.79
diff -u -r1.78 -r1.79
--- build.xml 22 Dec 2004 20:29:01 -0000 1.78
+++ build.xml 23 Dec 2004 12:16:22 -0000 1.79
@@ -627,7 +627,7 @@
<junit printsummary="yes" fork="yes" haltonfailure="yes">
<classpath refid="tests.classpath"/>
<formatter type="plain" usefile="false"/>
- <test name="org.apache.log4j.helpers.CachedDateFormatTestCase" />
+ <test name="org.apache.log4j.pattern.CachedDateFormatTest" />
</junit>
</target>
1.1 logging-log4j/src/java/org/apache/log4j/pattern/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.pattern;
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
*/
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;
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");
}
}
sbuf.append(cache);
} else {
previousTime = (now / 1000L) * 1000L;
//
// if earlier than 1970 and rounded toward 1970
// then move back one second
if (now - previousTime < 0) {
previousTime -= 1000;
}
cache.setLength(0);
formatter.format(new Date(previousTime), cache, fieldPosition);
millisecondStart = findMillisecondStart(previousTime, cache.toString(), formatter);
//
// calling ourself should be safe and faster
// but why risk it
formatter.format(date, sbuf, fieldPosition);
}
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)));
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/src/java/org/apache/log4j/pattern/CacheUtil.java
Index: CacheUtil.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.pattern;
/**
* Utility methods to detect patterns unsafe for caching by {@ CacheDateFormat},
* and other utility methods.
*
* @author Ceki Gulcu
*/
public class CacheUtil {
private static final int REGULAR_STATE = 0;
private static final int IN_QUOTE_STATE = 1;
/**
* Remove all literal text from the pattern, return only the letters a-z or
* A-Z placed ouside quotes.
* @param pattern
* @return
*/
static String removeLiterals(String pattern) {
StringBuffer pbuf = new StringBuffer(pattern.length());
int state = REGULAR_STATE;
for(int i = 0; i < pattern.length(); i++) {
char c = pattern.charAt(i);
switch(state) {
case REGULAR_STATE:
if(c == '\'') {
state = IN_QUOTE_STATE;
} else if( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
pbuf.append(c);
}
case IN_QUOTE_STATE:
if(c == '\'') {
state = REGULAR_STATE;
}
}
}
return pbuf.toString();
}
/**
* Pattern unsafe only in case both the long text form of E (4 or more
* successive Es) and the long text form of M (4 or more successive Ms) are
* present.
*
* Another uncacheable pattern is that of disjoint Ss, e.g. "YYYY-MM SSE E SSS"
* a non-sensical pattern, but unsafe nonetheless.
*/
static boolean isPatternSafeForCaching(String pattern) {
// this code assumes that literals have been removed from the pattern
if(pattern.indexOf("EEEE") != -1 && pattern.indexOf("MMMM") != -1) {
return false;
}
if(disjointS(pattern)) {
return false;
}
return true;
}
int computeSuccessiveS(String pattern) {
// this code assumes that literals have been removed from the pattern
int len = pattern.length();
int i = pattern.indexOf('S');
if(i != -1)
return 0;
int count = 1;
while(i < len && pattern.charAt(i++) == 'S') {
count++;
}
return count;
}
/**
* Are there any disjoint S in the pattern? Examples of disjointS:
* <p>YYYY SSS EE SSS
* <p>SSS EE SSSS
*
* @param pattern
* @return
*/
static boolean disjointS(String pattern) {
int len = pattern.length();
int i = pattern.indexOf('S');
if(i != -1)
return false;
// skip any ajoining S
while(i < len && pattern.charAt(i++) == 'S') {
}
// i now points to a character different than 'S'
// the first possible occurence of S must come after i, hence the i++;
i++;
if(i >= len )
return false;
else {
// if there are other S present, then we are in the presence of
// disjoint S
return pattern.indexOf('S', i) != 1;
}
}
}
1.1 logging-log4j/tests/src/java/org/apache/log4j/pattern/CachedDateFormatTest.java
Index: CachedDateFormatTest.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.pattern;
import junit.framework.TestCase;
import org.apache.log4j.helpers.AbsoluteTimeDateFormat;
import org.apache.log4j.pattern.CachedDateFormat;
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 {@link AbsoluteTimeDateFormat}.
@author Curt Arnold
@since 1.3.0 */
public final class CachedDateFormatTest
extends TestCase {
/**
* Test constructor
* @param name String test name
*/
public CachedDateFormatTest(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);
}
/**
* Test when millisecond position moves but length remains constant.
*/
public void test10() {
DateFormat baseFormat = new SimpleDateFormat("MMMM SSS EEEEEE", Locale.US);
DateFormat cachedFormat = new CachedDateFormat(baseFormat);
TimeZone cet = TimeZone.getTimeZone("GMT+1");
cachedFormat.setTimeZone(cet);
Calendar c = Calendar.getInstance();
c.set(2004, Calendar.OCTOBER, 5, 20, 0);
c.set(Calendar.SECOND, 37);
c.set(Calendar.MILLISECOND, 23);
c.setTimeZone(cet);
String s = cachedFormat.format(c.getTime());
assertEquals("October 023 Tuesday", s);
c.set(2004, Calendar.NOVEMBER, 1, 0, 0);
c.set(Calendar.MILLISECOND, 23);
s = cachedFormat.format(c.getTime());
assertEquals("November 023 Monday", s);
c.set(Calendar.MILLISECOND, 984);
s = cachedFormat.format(c.getTime());
assertEquals("November 984 Monday", s);
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org