Commit 4ddfda63 authored by DrKLO's avatar DrKLO

Bug fixes

parent 5ddd8a09
...@@ -18,6 +18,7 @@ import android.content.res.Configuration; ...@@ -18,6 +18,7 @@ import android.content.res.Configuration;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.util.Xml; import android.util.Xml;
import org.telegram.android.time.FastDateFormat;
import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.ConnectionsManager;
import org.telegram.messenger.FileLog; import org.telegram.messenger.FileLog;
import org.telegram.messenger.R; import org.telegram.messenger.R;
......
...@@ -3886,8 +3886,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter ...@@ -3886,8 +3886,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter
return null; return null;
} }
if (chat.seq_in != layer.out_seq_no && chat.seq_in != layer.out_seq_no - 2) { if (chat.seq_in != layer.out_seq_no && chat.seq_in != layer.out_seq_no - 2) {
/*TLRPC.Message decryptedMessage = processDecryptedObject(chat, message, layer.message); ArrayList<TLRPC.TL_decryptedMessageHolder> arr = secretHolesQueue.get(chat.id);
if (decryptedMessage == null) { if (arr == null) {
arr = new ArrayList<TLRPC.TL_decryptedMessageHolder>();
secretHolesQueue.put(chat.id, arr);
}
if (arr.size() >= 10) {
secretHolesQueue.remove(chat.id);
final TLRPC.TL_encryptedChatDiscarded newChat = new TLRPC.TL_encryptedChatDiscarded(); final TLRPC.TL_encryptedChatDiscarded newChat = new TLRPC.TL_encryptedChatDiscarded();
newChat.id = chat.id; newChat.id = chat.id;
newChat.user_id = chat.user_id; newChat.user_id = chat.user_id;
...@@ -3903,12 +3908,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter ...@@ -3903,12 +3908,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter
} }
}); });
declineSecretChat(chat.id); declineSecretChat(chat.id);
}*/ return null;
ArrayList<TLRPC.TL_decryptedMessageHolder> arr = secretHolesQueue.get(chat.id);
if (arr == null) {
arr = new ArrayList<TLRPC.TL_decryptedMessageHolder>();
secretHolesQueue.put(chat.id, arr);
} }
TLRPC.TL_decryptedMessageHolder holder = new TLRPC.TL_decryptedMessageHolder(); TLRPC.TL_decryptedMessageHolder holder = new TLRPC.TL_decryptedMessageHolder();
holder.layer = layer; holder.layer = layer;
holder.file = message.file; holder.file = message.file;
......
...@@ -110,7 +110,7 @@ public class MessagesStorage { ...@@ -110,7 +110,7 @@ public class MessagesStorage {
database.executeFast("CREATE TABLE download_queue(uid INTEGER, type INTEGER, date INTEGER, data BLOB, PRIMARY KEY (uid, type));").stepThis().dispose(); database.executeFast("CREATE TABLE download_queue(uid INTEGER, type INTEGER, date INTEGER, data BLOB, PRIMARY KEY (uid, type));").stepThis().dispose();
database.executeFast("CREATE TABLE dialog_settings(did INTEGER PRIMARY KEY, flags INTEGER);").stepThis().dispose(); database.executeFast("CREATE TABLE dialog_settings(did INTEGER PRIMARY KEY, flags INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE messages_seq(mid INTEGER PRIMARY KEY, seq_in INTEGER, seq_out INTEGER);").stepThis().dispose(); database.executeFast("CREATE TABLE messages_seq(mid INTEGER PRIMARY KEY, seq_in INTEGER, seq_out INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE secret_holes(uid INTEGER, seq_in INTEGER, seq_out INTEGER, data BLOB, PRIMARY KEY (uid, seq_in, seq_out));").stepThis().dispose(); //database.executeFast("CREATE TABLE secret_holes(uid INTEGER, seq_in INTEGER, seq_out INTEGER, data BLOB, PRIMARY KEY (uid, seq_in, seq_out));").stepThis().dispose();
//database.executeFast("CREATE TABLE attach_data(uid INTEGER, id INTEGER, data BLOB, PRIMARY KEY (uid, id))").stepThis().dispose(); //database.executeFast("CREATE TABLE attach_data(uid INTEGER, id INTEGER, data BLOB, PRIMARY KEY (uid, id))").stepThis().dispose();
...@@ -142,7 +142,7 @@ public class MessagesStorage { ...@@ -142,7 +142,7 @@ public class MessagesStorage {
database.executeFast("CREATE INDEX IF NOT EXISTS seq_idx_messages_seq ON messages_seq(seq_in, seq_out);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS seq_idx_messages_seq ON messages_seq(seq_in, seq_out);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 8").stepThis().dispose(); database.executeFast("PRAGMA user_version = 7").stepThis().dispose();
} else { } else {
try { try {
SQLiteCursor cursor = database.queryFinalized("SELECT seq, pts, date, qts, lsv, sg, pbytes FROM params WHERE id = 1"); SQLiteCursor cursor = database.queryFinalized("SELECT seq, pts, date, qts, lsv, sg, pbytes FROM params WHERE id = 1");
...@@ -174,7 +174,7 @@ public class MessagesStorage { ...@@ -174,7 +174,7 @@ public class MessagesStorage {
} }
int version = database.executeInt("PRAGMA user_version"); int version = database.executeInt("PRAGMA user_version");
if (version < 8) { if (version < 7) {
updateDbToLastVersion(version); updateDbToLastVersion(version);
} }
} }
...@@ -304,11 +304,11 @@ public class MessagesStorage { ...@@ -304,11 +304,11 @@ public class MessagesStorage {
database.executeFast("PRAGMA user_version = 7").stepThis().dispose(); database.executeFast("PRAGMA user_version = 7").stepThis().dispose();
version = 7; version = 7;
} }
if (version == 7 && version < 8) { /*if (version == 7 && version < 8) {
database.executeFast("CREATE TABLE IF NOT EXISTS secret_holes(uid INTEGER, seq_in INTEGER, seq_out INTEGER, data BLOB, PRIMARY KEY (uid, seq_in, seq_out));").stepThis().dispose(); database.executeFast("CREATE TABLE IF NOT EXISTS secret_holes(uid INTEGER, seq_in INTEGER, seq_out INTEGER, data BLOB, PRIMARY KEY (uid, seq_in, seq_out));").stepThis().dispose();
database.executeFast("PRAGMA user_version = 8").stepThis().dispose(); database.executeFast("PRAGMA user_version = 8").stepThis().dispose();
version = 8; version = 8;
} }*/
} catch (Exception e) { } catch (Exception e) {
FileLog.e("tmessages", e); FileLog.e("tmessages", e);
} }
...@@ -671,7 +671,7 @@ public class MessagesStorage { ...@@ -671,7 +671,7 @@ public class MessagesStorage {
} }
} else { } else {
database.executeFast("DELETE FROM enc_chats WHERE uid = " + high_id).stepThis().dispose(); database.executeFast("DELETE FROM enc_chats WHERE uid = " + high_id).stepThis().dispose();
database.executeFast("DELETE FROM secret_holes WHERE uid = " + high_id).stepThis().dispose(); //database.executeFast("DELETE FROM secret_holes WHERE uid = " + high_id).stepThis().dispose();
} }
} }
database.executeFast("UPDATE dialogs SET unread_count = 0 WHERE did = " + did).stepThis().dispose(); database.executeFast("UPDATE dialogs SET unread_count = 0 WHERE did = " + did).stepThis().dispose();
...@@ -2805,7 +2805,7 @@ public class MessagesStorage { ...@@ -2805,7 +2805,7 @@ public class MessagesStorage {
}); });
} }
public void getHoleMessages() { /*public void getHoleMessages() {
storageQueue.postRunnable(new Runnable() { storageQueue.postRunnable(new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -2857,7 +2857,7 @@ public class MessagesStorage { ...@@ -2857,7 +2857,7 @@ public class MessagesStorage {
} }
} }
}); });
} }*/
public void setMessageSeq(final int mid, final int seq_in, final int seq_out) { public void setMessageSeq(final int mid, final int seq_in, final int seq_out) {
storageQueue.postRunnable(new Runnable() { storageQueue.postRunnable(new Runnable() {
......
/*
* 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.telegram.android.time;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* <p>DateParser is the "missing" interface for the parsing methods of
* {@link java.text.DateFormat}.</p>
*
* @since 3.2
*/
public interface DateParser {
/**
* Equivalent to DateFormat.parse(String).
* <p/>
* See {@link java.text.DateFormat#parse(String)} for more information.
*
* @param source A <code>String</code> whose beginning should be parsed.
* @return A <code>Date</code> parsed from the string
* @throws ParseException if the beginning of the specified string cannot be parsed.
*/
Date parse(String source) throws ParseException;
/**
* Equivalent to DateFormat.parse(String, ParsePosition).
* <p/>
* See {@link java.text.DateFormat#parse(String, ParsePosition)} for more information.
*
* @param source A <code>String</code>, part of which should be parsed.
* @param pos A <code>ParsePosition</code> object with index and error index information
* as described above.
* @return A <code>Date</code> parsed from the string. In case of error, returns null.
* @throws NullPointerException if text or pos is null.
*/
Date parse(String source, ParsePosition pos);
// Accessors
//-----------------------------------------------------------------------
/**
* <p>Get the pattern used by this parser.</p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
String getPattern();
/**
* <p>
* Get the time zone used by this parser.
* </p>
* <p/>
* <p>
* The default {@link TimeZone} used to create a {@link Date} when the {@link TimeZone} is not specified by
* the format pattern.
* </p>
*
* @return the time zone
*/
TimeZone getTimeZone();
/**
* <p>Get the locale used by this parser.</p>
*
* @return the locale
*/
Locale getLocale();
/**
* Parses text from a string to produce a Date.
*
* @param source A <code>String</code> whose beginning should be parsed.
* @return a <code>java.util.Date</code> object
* @throws ParseException if the beginning of the specified string cannot be parsed.
* @see java.text.DateFormat#parseObject(String)
*/
Object parseObject(String source) throws ParseException;
/**
* Parse a date/time string according to the given parse position.
*
* @param source A <code>String</code> whose beginning should be parsed.
* @param pos the parse position
* @return a <code>java.util.Date</code> object
* @see java.text.DateFormat#parseObject(String, ParsePosition)
*/
Object parseObject(String source, ParsePosition pos);
}
/*
* 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.telegram.android.time;
import java.text.FieldPosition;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* <p>DatePrinter is the "missing" interface for the format methods of
* {@link java.text.DateFormat}.</p>
*
* @since 3.2
*/
public interface DatePrinter {
/**
* <p>Formats a millisecond {@code long} value.</p>
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/
String format(long millis);
/**
* <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @return the formatted string
*/
String format(Date date);
/**
* <p>Formats a {@code Calendar} object.</p>
*
* @param calendar the calendar to format
* @return the formatted string
*/
String format(Calendar calendar);
/**
* <p>Formats a milliseond {@code long} value into the
* supplied {@code StringBuffer}.</p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
*/
StringBuffer format(long millis, StringBuffer buf);
/**
* <p>Formats a {@code Date} object into the
* supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
*/
StringBuffer format(Date date, StringBuffer buf);
/**
* <p>Formats a {@code Calendar} object into the
* supplied {@code StringBuffer}.</p>
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
*/
StringBuffer format(Calendar calendar, StringBuffer buf);
// Accessors
//-----------------------------------------------------------------------
/**
* <p>Gets the pattern used by this printer.</p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
String getPattern();
/**
* <p>Gets the time zone used by this printer.</p>
* <p/>
* <p>This zone is always used for {@code Date} printing. </p>
*
* @return the time zone
*/
TimeZone getTimeZone();
/**
* <p>Gets the locale used by this printer.</p>
*
* @return the locale
*/
Locale getLocale();
/**
* <p>Formats a {@code Date}, {@code Calendar} or
* {@code Long} (milliseconds) object.</p>
* <p/>
* See {@link java.text.DateFormat#format(Object, StringBuffer, FieldPosition)}
*
* @param obj the object to format
* @param toAppendTo the buffer to append to
* @param pos the position - ignored
* @return the buffer passed in
*/
StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos);
}
/*
* 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.telegram.android.time;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
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} in most formatting and parsing situations.
* This class is especially useful in multi-threaded server environments.
* {@code SimpleDateFormat} is not thread-safe in any JDK version,
* nor will it be as Sun have closed the bug/RFE.
* </p>
*
* <p>All patterns are compatible with
* SimpleDateFormat (except time zones and some year patterns - see below).</p>
*
* <p>Since 3.2, FastDateFormat supports parsing as well as printing.</p>
*
* <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent
* time zones in RFC822 format (eg. {@code +0800} or {@code -1100}).
* This pattern letter can be used here (on all JDK versions).</p>
*
* <p>In addition, the pattern {@code 'ZZ'} has been made to represent
* ISO8601 full format time zones (eg. {@code +08:00} or {@code -11:00}).
* This introduces a minor incompatibility with Java 1.4, but at a gain of
* useful functionality.</p>
*
* <p>Javadoc cites for the year pattern: <i>For formatting, if the number of
* pattern letters is 2, the year is truncated to 2 digits; otherwise it is
* interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or
* 'YYY' will be formatted as '2003', while it was '03' in former Java
* versions. FastDateFormat implements the behavior of Java 7.</p>
*
* @since 2.0
* @version $Id: FastDateFormat.java 1572877 2014-02-28 08:42:25Z britter $
*/
public class FastDateFormat extends Format implements DateParser, DatePrinter {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 2L;
/**
* 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;
private static final FormatCache<FastDateFormat> cache = new FormatCache<FastDateFormat>() {
@Override
protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return new FastDateFormat(pattern, timeZone, locale);
}
};
private final FastDatePrinter printer;
private final FastDateParser parser;
//-----------------------------------------------------------------------
/**
* <p>Gets a formatter instance using the default pattern in the
* default locale.</p>
*
* @return a date/time formatter
*/
public static FastDateFormat getInstance() {
return cache.getInstance();
}
/**
* <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(final String pattern) {
return cache.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(final String pattern, final TimeZone timeZone) {
return cache.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(final String pattern, final Locale locale) {
return cache.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}
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return cache.getInstance(pattern, timeZone, locale);
}
//-----------------------------------------------------------------------
/**
* <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(final int style) {
return cache.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(final int style, final Locale locale) {
return cache.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(final int style, final TimeZone timeZone) {
return cache.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 FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) {
return cache.getDateInstance(style, timeZone, locale);
}
//-----------------------------------------------------------------------
/**
* <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(final int style) {
return cache.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(final int style, final Locale locale) {
return cache.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(final int style, final TimeZone timeZone) {
return cache.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 FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) {
return cache.getTimeInstance(style, timeZone, locale);
}
//-----------------------------------------------------------------------
/**
* <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(final int dateStyle, final int timeStyle) {
return cache.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(final int dateStyle, final int timeStyle, final Locale locale) {
return cache.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(final int dateStyle, final int timeStyle, final 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 FastDateFormat getDateTimeInstance(
final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
}
// Constructor
//-----------------------------------------------------------------------
/**
* <p>Constructs a new FastDateFormat.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale to use
* @throws NullPointerException if pattern, timeZone, or locale is null.
*/
protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) {
this(pattern, timeZone, locale, null);
}
// Constructor
//-----------------------------------------------------------------------
/**
* <p>Constructs a new FastDateFormat.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale to use
* @param centuryStart The start of the 100 year period to use as the "default century" for 2 digit year parsing. If centuryStart is null, defaults to now - 80 years
* @throws NullPointerException if pattern, timeZone, or locale is null.
*/
protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
printer = new FastDatePrinter(pattern, timeZone, locale);
parser = new FastDateParser(pattern, timeZone, locale, centuryStart);
}
// Format methods
//-----------------------------------------------------------------------
/**
* <p>Formats a {@code Date}, {@code Calendar} or
* {@code Long} (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
*/
@Override
public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
return printer.format(obj, toAppendTo, pos);
}
/**
* <p>Formats a millisecond {@code long} value.</p>
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/
@Override
public String format(final long millis) {
return printer.format(millis);
}
/**
* <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @return the formatted string
*/
@Override
public String format(final Date date) {
return printer.format(date);
}
/**
* <p>Formats a {@code Calendar} object.</p>
*
* @param calendar the calendar to format
* @return the formatted string
*/
@Override
public String format(final Calendar calendar) {
return printer.format(calendar);
}
/**
* <p>Formats a millisecond {@code long} value into the
* supplied {@code StringBuffer}.</p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 2.1
*/
@Override
public StringBuffer format(final long millis, final StringBuffer buf) {
return printer.format(millis, buf);
}
/**
* <p>Formats a {@code Date} object into the
* supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
*/
@Override
public StringBuffer format(final Date date, final StringBuffer buf) {
return printer.format(date, buf);
}
/**
* <p>Formats a {@code Calendar} object into the
* supplied {@code StringBuffer}.</p>
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
*/
@Override
public StringBuffer format(final Calendar calendar, final StringBuffer buf) {
return printer.format(calendar, buf);
}
// Parsing
//-----------------------------------------------------------------------
/* (non-Javadoc)
* @see DateParser#parse(java.lang.String)
*/
@Override
public Date parse(final String source) throws ParseException {
return parser.parse(source);
}
/* (non-Javadoc)
* @see DateParser#parse(java.lang.String, java.text.ParsePosition)
*/
@Override
public Date parse(final String source, final ParsePosition pos) {
return parser.parse(source, pos);
}
/* (non-Javadoc)
* @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
*/
@Override
public Object parseObject(final String source, final ParsePosition pos) {
return parser.parseObject(source, pos);
}
// Accessors
//-----------------------------------------------------------------------
/**
* <p>Gets the pattern used by this formatter.</p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
@Override
public String getPattern() {
return printer.getPattern();
}
/**
* <p>Gets the time zone used by this formatter.</p>
* <p/>
* <p>This zone is always used for {@code Date} formatting. </p>
*
* @return the time zone
*/
@Override
public TimeZone getTimeZone() {
return printer.getTimeZone();
}
/**
* <p>Gets the locale used by this formatter.</p>
*
* @return the locale
*/
@Override
public Locale getLocale() {
return printer.getLocale();
}
/**
* <p>Gets an estimate for the maximum string length that the
* formatter will produce.</p>
* <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 printer.getMaxLengthEstimate();
}
// Basics
//-----------------------------------------------------------------------
/**
* <p>Compares two objects for equality.</p>
*
* @param obj the object to compare to
* @return {@code true} if equal
*/
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof FastDateFormat)) {
return false;
}
final FastDateFormat other = (FastDateFormat) obj;
// no need to check parser, as it has same invariants as printer
return printer.equals(other.printer);
}
/**
* <p>Returns a hashcode compatible with equals.</p>
*
* @return a hashcode compatible with equals
*/
@Override
public int hashCode() {
return printer.hashCode();
}
/**
* <p>Gets a debugging string version of this formatter.</p>
*
* @return a debugging string
*/
@Override
public String toString() {
return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]";
}
/**
* <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(final Calendar calendar, final StringBuffer buf) {
return printer.applyRules(calendar, buf);
}
}
/*
* 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.telegram.android.time;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <p>FastDateParser is a fast and thread-safe version of
* {@link java.text.SimpleDateFormat}.</p>
*
* <p>This class can be used as a direct replacement for
* <code>SimpleDateFormat</code> in most parsing 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 has closed the
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4228335">bug</a>/RFE.
* </p>
*
* <p>Only parsing is supported, but all patterns are compatible with
* SimpleDateFormat.</p>
*
* <p>Timing tests indicate this class is as about as fast as SimpleDateFormat
* in single thread applications and about 25% faster in multi-thread applications.</p>
*
* @version $Id: FastDateParser.java 1572877 2014-02-28 08:42:25Z britter $
* @since 3.2
*/
public class FastDateParser implements DateParser, Serializable {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 2L;
static final Locale JAPANESE_IMPERIAL = new Locale("ja", "JP", "JP");
// defining fields
private final String pattern;
private final TimeZone timeZone;
private final Locale locale;
private final int century;
private final int startYear;
// derived fields
private transient Pattern parsePattern;
private transient Strategy[] strategies;
// dynamic fields to communicate with Strategy
private transient String currentFormatField;
private transient Strategy nextStrategy;
/**
* <p>Constructs a new FastDateParser.</p>
*
* @param pattern non-null {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale
*/
protected FastDateParser(final String pattern, final TimeZone timeZone, final Locale locale) {
this(pattern, timeZone, locale, null);
}
/**
* <p>Constructs a new FastDateParser.</p>
*
* @param pattern non-null {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale
* @param centuryStart The start of the century for 2 digit year parsing
* @since 3.3
*/
protected FastDateParser(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
this.pattern = pattern;
this.timeZone = timeZone;
this.locale = locale;
final Calendar definingCalendar = Calendar.getInstance(timeZone, locale);
int centuryStartYear;
if (centuryStart != null) {
definingCalendar.setTime(centuryStart);
centuryStartYear = definingCalendar.get(Calendar.YEAR);
} else if (locale.equals(JAPANESE_IMPERIAL)) {
centuryStartYear = 0;
} else {
// from 80 years ago to 20 years from now
definingCalendar.setTime(new Date());
centuryStartYear = definingCalendar.get(Calendar.YEAR) - 80;
}
century = centuryStartYear / 100 * 100;
startYear = centuryStartYear - century;
init(definingCalendar);
}
/**
* Initialize derived fields from defining fields.
* This is called from constructor and from readObject (de-serialization)
*
* @param definingCalendar the {@link java.util.Calendar} instance used to initialize this FastDateParser
*/
private void init(Calendar definingCalendar) {
final StringBuilder regex = new StringBuilder();
final List<Strategy> collector = new ArrayList<Strategy>();
final Matcher patternMatcher = formatPattern.matcher(pattern);
if (!patternMatcher.lookingAt()) {
throw new IllegalArgumentException(
"Illegal pattern character '" + pattern.charAt(patternMatcher.regionStart()) + "'");
}
currentFormatField = patternMatcher.group();
Strategy currentStrategy = getStrategy(currentFormatField, definingCalendar);
for (; ; ) {
patternMatcher.region(patternMatcher.end(), patternMatcher.regionEnd());
if (!patternMatcher.lookingAt()) {
nextStrategy = null;
break;
}
final String nextFormatField = patternMatcher.group();
nextStrategy = getStrategy(nextFormatField, definingCalendar);
if (currentStrategy.addRegex(this, regex)) {
collector.add(currentStrategy);
}
currentFormatField = nextFormatField;
currentStrategy = nextStrategy;
}
if (patternMatcher.regionStart() != patternMatcher.regionEnd()) {
throw new IllegalArgumentException("Failed to parse \"" + pattern + "\" ; gave up at index " + patternMatcher.regionStart());
}
if (currentStrategy.addRegex(this, regex)) {
collector.add(currentStrategy);
}
currentFormatField = null;
strategies = collector.toArray(new Strategy[collector.size()]);
parsePattern = Pattern.compile(regex.toString());
}
// Accessors
//-----------------------------------------------------------------------
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#getPattern()
*/
@Override
public String getPattern() {
return pattern;
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#getTimeZone()
*/
@Override
public TimeZone getTimeZone() {
return timeZone;
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#getLocale()
*/
@Override
public Locale getLocale() {
return locale;
}
/**
* Returns the generated pattern (for testing purposes).
*
* @return the generated pattern
*/
Pattern getParsePattern() {
return parsePattern;
}
// Basics
//-----------------------------------------------------------------------
/**
* <p>Compare another object for equality with this object.</p>
*
* @param obj the object to compare to
* @return <code>true</code>if equal to this instance
*/
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof FastDateParser)) {
return false;
}
final FastDateParser other = (FastDateParser) obj;
return pattern.equals(other.pattern)
&& timeZone.equals(other.timeZone)
&& locale.equals(other.locale);
}
/**
* <p>Return a hashcode compatible with equals.</p>
*
* @return a hashcode compatible with equals
*/
@Override
public int hashCode() {
return pattern.hashCode() + 13 * (timeZone.hashCode() + 13 * locale.hashCode());
}
/**
* <p>Get a string version of this formatter.</p>
*
* @return a debugging string
*/
@Override
public String toString() {
return "FastDateParser[" + pattern + "," + locale + "," + timeZone.getID() + "]";
}
// Serializing
//-----------------------------------------------------------------------
/**
* Create the object after serialization. This implementation reinitializes the
* transient properties.
*
* @param in ObjectInputStream from which the object is being deserialized.
* @throws IOException if there is an IO issue.
* @throws ClassNotFoundException if a class cannot be found.
*/
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
final Calendar definingCalendar = Calendar.getInstance(timeZone, locale);
init(definingCalendar);
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#parseObject(java.lang.String)
*/
@Override
public Object parseObject(final String source) throws ParseException {
return parse(source);
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#parse(java.lang.String)
*/
@Override
public Date parse(final String source) throws ParseException {
final Date date = parse(source, new ParsePosition(0));
if (date == null) {
// Add a note re supported date range
if (locale.equals(JAPANESE_IMPERIAL)) {
throw new ParseException(
"(The " + locale + " locale does not support dates before 1868 AD)\n" +
"Unparseable date: \"" + source + "\" does not match " + parsePattern.pattern(), 0);
}
throw new ParseException("Unparseable date: \"" + source + "\" does not match " + parsePattern.pattern(), 0);
}
return date;
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#parseObject(java.lang.String, java.text.ParsePosition)
*/
@Override
public Object parseObject(final String source, final ParsePosition pos) {
return parse(source, pos);
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#parse(java.lang.String, java.text.ParsePosition)
*/
@Override
public Date parse(final String source, final ParsePosition pos) {
final int offset = pos.getIndex();
final Matcher matcher = parsePattern.matcher(source.substring(offset));
if (!matcher.lookingAt()) {
return null;
}
// timing tests indicate getting new instance is 19% faster than cloning
final Calendar cal = Calendar.getInstance(timeZone, locale);
cal.clear();
for (int i = 0; i < strategies.length; ) {
final Strategy strategy = strategies[i++];
strategy.setCalendar(this, cal, matcher.group(i));
}
pos.setIndex(offset + matcher.end());
return cal.getTime();
}
// Support for strategies
//-----------------------------------------------------------------------
/**
* Escape constant fields into regular expression
*
* @param regex The destination regex
* @param value The source field
* @param unquote If true, replace two success quotes ('') with single quote (')
* @return The <code>StringBuilder</code>
*/
private static StringBuilder escapeRegex(final StringBuilder regex, final String value, final boolean unquote) {
regex.append("\\Q");
for (int i = 0; i < value.length(); ++i) {
char c = value.charAt(i);
switch (c) {
case '\'':
if (unquote) {
if (++i == value.length()) {
return regex;
}
c = value.charAt(i);
}
break;
case '\\':
if (++i == value.length()) {
break;
}
/*
* If we have found \E, we replace it with \E\\E\Q, i.e. we stop the quoting,
* quote the \ in \E, then restart the quoting.
*
* Otherwise we just output the two characters.
* In each case the initial \ needs to be output and the final char is done at the end
*/
regex.append(c); // we always want the original \
c = value.charAt(i); // Is it followed by E ?
if (c == 'E') { // \E detected
regex.append("E\\\\E\\"); // see comment above
c = 'Q'; // appended below
}
break;
default:
break;
}
regex.append(c);
}
regex.append("\\E");
return regex;
}
/**
* Get the short and long values displayed for a field
*
* @param field The field of interest
* @param definingCalendar The calendar to obtain the short and long values
* @param locale The locale of display names
* @return A Map of the field key / value pairs
*/
private static Map<String, Integer> getDisplayNames(final int field, final Calendar definingCalendar, final Locale locale) {
return definingCalendar.getDisplayNames(field, Calendar.ALL_STYLES, locale);
}
/**
* Adjust dates to be within appropriate century
*
* @param twoDigitYear The year to adjust
* @return A value between centuryStart(inclusive) to centuryStart+100(exclusive)
*/
private int adjustYear(final int twoDigitYear) {
int trial = century + twoDigitYear;
return twoDigitYear >= startYear ? trial : trial + 100;
}
/**
* Is the next field a number?
*
* @return true, if next field will be a number
*/
boolean isNextNumber() {
return nextStrategy != null && nextStrategy.isNumber();
}
/**
* What is the width of the current field?
*
* @return The number of characters in the current format field
*/
int getFieldWidth() {
return currentFormatField.length();
}
/**
* A strategy to parse a single field from the parsing pattern
*/
private static abstract class Strategy {
/**
* Is this field a number?
* The default implementation returns false.
*
* @return true, if field is a number
*/
boolean isNumber() {
return false;
}
/**
* Set the Calendar with the parsed field.
* <p/>
* The default implementation does nothing.
*
* @param parser The parser calling this strategy
* @param cal The <code>Calendar</code> to set
* @param value The parsed field to translate and set in cal
*/
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
}
/**
* Generate a <code>Pattern</code> regular expression to the <code>StringBuilder</code>
* which will accept this field
*
* @param parser The parser calling this strategy
* @param regex The <code>StringBuilder</code> to append to
* @return true, if this field will set the calendar;
* false, if this field is a constant value
*/
abstract boolean addRegex(FastDateParser parser, StringBuilder regex);
}
/**
* A <code>Pattern</code> to parse the user supplied SimpleDateFormat pattern
*/
private static final Pattern formatPattern = Pattern.compile(
"D+|E+|F+|G+|H+|K+|M+|S+|W+|Z+|a+|d+|h+|k+|m+|s+|w+|y+|z+|''|'[^']++(''[^']*+)*+'|[^'A-Za-z]++");
/**
* Obtain a Strategy given a field from a SimpleDateFormat pattern
*
* @param formatField A sub-sequence of the SimpleDateFormat pattern
* @param definingCalendar The calendar to obtain the short and long values
* @return The Strategy that will handle parsing for the field
*/
private Strategy getStrategy(final String formatField, final Calendar definingCalendar) {
switch (formatField.charAt(0)) {
case '\'':
if (formatField.length() > 2) {
return new CopyQuotedStrategy(formatField.substring(1, formatField.length() - 1));
}
//$FALL-THROUGH$
default:
return new CopyQuotedStrategy(formatField);
case 'D':
return DAY_OF_YEAR_STRATEGY;
case 'E':
return getLocaleSpecificStrategy(Calendar.DAY_OF_WEEK, definingCalendar);
case 'F':
return DAY_OF_WEEK_IN_MONTH_STRATEGY;
case 'G':
return getLocaleSpecificStrategy(Calendar.ERA, definingCalendar);
case 'H':
return MODULO_HOUR_OF_DAY_STRATEGY;
case 'K':
return HOUR_STRATEGY;
case 'M':
return formatField.length() >= 3 ? getLocaleSpecificStrategy(Calendar.MONTH, definingCalendar) : NUMBER_MONTH_STRATEGY;
case 'S':
return MILLISECOND_STRATEGY;
case 'W':
return WEEK_OF_MONTH_STRATEGY;
case 'a':
return getLocaleSpecificStrategy(Calendar.AM_PM, definingCalendar);
case 'd':
return DAY_OF_MONTH_STRATEGY;
case 'h':
return MODULO_HOUR_STRATEGY;
case 'k':
return HOUR_OF_DAY_STRATEGY;
case 'm':
return MINUTE_STRATEGY;
case 's':
return SECOND_STRATEGY;
case 'w':
return WEEK_OF_YEAR_STRATEGY;
case 'y':
return formatField.length() > 2 ? LITERAL_YEAR_STRATEGY : ABBREVIATED_YEAR_STRATEGY;
case 'Z':
case 'z':
return getLocaleSpecificStrategy(Calendar.ZONE_OFFSET, definingCalendar);
}
}
@SuppressWarnings("unchecked") // OK because we are creating an array with no entries
private static final ConcurrentMap<Locale, Strategy>[] caches = new ConcurrentMap[Calendar.FIELD_COUNT];
/**
* Get a cache of Strategies for a particular field
*
* @param field The Calendar field
* @return a cache of Locale to Strategy
*/
private static ConcurrentMap<Locale, Strategy> getCache(final int field) {
synchronized (caches) {
if (caches[field] == null) {
caches[field] = new ConcurrentHashMap<Locale, Strategy>(3);
}
return caches[field];
}
}
/**
* Construct a Strategy that parses a Text field
*
* @param field The Calendar field
* @param definingCalendar The calendar to obtain the short and long values
* @return a TextStrategy for the field and Locale
*/
private Strategy getLocaleSpecificStrategy(final int field, final Calendar definingCalendar) {
final ConcurrentMap<Locale, Strategy> cache = getCache(field);
Strategy strategy = cache.get(locale);
if (strategy == null) {
strategy = field == Calendar.ZONE_OFFSET
? new TimeZoneStrategy(locale)
: new TextStrategy(field, definingCalendar, locale);
final Strategy inCache = cache.putIfAbsent(locale, strategy);
if (inCache != null) {
return inCache;
}
}
return strategy;
}
/**
* A strategy that copies the static or quoted field in the parsing pattern
*/
private static class CopyQuotedStrategy extends Strategy {
private final String formatField;
/**
* Construct a Strategy that ensures the formatField has literal text
*
* @param formatField The literal text to match
*/
CopyQuotedStrategy(final String formatField) {
this.formatField = formatField;
}
/**
* {@inheritDoc}
*/
@Override
boolean isNumber() {
char c = formatField.charAt(0);
if (c == '\'') {
c = formatField.charAt(1);
}
return Character.isDigit(c);
}
/**
* {@inheritDoc}
*/
@Override
boolean addRegex(final FastDateParser parser, final StringBuilder regex) {
escapeRegex(regex, formatField, true);
return false;
}
}
/**
* A strategy that handles a text field in the parsing pattern
*/
private static class TextStrategy extends Strategy {
private final int field;
private final Map<String, Integer> keyValues;
/**
* Construct a Strategy that parses a Text field
*
* @param field The Calendar field
* @param definingCalendar The Calendar to use
* @param locale The Locale to use
*/
TextStrategy(final int field, final Calendar definingCalendar, final Locale locale) {
this.field = field;
this.keyValues = getDisplayNames(field, definingCalendar, locale);
}
/**
* {@inheritDoc}
*/
@Override
boolean addRegex(final FastDateParser parser, final StringBuilder regex) {
regex.append('(');
for (final String textKeyValue : keyValues.keySet()) {
escapeRegex(regex, textKeyValue, false).append('|');
}
regex.setCharAt(regex.length() - 1, ')');
return true;
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
final Integer iVal = keyValues.get(value);
if (iVal == null) {
final StringBuilder sb = new StringBuilder(value);
sb.append(" not in (");
for (final String textKeyValue : keyValues.keySet()) {
sb.append(textKeyValue).append(' ');
}
sb.setCharAt(sb.length() - 1, ')');
throw new IllegalArgumentException(sb.toString());
}
cal.set(field, iVal.intValue());
}
}
/**
* A strategy that handles a number field in the parsing pattern
*/
private static class NumberStrategy extends Strategy {
private final int field;
/**
* Construct a Strategy that parses a Number field
*
* @param field The Calendar field
*/
NumberStrategy(final int field) {
this.field = field;
}
/**
* {@inheritDoc}
*/
@Override
boolean isNumber() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
boolean addRegex(final FastDateParser parser, final StringBuilder regex) {
// See LANG-954: We use {Nd} rather than {IsNd} because Android does not support the Is prefix
if (parser.isNextNumber()) {
regex.append("(\\p{Nd}{").append(parser.getFieldWidth()).append("}+)");
} else {
regex.append("(\\p{Nd}++)");
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
cal.set(field, modify(Integer.parseInt(value)));
}
/**
* Make any modifications to parsed integer
*
* @param iValue The parsed integer
* @return The modified value
*/
int modify(final int iValue) {
return iValue;
}
}
private static final Strategy ABBREVIATED_YEAR_STRATEGY = new NumberStrategy(Calendar.YEAR) {
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
int iValue = Integer.parseInt(value);
if (iValue < 100) {
iValue = parser.adjustYear(iValue);
}
cal.set(Calendar.YEAR, iValue);
}
};
/**
* A strategy that handles a timezone field in the parsing pattern
*/
private static class TimeZoneStrategy extends Strategy {
private final String validTimeZoneChars;
private final SortedMap<String, TimeZone> tzNames = new TreeMap<String, TimeZone>(String.CASE_INSENSITIVE_ORDER);
/**
* Index of zone id
*/
private static final int ID = 0;
/**
* Index of the long name of zone in standard time
*/
private static final int LONG_STD = 1;
/**
* Index of the short name of zone in standard time
*/
private static final int SHORT_STD = 2;
/**
* Index of the long name of zone in daylight saving time
*/
private static final int LONG_DST = 3;
/**
* Index of the short name of zone in daylight saving time
*/
private static final int SHORT_DST = 4;
/**
* Construct a Strategy that parses a TimeZone
*
* @param locale The Locale
*/
TimeZoneStrategy(final Locale locale) {
final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings();
for (String[] zone : zones) {
if (zone[ID].startsWith("GMT")) {
continue;
}
final TimeZone tz = TimeZone.getTimeZone(zone[ID]);
if (!tzNames.containsKey(zone[LONG_STD])) {
tzNames.put(zone[LONG_STD], tz);
}
if (!tzNames.containsKey(zone[SHORT_STD])) {
tzNames.put(zone[SHORT_STD], tz);
}
if (tz.useDaylightTime()) {
if (!tzNames.containsKey(zone[LONG_DST])) {
tzNames.put(zone[LONG_DST], tz);
}
if (!tzNames.containsKey(zone[SHORT_DST])) {
tzNames.put(zone[SHORT_DST], tz);
}
}
}
final StringBuilder sb = new StringBuilder();
sb.append("(GMT[+\\-]\\d{0,1}\\d{2}|[+\\-]\\d{2}:?\\d{2}|");
for (final String id : tzNames.keySet()) {
escapeRegex(sb, id, false).append('|');
}
sb.setCharAt(sb.length() - 1, ')');
validTimeZoneChars = sb.toString();
}
/**
* {@inheritDoc}
*/
@Override
boolean addRegex(final FastDateParser parser, final StringBuilder regex) {
regex.append(validTimeZoneChars);
return true;
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
TimeZone tz;
if (value.charAt(0) == '+' || value.charAt(0) == '-') {
tz = TimeZone.getTimeZone("GMT" + value);
} else if (value.startsWith("GMT")) {
tz = TimeZone.getTimeZone(value);
} else {
tz = tzNames.get(value);
if (tz == null) {
throw new IllegalArgumentException(value + " is not a supported timezone name");
}
}
cal.setTimeZone(tz);
}
}
private static final Strategy NUMBER_MONTH_STRATEGY = new NumberStrategy(Calendar.MONTH) {
@Override
int modify(final int iValue) {
return iValue - 1;
}
};
private static final Strategy LITERAL_YEAR_STRATEGY = new NumberStrategy(Calendar.YEAR);
private static final Strategy WEEK_OF_YEAR_STRATEGY = new NumberStrategy(Calendar.WEEK_OF_YEAR);
private static final Strategy WEEK_OF_MONTH_STRATEGY = new NumberStrategy(Calendar.WEEK_OF_MONTH);
private static final Strategy DAY_OF_YEAR_STRATEGY = new NumberStrategy(Calendar.DAY_OF_YEAR);
private static final Strategy DAY_OF_MONTH_STRATEGY = new NumberStrategy(Calendar.DAY_OF_MONTH);
private static final Strategy DAY_OF_WEEK_IN_MONTH_STRATEGY = new NumberStrategy(Calendar.DAY_OF_WEEK_IN_MONTH);
private static final Strategy HOUR_OF_DAY_STRATEGY = new NumberStrategy(Calendar.HOUR_OF_DAY);
private static final Strategy MODULO_HOUR_OF_DAY_STRATEGY = new NumberStrategy(Calendar.HOUR_OF_DAY) {
@Override
int modify(final int iValue) {
return iValue % 24;
}
};
private static final Strategy MODULO_HOUR_STRATEGY = new NumberStrategy(Calendar.HOUR) {
@Override
int modify(final int iValue) {
return iValue % 12;
}
};
private static final Strategy HOUR_STRATEGY = new NumberStrategy(Calendar.HOUR);
private static final Strategy MINUTE_STRATEGY = new NumberStrategy(Calendar.MINUTE);
private static final Strategy SECOND_STRATEGY = new NumberStrategy(Calendar.SECOND);
private static final Strategy MILLISECOND_STRATEGY = new NumberStrategy(Calendar.MILLISECOND);
}
/* /*
* This is the source code of Telegram for Android v. 1.3.2. * Licensed to the Apache Software Foundation (ASF) under one or more
* It is licensed under GNU GPL v. 2 or later. * contributor license agreements. See the NOTICE file distributed with
* You should have received a copy of the license in this archive (see LICENSE). * 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
* *
* Copyright Nikolai Kudashov, 2013. * 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.telegram.android; package org.telegram.android.time;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.Serializable;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.DateFormatSymbols; import java.text.DateFormatSymbols;
import java.text.FieldPosition; import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/** /**
* <p>FastDateFormat is a fast and thread-safe version of * <p>FastDatePrinter is a fast and thread-safe version of
* {@link java.text.SimpleDateFormat}.</p> * {@link java.text.SimpleDateFormat}.</p>
* *
* <p>This class can be used as a direct replacement to * <p>This class can be used as a direct replacement to
* <code>SimpleDateFormat</code> in most formatting situations. * {@code SimpleDateFormat} in most formatting situations.
* This class is especially useful in multi-threaded server environments. * This class is especially useful in multi-threaded server environments.
* <code>SimpleDateFormat</code> is not thread-safe in any JDK version, * {@code SimpleDateFormat} is not thread-safe in any JDK version,
* nor will it be as Sun have closed the bug/RFE. * nor will it be as Sun have closed the bug/RFE.
* </p> * </p>
* *
* <p>Only formatting is supported, but all patterns are compatible with * <p>Only formatting is supported, but all patterns are compatible with
* SimpleDateFormat (except time zones - see below).</p> * SimpleDateFormat (except time zones and some year patterns - see below).</p>
* *
* <p>Java 1.4 introduced a new pattern letter, <code>'Z'</code>, to represent * <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent
* time zones in RFC822 format (eg. <code>+0800</code> or <code>-1100</code>). * time zones in RFC822 format (eg. {@code +0800} or {@code -1100}).
* This pattern letter can be used here (on all JDK versions).</p> * 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 * <p>In addition, the pattern {@code 'ZZ'} has been made to represent
* ISO8601 full format time zones (eg. <code>+08:00</code> or <code>-11:00</code>). * ISO8601 full format time zones (eg. {@code +08:00} or {@code -11:00}).
* This introduces a minor incompatibility with Java 1.4, but at a gain of * This introduces a minor incompatibility with Java 1.4, but at a gain of
* useful functionality.</p> * useful functionality.</p>
* *
* @author TeaTrove project * <p>Javadoc cites for the year pattern: <i>For formatting, if the number of
* @author Brian S O'Neill * pattern letters is 2, the year is truncated to 2 digits; otherwise it is
* @author Sean Schofield * interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or
* @author Gary Gregory * 'YYY' will be formatted as '2003', while it was '03' in former Java
* @author Stephen Colebourne * versions. FastDatePrinter implements the behavior of Java 7.</p>
* @author Nikolay Metchev *
* @since 2.0 * @version $Id: FastDatePrinter.java 1567799 2014-02-12 23:25:58Z sebb $
* @version $Id: FastDateFormat.java 590552 2007-10-31 04:04:32Z bayard $ * @since 3.2
*/ */
public class FastDateFormat extends Format { public class FastDatePrinter implements DatePrinter, Serializable {
// A lot of the speed in this class comes from caching, but some comes // A lot of the speed in this class comes from caching, but some comes
// from the special int to StringBuffer conversion. // from the special int to StringBuffer conversion.
// //
...@@ -94,14 +101,6 @@ public class FastDateFormat extends Format { ...@@ -94,14 +101,6 @@ public class FastDateFormat extends Format {
*/ */
public static final int SHORT = DateFormat.SHORT; public static final int SHORT = DateFormat.SHORT;
private static String cDefaultPattern;
private static final Map cInstanceCache = new HashMap(7);
private static final Map cDateInstanceCache = new HashMap(7);
private static final Map cTimeInstanceCache = new HashMap(7);
private static final Map cDateTimeInstanceCache = new HashMap(7);
private static final Map cTimeZoneDisplayCache = new HashMap(7);
/** /**
* The pattern. * The pattern.
*/ */
...@@ -110,18 +109,10 @@ public class FastDateFormat extends Format { ...@@ -110,18 +109,10 @@ public class FastDateFormat extends Format {
* The time zone. * The time zone.
*/ */
private final TimeZone mTimeZone; private final TimeZone mTimeZone;
/**
* Whether the time zone overrides any on Calendars.
*/
private final boolean mTimeZoneForced;
/** /**
* The locale. * The locale.
*/ */
private final Locale mLocale; private final Locale mLocale;
/**
* Whether the locale overrides the default.
*/
private final boolean mLocaleForced;
/** /**
* The parsed rules. * The parsed rules.
*/ */
...@@ -131,423 +122,34 @@ public class FastDateFormat extends Format { ...@@ -131,423 +122,34 @@ public class FastDateFormat extends Format {
*/ */
private transient int mMaxLengthEstimate; private transient int mMaxLengthEstimate;
// Constructor
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/**
* <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) {
locale = Locale.getDefault();
}
key = new Pair(key, locale);
FastDateFormat format = (FastDateFormat) cDateInstanceCache.get(key);
if (format == null) {
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) {
locale = Locale.getDefault();
}
key = new Pair(key, locale);
FastDateFormat format = (FastDateFormat) cDateTimeInstanceCache.get(key);
if (format == null) {
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> * <p>Constructs a new FastDatePrinter.</p>
* *
* @return the default pattern * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale to use
* @throws NullPointerException if pattern, timeZone, or locale is null.
*/ */
private static synchronized String getDefaultPattern() { protected FastDatePrinter(final String pattern, final TimeZone timeZone, final Locale locale) {
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; mPattern = pattern;
mTimeZoneForced = (timeZone != null);
if (timeZone == null) {
timeZone = TimeZone.getDefault();
}
mTimeZone = timeZone; mTimeZone = timeZone;
mLocaleForced = (locale != null);
if (locale == null) {
locale = Locale.getDefault();
}
mLocale = locale; mLocale = locale;
init();
} }
/** /**
* <p>Initializes the instance for first use.</p> * <p>Initializes the instance for first use.</p>
*/ */
protected void init() { private void init() {
List rulesList = parsePattern(); final List<Rule> rulesList = parsePattern();
mRules = (Rule[]) rulesList.toArray(new Rule[rulesList.size()]); mRules = rulesList.toArray(new Rule[rulesList.size()]);
int len = 0; int len = 0;
for (int i=mRules.length; --i >= 0; ) { for (int i = mRules.length; --i >= 0; ) {
len += mRules[i].estimateLength(); len += mRules[i].estimateLength();
} }
...@@ -556,48 +158,49 @@ public class FastDateFormat extends Format { ...@@ -556,48 +158,49 @@ public class FastDateFormat extends Format {
// Parse the pattern // Parse the pattern
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* <p>Returns a list of Rules given a pattern.</p> * <p>Returns a list of Rules given a pattern.</p>
* *
* @return a <code>List</code> of Rule objects * @return a {@code List} of Rule objects
* @throws IllegalArgumentException if pattern is invalid * @throws IllegalArgumentException if pattern is invalid
*/ */
protected List parsePattern() { protected List<Rule> parsePattern() {
DateFormatSymbols symbols = new DateFormatSymbols(mLocale); final DateFormatSymbols symbols = new DateFormatSymbols(mLocale);
List rules = new ArrayList(); final List<Rule> rules = new ArrayList<Rule>();
String[] ERAs = symbols.getEras(); final String[] ERAs = symbols.getEras();
String[] months = symbols.getMonths(); final String[] months = symbols.getMonths();
String[] shortMonths = symbols.getShortMonths(); final String[] shortMonths = symbols.getShortMonths();
String[] weekdays = symbols.getWeekdays(); final String[] weekdays = symbols.getWeekdays();
String[] shortWeekdays = symbols.getShortWeekdays(); final String[] shortWeekdays = symbols.getShortWeekdays();
String[] AmPmStrings = symbols.getAmPmStrings(); final String[] AmPmStrings = symbols.getAmPmStrings();
int length = mPattern.length(); final int length = mPattern.length();
int[] indexRef = new int[1]; final int[] indexRef = new int[1];
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
indexRef[0] = i; indexRef[0] = i;
String token = parseToken(mPattern, indexRef); final String token = parseToken(mPattern, indexRef);
i = indexRef[0]; i = indexRef[0];
int tokenLen = token.length(); final int tokenLen = token.length();
if (tokenLen == 0) { if (tokenLen == 0) {
break; break;
} }
Rule rule; Rule rule;
char c = token.charAt(0); final char c = token.charAt(0);
switch (c) { switch (c) {
case 'G': // era designator (text) case 'G': // era designator (text)
rule = new TextField(Calendar.ERA, ERAs); rule = new TextField(Calendar.ERA, ERAs);
break; break;
case 'y': // year (number) case 'y': // year (number)
if (tokenLen >= 4) { if (tokenLen == 2) {
rule = selectNumberRule(Calendar.YEAR, tokenLen);
} else {
rule = TwoDigitYearField.INSTANCE; rule = TwoDigitYearField.INSTANCE;
} else {
rule = selectNumberRule(Calendar.YEAR, tokenLen < 4 ? 4 : tokenLen);
} }
break; break;
case 'M': // month in year (text and number) case 'M': // month in year (text and number)
...@@ -655,9 +258,9 @@ public class FastDateFormat extends Format { ...@@ -655,9 +258,9 @@ public class FastDateFormat extends Format {
break; break;
case 'z': // time zone (text) case 'z': // time zone (text)
if (tokenLen >= 4) { if (tokenLen >= 4) {
rule = new TimeZoneNameRule(mTimeZone, mTimeZoneForced, mLocale, TimeZone.LONG); rule = new TimeZoneNameRule(mTimeZone, mLocale, TimeZone.LONG);
} else { } else {
rule = new TimeZoneNameRule(mTimeZone, mTimeZoneForced, mLocale, TimeZone.SHORT); rule = new TimeZoneNameRule(mTimeZone, mLocale, TimeZone.SHORT);
} }
break; break;
case 'Z': // time zone (value) case 'Z': // time zone (value)
...@@ -668,7 +271,7 @@ public class FastDateFormat extends Format { ...@@ -668,7 +271,7 @@ public class FastDateFormat extends Format {
} }
break; break;
case '\'': // literal text case '\'': // literal text
String sub = token.substring(1); final String sub = token.substring(1);
if (sub.length() == 1) { if (sub.length() == 1) {
rule = new CharacterLiteral(sub.charAt(0)); rule = new CharacterLiteral(sub.charAt(0));
} else { } else {
...@@ -692,11 +295,11 @@ public class FastDateFormat extends Format { ...@@ -692,11 +295,11 @@ public class FastDateFormat extends Format {
* @param indexRef index references * @param indexRef index references
* @return parsed token * @return parsed token
*/ */
protected String parseToken(String pattern, int[] indexRef) { protected String parseToken(final String pattern, final int[] indexRef) {
StringBuffer buf = new StringBuffer(); final StringBuilder buf = new StringBuilder();
int i = indexRef[0]; int i = indexRef[0];
int length = pattern.length(); final int length = pattern.length();
char c = pattern.charAt(i); char c = pattern.charAt(i);
if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') { if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') {
...@@ -705,7 +308,7 @@ public class FastDateFormat extends Format { ...@@ -705,7 +308,7 @@ public class FastDateFormat extends Format {
buf.append(c); buf.append(c);
while (i + 1 < length) { while (i + 1 < length) {
char peek = pattern.charAt(i + 1); final char peek = pattern.charAt(i + 1);
if (peek == c) { if (peek == c) {
buf.append(c); buf.append(c);
i++; i++;
...@@ -751,7 +354,7 @@ public class FastDateFormat extends Format { ...@@ -751,7 +354,7 @@ public class FastDateFormat extends Format {
* @param padding the padding required * @param padding the padding required
* @return a new rule with the correct padding * @return a new rule with the correct padding
*/ */
protected NumberRule selectNumberRule(int field, int padding) { protected NumberRule selectNumberRule(final int field, final int padding) {
switch (padding) { switch (padding) {
case 1: case 1:
return new UnpaddedNumberField(field); return new UnpaddedNumberField(field);
...@@ -764,16 +367,18 @@ public class FastDateFormat extends Format { ...@@ -764,16 +367,18 @@ public class FastDateFormat extends Format {
// Format methods // Format methods
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* <p>Formats a <code>Date</code>, <code>Calendar</code> or * <p>Formats a {@code Date}, {@code Calendar} or
* <code>Long</code> (milliseconds) object.</p> * {@code Long} (milliseconds) object.</p>
* *
* @param obj the object to format * @param obj the object to format
* @param toAppendTo the buffer to append to * @param toAppendTo the buffer to append to
* @param pos the position - ignored * @param pos the position - ignored
* @return the buffer passed in * @return the buffer passed in
*/ */
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { @Override
public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
if (obj instanceof Date) { if (obj instanceof Date) {
return format((Date) obj, toAppendTo); return format((Date) obj, toAppendTo);
} else if (obj instanceof Calendar) { } else if (obj instanceof Calendar) {
...@@ -786,79 +391,77 @@ public class FastDateFormat extends Format { ...@@ -786,79 +391,77 @@ public class FastDateFormat extends Format {
} }
} }
/** /* (non-Javadoc)
* <p>Formats a millisecond <code>long</code> value.</p> * @see org.apache.commons.lang3.time.DatePrinter#format(long)
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/ */
public String format(long millis) { @Override
return format(new Date(millis)); public String format(final long millis) {
final Calendar c = newCalendar(); // hard code GregorianCalendar
c.setTimeInMillis(millis);
return applyRulesToString(c);
} }
/** /**
* <p>Formats a <code>Date</code> object.</p> * Creates a String representation of the given Calendar by applying the rules of this printer to it.
* *
* @param date the date to format * @param c the Calender to apply the rules to.
* @return the formatted string * @return a String representation of the given Calendar.
*/ */
public String format(Date date) { private String applyRulesToString(final Calendar c) {
Calendar c = new GregorianCalendar(mTimeZone);
c.setTime(date);
return applyRules(c, new StringBuffer(mMaxLengthEstimate)).toString(); return applyRules(c, new StringBuffer(mMaxLengthEstimate)).toString();
} }
/** /**
* <p>Formats a <code>Calendar</code> object.</p> * Creation method for ne calender instances.
* *
* @param calendar the calendar to format * @return a new Calendar instance.
* @return the formatted string
*/ */
public String format(Calendar calendar) { private GregorianCalendar newCalendar() {
// hard code GregorianCalendar
return new GregorianCalendar(mTimeZone, mLocale);
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DatePrinter#format(java.util.Date)
*/
@Override
public String format(final Date date) {
final Calendar c = newCalendar(); // hard code GregorianCalendar
c.setTime(date);
return applyRulesToString(c);
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DatePrinter#format(java.util.Calendar)
*/
@Override
public String format(final Calendar calendar) {
return format(calendar, new StringBuffer(mMaxLengthEstimate)).toString(); return format(calendar, new StringBuffer(mMaxLengthEstimate)).toString();
} }
/** /* (non-Javadoc)
* <p>Formats a milliseond <code>long</code> value into the * @see org.apache.commons.lang3.time.DatePrinter#format(long, java.lang.StringBuffer)
* 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) { @Override
public StringBuffer format(final long millis, final StringBuffer buf) {
return format(new Date(millis), buf); return format(new Date(millis), buf);
} }
/** /* (non-Javadoc)
* <p>Formats a <code>Date</code> object into the * @see org.apache.commons.lang3.time.DatePrinter#format(java.util.Date, java.lang.StringBuffer)
* 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) { @Override
Calendar c = new GregorianCalendar(mTimeZone); public StringBuffer format(final Date date, final StringBuffer buf) {
final Calendar c = newCalendar(); // hard code GregorianCalendar
c.setTime(date); c.setTime(date);
return applyRules(c, buf); return applyRules(c, buf);
} }
/** /* (non-Javadoc)
* <p>Formats a <code>Calendar</code> object into the * @see org.apache.commons.lang3.time.DatePrinter#format(java.util.Calendar, java.lang.StringBuffer)
* 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) { @Override
if (mTimeZoneForced) { public StringBuffer format(final Calendar calendar, final StringBuffer buf) {
calendar = (Calendar) calendar.clone();
calendar.setTimeZone(mTimeZone);
}
return applyRules(calendar, buf); return applyRules(calendar, buf);
} }
...@@ -870,71 +473,35 @@ public class FastDateFormat extends Format { ...@@ -870,71 +473,35 @@ public class FastDateFormat extends Format {
* @param buf the buffer to format into * @param buf the buffer to format into
* @return the specified string buffer * @return the specified string buffer
*/ */
protected StringBuffer applyRules(Calendar calendar, StringBuffer buf) { protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) {
Rule[] rules = mRules; for (final Rule rule : mRules) {
int len = mRules.length; rule.appendTo(buf, calendar);
for (int i = 0; i < len; i++) {
rules[i].appendTo(buf, calendar);
} }
return buf; return buf;
} }
// Parsing
//-----------------------------------------------------------------------
/**
* <p>Parsing is 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 // Accessors
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /* (non-Javadoc)
* <p>Gets the pattern used by this formatter.</p> * @see org.apache.commons.lang3.time.DatePrinter#getPattern()
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/ */
@Override
public String getPattern() { public String getPattern() {
return mPattern; return mPattern;
} }
/** /* (non-Javadoc)
* <p>Gets the time zone used by this formatter.</p> * @see org.apache.commons.lang3.time.DatePrinter#getTimeZone()
*
* <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
*/ */
@Override
public TimeZone getTimeZone() { public TimeZone getTimeZone() {
return mTimeZone; return mTimeZone;
} }
/** /* (non-Javadoc)
* <p>Returns <code>true</code> if the time zone of the * @see org.apache.commons.lang3.time.DatePrinter#getLocale()
* 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
*/ */
@Override
public Locale getLocale() { public Locale getLocale() {
return mLocale; return mLocale;
} }
...@@ -942,7 +509,7 @@ public class FastDateFormat extends Format { ...@@ -942,7 +509,7 @@ public class FastDateFormat extends Format {
/** /**
* <p>Gets an estimate for the maximum string length that the * <p>Gets an estimate for the maximum string length that the
* formatter will produce.</p> * formatter will produce.</p>
* * <p/>
* <p>The actual formatted length will almost always be less than or * <p>The actual formatted length will almost always be less than or
* equal to this amount.</p> * equal to this amount.</p>
* *
...@@ -954,22 +521,22 @@ public class FastDateFormat extends Format { ...@@ -954,22 +521,22 @@ public class FastDateFormat extends Format {
// Basics // Basics
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* <p>Compares two objects for equality.</p> * <p>Compares two objects for equality.</p>
* *
* @param obj the object to compare to * @param obj the object to compare to
* @return <code>true</code> if equal * @return {@code true} if equal
*/ */
public boolean equals(Object obj) { @Override
if (!(obj instanceof FastDateFormat)) { public boolean equals(final Object obj) {
if (!(obj instanceof FastDatePrinter)) {
return false; return false;
} }
FastDateFormat other = (FastDateFormat) obj; final FastDatePrinter other = (FastDatePrinter) obj;
return (mPattern == other.mPattern || mPattern.equals(other.mPattern)) && return mPattern.equals(other.mPattern)
(mTimeZone == other.mTimeZone || mTimeZone.equals(other.mTimeZone)) && && mTimeZone.equals(other.mTimeZone)
(mLocale == other.mLocale || mLocale.equals(other.mLocale)) && && mLocale.equals(other.mLocale);
(mTimeZoneForced == other.mTimeZoneForced) &&
(mLocaleForced == other.mLocaleForced);
} }
/** /**
...@@ -977,14 +544,9 @@ public class FastDateFormat extends Format { ...@@ -977,14 +544,9 @@ public class FastDateFormat extends Format {
* *
* @return a hashcode compatible with equals * @return a hashcode compatible with equals
*/ */
@Override
public int hashCode() { public int hashCode() {
int total = 0; return mPattern.hashCode() + 13 * (mTimeZone.hashCode() + 13 * mLocale.hashCode());
total += mPattern.hashCode();
total += mTimeZone.hashCode();
total += (mTimeZoneForced ? 1 : 0);
total += mLocale.hashCode();
total += (mLocaleForced ? 1 : 0);
return total;
} }
/** /**
...@@ -992,12 +554,14 @@ public class FastDateFormat extends Format { ...@@ -992,12 +554,14 @@ public class FastDateFormat extends Format {
* *
* @return a debugging string * @return a debugging string
*/ */
@Override
public String toString() { public String toString() {
return "FastDateFormat[" + mPattern + "]"; return "FastDatePrinter[" + mPattern + "," + mLocale + "," + mTimeZone.getID() + "]";
} }
// Serializing // Serializing
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Create the object after serialization. This implementation reinitializes the * Create the object after serialization. This implementation reinitializes the
* transient properties. * transient properties.
...@@ -1006,13 +570,14 @@ public class FastDateFormat extends Format { ...@@ -1006,13 +570,14 @@ public class FastDateFormat extends Format {
* @throws IOException if there is an IO issue. * @throws IOException if there is an IO issue.
* @throws ClassNotFoundException if a class cannot be found. * @throws ClassNotFoundException if a class cannot be found.
*/ */
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); in.defaultReadObject();
init(); init();
} }
// Rules // Rules
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* <p>Inner class defining a rule.</p> * <p>Inner class defining a rule.</p>
*/ */
...@@ -1053,18 +618,19 @@ public class FastDateFormat extends Format { ...@@ -1053,18 +618,19 @@ public class FastDateFormat extends Format {
private final char mValue; private final char mValue;
/** /**
* Constructs a new instance of <code>CharacterLiteral</code> * Constructs a new instance of {@code CharacterLiteral}
* to hold the specified value. * to hold the specified value.
* *
* @param value the character literal * @param value the character literal
*/ */
CharacterLiteral(char value) { CharacterLiteral(final char value) {
mValue = value; mValue = value;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
return 1; return 1;
} }
...@@ -1072,7 +638,8 @@ public class FastDateFormat extends Format { ...@@ -1072,7 +638,8 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
buffer.append(mValue); buffer.append(mValue);
} }
} }
...@@ -1084,18 +651,19 @@ public class FastDateFormat extends Format { ...@@ -1084,18 +651,19 @@ public class FastDateFormat extends Format {
private final String mValue; private final String mValue;
/** /**
* Constructs a new instance of <code>StringLiteral</code> * Constructs a new instance of {@code StringLiteral}
* to hold the specified value. * to hold the specified value.
* *
* @param value the string literal * @param value the string literal
*/ */
StringLiteral(String value) { StringLiteral(final String value) {
mValue = value; mValue = value;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
return mValue.length(); return mValue.length();
} }
...@@ -1103,7 +671,8 @@ public class FastDateFormat extends Format { ...@@ -1103,7 +671,8 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
buffer.append(mValue); buffer.append(mValue);
} }
} }
...@@ -1116,13 +685,13 @@ public class FastDateFormat extends Format { ...@@ -1116,13 +685,13 @@ public class FastDateFormat extends Format {
private final String[] mValues; private final String[] mValues;
/** /**
* Constructs an instance of <code>TextField</code> * Constructs an instance of {@code TextField}
* with the specified field and values. * with the specified field and values.
* *
* @param field the field * @param field the field
* @param values the field values * @param values the field values
*/ */
TextField(int field, String[] values) { TextField(final int field, final String[] values) {
mField = field; mField = field;
mValues = values; mValues = values;
} }
...@@ -1130,10 +699,11 @@ public class FastDateFormat extends Format { ...@@ -1130,10 +699,11 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
int max = 0; int max = 0;
for (int i=mValues.length; --i >= 0; ) { for (int i = mValues.length; --i >= 0; ) {
int len = mValues[i].length(); final int len = mValues[i].length();
if (len > max) { if (len > max) {
max = len; max = len;
} }
...@@ -1144,7 +714,8 @@ public class FastDateFormat extends Format { ...@@ -1144,7 +714,8 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
buffer.append(mValues[calendar.get(mField)]); buffer.append(mValues[calendar.get(mField)]);
} }
} }
...@@ -1153,22 +724,21 @@ public class FastDateFormat extends Format { ...@@ -1153,22 +724,21 @@ public class FastDateFormat extends Format {
* <p>Inner class to output an unpadded number.</p> * <p>Inner class to output an unpadded number.</p>
*/ */
private static class UnpaddedNumberField implements NumberRule { private static class UnpaddedNumberField implements NumberRule {
static final UnpaddedNumberField INSTANCE_YEAR = new UnpaddedNumberField(Calendar.YEAR);
private final int mField; private final int mField;
/** /**
* Constructs an instance of <code>UnpadedNumberField</code> with the specified field. * Constructs an instance of {@code UnpadedNumberField} with the specified field.
* *
* @param field the field * @param field the field
*/ */
UnpaddedNumberField(int field) { UnpaddedNumberField(final int field) {
mField = field; mField = field;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
return 4; return 4;
} }
...@@ -1176,19 +746,21 @@ public class FastDateFormat extends Format { ...@@ -1176,19 +746,21 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
appendTo(buffer, calendar.get(mField)); appendTo(buffer, calendar.get(mField));
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public final void appendTo(StringBuffer buffer, int value) { @Override
public final void appendTo(final StringBuffer buffer, final int value) {
if (value < 10) { if (value < 10) {
buffer.append((char)(value + '0')); buffer.append((char) (value + '0'));
} else if (value < 100) { } else if (value < 100) {
buffer.append((char)(value / 10 + '0')); buffer.append((char) (value / 10 + '0'));
buffer.append((char)(value % 10 + '0')); buffer.append((char) (value % 10 + '0'));
} else { } else {
buffer.append(Integer.toString(value)); buffer.append(Integer.toString(value));
} }
...@@ -1202,8 +774,7 @@ public class FastDateFormat extends Format { ...@@ -1202,8 +774,7 @@ public class FastDateFormat extends Format {
static final UnpaddedMonthField INSTANCE = new UnpaddedMonthField(); static final UnpaddedMonthField INSTANCE = new UnpaddedMonthField();
/** /**
* Constructs an instance of <code>UnpaddedMonthField</code>. * Constructs an instance of {@code UnpaddedMonthField}.
*
*/ */
UnpaddedMonthField() { UnpaddedMonthField() {
super(); super();
...@@ -1212,6 +783,7 @@ public class FastDateFormat extends Format { ...@@ -1212,6 +783,7 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
return 2; return 2;
} }
...@@ -1219,19 +791,21 @@ public class FastDateFormat extends Format { ...@@ -1219,19 +791,21 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
appendTo(buffer, calendar.get(Calendar.MONTH) + 1); appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public final void appendTo(StringBuffer buffer, int value) { @Override
public final void appendTo(final StringBuffer buffer, final int value) {
if (value < 10) { if (value < 10) {
buffer.append((char)(value + '0')); buffer.append((char) (value + '0'));
} else { } else {
buffer.append((char)(value / 10 + '0')); buffer.append((char) (value / 10 + '0'));
buffer.append((char)(value % 10 + '0')); buffer.append((char) (value % 10 + '0'));
} }
} }
} }
...@@ -1244,12 +818,12 @@ public class FastDateFormat extends Format { ...@@ -1244,12 +818,12 @@ public class FastDateFormat extends Format {
private final int mSize; private final int mSize;
/** /**
* Constructs an instance of <code>PaddedNumberField</code>. * Constructs an instance of {@code PaddedNumberField}.
* *
* @param field the field * @param field the field
* @param size size of the output field * @param size size of the output field
*/ */
PaddedNumberField(int field, int size) { PaddedNumberField(final int field, final int size) {
if (size < 3) { if (size < 3) {
// Should use UnpaddedNumberField or TwoDigitNumberField. // Should use UnpaddedNumberField or TwoDigitNumberField.
throw new IllegalArgumentException(); throw new IllegalArgumentException();
...@@ -1261,6 +835,7 @@ public class FastDateFormat extends Format { ...@@ -1261,6 +835,7 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
return 4; return 4;
} }
...@@ -1268,26 +843,27 @@ public class FastDateFormat extends Format { ...@@ -1268,26 +843,27 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
appendTo(buffer, calendar.get(mField)); appendTo(buffer, calendar.get(mField));
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public final void appendTo(StringBuffer buffer, int value) { @Override
public final void appendTo(final StringBuffer buffer, final int value) {
if (value < 100) { if (value < 100) {
for (int i = mSize; --i >= 2; ) { for (int i = mSize; --i >= 2; ) {
buffer.append('0'); buffer.append('0');
} }
buffer.append((char)(value / 10 + '0')); buffer.append((char) (value / 10 + '0'));
buffer.append((char)(value % 10 + '0')); buffer.append((char) (value % 10 + '0'));
} else { } else {
int digits; int digits;
if (value < 1000) { if (value < 1000) {
digits = 3; digits = 3;
} else { } else {
//Validate.isTrue(value > -1, "Negative values should not be possible", value);
digits = Integer.toString(value).length(); digits = Integer.toString(value).length();
} }
for (int i = mSize; --i >= digits; ) { for (int i = mSize; --i >= digits; ) {
...@@ -1305,17 +881,18 @@ public class FastDateFormat extends Format { ...@@ -1305,17 +881,18 @@ public class FastDateFormat extends Format {
private final int mField; private final int mField;
/** /**
* Constructs an instance of <code>TwoDigitNumberField</code> with the specified field. * Constructs an instance of {@code TwoDigitNumberField} with the specified field.
* *
* @param field the field * @param field the field
*/ */
TwoDigitNumberField(int field) { TwoDigitNumberField(final int field) {
mField = field; mField = field;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
return 2; return 2;
} }
...@@ -1323,17 +900,19 @@ public class FastDateFormat extends Format { ...@@ -1323,17 +900,19 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
appendTo(buffer, calendar.get(mField)); appendTo(buffer, calendar.get(mField));
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public final void appendTo(StringBuffer buffer, int value) { @Override
public final void appendTo(final StringBuffer buffer, final int value) {
if (value < 100) { if (value < 100) {
buffer.append((char)(value / 10 + '0')); buffer.append((char) (value / 10 + '0'));
buffer.append((char)(value % 10 + '0')); buffer.append((char) (value % 10 + '0'));
} else { } else {
buffer.append(Integer.toString(value)); buffer.append(Integer.toString(value));
} }
...@@ -1347,7 +926,7 @@ public class FastDateFormat extends Format { ...@@ -1347,7 +926,7 @@ public class FastDateFormat extends Format {
static final TwoDigitYearField INSTANCE = new TwoDigitYearField(); static final TwoDigitYearField INSTANCE = new TwoDigitYearField();
/** /**
* Constructs an instance of <code>TwoDigitYearField</code>. * Constructs an instance of {@code TwoDigitYearField}.
*/ */
TwoDigitYearField() { TwoDigitYearField() {
super(); super();
...@@ -1356,6 +935,7 @@ public class FastDateFormat extends Format { ...@@ -1356,6 +935,7 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
return 2; return 2;
} }
...@@ -1363,16 +943,18 @@ public class FastDateFormat extends Format { ...@@ -1363,16 +943,18 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
appendTo(buffer, calendar.get(Calendar.YEAR) % 100); appendTo(buffer, calendar.get(Calendar.YEAR) % 100);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public final void appendTo(StringBuffer buffer, int value) { @Override
buffer.append((char)(value / 10 + '0')); public final void appendTo(final StringBuffer buffer, final int value) {
buffer.append((char)(value % 10 + '0')); buffer.append((char) (value / 10 + '0'));
buffer.append((char) (value % 10 + '0'));
} }
} }
...@@ -1383,7 +965,7 @@ public class FastDateFormat extends Format { ...@@ -1383,7 +965,7 @@ public class FastDateFormat extends Format {
static final TwoDigitMonthField INSTANCE = new TwoDigitMonthField(); static final TwoDigitMonthField INSTANCE = new TwoDigitMonthField();
/** /**
* Constructs an instance of <code>TwoDigitMonthField</code>. * Constructs an instance of {@code TwoDigitMonthField}.
*/ */
TwoDigitMonthField() { TwoDigitMonthField() {
super(); super();
...@@ -1392,6 +974,7 @@ public class FastDateFormat extends Format { ...@@ -1392,6 +974,7 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
return 2; return 2;
} }
...@@ -1399,16 +982,18 @@ public class FastDateFormat extends Format { ...@@ -1399,16 +982,18 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
appendTo(buffer, calendar.get(Calendar.MONTH) + 1); appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public final void appendTo(StringBuffer buffer, int value) { @Override
buffer.append((char)(value / 10 + '0')); public final void appendTo(final StringBuffer buffer, final int value) {
buffer.append((char)(value % 10 + '0')); buffer.append((char) (value / 10 + '0'));
buffer.append((char) (value % 10 + '0'));
} }
} }
...@@ -1419,18 +1004,19 @@ public class FastDateFormat extends Format { ...@@ -1419,18 +1004,19 @@ public class FastDateFormat extends Format {
private final NumberRule mRule; private final NumberRule mRule;
/** /**
* Constructs an instance of <code>TwelveHourField</code> with the specified * Constructs an instance of {@code TwelveHourField} with the specified
* <code>NumberRule</code>. * {@code NumberRule}.
* *
* @param rule the rule * @param rule the rule
*/ */
TwelveHourField(NumberRule rule) { TwelveHourField(final NumberRule rule) {
mRule = rule; mRule = rule;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
return mRule.estimateLength(); return mRule.estimateLength();
} }
...@@ -1438,7 +1024,8 @@ public class FastDateFormat extends Format { ...@@ -1438,7 +1024,8 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
int value = calendar.get(Calendar.HOUR); int value = calendar.get(Calendar.HOUR);
if (value == 0) { if (value == 0) {
value = calendar.getLeastMaximum(Calendar.HOUR) + 1; value = calendar.getLeastMaximum(Calendar.HOUR) + 1;
...@@ -1449,7 +1036,8 @@ public class FastDateFormat extends Format { ...@@ -1449,7 +1036,8 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, int value) { @Override
public void appendTo(final StringBuffer buffer, final int value) {
mRule.appendTo(buffer, value); mRule.appendTo(buffer, value);
} }
} }
...@@ -1461,18 +1049,19 @@ public class FastDateFormat extends Format { ...@@ -1461,18 +1049,19 @@ public class FastDateFormat extends Format {
private final NumberRule mRule; private final NumberRule mRule;
/** /**
* Constructs an instance of <code>TwentyFourHourField</code> with the specified * Constructs an instance of {@code TwentyFourHourField} with the specified
* <code>NumberRule</code>. * {@code NumberRule}.
* *
* @param rule the rule * @param rule the rule
*/ */
TwentyFourHourField(NumberRule rule) { TwentyFourHourField(final NumberRule rule) {
mRule = rule; mRule = rule;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
return mRule.estimateLength(); return mRule.estimateLength();
} }
...@@ -1480,7 +1069,8 @@ public class FastDateFormat extends Format { ...@@ -1480,7 +1069,8 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
int value = calendar.get(Calendar.HOUR_OF_DAY); int value = calendar.get(Calendar.HOUR_OF_DAY);
if (value == 0) { if (value == 0) {
value = calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1; value = calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1;
...@@ -1491,82 +1081,93 @@ public class FastDateFormat extends Format { ...@@ -1491,82 +1081,93 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, int value) { @Override
public void appendTo(final StringBuffer buffer, final int value) {
mRule.appendTo(buffer, value); mRule.appendTo(buffer, value);
} }
} }
//-----------------------------------------------------------------------
private static final ConcurrentMap<TimeZoneDisplayKey, String> cTimeZoneDisplayCache =
new ConcurrentHashMap<TimeZoneDisplayKey, String>(7);
/**
* <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} or {@code TimeZone.SHORT}
* @param locale the locale to use
* @return the textual name of the time zone
*/
static String getTimeZoneDisplay(final TimeZone tz, final boolean daylight, final int style, final Locale locale) {
final TimeZoneDisplayKey key = new TimeZoneDisplayKey(tz, daylight, style, locale);
String value = cTimeZoneDisplayCache.get(key);
if (value == null) {
// This is a very slow call, so cache the results.
value = tz.getDisplayName(daylight, style, locale);
final String prior = cTimeZoneDisplayCache.putIfAbsent(key, value);
if (prior != null) {
value = prior;
}
}
return value;
}
/** /**
* <p>Inner class to output a time zone name.</p> * <p>Inner class to output a time zone name.</p>
*/ */
private static class TimeZoneNameRule implements Rule { private static class TimeZoneNameRule implements Rule {
private final TimeZone mTimeZone;
private final boolean mTimeZoneForced;
private final Locale mLocale; private final Locale mLocale;
private final int mStyle; private final int mStyle;
private final String mStandard; private final String mStandard;
private final String mDaylight; private final String mDaylight;
/** /**
* Constructs an instance of <code>TimeZoneNameRule</code> with the specified properties. * Constructs an instance of {@code TimeZoneNameRule} with the specified properties.
* *
* @param timeZone the time zone * @param timeZone the time zone
* @param timeZoneForced if <code>true</code> the time zone is forced into standard and daylight
* @param locale the locale * @param locale the locale
* @param style the style * @param style the style
*/ */
TimeZoneNameRule(TimeZone timeZone, boolean timeZoneForced, Locale locale, int style) { TimeZoneNameRule(final TimeZone timeZone, final Locale locale, final int style) {
mTimeZone = timeZone;
mTimeZoneForced = timeZoneForced;
mLocale = locale; mLocale = locale;
mStyle = style; mStyle = style;
if (timeZoneForced) {
mStandard = getTimeZoneDisplay(timeZone, false, style, locale); mStandard = getTimeZoneDisplay(timeZone, false, style, locale);
mDaylight = getTimeZoneDisplay(timeZone, true, style, locale); mDaylight = getTimeZoneDisplay(timeZone, true, style, locale);
} else {
mStandard = null;
mDaylight = null;
}
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
if (mTimeZoneForced) { // We have no access to the Calendar object that will be passed to
// appendTo so base estimate on the TimeZone passed to the
// constructor
return Math.max(mStandard.length(), mDaylight.length()); return Math.max(mStandard.length(), mDaylight.length());
} else if (mStyle == TimeZone.SHORT) {
return 4;
} else {
return 40;
}
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
if (mTimeZoneForced) { public void appendTo(final StringBuffer buffer, final Calendar calendar) {
if (mTimeZone.useDaylightTime() && calendar.get(Calendar.DST_OFFSET) != 0) { final TimeZone zone = calendar.getTimeZone();
buffer.append(mDaylight); if (zone.useDaylightTime()
&& calendar.get(Calendar.DST_OFFSET) != 0) {
buffer.append(getTimeZoneDisplay(zone, true, mStyle, mLocale));
} else { } else {
buffer.append(mStandard); buffer.append(getTimeZoneDisplay(zone, false, mStyle, mLocale));
}
} 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> * <p>Inner class to output a time zone as a number {@code +/-HHMM}
* or <code>+/-HH:MM</code>.</p> * or {@code +/-HH:MM}.</p>
*/ */
private static class TimeZoneNumberRule implements Rule { private static class TimeZoneNumberRule implements Rule {
static final TimeZoneNumberRule INSTANCE_COLON = new TimeZoneNumberRule(true); static final TimeZoneNumberRule INSTANCE_COLON = new TimeZoneNumberRule(true);
...@@ -1575,17 +1176,18 @@ public class FastDateFormat extends Format { ...@@ -1575,17 +1176,18 @@ public class FastDateFormat extends Format {
final boolean mColon; final boolean mColon;
/** /**
* Constructs an instance of <code>TimeZoneNumberRule</code> with the specified properties. * Constructs an instance of {@code TimeZoneNumberRule} with the specified properties.
* *
* @param colon add colon between HH and MM in the output if <code>true</code> * @param colon add colon between HH and MM in the output if {@code true}
*/ */
TimeZoneNumberRule(boolean colon) { TimeZoneNumberRule(final boolean colon) {
mColon = colon; mColon = colon;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int estimateLength() { public int estimateLength() {
return 5; return 5;
} }
...@@ -1593,7 +1195,8 @@ public class FastDateFormat extends Format { ...@@ -1593,7 +1195,8 @@ public class FastDateFormat extends Format {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void appendTo(StringBuffer buffer, Calendar calendar) { @Override
public void appendTo(final StringBuffer buffer, final Calendar calendar) {
int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET); int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
if (offset < 0) { if (offset < 0) {
...@@ -1603,21 +1206,22 @@ public class FastDateFormat extends Format { ...@@ -1603,21 +1206,22 @@ public class FastDateFormat extends Format {
buffer.append('+'); buffer.append('+');
} }
int hours = offset / (60 * 60 * 1000); final int hours = offset / (60 * 60 * 1000);
buffer.append((char)(hours / 10 + '0')); buffer.append((char) (hours / 10 + '0'));
buffer.append((char)(hours % 10 + '0')); buffer.append((char) (hours % 10 + '0'));
if (mColon) { if (mColon) {
buffer.append(':'); buffer.append(':');
} }
int minutes = offset / (60 * 1000) - 60 * hours; final int minutes = offset / (60 * 1000) - 60 * hours;
buffer.append((char)(minutes / 10 + '0')); buffer.append((char) (minutes / 10 + '0'));
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> * <p>Inner class that acts as a compound key for time zone names.</p>
*/ */
...@@ -1627,39 +1231,42 @@ public class FastDateFormat extends Format { ...@@ -1627,39 +1231,42 @@ public class FastDateFormat extends Format {
private final Locale mLocale; private final Locale mLocale;
/** /**
* Constructs an instance of <code>TimeZoneDisplayKey</code> with the specified properties. * Constructs an instance of {@code TimeZoneDisplayKey} with the specified properties.
* *
* @param timeZone the time zone * @param timeZone the time zone
* @param daylight adjust the style for daylight saving time if <code>true</code> * @param daylight adjust the style for daylight saving time if {@code true}
* @param style the timezone style * @param style the timezone style
* @param locale the timezone locale * @param locale the timezone locale
*/ */
TimeZoneDisplayKey(TimeZone timeZone, TimeZoneDisplayKey(final TimeZone timeZone,
boolean daylight, int style, Locale locale) { final boolean daylight, final int style, final Locale locale) {
mTimeZone = timeZone; mTimeZone = timeZone;
if (daylight) { if (daylight) {
style |= 0x80000000; mStyle = style | 0x80000000;
} } else {
mStyle = style; mStyle = style;
}
mLocale = locale; mLocale = locale;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public int hashCode() { public int hashCode() {
return mStyle * 31 + mLocale.hashCode(); return (mStyle * 31 + mLocale.hashCode()) * 31 + mTimeZone.hashCode();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public boolean equals(Object obj) { @Override
public boolean equals(final Object obj) {
if (this == obj) { if (this == obj) {
return true; return true;
} }
if (obj instanceof TimeZoneDisplayKey) { if (obj instanceof TimeZoneDisplayKey) {
TimeZoneDisplayKey other = (TimeZoneDisplayKey)obj; final TimeZoneDisplayKey other = (TimeZoneDisplayKey) obj;
return return
mTimeZone.equals(other.mTimeZone) && mTimeZone.equals(other.mTimeZone) &&
mStyle == other.mStyle && mStyle == other.mStyle &&
...@@ -1668,64 +1275,4 @@ public class FastDateFormat extends Format { ...@@ -1668,64 +1275,4 @@ public class FastDateFormat extends Format {
return false; 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;
/**
* Constructs an instance of <code>Pair</code> to hold the specified objects.
* @param obj1 one object in the pair
* @param obj2 second object in the pair
*/
public Pair(Object obj1, Object obj2) {
mObj1 = obj1;
mObj2 = obj2;
}
/**
* {@inheritDoc}
*/
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));
}
/**
* {@inheritDoc}
*/
public int hashCode() {
return
(mObj1 == null ? 0 : mObj1.hashCode()) +
(mObj2 == null ? 0 : mObj2.hashCode());
}
/**
* {@inheritDoc}
*/
public String toString() {
return "[" + mObj1 + ':' + mObj2 + ']';
}
}
} }
/*
* 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.telegram.android.time;
import java.text.DateFormat;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* <p>FormatCache is a cache and factory for {@link Format}s.</p>
*
* @since 3.0
* @version $Id: FormatCache 892161 2009-12-18 07:21:10Z $
*/
// TODO: Before making public move from getDateTimeInstance(Integer,...) to int; or some other approach.
abstract class FormatCache<F extends Format> {
/**
* No date or no time. Used in same parameters as DateFormat.SHORT or DateFormat.LONG
*/
static final int NONE = -1;
private final ConcurrentMap<MultipartKey, F> cInstanceCache
= new ConcurrentHashMap<MultipartKey, F>(7);
private static final ConcurrentMap<MultipartKey, String> cDateTimeInstanceCache
= new ConcurrentHashMap<MultipartKey, String>(7);
/**
* <p>Gets a formatter instance using the default pattern in the
* default timezone and locale.</p>
*
* @return a date/time formatter
*/
public F getInstance() {
return getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, TimeZone.getDefault(), Locale.getDefault());
}
/**
* <p>Gets a formatter instance using the specified pattern, time zone
* and locale.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern, non-null
* @param timeZone the time zone, null means use the default TimeZone
* @param locale the locale, null means use the default Locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or <code>null</code>
*/
public F getInstance(final String pattern, TimeZone timeZone, Locale locale) {
if (pattern == null) {
throw new NullPointerException("pattern must not be null");
}
if (timeZone == null) {
timeZone = TimeZone.getDefault();
}
if (locale == null) {
locale = Locale.getDefault();
}
final MultipartKey key = new MultipartKey(pattern, timeZone, locale);
F format = cInstanceCache.get(key);
if (format == null) {
format = createInstance(pattern, timeZone, locale);
final F previousValue = cInstanceCache.putIfAbsent(key, format);
if (previousValue != null) {
// another thread snuck in and did the same work
// we should return the instance that is in ConcurrentMap
format = previousValue;
}
}
return format;
}
/**
* <p>Create a format instance using the specified pattern, time zone
* and locale.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern, this will not be null.
* @param timeZone time zone, this will not be null.
* @param locale locale, this will not be null.
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or <code>null</code>
*/
abstract protected F createInstance(String pattern, TimeZone timeZone, Locale locale);
/**
* <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, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @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
*/
// This must remain private, see LANG-884
private F getDateTimeInstance(final Integer dateStyle, final Integer timeStyle, final TimeZone timeZone, Locale locale) {
if (locale == null) {
locale = Locale.getDefault();
}
final String pattern = getPatternForStyle(dateStyle, timeStyle, locale);
return getInstance(pattern, timeZone, locale);
}
/**
* <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, null means use default Locale
* @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
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* <p>Gets a date formatter instance using the specified style,
* time zone and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @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
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateInstance(final int dateStyle, final TimeZone timeZone, Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), null, timeZone, locale);
}
/**
* <p>Gets a time formatter instance using the specified style,
* time zone and locale.</p>
*
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @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
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getTimeInstance(final int timeStyle, final TimeZone timeZone, Locale locale) {
return getDateTimeInstance(null, Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* <p>Gets a date/time format for the specified styles and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param locale The non-null locale of the desired format
* @return a localized standard date/time format
* @throws IllegalArgumentException if the Locale has no date/time pattern defined
*/
// package protected, for access from test code; do not make public or protected
static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) {
final MultipartKey key = new MultipartKey(dateStyle, timeStyle, locale);
String pattern = cDateTimeInstanceCache.get(key);
if (pattern == null) {
try {
DateFormat formatter;
if (dateStyle == null) {
formatter = DateFormat.getTimeInstance(timeStyle.intValue(), locale);
} else if (timeStyle == null) {
formatter = DateFormat.getDateInstance(dateStyle.intValue(), locale);
} else {
formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), locale);
}
pattern = ((SimpleDateFormat) formatter).toPattern();
final String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern);
if (previous != null) {
// even though it doesn't matter if another thread put the pattern
// it's still good practice to return the String instance that is
// actually in the ConcurrentMap
pattern = previous;
}
} catch (final ClassCastException ex) {
throw new IllegalArgumentException("No date time pattern for locale: " + locale);
}
}
return pattern;
}
// ----------------------------------------------------------------------
/**
* <p>Helper class to hold multi-part Map keys</p>
*/
private static class MultipartKey {
private final Object[] keys;
private int hashCode;
/**
* Constructs an instance of <code>MultipartKey</code> to hold the specified objects.
*
* @param keys the set of objects that make up the key. Each key may be null.
*/
public MultipartKey(final Object... keys) {
this.keys = keys;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(final Object obj) {
// Eliminate the usual boilerplate because
// this inner static class is only used in a generic ConcurrentHashMap
// which will not compare against other Object types
return Arrays.equals(keys, ((MultipartKey) obj).keys);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
if (hashCode == 0) {
int rc = 0;
for (final Object key : keys) {
if (key != null) {
rc = rc * 7 + key.hashCode();
}
}
hashCode = rc;
}
return hashCode;
}
}
}
...@@ -11,7 +11,7 @@ package org.telegram.messenger; ...@@ -11,7 +11,7 @@ package org.telegram.messenger;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import org.telegram.android.FastDateFormat; import org.telegram.android.time.FastDateFormat;
import org.telegram.ui.ApplicationLoader; import org.telegram.ui.ApplicationLoader;
import java.io.File; import java.io.File;
......
...@@ -89,8 +89,8 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur ...@@ -89,8 +89,8 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur
private float videoDuration = 0; private float videoDuration = 0;
private long startTime = 0; private long startTime = 0;
private long endTime = 0; private long endTime = 0;
private int audioFramesSize = 0; private long audioFramesSize = 0;
private int videoFramesSize = 0; private long videoFramesSize = 0;
private int estimatedSize = 0; private int estimatedSize = 0;
private long esimatedDuration = 0; private long esimatedDuration = 0;
private long originalSize = 0; private long originalSize = 0;
...@@ -292,7 +292,9 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur ...@@ -292,7 +292,9 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur
name.equals("OMX.ST.VFM.H264Enc") || name.equals("OMX.ST.VFM.H264Enc") ||
name.equals("OMX.Exynos.avc.enc") || name.equals("OMX.Exynos.avc.enc") ||
name.equals("OMX.MARVELL.VIDEO.HW.CODA7542ENCODER") || name.equals("OMX.MARVELL.VIDEO.HW.CODA7542ENCODER") ||
name.equals("OMX.MARVELL.VIDEO.H264ENCODER")) { name.equals("OMX.MARVELL.VIDEO.H264ENCODER") ||
name.equals("OMX.k3.video.encoder.avc") || //fix this later
name.equals("OMX.TI.DUCATI1.VIDEO.H264E")) { //fix this later
compressVideo.setVisibility(View.GONE); compressVideo.setVisibility(View.GONE);
} else { } else {
if (MediaController.selectColorFormat(codecInfo, MediaController.MIME_TYPE) == 0) { if (MediaController.selectColorFormat(codecInfo, MediaController.MIME_TYPE) == 0) {
...@@ -721,8 +723,8 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur ...@@ -721,8 +723,8 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur
for (Box box : boxes) { for (Box box : boxes) {
TrackBox trackBox = (TrackBox)box; TrackBox trackBox = (TrackBox)box;
int sampleSizes = 0; long sampleSizes = 0;
int trackBitrate = 0; long trackBitrate = 0;
try { try {
MediaBox mediaBox = trackBox.getMediaBox(); MediaBox mediaBox = trackBox.getMediaBox();
MediaHeaderBox mediaHeaderBox = mediaBox.getMediaHeaderBox(); MediaHeaderBox mediaHeaderBox = mediaBox.getMediaHeaderBox();
...@@ -738,7 +740,7 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur ...@@ -738,7 +740,7 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur
TrackHeaderBox headerBox = trackBox.getTrackHeaderBox(); TrackHeaderBox headerBox = trackBox.getTrackHeaderBox();
if (headerBox.getWidth() != 0 && headerBox.getHeight() != 0) { if (headerBox.getWidth() != 0 && headerBox.getHeight() != 0) {
trackHeaderBox = headerBox; trackHeaderBox = headerBox;
bitrate = trackBitrate / 100000 * 100000; bitrate = (int)(trackBitrate / 100000 * 100000);
if (bitrate > 900000) { if (bitrate > 900000) {
bitrate = 900000; bitrate = 900000;
} }
...@@ -768,7 +770,7 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur ...@@ -768,7 +770,7 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur
resultHeight *= scale; resultHeight *= scale;
if (bitrate != 0) { if (bitrate != 0) {
bitrate *= Math.max(0.5f, scale); bitrate *= Math.max(0.5f, scale);
videoFramesSize = (int)(bitrate / 8 * videoDuration); videoFramesSize = (long)(bitrate / 8 * videoDuration);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment