You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by tr...@apache.org on 2006/01/20 14:48:55 UTC
svn commit: r370807 [22/22] - in /directory/sandbox/trustin/mina-spi: ./
core/src/main/java/org/apache/mina/common/
core/src/main/java/org/apache/mina/common/support/
core/src/main/java/org/apache/mina/common/support/discovery/
core/src/main/java/org/a...
Added: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/FastDateFormat.java
URL: http://svn.apache.org/viewcvs/directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/FastDateFormat.java?rev=370807&view=auto
==============================================================================
--- directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/FastDateFormat.java (added)
+++ directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/FastDateFormat.java Fri Jan 20 05:47:50 2006
@@ -0,0 +1,1505 @@
+/*
+ * Copyright 2002-2005 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.mina.common.support.lang.time;
+
+import java.text.DateFormat;
+import java.text.DateFormatSymbols;
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * <p>FastDateFormat is a fast and thread-safe version of
+ * {@link java.text.SimpleDateFormat}.</p>
+ *
+ * <p>This class can be used as a direct replacement to
+ * <code>SimpleDateFormat</code> in most formatting situations.
+ * This class is especially useful in multi-threaded server environments.
+ * <code>SimpleDateFormat</code> is not thread-safe in any JDK version,
+ * nor will it be as Sun have closed the bug/RFE.
+ * </p>
+ *
+ * <p>Only formatting is supported, but all patterns are compatible with
+ * SimpleDateFormat (except time zones - see below).</p>
+ *
+ * <p>Java 1.4 introduced a new pattern letter, <code>'Z'</code>, to represent
+ * time zones in RFC822 format (eg. <code>+0800</code> or <code>-1100</code>).
+ * This pattern letter can be used here (on all JDK versions).</p>
+ *
+ * <p>In addition, the pattern <code>'ZZ'</code> has been made to represent
+ * ISO8601 full format time zones (eg. <code>+08:00</code> or <code>-11:00</code>).
+ * This introduces a minor incompatibility with Java 1.4, but at a gain of
+ * useful functionality.</p>
+ *
+ * @author TeaTrove project
+ * @author Brian S O'Neill
+ * @author Sean Schofield
+ * @author Gary Gregory
+ * @author Stephen Colebourne
+ * @author Nikolay Metchev
+ * @since 2.0
+ * @version $Id$
+ */
+public class FastDateFormat extends Format {
+ // A lot of the speed in this class comes from caching, but some comes
+ // from the special int to StringBuffer conversion.
+ //
+ // The following produces a padded 2 digit number:
+ // buffer.append((char)(value / 10 + '0'));
+ // buffer.append((char)(value % 10 + '0'));
+ //
+ // Note that the fastest append to StringBuffer is a single char (used here).
+ // Note that Integer.toString() is not called, the conversion is simply
+ // taking the value and adding (mathematically) the ASCII value for '0'.
+ // So, don't change this code! It works and is very fast.
+
+ /**
+ * FULL locale dependent date or time style.
+ */
+ public static final int FULL = DateFormat.FULL;
+ /**
+ * LONG locale dependent date or time style.
+ */
+ public static final int LONG = DateFormat.LONG;
+ /**
+ * MEDIUM locale dependent date or time style.
+ */
+ public static final int MEDIUM = DateFormat.MEDIUM;
+ /**
+ * SHORT locale dependent date or time style.
+ */
+ public static final int SHORT = DateFormat.SHORT;
+
+ // package scoped as used by inner class
+ static final double LOG_10 = Math.log(10);
+
+ private static String cDefaultPattern;
+
+ private static Map cInstanceCache = new HashMap(7);
+ private static Map cDateInstanceCache = new HashMap(7);
+ private static Map cTimeInstanceCache = new HashMap(7);
+ private static Map cDateTimeInstanceCache = new HashMap(7);
+ private static Map cTimeZoneDisplayCache = new HashMap(7);
+
+ /**
+ * The pattern.
+ */
+ private final String mPattern;
+ /**
+ * The time zone.
+ */
+ private final TimeZone mTimeZone;
+ /**
+ * Whether the time zone overrides any on Calendars.
+ */
+ private final boolean mTimeZoneForced;
+ /**
+ * The locale.
+ */
+ private final Locale mLocale;
+ /**
+ * Whether the locale overrides the default.
+ */
+ private final boolean mLocaleForced;
+ /**
+ * The parsed rules.
+ */
+ private Rule[] mRules;
+ /**
+ * The estimated maximum length.
+ */
+ private int mMaxLengthEstimate;
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Gets a formatter instance using the default pattern in the
+ * default locale.</p>
+ *
+ * @return a date/time formatter
+ */
+ public static FastDateFormat getInstance() {
+ return getInstance(getDefaultPattern(), null, null);
+ }
+
+ /**
+ * <p>Gets a formatter instance using the specified pattern in the
+ * default locale.</p>
+ *
+ * @param pattern {@link java.text.SimpleDateFormat} compatible
+ * pattern
+ * @return a pattern based date/time formatter
+ * @throws IllegalArgumentException if pattern is invalid
+ */
+ public static FastDateFormat getInstance(String pattern) {
+ return getInstance(pattern, null, null);
+ }
+
+ /**
+ * <p>Gets a formatter instance using the specified pattern and
+ * time zone.</p>
+ *
+ * @param pattern {@link java.text.SimpleDateFormat} compatible
+ * pattern
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted date
+ * @return a pattern based date/time formatter
+ * @throws IllegalArgumentException if pattern is invalid
+ */
+ public static FastDateFormat getInstance(String pattern, TimeZone timeZone) {
+ return getInstance(pattern, timeZone, null);
+ }
+
+ /**
+ * <p>Gets a formatter instance using the specified pattern and
+ * locale.</p>
+ *
+ * @param pattern {@link java.text.SimpleDateFormat} compatible
+ * pattern
+ * @param locale optional locale, overrides system locale
+ * @return a pattern based date/time formatter
+ * @throws IllegalArgumentException if pattern is invalid
+ */
+ public static FastDateFormat getInstance(String pattern, Locale locale) {
+ return getInstance(pattern, null, locale);
+ }
+
+ /**
+ * <p>Gets a formatter instance using the specified pattern, time zone
+ * and locale.</p>
+ *
+ * @param pattern {@link java.text.SimpleDateFormat} compatible
+ * pattern
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted date
+ * @param locale optional locale, overrides system locale
+ * @return a pattern based date/time formatter
+ * @throws IllegalArgumentException if pattern is invalid
+ * or <code>null</code>
+ */
+ public static synchronized FastDateFormat getInstance(String pattern, TimeZone timeZone, Locale locale) {
+ FastDateFormat emptyFormat = new FastDateFormat(pattern, timeZone, locale);
+ FastDateFormat format = (FastDateFormat) cInstanceCache.get(emptyFormat);
+ if (format == null) {
+ format = emptyFormat;
+ format.init(); // convert shell format into usable one
+ cInstanceCache.put(format, format); // this is OK!
+ }
+ return format;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Gets a date formatter instance using the specified style in the
+ * default time zone and locale.</p>
+ *
+ * @param style date style: FULL, LONG, MEDIUM, or SHORT
+ * @return a localized standard date formatter
+ * @throws IllegalArgumentException if the Locale has no date
+ * pattern defined
+ * @since 2.1
+ */
+ public static FastDateFormat getDateInstance(int style) {
+ return getDateInstance(style, null, null);
+ }
+
+ /**
+ * <p>Gets a date formatter instance using the specified style and
+ * locale in the default time zone.</p>
+ *
+ * @param style date style: FULL, LONG, MEDIUM, or SHORT
+ * @param locale optional locale, overrides system locale
+ * @return a localized standard date formatter
+ * @throws IllegalArgumentException if the Locale has no date
+ * pattern defined
+ * @since 2.1
+ */
+ public static FastDateFormat getDateInstance(int style, Locale locale) {
+ return getDateInstance(style, null, locale);
+ }
+
+ /**
+ * <p>Gets a date formatter instance using the specified style and
+ * time zone in the default locale.</p>
+ *
+ * @param style date style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted date
+ * @return a localized standard date formatter
+ * @throws IllegalArgumentException if the Locale has no date
+ * pattern defined
+ * @since 2.1
+ */
+ public static FastDateFormat getDateInstance(int style, TimeZone timeZone) {
+ return getDateInstance(style, timeZone, null);
+ }
+ /**
+ * <p>Gets a date formatter instance using the specified style, time
+ * zone and locale.</p>
+ *
+ * @param style date style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted date
+ * @param locale optional locale, overrides system locale
+ * @return a localized standard date formatter
+ * @throws IllegalArgumentException if the Locale has no date
+ * pattern defined
+ */
+ public static synchronized FastDateFormat getDateInstance(int style, TimeZone timeZone, Locale locale) {
+ Object key = new Integer(style);
+ if (timeZone != null) {
+ key = new Pair(key, timeZone);
+ }
+ if (locale != null) {
+ key = new Pair(key, locale);
+ }
+
+ FastDateFormat format = (FastDateFormat) cDateInstanceCache.get(key);
+ if (format == null) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+
+ try {
+ SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateInstance(style, locale);
+ String pattern = formatter.toPattern();
+ format = getInstance(pattern, timeZone, locale);
+ cDateInstanceCache.put(key, format);
+
+ } catch (ClassCastException ex) {
+ throw new IllegalArgumentException("No date pattern for locale: " + locale);
+ }
+ }
+ return format;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Gets a time formatter instance using the specified style in the
+ * default time zone and locale.</p>
+ *
+ * @param style time style: FULL, LONG, MEDIUM, or SHORT
+ * @return a localized standard time formatter
+ * @throws IllegalArgumentException if the Locale has no time
+ * pattern defined
+ * @since 2.1
+ */
+ public static FastDateFormat getTimeInstance(int style) {
+ return getTimeInstance(style, null, null);
+ }
+
+ /**
+ * <p>Gets a time formatter instance using the specified style and
+ * locale in the default time zone.</p>
+ *
+ * @param style time style: FULL, LONG, MEDIUM, or SHORT
+ * @param locale optional locale, overrides system locale
+ * @return a localized standard time formatter
+ * @throws IllegalArgumentException if the Locale has no time
+ * pattern defined
+ * @since 2.1
+ */
+ public static FastDateFormat getTimeInstance(int style, Locale locale) {
+ return getTimeInstance(style, null, locale);
+ }
+
+ /**
+ * <p>Gets a time formatter instance using the specified style and
+ * time zone in the default locale.</p>
+ *
+ * @param style time style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted time
+ * @return a localized standard time formatter
+ * @throws IllegalArgumentException if the Locale has no time
+ * pattern defined
+ * @since 2.1
+ */
+ public static FastDateFormat getTimeInstance(int style, TimeZone timeZone) {
+ return getTimeInstance(style, timeZone, null);
+ }
+
+ /**
+ * <p>Gets a time formatter instance using the specified style, time
+ * zone and locale.</p>
+ *
+ * @param style time style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted time
+ * @param locale optional locale, overrides system locale
+ * @return a localized standard time formatter
+ * @throws IllegalArgumentException if the Locale has no time
+ * pattern defined
+ */
+ public static synchronized FastDateFormat getTimeInstance(int style, TimeZone timeZone, Locale locale) {
+ Object key = new Integer(style);
+ if (timeZone != null) {
+ key = new Pair(key, timeZone);
+ }
+ if (locale != null) {
+ key = new Pair(key, locale);
+ }
+
+ FastDateFormat format = (FastDateFormat) cTimeInstanceCache.get(key);
+ if (format == null) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+
+ try {
+ SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getTimeInstance(style, locale);
+ String pattern = formatter.toPattern();
+ format = getInstance(pattern, timeZone, locale);
+ cTimeInstanceCache.put(key, format);
+
+ } catch (ClassCastException ex) {
+ throw new IllegalArgumentException("No date pattern for locale: " + locale);
+ }
+ }
+ return format;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Gets a date/time formatter instance using the specified style
+ * in the default time zone and locale.</p>
+ *
+ * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
+ * @return a localized standard date/time formatter
+ * @throws IllegalArgumentException if the Locale has no date/time
+ * pattern defined
+ * @since 2.1
+ */
+ public static FastDateFormat getDateTimeInstance(
+ int dateStyle, int timeStyle) {
+ return getDateTimeInstance(dateStyle, timeStyle, null, null);
+ }
+
+ /**
+ * <p>Gets a date/time formatter instance using the specified style and
+ * locale in the default time zone.</p>
+ *
+ * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
+ * @param locale optional locale, overrides system locale
+ * @return a localized standard date/time formatter
+ * @throws IllegalArgumentException if the Locale has no date/time
+ * pattern defined
+ * @since 2.1
+ */
+ public static FastDateFormat getDateTimeInstance(
+ int dateStyle, int timeStyle, Locale locale) {
+ return getDateTimeInstance(dateStyle, timeStyle, null, locale);
+ }
+
+ /**
+ * <p>Gets a date/time formatter instance using the specified style and
+ * time zone in the default locale.</p>
+ *
+ * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted date
+ * @return a localized standard date/time formatter
+ * @throws IllegalArgumentException if the Locale has no date/time
+ * pattern defined
+ * @since 2.1
+ */
+ public static FastDateFormat getDateTimeInstance(
+ int dateStyle, int timeStyle, TimeZone timeZone) {
+ return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
+ }
+ /**
+ * <p>Gets a date/time formatter instance using the specified style,
+ * time zone and locale.</p>
+ *
+ * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted date
+ * @param locale optional locale, overrides system locale
+ * @return a localized standard date/time formatter
+ * @throws IllegalArgumentException if the Locale has no date/time
+ * pattern defined
+ */
+ public static synchronized FastDateFormat getDateTimeInstance(int dateStyle, int timeStyle, TimeZone timeZone,
+ Locale locale) {
+
+ Object key = new Pair(new Integer(dateStyle), new Integer(timeStyle));
+ if (timeZone != null) {
+ key = new Pair(key, timeZone);
+ }
+ if (locale != null) {
+ key = new Pair(key, locale);
+ }
+
+ FastDateFormat format = (FastDateFormat) cDateTimeInstanceCache.get(key);
+ if (format == null) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+
+ try {
+ SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateTimeInstance(dateStyle, timeStyle,
+ locale);
+ String pattern = formatter.toPattern();
+ format = getInstance(pattern, timeZone, locale);
+ cDateTimeInstanceCache.put(key, format);
+
+ } catch (ClassCastException ex) {
+ throw new IllegalArgumentException("No date time pattern for locale: " + locale);
+ }
+ }
+ return format;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Gets the time zone display name, using a cache for performance.</p>
+ *
+ * @param tz the zone to query
+ * @param daylight true if daylight savings
+ * @param style the style to use <code>TimeZone.LONG</code>
+ * or <code>TimeZone.SHORT</code>
+ * @param locale the locale to use
+ * @return the textual name of the time zone
+ */
+ static synchronized String getTimeZoneDisplay(TimeZone tz, boolean daylight, int style, Locale locale) {
+ Object key = new TimeZoneDisplayKey(tz, daylight, style, locale);
+ String value = (String) cTimeZoneDisplayCache.get(key);
+ if (value == null) {
+ // This is a very slow call, so cache the results.
+ value = tz.getDisplayName(daylight, style, locale);
+ cTimeZoneDisplayCache.put(key, value);
+ }
+ return value;
+ }
+
+ /**
+ * <p>Gets the default pattern.</p>
+ *
+ * @return the default pattern
+ */
+ private static synchronized String getDefaultPattern() {
+ if (cDefaultPattern == null) {
+ cDefaultPattern = new SimpleDateFormat().toPattern();
+ }
+ return cDefaultPattern;
+ }
+
+ // Constructor
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Constructs a new FastDateFormat.</p>
+ *
+ * @param pattern {@link java.text.SimpleDateFormat} compatible
+ * pattern
+ * @param timeZone time zone to use, <code>null</code> means use
+ * default for <code>Date</code> and value within for
+ * <code>Calendar</code>
+ * @param locale locale, <code>null</code> means use system
+ * default
+ * @throws IllegalArgumentException if pattern is invalid or
+ * <code>null</code>
+ */
+ protected FastDateFormat(String pattern, TimeZone timeZone, Locale locale) {
+ super();
+ if (pattern == null) {
+ throw new IllegalArgumentException("The pattern must not be null");
+ }
+ mPattern = pattern;
+
+ mTimeZoneForced = (timeZone != null);
+ if (timeZone == null) {
+ timeZone = TimeZone.getDefault();
+ }
+ mTimeZone = timeZone;
+
+ mLocaleForced = (locale != null);
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+ mLocale = locale;
+ }
+
+ /**
+ * <p>Initialise the instance for first use.</p>
+ */
+ protected void init() {
+ List rulesList = parsePattern();
+ mRules = (Rule[]) rulesList.toArray(new Rule[rulesList.size()]);
+
+ int len = 0;
+ for (int i=mRules.length; --i >= 0; ) {
+ len += mRules[i].estimateLength();
+ }
+
+ mMaxLengthEstimate = len;
+ }
+
+ // Parse the pattern
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Returns a list of Rules given a pattern.</p>
+ *
+ * @return a <code>List</code> of Rule objects
+ * @throws IllegalArgumentException if pattern is invalid
+ */
+ protected List parsePattern() {
+ DateFormatSymbols symbols = new DateFormatSymbols(mLocale);
+ List rules = new ArrayList();
+
+ String[] ERAs = symbols.getEras();
+ String[] months = symbols.getMonths();
+ String[] shortMonths = symbols.getShortMonths();
+ String[] weekdays = symbols.getWeekdays();
+ String[] shortWeekdays = symbols.getShortWeekdays();
+ String[] AmPmStrings = symbols.getAmPmStrings();
+
+ int length = mPattern.length();
+ int[] indexRef = new int[1];
+
+ for (int i = 0; i < length; i++) {
+ indexRef[0] = i;
+ String token = parseToken(mPattern, indexRef);
+ i = indexRef[0];
+
+ int tokenLen = token.length();
+ if (tokenLen == 0) {
+ break;
+ }
+
+ Rule rule;
+ char c = token.charAt(0);
+
+ switch (c) {
+ case 'G': // era designator (text)
+ rule = new TextField(Calendar.ERA, ERAs);
+ break;
+ case 'y': // year (number)
+ if (tokenLen >= 4) {
+ rule = selectNumberRule(Calendar.YEAR, tokenLen);
+ } else {
+ rule = TwoDigitYearField.INSTANCE;
+ }
+ break;
+ case 'M': // month in year (text and number)
+ if (tokenLen >= 4) {
+ rule = new TextField(Calendar.MONTH, months);
+ } else if (tokenLen == 3) {
+ rule = new TextField(Calendar.MONTH, shortMonths);
+ } else if (tokenLen == 2) {
+ rule = TwoDigitMonthField.INSTANCE;
+ } else {
+ rule = UnpaddedMonthField.INSTANCE;
+ }
+ break;
+ case 'd': // day in month (number)
+ rule = selectNumberRule(Calendar.DAY_OF_MONTH, tokenLen);
+ break;
+ case 'h': // hour in am/pm (number, 1..12)
+ rule = new TwelveHourField(selectNumberRule(Calendar.HOUR, tokenLen));
+ break;
+ case 'H': // hour in day (number, 0..23)
+ rule = selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen);
+ break;
+ case 'm': // minute in hour (number)
+ rule = selectNumberRule(Calendar.MINUTE, tokenLen);
+ break;
+ case 's': // second in minute (number)
+ rule = selectNumberRule(Calendar.SECOND, tokenLen);
+ break;
+ case 'S': // millisecond (number)
+ rule = selectNumberRule(Calendar.MILLISECOND, tokenLen);
+ break;
+ case 'E': // day in week (text)
+ rule = new TextField(Calendar.DAY_OF_WEEK, tokenLen < 4 ? shortWeekdays : weekdays);
+ break;
+ case 'D': // day in year (number)
+ rule = selectNumberRule(Calendar.DAY_OF_YEAR, tokenLen);
+ break;
+ case 'F': // day of week in month (number)
+ rule = selectNumberRule(Calendar.DAY_OF_WEEK_IN_MONTH, tokenLen);
+ break;
+ case 'w': // week in year (number)
+ rule = selectNumberRule(Calendar.WEEK_OF_YEAR, tokenLen);
+ break;
+ case 'W': // week in month (number)
+ rule = selectNumberRule(Calendar.WEEK_OF_MONTH, tokenLen);
+ break;
+ case 'a': // am/pm marker (text)
+ rule = new TextField(Calendar.AM_PM, AmPmStrings);
+ break;
+ case 'k': // hour in day (1..24)
+ rule = new TwentyFourHourField(selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen));
+ break;
+ case 'K': // hour in am/pm (0..11)
+ rule = selectNumberRule(Calendar.HOUR, tokenLen);
+ break;
+ case 'z': // time zone (text)
+ if (tokenLen >= 4) {
+ rule = new TimeZoneNameRule(mTimeZone, mTimeZoneForced, mLocale, TimeZone.LONG);
+ } else {
+ rule = new TimeZoneNameRule(mTimeZone, mTimeZoneForced, mLocale, TimeZone.SHORT);
+ }
+ break;
+ case 'Z': // time zone (value)
+ if (tokenLen == 1) {
+ rule = TimeZoneNumberRule.INSTANCE_NO_COLON;
+ } else {
+ rule = TimeZoneNumberRule.INSTANCE_COLON;
+ }
+ break;
+ case '\'': // literal text
+ String sub = token.substring(1);
+ if (sub.length() == 1) {
+ rule = new CharacterLiteral(sub.charAt(0));
+ } else {
+ rule = new StringLiteral(sub);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal pattern component: " + token);
+ }
+
+ rules.add(rule);
+ }
+
+ return rules;
+ }
+
+ /**
+ * <p>Performs the parsing of tokens.</p>
+ *
+ * @param pattern the pattern
+ * @param indexRef index references
+ * @return parsed token
+ */
+ protected String parseToken(String pattern, int[] indexRef) {
+ StringBuffer buf = new StringBuffer();
+
+ int i = indexRef[0];
+ int length = pattern.length();
+
+ char c = pattern.charAt(i);
+ if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') {
+ // Scan a run of the same character, which indicates a time
+ // pattern.
+ buf.append(c);
+
+ while (i + 1 < length) {
+ char peek = pattern.charAt(i + 1);
+ if (peek == c) {
+ buf.append(c);
+ i++;
+ } else {
+ break;
+ }
+ }
+ } else {
+ // This will identify token as text.
+ buf.append('\'');
+
+ boolean inLiteral = false;
+
+ for (; i < length; i++) {
+ c = pattern.charAt(i);
+
+ if (c == '\'') {
+ if (i + 1 < length && pattern.charAt(i + 1) == '\'') {
+ // '' is treated as escaped '
+ i++;
+ buf.append(c);
+ } else {
+ inLiteral = !inLiteral;
+ }
+ } else if (!inLiteral &&
+ (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')) {
+ i--;
+ break;
+ } else {
+ buf.append(c);
+ }
+ }
+ }
+
+ indexRef[0] = i;
+ return buf.toString();
+ }
+
+ /**
+ * <p>Gets an appropriate rule for the padding required.</p>
+ *
+ * @param field the field to get a rule for
+ * @param padding the padding required
+ * @return a new rule with the correct padding
+ */
+ protected NumberRule selectNumberRule(int field, int padding) {
+ switch (padding) {
+ case 1:
+ return new UnpaddedNumberField(field);
+ case 2:
+ return new TwoDigitNumberField(field);
+ default:
+ return new PaddedNumberField(field, padding);
+ }
+ }
+
+ // Format methods
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Formats a <code>Date</code>, <code>Calendar</code> or
+ * <code>Long</code> (milliseconds) object.</p>
+ *
+ * @param obj the object to format
+ * @param toAppendTo the buffer to append to
+ * @param pos the position - ignored
+ * @return the buffer passed in
+ */
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ if (obj instanceof Date) {
+ return format((Date) obj, toAppendTo);
+ } else if (obj instanceof Calendar) {
+ return format((Calendar) obj, toAppendTo);
+ } else if (obj instanceof Long) {
+ return format(((Long) obj).longValue(), toAppendTo);
+ } else {
+ throw new IllegalArgumentException("Unknown class: " +
+ (obj == null ? "<null>" : obj.getClass().getName()));
+ }
+ }
+
+ /**
+ * <p>Formats a millisecond <code>long</code> value.</p>
+ *
+ * @param millis the millisecond value to format
+ * @return the formatted string
+ * @since 2.1
+ */
+ public String format(long millis) {
+ return format(new Date(millis));
+ }
+
+ /**
+ * <p>Formats a <code>Date</code> object.</p>
+ *
+ * @param date the date to format
+ * @return the formatted string
+ */
+ public String format(Date date) {
+ Calendar c = new GregorianCalendar(mTimeZone);
+ c.setTime(date);
+ return applyRules(c, new StringBuffer(mMaxLengthEstimate)).toString();
+ }
+
+ /**
+ * <p>Formats a <code>Calendar</code> object.</p>
+ *
+ * @param calendar the calendar to format
+ * @return the formatted string
+ */
+ public String format(Calendar calendar) {
+ return format(calendar, new StringBuffer(mMaxLengthEstimate)).toString();
+ }
+
+ /**
+ * <p>Formats a milliseond <code>long</code> value into the
+ * supplied <code>StringBuffer</code>.</p>
+ *
+ * @param millis the millisecond value to format
+ * @param buf the buffer to format into
+ * @return the specified string buffer
+ * @since 2.1
+ */
+ public StringBuffer format(long millis, StringBuffer buf) {
+ return format(new Date(millis), buf);
+ }
+
+ /**
+ * <p>Formats a <code>Date</code> object into the
+ * supplied <code>StringBuffer</code>.</p>
+ *
+ * @param date the date to format
+ * @param buf the buffer to format into
+ * @return the specified string buffer
+ */
+ public StringBuffer format(Date date, StringBuffer buf) {
+ Calendar c = new GregorianCalendar(mTimeZone);
+ c.setTime(date);
+ return applyRules(c, buf);
+ }
+
+ /**
+ * <p>Formats a <code>Calendar</code> object into the
+ * supplied <code>StringBuffer</code>.</p>
+ *
+ * @param calendar the calendar to format
+ * @param buf the buffer to format into
+ * @return the specified string buffer
+ */
+ public StringBuffer format(Calendar calendar, StringBuffer buf) {
+ if (mTimeZoneForced) {
+ calendar = (Calendar) calendar.clone();
+ calendar.setTimeZone(mTimeZone);
+ }
+ return applyRules(calendar, buf);
+ }
+
+ /**
+ * <p>Performs the formatting by applying the rules to the
+ * specified calendar.</p>
+ *
+ * @param calendar the calendar to format
+ * @param buf the buffer to format into
+ * @return the specified string buffer
+ */
+ protected StringBuffer applyRules(Calendar calendar, StringBuffer buf) {
+ Rule[] rules = mRules;
+ int len = mRules.length;
+ for (int i = 0; i < len; i++) {
+ rules[i].appendTo(buf, calendar);
+ }
+ return buf;
+ }
+
+ // Parsing
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Parsing not supported.</p>
+ *
+ * @param source the string to parse
+ * @param pos the parsing position
+ * @return <code>null</code> as not supported
+ */
+ public Object parseObject(String source, ParsePosition pos) {
+ pos.setIndex(0);
+ pos.setErrorIndex(0);
+ return null;
+ }
+
+ // Accessors
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Gets the pattern used by this formatter.</p>
+ *
+ * @return the pattern, {@link java.text.SimpleDateFormat} compatible
+ */
+ public String getPattern() {
+ return mPattern;
+ }
+
+ /**
+ * <p>Gets the time zone used by this formatter.</p>
+ *
+ * <p>This zone is always used for <code>Date</code> formatting.
+ * If a <code>Calendar</code> is passed in to be formatted, the
+ * time zone on that may be used depending on
+ * {@link #getTimeZoneOverridesCalendar()}.</p>
+ *
+ * @return the time zone
+ */
+ public TimeZone getTimeZone() {
+ return mTimeZone;
+ }
+
+ /**
+ * <p>Returns <code>true</code> if the time zone of the
+ * calendar overrides the time zone of the formatter.</p>
+ *
+ * @return <code>true</code> if time zone of formatter
+ * overridden for calendars
+ */
+ public boolean getTimeZoneOverridesCalendar() {
+ return mTimeZoneForced;
+ }
+
+ /**
+ * <p>Gets the locale used by this formatter.</p>
+ *
+ * @return the locale
+ */
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /**
+ * <p>Gets an estimate for the maximum string length that the
+ * formatter will produce.</p>
+ *
+ * <p>The actual formatted length will almost always be less than or
+ * equal to this amount.</p>
+ *
+ * @return the maximum formatted length
+ */
+ public int getMaxLengthEstimate() {
+ return mMaxLengthEstimate;
+ }
+
+ // Basics
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Compare two objects for equality.</p>
+ *
+ * @param obj the object to compare to
+ * @return <code>true</code> if equal
+ */
+ public boolean equals(Object obj) {
+ if (obj instanceof FastDateFormat == false) {
+ return false;
+ }
+ FastDateFormat other = (FastDateFormat) obj;
+ if (
+ (mPattern == other.mPattern || mPattern.equals(other.mPattern)) &&
+ (mTimeZone == other.mTimeZone || mTimeZone.equals(other.mTimeZone)) &&
+ (mLocale == other.mLocale || mLocale.equals(other.mLocale)) &&
+ (mTimeZoneForced == other.mTimeZoneForced) &&
+ (mLocaleForced == other.mLocaleForced)
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * <p>A suitable hashcode.</p>
+ *
+ * @return a hashcode compatible with equals
+ */
+ public int hashCode() {
+ int total = 0;
+ total += mPattern.hashCode();
+ total += mTimeZone.hashCode();
+ total += (mTimeZoneForced ? 1 : 0);
+ total += mLocale.hashCode();
+ total += (mLocaleForced ? 1 : 0);
+ return total;
+ }
+
+ /**
+ * <p>Gets a debugging string version of this formatter.</p>
+ *
+ * @return a debugging string
+ */
+ public String toString() {
+ return "FastDateFormat[" + mPattern + "]";
+ }
+
+ // Rules
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Inner class defining a rule.</p>
+ */
+ private interface Rule {
+ int estimateLength();
+ void appendTo(StringBuffer buffer, Calendar calendar);
+ }
+
+ /**
+ * <p>Inner class defining a numeric rule.</p>
+ */
+ private interface NumberRule extends Rule {
+ void appendTo(StringBuffer buffer, int value);
+ }
+
+ /**
+ * <p>Inner class to output a constant single character.</p>
+ */
+ private static class CharacterLiteral implements Rule {
+ private final char mValue;
+
+ CharacterLiteral(char value) {
+ mValue = value;
+ }
+
+ public int estimateLength() {
+ return 1;
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ buffer.append(mValue);
+ }
+ }
+
+ /**
+ * <p>Inner class to output a constant string.</p>
+ */
+ private static class StringLiteral implements Rule {
+ private final String mValue;
+
+ StringLiteral(String value) {
+ mValue = value;
+ }
+
+ public int estimateLength() {
+ return mValue.length();
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ buffer.append(mValue);
+ }
+ }
+
+ /**
+ * <p>Inner class to output one of a set of values.</p>
+ */
+ private static class TextField implements Rule {
+ private final int mField;
+ private final String[] mValues;
+
+ TextField(int field, String[] values) {
+ mField = field;
+ mValues = values;
+ }
+
+ public int estimateLength() {
+ int max = 0;
+ for (int i=mValues.length; --i >= 0; ) {
+ int len = mValues[i].length();
+ if (len > max) {
+ max = len;
+ }
+ }
+ return max;
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ buffer.append(mValues[calendar.get(mField)]);
+ }
+ }
+
+ /**
+ * <p>Inner class to output an unpadded number.</p>
+ */
+ private static class UnpaddedNumberField implements NumberRule {
+ static final UnpaddedNumberField INSTANCE_YEAR = new UnpaddedNumberField(Calendar.YEAR);
+
+ private final int mField;
+
+ UnpaddedNumberField(int field) {
+ mField = field;
+ }
+
+ public int estimateLength() {
+ return 4;
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(mField));
+ }
+
+ public final void appendTo(StringBuffer buffer, int value) {
+ if (value < 10) {
+ buffer.append((char)(value + '0'));
+ } else if (value < 100) {
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ } else {
+ buffer.append(Integer.toString(value));
+ }
+ }
+ }
+
+ /**
+ * <p>Inner class to output an unpadded month.</p>
+ */
+ private static class UnpaddedMonthField implements NumberRule {
+ static final UnpaddedMonthField INSTANCE = new UnpaddedMonthField();
+
+ UnpaddedMonthField() {
+ }
+
+ public int estimateLength() {
+ return 2;
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
+ }
+
+ public final void appendTo(StringBuffer buffer, int value) {
+ if (value < 10) {
+ buffer.append((char)(value + '0'));
+ } else {
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ }
+ }
+ }
+
+ /**
+ * <p>Inner class to output a padded number.</p>
+ */
+ private static class PaddedNumberField implements NumberRule {
+ private final int mField;
+ private final int mSize;
+
+ PaddedNumberField(int field, int size) {
+ if (size < 3) {
+ // Should use UnpaddedNumberField or TwoDigitNumberField.
+ throw new IllegalArgumentException();
+ }
+ mField = field;
+ mSize = size;
+ }
+
+ public int estimateLength() {
+ return 4;
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(mField));
+ }
+
+ public final void appendTo(StringBuffer buffer, int value) {
+ if (value < 100) {
+ for (int i = mSize; --i >= 2; ) {
+ buffer.append('0');
+ }
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ } else {
+ int digits;
+ if (value < 1000) {
+ digits = 3;
+ } else {
+ digits = (int)(Math.log(value) / LOG_10) + 1;
+ }
+ for (int i = mSize; --i >= digits; ) {
+ buffer.append('0');
+ }
+ buffer.append(Integer.toString(value));
+ }
+ }
+ }
+
+ /**
+ * <p>Inner class to output a two digit number.</p>
+ */
+ private static class TwoDigitNumberField implements NumberRule {
+ private final int mField;
+
+ TwoDigitNumberField(int field) {
+ mField = field;
+ }
+
+ public int estimateLength() {
+ return 2;
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(mField));
+ }
+
+ public final void appendTo(StringBuffer buffer, int value) {
+ if (value < 100) {
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ } else {
+ buffer.append(Integer.toString(value));
+ }
+ }
+ }
+
+ /**
+ * <p>Inner class to output a two digit year.</p>
+ */
+ private static class TwoDigitYearField implements NumberRule {
+ static final TwoDigitYearField INSTANCE = new TwoDigitYearField();
+
+ TwoDigitYearField() {
+ }
+
+ public int estimateLength() {
+ return 2;
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(Calendar.YEAR) % 100);
+ }
+
+ public final void appendTo(StringBuffer buffer, int value) {
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ }
+ }
+
+ /**
+ * <p>Inner class to output a two digit month.</p>
+ */
+ private static class TwoDigitMonthField implements NumberRule {
+ static final TwoDigitMonthField INSTANCE = new TwoDigitMonthField();
+
+ TwoDigitMonthField() {
+ }
+
+ public int estimateLength() {
+ return 2;
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
+ }
+
+ public final void appendTo(StringBuffer buffer, int value) {
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ }
+ }
+
+ /**
+ * <p>Inner class to output the twelve hour field.</p>
+ */
+ private static class TwelveHourField implements NumberRule {
+ private final NumberRule mRule;
+
+ TwelveHourField(NumberRule rule) {
+ mRule = rule;
+ }
+
+ public int estimateLength() {
+ return mRule.estimateLength();
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ int value = calendar.get(Calendar.HOUR);
+ if (value == 0) {
+ value = calendar.getLeastMaximum(Calendar.HOUR) + 1;
+ }
+ mRule.appendTo(buffer, value);
+ }
+
+ public void appendTo(StringBuffer buffer, int value) {
+ mRule.appendTo(buffer, value);
+ }
+ }
+
+ /**
+ * <p>Inner class to output the twenty four hour field.</p>
+ */
+ private static class TwentyFourHourField implements NumberRule {
+ private final NumberRule mRule;
+
+ TwentyFourHourField(NumberRule rule) {
+ mRule = rule;
+ }
+
+ public int estimateLength() {
+ return mRule.estimateLength();
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ int value = calendar.get(Calendar.HOUR_OF_DAY);
+ if (value == 0) {
+ value = calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1;
+ }
+ mRule.appendTo(buffer, value);
+ }
+
+ public void appendTo(StringBuffer buffer, int value) {
+ mRule.appendTo(buffer, value);
+ }
+ }
+
+ /**
+ * <p>Inner class to output a time zone name.</p>
+ */
+ private static class TimeZoneNameRule implements Rule {
+ private final TimeZone mTimeZone;
+ private final boolean mTimeZoneForced;
+ private final Locale mLocale;
+ private final int mStyle;
+ private final String mStandard;
+ private final String mDaylight;
+
+ TimeZoneNameRule(TimeZone timeZone, boolean timeZoneForced, Locale locale, int style) {
+ mTimeZone = timeZone;
+ mTimeZoneForced = timeZoneForced;
+ mLocale = locale;
+ mStyle = style;
+
+ if (timeZoneForced) {
+ mStandard = getTimeZoneDisplay(timeZone, false, style, locale);
+ mDaylight = getTimeZoneDisplay(timeZone, true, style, locale);
+ } else {
+ mStandard = null;
+ mDaylight = null;
+ }
+ }
+
+ public int estimateLength() {
+ if (mTimeZoneForced) {
+ return Math.max(mStandard.length(), mDaylight.length());
+ } else if (mStyle == TimeZone.SHORT) {
+ return 4;
+ } else {
+ return 40;
+ }
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ if (mTimeZoneForced) {
+ if (mTimeZone.useDaylightTime() && calendar.get(Calendar.DST_OFFSET) != 0) {
+ buffer.append(mDaylight);
+ } else {
+ buffer.append(mStandard);
+ }
+ } else {
+ TimeZone timeZone = calendar.getTimeZone();
+ if (timeZone.useDaylightTime() && calendar.get(Calendar.DST_OFFSET) != 0) {
+ buffer.append(getTimeZoneDisplay(timeZone, true, mStyle, mLocale));
+ } else {
+ buffer.append(getTimeZoneDisplay(timeZone, false, mStyle, mLocale));
+ }
+ }
+ }
+ }
+
+ /**
+ * <p>Inner class to output a time zone as a number <code>+/-HHMM</code>
+ * or <code>+/-HH:MM</code>.</p>
+ */
+ private static class TimeZoneNumberRule implements Rule {
+ static final TimeZoneNumberRule INSTANCE_COLON = new TimeZoneNumberRule(true);
+ static final TimeZoneNumberRule INSTANCE_NO_COLON = new TimeZoneNumberRule(false);
+
+ final boolean mColon;
+
+ TimeZoneNumberRule(boolean colon) {
+ mColon = colon;
+ }
+
+ public int estimateLength() {
+ return 5;
+ }
+
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
+
+ if (offset < 0) {
+ buffer.append('-');
+ offset = -offset;
+ } else {
+ buffer.append('+');
+ }
+
+ int hours = offset / (60 * 60 * 1000);
+ buffer.append((char)(hours / 10 + '0'));
+ buffer.append((char)(hours % 10 + '0'));
+
+ if (mColon) {
+ buffer.append(':');
+ }
+
+ int minutes = offset / (60 * 1000) - 60 * hours;
+ buffer.append((char)(minutes / 10 + '0'));
+ buffer.append((char)(minutes % 10 + '0'));
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ /**
+ * <p>Inner class that acts as a compound key for time zone names.</p>
+ */
+ private static class TimeZoneDisplayKey {
+ private final TimeZone mTimeZone;
+ private final int mStyle;
+ private final Locale mLocale;
+
+ TimeZoneDisplayKey(TimeZone timeZone,
+ boolean daylight, int style, Locale locale) {
+ mTimeZone = timeZone;
+ if (daylight) {
+ style |= 0x80000000;
+ }
+ mStyle = style;
+ mLocale = locale;
+ }
+
+ public int hashCode() {
+ return mStyle * 31 + mLocale.hashCode();
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof TimeZoneDisplayKey) {
+ TimeZoneDisplayKey other = (TimeZoneDisplayKey)obj;
+ return
+ mTimeZone.equals(other.mTimeZone) &&
+ mStyle == other.mStyle &&
+ mLocale.equals(other.mLocale);
+ }
+ return false;
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ /**
+ * <p>Helper class for creating compound objects.</p>
+ *
+ * <p>One use for this class is to create a hashtable key
+ * out of multiple objects.</p>
+ */
+ private static class Pair {
+ private final Object mObj1;
+ private final Object mObj2;
+
+ public Pair(Object obj1, Object obj2) {
+ mObj1 = obj1;
+ mObj2 = obj2;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof Pair)) {
+ return false;
+ }
+
+ Pair key = (Pair)obj;
+
+ return
+ (mObj1 == null ?
+ key.mObj1 == null : mObj1.equals(key.mObj1)) &&
+ (mObj2 == null ?
+ key.mObj2 == null : mObj2.equals(key.mObj2));
+ }
+
+ public int hashCode() {
+ return
+ (mObj1 == null ? 0 : mObj1.hashCode()) +
+ (mObj2 == null ? 0 : mObj2.hashCode());
+ }
+
+ public String toString() {
+ return "[" + mObj1 + ':' + mObj2 + ']';
+ }
+ }
+
+}
Propchange: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/FastDateFormat.java
------------------------------------------------------------------------------
svn:keywords = HeadURL Id LastChangedBy LastChangedDate LastChangedRevision
Added: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/StopWatch.java
URL: http://svn.apache.org/viewcvs/directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/StopWatch.java?rev=370807&view=auto
==============================================================================
--- directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/StopWatch.java (added)
+++ directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/StopWatch.java Fri Jan 20 05:47:50 2006
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2002-2005 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.mina.common.support.lang.time;
+
+/**
+ * <p><code>StopWatch</code> provides a convenient API for timings.</p>
+ *
+ * <p>To start the watch, call {@link #start()}. At this point you can:</p>
+ * <ul>
+ * <li>{@link #split()} the watch to get the time whilst the watch continues in the
+ * background. {@link #unsplit()} will remove the effect of the split. At this point,
+ * these three options are available again.</li>
+ * <li>{@link #suspend()} the watch to pause it. {@link #resume()} allows the watch
+ * to continue. Any time between the suspend and resume will not be counted in
+ * the total. At this point, these three options are available again.</li>
+ * <li>{@link #stop()} the watch to complete the timing session.</li>
+ * </ul>
+ *
+ * <p>It is intended that the output methods {@link #toString()} and {@link #getTime()}
+ * should only be called after stop, split or suspend, however a suitable result will
+ * be returned at other points.</p>
+ *
+ * <p>NOTE: As from v2.1, the methods protect against inappropriate calls.
+ * Thus you cannot now call stop before start, resume before suspend or
+ * unsplit before split.</p>
+ *
+ * <p>1. split(), suspend(), or stop() cannot be invoked twice<br />
+ * 2. unsplit() may only be called if the watch has been split()<br />
+ * 3. resume() may only be called if the watch has been suspend()<br />
+ * 4. start() cannot be called twice without calling reset()</p>
+ *
+ * @author Henri Yandell
+ * @author Stephen Colebourne
+ * @since 2.0
+ * @version $Id$
+ */
+public class StopWatch {
+
+ // running states
+ private static final int STATE_UNSTARTED = 0;
+ private static final int STATE_RUNNING = 1;
+ private static final int STATE_STOPPED = 2;
+ private static final int STATE_SUSPENDED = 3;
+
+ // split state
+ private static final int STATE_UNSPLIT = 10;
+ private static final int STATE_SPLIT = 11;
+
+ /**
+ * The current running state of the StopWatch.
+ */
+ private int runningState = STATE_UNSTARTED;
+
+ /**
+ * Whether the stopwatch has a split time recorded.
+ */
+ private int splitState = STATE_UNSPLIT;
+
+ /**
+ * The start time.
+ */
+ private long startTime = -1;
+ /**
+ * The stop time.
+ */
+ private long stopTime = -1;
+
+ /**
+ * <p>Constructor.</p>
+ */
+ public StopWatch() {
+ }
+
+ /**
+ * <p>Start the stopwatch.</p>
+ *
+ * <p>This method starts a new timing session, clearing any previous values.</p>
+ *
+ * @throws IllegalStateException if the StopWatch is already running.
+ */
+ public void start() {
+ if(this.runningState == STATE_STOPPED) {
+ throw new IllegalStateException("Stopwatch must be reset before being restarted. ");
+ }
+ if(this.runningState != STATE_UNSTARTED) {
+ throw new IllegalStateException("Stopwatch already started. ");
+ }
+ stopTime = -1;
+ startTime = System.currentTimeMillis();
+ this.runningState = STATE_RUNNING;
+ }
+
+ /**
+ * <p>Stop the stopwatch.</p>
+ *
+ * <p>This method ends a new timing session, allowing the time to be retrieved.</p>
+ *
+ * @throws IllegalStateException if the StopWatch is not running.
+ */
+ public void stop() {
+ if(this.runningState != STATE_RUNNING && this.runningState != STATE_SUSPENDED) {
+ throw new IllegalStateException("Stopwatch is not running. ");
+ }
+ stopTime = System.currentTimeMillis();
+ this.runningState = STATE_STOPPED;
+ }
+
+ /**
+ * <p>Resets the stopwatch. Stops it if need be. </p>
+ *
+ * <p>This method clears the internal values to allow the object to be reused.</p>
+ */
+ public void reset() {
+ this.runningState = STATE_UNSTARTED;
+ this.splitState = STATE_UNSPLIT;
+ startTime = -1;
+ stopTime = -1;
+ }
+
+ /**
+ * <p>Split the time.</p>
+ *
+ * <p>This method sets the stop time of the watch to allow a time to be extracted.
+ * The start time is unaffected, enabling {@link #unsplit()} to continue the
+ * timing from the original start point.</p>
+ *
+ * @throws IllegalStateException if the StopWatch is not running.
+ */
+ public void split() {
+ if(this.runningState != STATE_RUNNING) {
+ throw new IllegalStateException("Stopwatch is not running. ");
+ }
+ stopTime = System.currentTimeMillis();
+ this.splitState = STATE_SPLIT;
+ }
+
+ /**
+ * <p>Remove a split.</p>
+ *
+ * <p>This method clears the stop time. The start time is unaffected, enabling
+ * timing from the original start point to continue.</p>
+ *
+ * @throws IllegalStateException if the StopWatch has not been split.
+ */
+ public void unsplit() {
+ if(this.splitState != STATE_SPLIT) {
+ throw new IllegalStateException("Stopwatch has not been split. ");
+ }
+ stopTime = -1;
+ this.splitState = STATE_UNSPLIT;
+ }
+
+ /**
+ * <p>Suspend the stopwatch for later resumption.</p>
+ *
+ * <p>This method suspends the watch until it is resumed. The watch will not include
+ * time between the suspend and resume calls in the total time.</p>
+ *
+ * @throws IllegalStateException if the StopWatch is not currently running.
+ */
+ public void suspend() {
+ if(this.runningState != STATE_RUNNING) {
+ throw new IllegalStateException("Stopwatch must be running to suspend. ");
+ }
+ stopTime = System.currentTimeMillis();
+ this.runningState = STATE_SUSPENDED;
+ }
+
+ /**
+ * <p>Resume the stopwatch after a suspend.</p>
+ *
+ * <p>This method resumes the watch after it was suspended. The watch will not include
+ * time between the suspend and resume calls in the total time.</p>
+ *
+ * @throws IllegalStateException if the StopWatch has not been suspended.
+ */
+ public void resume() {
+ if(this.runningState != STATE_SUSPENDED) {
+ throw new IllegalStateException("Stopwatch must be suspended to resume. ");
+ }
+ startTime += (System.currentTimeMillis() - stopTime);
+ stopTime = -1;
+ this.runningState = STATE_RUNNING;
+ }
+
+ /**
+ * <p>Get the time on the stopwatch.</p>
+ *
+ * <p>This is either the time between the start and the moment this method
+ * is called, or the amount of time between start and stop.</p>
+ *
+ * @return the time in milliseconds
+ */
+ public long getTime() {
+ if(this.runningState == STATE_STOPPED || this.runningState == STATE_SUSPENDED) {
+ return this.stopTime - this.startTime;
+ } else
+ if(this.runningState == STATE_UNSTARTED) {
+ return 0;
+ } else
+ if(this.runningState == STATE_RUNNING) {
+ return System.currentTimeMillis() - this.startTime;
+ }
+ throw new RuntimeException("Illegal running state has occured. ");
+ }
+
+ /**
+ * <p>Get the split time on the stopwatch.</p>
+ *
+ * <p>This is the time between start and latest split. </p>
+ *
+ * @return the split time in milliseconds
+ *
+ * @throws IllegalStateException if the StopWatch has not yet been split.
+ * @since 2.1
+ */
+ public long getSplitTime() {
+ if(this.splitState != STATE_SPLIT) {
+ throw new IllegalStateException("Stopwatch must be split to get the split time. ");
+ }
+ return this.stopTime - this.startTime;
+ }
+
+ /**
+ * <p>Gets a summary of the time that the stopwatch recorded as a string.</p>
+ *
+ * <p>The format used is ISO8601-like,
+ * <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.</p>
+ *
+ * @return the time as a String
+ */
+ public String toString() {
+ return DurationFormatUtils.formatDurationHMS(getTime());
+ }
+
+ /**
+ * <p>Gets a summary of the split time that the stopwatch recorded as a string.</p>
+ *
+ * <p>The format used is ISO8601-like,
+ * <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.</p>
+ *
+ * @return the split time as a String
+ * @since 2.1
+ */
+ public String toSplitString() {
+ return DurationFormatUtils.formatDurationHMS(getSplitTime());
+ }
+
+}
Propchange: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/StopWatch.java
------------------------------------------------------------------------------
svn:keywords = HeadURL Id LastChangedBy LastChangedDate LastChangedRevision
Added: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/package.html
URL: http://svn.apache.org/viewcvs/directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/package.html?rev=370807&view=auto
==============================================================================
--- directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/package.html (added)
+++ directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/package.html Fri Jan 20 05:47:50 2006
@@ -0,0 +1,30 @@
+<!--
+Copyright 2002-2005 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.
+-->
+<html>
+<body>
+<p>
+Provides classes and methods to work with dates and durations.
+<p>
+This includes:
+<ul>
+<li><code>DateUtils</code> - a set of static utility methods for working with dates
+<li><code>FastDateFormat</code> - a replacement for <code>SimpleDateFormat</code> that is fast and thread-safe
+<li><code>DateFormatUtils</code> - a formatting class for dates
+<li><code>StopWatch</code> - a duration timer
+</ul>
+@since 2.0
+</body>
+</html>
Propchange: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/time/package.html
------------------------------------------------------------------------------
svn:keywords = HeadURL Id LastChangedBy LastChangedDate LastChangedRevision
Propchange: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/package.html
------------------------------------------------------------------------------
svn:keywords = HeadURL Id LastChangedBy LastChangedDate LastChangedRevision
Added: directory/sandbox/trustin/mina-spi/core/src/main/resources/META-INF/services/org.apache.mina.common.IoServiceProvider
URL: http://svn.apache.org/viewcvs/directory/sandbox/trustin/mina-spi/core/src/main/resources/META-INF/services/org.apache.mina.common.IoServiceProvider?rev=370807&view=auto
==============================================================================
(empty)
Added: directory/sandbox/trustin/mina-spi/core/src/test/java/org/apache/mina/common/IoAddressTest.java
URL: http://svn.apache.org/viewcvs/directory/sandbox/trustin/mina-spi/core/src/test/java/org/apache/mina/common/IoAddressTest.java?rev=370807&view=auto
==============================================================================
--- directory/sandbox/trustin/mina-spi/core/src/test/java/org/apache/mina/common/IoAddressTest.java (added)
+++ directory/sandbox/trustin/mina-spi/core/src/test/java/org/apache/mina/common/IoAddressTest.java Fri Jan 20 05:47:50 2006
@@ -0,0 +1,60 @@
+package org.apache.mina.common;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+public class IoAddressTest extends TestCase
+{
+
+ public static void main( String[] args )
+ {
+ junit.textui.TestRunner.run( IoAddressTest.class );
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ public void testConstructor1() throws Exception
+ {
+ IoAddress uri;
+
+ uri = new IoAddress( "nio", "socket", "192.168.0.1:8080" );
+ Assert.assertEquals( "nio:socket:192.168.0.1:8080", uri.toString() );
+
+ try
+ {
+ new IoAddress( "n:o", "socket", "192.168.0.2:8080" );
+ Assert.fail();
+ }
+ catch( IllegalArgumentException e )
+ {
+ // OK
+ }
+
+ try
+ {
+ new IoAddress( "nio", "sock:t", "192.168.0.2:8080" );
+ Assert.fail();
+ }
+ catch( IllegalArgumentException e )
+ {
+ // OK
+ }
+ }
+
+ public void testConstructor2() throws Exception
+ {
+ IoAddress uri = new IoAddress( "nio:socket:192.168.0.1:8080" );
+ Assert.assertEquals( "nio", uri.getProvider() );
+ Assert.assertEquals( "socket", uri.getTransportType() );
+ Assert.assertEquals( "192.168.0.1:8080", uri.getAddress() );
+ }
+
+}
Propchange: directory/sandbox/trustin/mina-spi/core/src/test/java/org/apache/mina/common/IoAddressTest.java
------------------------------------------------------------------------------
svn:keywords = HeadURL Id LastChangedBy LastChangedDate LastChangedRevision