X-Git-Url: http://source.jalview.org/gitweb/?p=jalview.git;a=blobdiff_plain;f=srcjar_unused%2Forg%2Fapache%2Flog4j%2Fpattern%2FCachedDateFormat.java;fp=srcjar_unused%2Forg%2Fapache%2Flog4j%2Fpattern%2FCachedDateFormat.java;h=9a468aa4c070c8158619a8772806b27e81f52a48;hp=0000000000000000000000000000000000000000;hb=65740880573a48adc758bec3939ece9d9ae104dd;hpb=71aa78b8a7d54e5aeb6b278310dfd735efb77477 diff --git a/srcjar_unused/org/apache/log4j/pattern/CachedDateFormat.java b/srcjar_unused/org/apache/log4j/pattern/CachedDateFormat.java new file mode 100644 index 0000000..9a468aa --- /dev/null +++ b/srcjar_unused/org/apache/log4j/pattern/CachedDateFormat.java @@ -0,0 +1,372 @@ +/* + * 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. + */ + +package org.apache.log4j.pattern; + +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Date; +import java.util.TimeZone; + + +/** + * CachedDateFormat optimizes the performance of a wrapped + * DateFormat. The implementation is not thread-safe. + * If the millisecond pattern is not recognized, + * the class will only use the cache if the + * same value is requested. + * + */ +public final class CachedDateFormat extends DateFormat { + /** + * Serialization version. + */ + private static final long serialVersionUID = 1; + /** + * Constant used to represent that there was no change + * observed when changing the millisecond count. + */ + public static final int NO_MILLISECONDS = -2; + + /** + * Supported digit set. If the wrapped DateFormat uses + * a different unit set, the millisecond pattern + * will not be recognized and duplicate requests + * will use the cache. + */ + private static final String DIGITS = "0123456789"; + + /** + * Constant used to represent that there was an + * observed change, but was an expected change. + */ + public static final int UNRECOGNIZED_MILLISECONDS = -1; + + /** + * First magic number used to detect the millisecond position. + */ + private static final int MAGIC1 = 654; + + /** + * Expected representation of first magic number. + */ + private static final String MAGICSTRING1 = "654"; + + /** + * Second magic number used to detect the millisecond position. + */ + private static final int MAGIC2 = 987; + + /** + * Expected representation of second magic number. + */ + private static final String MAGICSTRING2 = "987"; + + /** + * Expected representation of 0 milliseconds. + */ + private static final String ZERO_STRING = "000"; + + /** + * Wrapped formatter. + */ + private final DateFormat formatter; + + /** + * Index of initial digit of millisecond pattern or + * UNRECOGNIZED_MILLISECONDS or NO_MILLISECONDS. + */ + private int millisecondStart; + + /** + * Integral second preceding the previous convered Date. + */ + private long slotBegin; + + /** + * Cache of previous conversion. + */ + private StringBuffer cache = new StringBuffer(50); + + /** + * Maximum validity period for the cache. + * Typically 1, use cache for duplicate requests only, or + * 1000, use cache for requests within the same integral second. + */ + private final int expiration; + + /** + * Date requested in previous conversion. + */ + private long previousTime; + + /** + * Scratch date object used to minimize date object creation. + */ + private final Date tmpDate = new Date(0); + + /** + * Creates a new CachedDateFormat object. + * @param dateFormat Date format, may not be null. + * @param expiration maximum cached range in milliseconds. + * If the dateFormat is known to be incompatible with the + * caching algorithm, use a value of 0 to totally disable + * caching or 1 to only use cache for duplicate requests. + */ + public CachedDateFormat(final DateFormat dateFormat, final int expiration) { + if (dateFormat == null) { + throw new IllegalArgumentException("dateFormat cannot be null"); + } + + if (expiration < 0) { + throw new IllegalArgumentException("expiration must be non-negative"); + } + + formatter = dateFormat; + this.expiration = expiration; + millisecondStart = 0; + + // + // set the previousTime so the cache will be invalid + // for the next request. + previousTime = Long.MIN_VALUE; + slotBegin = Long.MIN_VALUE; + } + + /** + * 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) + */ + public static int findMillisecondStart( + final long time, final String formatted, final DateFormat formatter) { + long slotBegin = (time / 1000) * 1000; + + if (slotBegin > time) { + slotBegin -= 1000; + } + + int millis = (int) (time - slotBegin); + + int magic = MAGIC1; + String magicString = MAGICSTRING1; + + if (millis == MAGIC1) { + magic = MAGIC2; + magicString = MAGICSTRING2; + } + + String plusMagic = formatter.format(new Date(slotBegin + magic)); + + /** + * If the string lengths differ then + * we can't use the cache except for duplicate requests. + */ + if (plusMagic.length() != formatted.length()) { + return UNRECOGNIZED_MILLISECONDS; + } else { + // find first difference between values + for (int i = 0; i < formatted.length(); i++) { + if (formatted.charAt(i) != plusMagic.charAt(i)) { + // + // determine the expected digits for the base time + StringBuffer formattedMillis = new StringBuffer("ABC"); + millisecondFormat(millis, formattedMillis, 0); + + String plusZero = formatter.format(new Date(slotBegin)); + + // If the next 3 characters match the magic + // string and the expected string + if ( + (plusZero.length() == formatted.length()) + && magicString.regionMatches( + 0, plusMagic, i, magicString.length()) + && formattedMillis.toString().regionMatches( + 0, formatted, i, magicString.length()) + && ZERO_STRING.regionMatches( + 0, plusZero, i, ZERO_STRING.length())) { + return i; + } else { + return UNRECOGNIZED_MILLISECONDS; + } + } + } + } + + return NO_MILLISECONDS; + } + + /** + * Formats a Date into a date/time string. + * + * @param date the date to format. + * @param sbuf the string buffer to write to. + * @param fieldPosition remains untouched. + * @return the formatted time string. + */ + public StringBuffer format( + Date date, StringBuffer sbuf, FieldPosition fieldPosition) { + format(date.getTime(), sbuf); + + return sbuf; + } + + /** + * Formats a millisecond count into a date/time string. + * + * @param now Number of milliseconds after midnight 1 Jan 1970 GMT. + * @param buf the string buffer to write to. + * @return the formatted time string. + */ + public StringBuffer format(long now, StringBuffer buf) { + // + // If the current requested time is identical to the previously + // requested time, then append the cache contents. + // + if (now == previousTime) { + buf.append(cache); + + return buf; + } + + // + // If millisecond pattern was not unrecognized + // (that is if it was found or milliseconds did not appear) + // + if (millisecondStart != UNRECOGNIZED_MILLISECONDS && + // Check if the cache is still valid. + // If the requested time is within the same integral second + // as the last request and a shorter expiration was not requested. + (now < (slotBegin + expiration)) && (now >= slotBegin) + && (now < (slotBegin + 1000L))) { + // + // if there was a millisecond field then update it + // + if (millisecondStart >= 0) { + millisecondFormat((int) (now - slotBegin), cache, millisecondStart); + } + + // + // update the previously requested time + // (the slot begin should be unchanged) + previousTime = now; + buf.append(cache); + + return buf; + } + + // + // could not use previous value. + // Call underlying formatter to format date. + cache.setLength(0); + tmpDate.setTime(now); + cache.append(formatter.format(tmpDate)); + buf.append(cache); + previousTime = now; + slotBegin = (previousTime / 1000) * 1000; + + if (slotBegin > previousTime) { + slotBegin -= 1000; + } + + // + // if the milliseconds field was previous found + // then reevaluate in case it moved. + // + if (millisecondStart >= 0) { + millisecondStart = + findMillisecondStart(now, cache.toString(), formatter); + } + + return buf; + } + + /** + * Formats a count of milliseconds (0-999) into a numeric representation. + * @param millis Millisecond coun between 0 and 999. + * @param buf String buffer, may not be null. + * @param offset Starting position in buffer, the length of the + * buffer must be at least offset + 3. + */ + private static void millisecondFormat( + final int millis, final StringBuffer buf, final int offset) { + buf.setCharAt(offset, DIGITS.charAt(millis / 100)); + buf.setCharAt(offset + 1, DIGITS.charAt((millis / 10) % 10)); + buf.setCharAt(offset + 2, DIGITS.charAt(millis % 10)); + } + + /** + * Set timezone. + * + * 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); + previousTime = Long.MIN_VALUE; + slotBegin = Long.MIN_VALUE; + } + + /** + * This method is delegated to the formatter which most + * likely returns null. + * @param s string representation of date. + * @param pos field position, unused. + * @return parsed date, likely 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(); + } + + /** + * Gets maximum cache validity for the specified SimpleDateTime + * conversion pattern. + * @param pattern conversion pattern, may not be null. + * @return Duration in milliseconds from an integral second + * that the cache will return consistent results. + */ + public static int getMaximumCacheValidity(final String pattern) { + // + // If there are more "S" in the pattern than just one "SSS" then + // (for example, "HH:mm:ss,SSS SSS"), then set the expiration to + // one millisecond which should only perform duplicate request caching. + // + int firstS = pattern.indexOf('S'); + + if ((firstS >= 0) && (firstS != pattern.lastIndexOf("SSS"))) { + return 1; + } + + return 1000; + } +}