package com.ibm.nmon.util; import java.text.SimpleDateFormat; import java.util.Map; import java.util.LinkedHashMap; import java.util.TimeZone; import org.slf4j.Logger; import com.ibm.nmon.gui.Styles; import com.ibm.nmon.interval.Interval; /** * <p> * Helper cache class for storing formatted time and interval strings. This class should be used * when displaying such Strings in the UI rather than recreating them each time they are displayed. * </p> * * <p> * This classes uses an LRU caching strategy to keep the number of cached strings small. The UI is * responsible for updating this cache when the time zone, and thus the format of the strings, * changes - see {@link #setTimeZone(TimeZone) setTimeZone()}. * </p> */ public final class TimeFormatCache { private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(TimeFormatCache.class); // assume all access to these fields is in the Swing thread, so no need to synchronize private static final Map<Interval, String> FORMATTED_INTERVALS = new LRUMap<Interval, String>(25); private static final Map<Long, String> FORMATTED_DATETIMES = new LRUMap<Long, String>(100); private static final Map<Long, String> FORMATTED_TIMES = new LRUMap<Long, String>(100); private static final SimpleDateFormat DATETIME_FORMAT = new SimpleDateFormat(Styles.DATE_FORMAT_STRING); private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat(Styles.DATE_FORMAT_STRING_SHORT); private static long DEFAULT_INTERVAL_MIN; private static long DEFAULT_INTERVAL_MAX; public static String formatInterval(Interval interval) { String formattedInterval = FORMATTED_INTERVALS.get(interval); if (formattedInterval == null) { if (Interval.DEFAULT.equals(interval)) { if ((DEFAULT_INTERVAL_MIN > 0) && (DEFAULT_INTERVAL_MAX < Long.MAX_VALUE)) { formattedInterval = "All Data" + ": " + formatDateTime(DEFAULT_INTERVAL_MIN) + " - " + formatDateTime(DEFAULT_INTERVAL_MAX); } else { formattedInterval = "All Data"; } } else { formattedInterval = formatDateTime(interval.getStart()) + " - " + formatDateTime(interval.getEnd()); if (!"".equals(interval.getName())) { String name = interval.getName(); if (name.length() > 25) { name = name.substring(0, 22); name += "..."; } formattedInterval = name + ": " + formattedInterval; } } FORMATTED_INTERVALS.put(interval, formattedInterval); } return formattedInterval; } public static void setDefaultIntervalRange(long minTime, long maxTime) { TimeFormatCache.DEFAULT_INTERVAL_MIN = minTime; TimeFormatCache.DEFAULT_INTERVAL_MAX = maxTime; FORMATTED_INTERVALS.remove(Interval.DEFAULT); } public static void renameInterval(Interval i) { FORMATTED_INTERVALS.remove(i); } public static String formatDateTime(long data) { String formattedTime = FORMATTED_DATETIMES.get(data); if (formattedTime == null) { formattedTime = DATETIME_FORMAT.format(data); FORMATTED_DATETIMES.put(data, formattedTime); } return formattedTime; } public static String formatTime(long data) { String formattedTime = FORMATTED_TIMES.get(data); if (formattedTime == null) { formattedTime = TIME_FORMAT.format(data); FORMATTED_TIMES.put(data, formattedTime); } return formattedTime; } public static void setTimeZone(TimeZone timeZone) { DATETIME_FORMAT.setTimeZone(timeZone); TIME_FORMAT.setTimeZone(timeZone); FORMATTED_INTERVALS.clear(); FORMATTED_DATETIMES.clear(); FORMATTED_TIMES.clear(); } private TimeFormatCache() {} private static final class LRUMap<K, V> extends LinkedHashMap<K, V> { private static final long serialVersionUID = -1440114072711805032L; private final int maxSize; LRUMap(int maxSize) { this.maxSize = maxSize; } public V put(K key, V value) { V v = super.put(key, value); if (v == null) { TimeFormatCache.LOGGER.trace("cached {}={}", key, value); } return v; }; protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) { boolean oversized = size() > maxSize; if (oversized) { TimeFormatCache.LOGGER.trace("evicted {}={}", eldest.getKey(), eldest.getValue()); } return oversized; }; } }