/*
* Copyright (C) 2014 Civilian Framework.
*
* Licensed under the Civilian License (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.civilian-framework.org/license.txt
*
* 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.civilian.text;
import java.util.Locale;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.text.DateFormatSymbols;
import org.civilian.util.Check;
import org.civilian.util.Date;
import org.civilian.util.StringUtil;
/**
* DateFormat provides locale dependent formatting of dates.
*/
public class DateFormat implements Serializable
{
private static final long serialVersionUID = 1L;
public static final char SYMBOL_MONTH = 'M';
public static final char SYMBOL_DAY = 'd';
public static final char SYMBOL_YEAR = 'y';
/**
* A Service which can create a date object.
*/
public static interface Factory<T>
{
public abstract T create(int year, int month, int day);
}
/**
* Creates a new DateFormat.
*/
public DateFormat(Locale locale)
{
locale_ = Check.notNull(locale, "locale");
SimpleDateFormat sdf = getSimpleDateFormat(locale);
symbols_ = sdf.getDateFormatSymbols();
String pattern = sdf.toPattern();
int len = pattern.length();
int pos = 0;
for (int i=0; i<len; i++)
{
char c = pattern.charAt(i);
if (Character.isLetter(c))
{
switch(c)
{
case SYMBOL_DAY:
dayPosition_ = pos;
break;
case SYMBOL_MONTH:
monthPosition_ = pos;
break;
case SYMBOL_YEAR:
yearPosition_ = pos;
break;
}
}
else
{
separatorSymbol_ = c;
++pos;
}
}
}
private SimpleDateFormat getSimpleDateFormat(Locale locale)
{
try
{
return (SimpleDateFormat)java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT, locale);
}
catch(ClassCastException e)
{
throw new IllegalArgumentException("cannot create a SimpleDateFormat for locale " + locale, e);
}
}
@SuppressWarnings("unused")
private SimpleDateFormat getSimpleTimeFormat(Locale locale)
{
try
{
return (SimpleDateFormat)java.text.DateFormat.getTimeInstance(java.text.DateFormat.MEDIUM, locale);
}
catch(ClassCastException e)
{
throw new IllegalArgumentException("cannot create a SimpleDateFormat for locale " + locale, e);
}
}
/**
* Returns the locale of the date format.
*/
public Locale getLocale()
{
return locale_;
}
/**
* Returns the locale dependent symbol which separates date parts
* (e.g. '.' or '/').
*/
public char getSeparatorSymbol()
{
return separatorSymbol_;
}
/**
* Returns the position (0-2) of the day value in date string.
*/
public int getDayPosition()
{
return dayPosition_;
}
/**
* Returns the position (0-2) of the month value in date string.
*/
public int getMonthPosition()
{
return monthPosition_;
}
/**
* Returns the position (0-2) of the year value in date string.
*/
public int getYearPosition()
{
return yearPosition_;
}
/**
* Returns the name of a month
* @param month the month (ranging from 1 to 12)
*/
public String getMonthName(int month)
{
return getName(symbols_.getMonths(), month - 1);
}
/**
* Returns the short name of a month
* @param month the month (ranging from 1 to 12)
*/
public String getShortMonthName(int month)
{
return getName(symbols_.getShortMonths(), month - 1);
}
/**
* Returns the name of a weekday.
* @param weekday the weekday
* @see Date#WEEKDAY_SUNDAY
*/
public String getWeekdayName(int weekday)
{
return getName(symbols_.getWeekdays(), weekday);
}
/**
* Returns the short name of a weekday
* @param weekday the weekday
* @see Date#WEEKDAY_SUNDAY
*/
public String getShortWeekdayName(int weekday)
{
return getName(symbols_.getShortWeekdays(), weekday);
}
private String getName(String names[], int index)
{
try
{
return names[index];
}
catch(ArrayIndexOutOfBoundsException e)
{
throw new IllegalArgumentException("invalid index " + index);
}
}
//------------------------------
// parse
//------------------------------
/**
* Parses a date from a string representation.
* @exception java.text.ParseException thrown if an parse error occurs
*/
public <T> T parse(Factory<T> factory, String text) throws ParseException
{
int values[] = new int[6]; // 0-2: value 3-5: length of string part
int errorPos = parseValues(text, values);
if (errorPos >= 0)
throw new ParseException("'" + text + "' has an invalid date format", errorPos);
int day = values[dayPosition_];
int month = values[monthPosition_];
int year = values[yearPosition_];
if (values[yearPosition_ + 3] == 2)
year += 2000;
try
{
return factory.create(year, month, day);
}
catch(IllegalArgumentException e)
{
throw new ParseException("'" + text + "' is an invalid date", 0);
}
}
private int parseValues(String text, int values[])
{
StringBuilder s = new StringBuilder();
int pos = 0;
int len = text.length();
for (int i=0; i<3; i++)
{
if (pos >= len)
return pos;
int p = pos;
s.setLength(0);
while (p < len)
{
char c = text.charAt(p++);
if (c == separatorSymbol_)
break;
if (c != ' ')
s.append(c);
}
try
{
values[i] = Integer.parseInt(s.toString());
values[i + 3] = s.length();
}
catch(Exception e)
{
return pos;
}
pos = p;
}
return -1; // ok
}
//------------------------------
// format
//------------------------------
/**
* Returns a string representation of a date.
*/
public String format(Date date)
{
return format(date.getYear(), date.getMonth(), date.getDay());
}
/**
* Returns a string representation of a date.
* @param date the date
* @param s a StringBuilder to hold the result
*/
public void format(Date date, StringBuilder s)
{
format(date.getYear(), date.getMonth(), date.getDay(), s);
}
/**
* Returns a string representation of a date.
* @param date the date
* @param s a StringBuilder to hold the result
* @param ignorePart exclude a date part from the result string
* @see #SYMBOL_MONTH
* @see #SYMBOL_DAY
* @see #SYMBOL_YEAR
*/
public void format(Date date, StringBuilder s, char ignorePart)
{
format(date.getYear(), date.getMonth(), date.getDay(), s, ignorePart);
}
/**
* Returns a string representation of a date.
* @param year the year (the year 2001 is specified as 2001)
* @param month the month (counting from 1 to 12)
* @param day the day (counting from 1 to 31)
*/
public String format(int year, int month, int day)
{
StringBuilder s = new StringBuilder(10);
format(year, month, day, s, '@');
return s.toString();
}
/**
* Returns a string representation of a date.
* @param year the year (the year 2001 is specified as 2001)
* @param month the month (counting from 1 to 12)
* @param day the day (counting from 1 to 31)
* @param s a StringBuilder to hold the result
*/
public void format(int year, int month, int day, StringBuilder s)
{
format(year, month, day, s, '@');
}
/**
* Returns a string representation of a date.
* @param year the year (the year 2001 is specified as 2001)
* @param month the month (counting from 1 to 12)
* @param day the day (counting from 1 to 31)
* @param s a StringBuilder to hold the result
* @param ignorePart exclude a date part from the result string
* @see #SYMBOL_MONTH
* @see #SYMBOL_DAY
* @see #SYMBOL_YEAR
*/
public void format(int year, int month, int day, StringBuilder s, char ignorePart)
{
Check.notNull(s, "StringBuilder");
int parts = 0;
for (int i=0; i<3; i++)
{
int val = -1;
int len = 2;
char symbol = '@';
if (dayPosition_ == i)
{
val = day;
symbol = SYMBOL_DAY;
}
else if (monthPosition_ == i)
{
val = month;
symbol = SYMBOL_MONTH;
}
else if (yearPosition_ == i)
{
val = year;
symbol = SYMBOL_YEAR;
len = 4;
}
if (ignorePart != symbol)
{
if ((parts > 0) && (parts <= 2))
s.append(separatorSymbol_);
s.append(StringUtil.fillLeft(String.valueOf(val), len, '0'));
parts++;
}
}
}
private int dayPosition_;
private int monthPosition_;
private int yearPosition_;
private char separatorSymbol_;
private DateFormatSymbols symbols_;
private final Locale locale_;
}