Initial revision

From-SVN: r26263
This commit is contained in:
Tom Tromey 1999-04-07 14:42:40 +00:00
parent 140fa895c6
commit ee9dd3721b
370 changed files with 173494 additions and 0 deletions

View file

@ -0,0 +1,160 @@
// BreakIterator.java - Iterate over logical breaks in text.
/* Copyright (C) 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
import java.io.Serializable;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* @author Tom Tromey <tromey@cygnus.com>
* @date March 19, 1999
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 beta from http://www.javasoft.com.
* Status: Believed complete and correct to 1.1.
*/
public abstract class BreakIterator implements Cloneable, Serializable
{
// The value was discovered by writing a test program.
public static final int DONE = -1;
protected BreakIterator ()
{
}
public abstract int current ();
public abstract int first ();
public abstract int following (int pos);
public static synchronized Locale[] getAvailableLocales ()
{
// FIXME.
return null;
}
private static BreakIterator getInstance (String type, Locale loc)
{
String className;
try
{
ResourceBundle res
= ResourceBundle.getBundle("gnu.gcj.text.LocaleData", loc);
className = res.getString(type);
}
catch (MissingResourceException x)
{
return null;
}
try
{
Class k = Class.forName(className);
return (BreakIterator) k.newInstance();
}
catch (ClassNotFoundException x1)
{
return null;
}
catch (InstantiationException x2)
{
return null;
}
catch (IllegalAccessException x3)
{
return null;
}
}
public static BreakIterator getCharacterInstance ()
{
return getCharacterInstance (Locale.getDefault());
}
public static BreakIterator getCharacterInstance (Locale loc)
{
BreakIterator r = getInstance ("CharacterIterator", loc);
if (r == null)
r = new gnu.gcj.text.CharacterBreakIterator ();
return r;
}
public static BreakIterator getLineInstance ()
{
return getLineInstance (Locale.getDefault());
}
public static BreakIterator getLineInstance (Locale loc)
{
BreakIterator r = getInstance ("LineIterator", loc);
if (r == null)
r = new gnu.gcj.text.LineBreakIterator ();
return r;
}
public static BreakIterator getSentenceInstance ()
{
return getSentenceInstance (Locale.getDefault());
}
public static BreakIterator getSentenceInstance (Locale loc)
{
BreakIterator r = getInstance ("SentenceIterator", loc);
if (r == null)
r = new gnu.gcj.text.SentenceBreakIterator ();
return r;
}
public abstract CharacterIterator getText ();
public static BreakIterator getWordInstance ()
{
return getWordInstance (Locale.getDefault());
}
public static BreakIterator getWordInstance (Locale loc)
{
BreakIterator r = getInstance ("WordIterator", loc);
if (r == null)
r = new gnu.gcj.text.WordBreakIterator ();
return r;
}
public boolean isBoundary (int pos)
{
if (pos == 0)
return true;
return following (pos - 1) == pos;
}
public abstract int last ();
public abstract int next ();
public abstract int next (int n);
public int preceding (int pos)
{
if (following (pos) == DONE)
last ();
while (previous () >= pos)
;
return current ();
}
public abstract int previous ();
public void setText (String newText)
{
setText (new StringCharacterIterator (newText));
}
public abstract void setText (CharacterIterator newText);
}

View file

@ -0,0 +1,36 @@
// CharacterIterator.java - Protocol for iterating over Unicode characters.
/* Copyright (C) 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
/**
* @author Tom Tromey <tromey@cygnus.com>
* @date February 22, 1999
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 beta from http://www.javasoft.com.
* Status: Believed complete and correct to 1.1.
*/
public interface CharacterIterator extends Cloneable
{
public abstract Object clone ();
public abstract char current ();
public abstract char first ();
public abstract int getBeginIndex ();
public abstract int getEndIndex ();
public abstract int getIndex ();
public abstract char last ();
public abstract char next ();
public abstract char previous ();
public abstract char setIndex (int idx);
public static final char DONE = '\uffff';
}

View file

@ -0,0 +1,310 @@
// ChoiceFormat.java - Formatter for `switch'-like string substitution.
/* Copyright (C) 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
import java.util.Vector;
/**
* @author Tom Tromey <tromey@cygnus.com>
* @date March 9, 1999
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 from http://www.javasoft.com.
* Status: Believed complete and correct to 1.1.
*/
public class ChoiceFormat extends NumberFormat
{
// Note: we assume the same kind of quoting rules apply here.
// This isn't explicitly documented. But for instance we accept
// '#' as a literal hash in a format string.
public void applyPattern (String newPattern)
{
int index = 0, max = newPattern.length();
Vector stringVec = new Vector ();
Vector limitVec = new Vector ();
StringBuffer buf = new StringBuffer ();
while (true)
{
// Find end of double.
int dstart = index;
while (index < max)
{
char c = newPattern.charAt(index);
if (c == '#' || c == '\u2064' || c == '<')
break;
++index;
}
if (index == max)
throw new IllegalArgumentException ("unexpected end of text");
Double d = new Double (newPattern.substring(dstart, index));
if (newPattern.charAt(index) == '<')
d = new Double (nextDouble (d.doubleValue()));
limitVec.addElement(d);
// Scan text.
++index;
buf.setLength(0);
while (index < max)
{
char c = newPattern.charAt(index);
if (c == '\'' && index < max + 1
&& newPattern.charAt(index + 1) == '\'')
{
buf.append(c);
++index;
}
else if (c == '\'' && index < max + 2)
{
buf.append(newPattern.charAt(index + 1));
index += 2;
}
else if (c == '|')
break;
else
buf.append(c);
++index;
}
stringVec.addElement(buf.toString());
if (index == max)
break;
++index;
}
strings = new String[stringVec.size()];
stringVec.copyInto(strings);
limits = new double[limitVec.size()];
for (int i = 0; i < limits.length; ++i)
{
Double d = (Double) limitVec.elementAt(i);
limits[i] = d.doubleValue();
}
}
public ChoiceFormat (String newPattern)
{
super ();
applyPattern (newPattern);
}
public ChoiceFormat (double[] limits, String[] strings)
{
super ();
setChoices (limits, strings);
}
public Object clone ()
{
return new ChoiceFormat (limits, strings);
}
public boolean equals (Object obj)
{
if (! (obj instanceof ChoiceFormat))
return false;
ChoiceFormat cf = (ChoiceFormat) obj;
if (limits.length != cf.limits.length)
return false;
for (int i = limits.length - 1; i >= 0; --i)
{
if (limits[i] != cf.limits[i]
|| !strings[i].equals(cf.strings[i]))
return false;
}
return true;
}
public StringBuffer format (long num, StringBuffer appendBuf,
FieldPosition pos)
{
return format ((double) num, appendBuf, pos);
}
public StringBuffer format (double num, StringBuffer appendBuf,
FieldPosition pos)
{
if (limits.length == 0)
return appendBuf;
int index = 0;
if (! Double.isNaN(num) && num >= limits[0])
{
for (; index < limits.length - 1; ++index)
{
if (limits[index] <= num
&& index != limits.length - 2
&& num < limits[index + 1])
break;
}
}
return appendBuf.append(strings[index]);
}
public Object[] getFormats ()
{
return (Object[]) strings.clone();
}
public double[] getLimits ()
{
return (double[]) limits.clone();
}
public int hashCode ()
{
int hash = 0;
for (int i = 0; i < limits.length; ++i)
{
long v = Double.doubleToLongBits(limits[i]);
hash ^= (v ^ (v >>> 32));
hash ^= strings[i].hashCode();
}
return hash;
}
public static final double nextDouble (double d)
{
return nextDouble (d, true);
}
public static final double nextDouble (double d, boolean next)
{
if (Double.isInfinite(d) || Double.isNaN(d))
return d;
long bits = Double.doubleToLongBits(d);
long mantMask = (1L << mantissaBits) - 1;
long mantissa = bits & mantMask;
long expMask = (1L << exponentBits) - 1;
long exponent = (bits >>> mantissaBits) & expMask;
if (next ^ (bits < 0)) // Increment magnitude
{
if (mantissa == (1L << mantissaBits) - 1)
{
mantissa = 0L;
exponent++;
// Check for absolute overflow.
if (exponent >= (1L << mantissaBits))
return (bits > 0) ? Double.POSITIVE_INFINITY
: Double.NEGATIVE_INFINITY;
}
else
mantissa++;
}
else // Decrement magnitude
{
if (exponent == 0L && mantissa == 0L)
{
// The only case where there is a change of sign
return next ? Double.MIN_VALUE : -Double.MIN_VALUE;
}
else
{
if (mantissa == 0L)
{
mantissa = (1L << mantissaBits) - 1;
exponent--;
}
else
mantissa--;
}
}
long result = bits < 0 ? 1 : 0;
result = (result << exponentBits) | exponent;
result = (result << mantissaBits) | mantissa;
return Double.longBitsToDouble(result);
}
public Number parse (String sourceStr, ParsePosition pos)
{
int index = pos.getIndex();
for (int i = 0; i < limits.length; ++i)
{
if (sourceStr.startsWith(strings[i], index))
{
pos.setIndex(index + strings[i].length());
return new Double (limits[i]);
}
}
pos.setErrorIndex(index);
return new Double (Double.NaN);
}
public static final double previousDouble (double d)
{
return nextDouble (d, false);
}
public void setChoices (double[] limits, String[] strings)
{
if (limits == null || strings == null)
throw new NullPointerException ();
if (limits.length != strings.length)
throw new IllegalArgumentException ();
this.strings = (String[]) strings.clone();
this.limits = (double[]) limits.clone();
}
private final void quoteString (StringBuffer dest, String text)
{
int max = text.length();
for (int i = 0; i < max; ++i)
{
char c = text.charAt(i);
if (c == '\'')
{
dest.append(c);
dest.append(c);
}
else if (c == '#' || c == '|' || c == '\u2064' || c == '<')
{
dest.append('\'');
dest.append(c);
dest.append('\'');
}
else
dest.append(c);
}
}
public String toPattern ()
{
StringBuffer result = new StringBuffer ();
for (int i = 0; i < limits.length; ++i)
{
result.append(limits[i]);
result.append('#');
quoteString (result, strings[i]);
}
return result.toString();
}
// Formats and limits.
private String[] strings;
private double[] limits;
// Number of mantissa bits in double.
private static final int mantissaBits = 52;
// Number of exponent bits in a double.
private static final int exponentBits = 11;
}

View file

@ -0,0 +1,301 @@
/* Copyright (C) 1998, 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
import java.util.*;
/**
* @author Per Bothner <bothner@cygnus.com>
* @date October 25, 1998.
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 beta from http://www.javasoft.com.
* Status: Mostly complete; search for FIXME to see omissions.
*/
public abstract class DateFormat extends Format implements Cloneable
{
protected Calendar calendar;
protected NumberFormat numberFormat;
// (Values determined using a test program.)
public final static int FULL = 0;
public final static int LONG = 1;
public final static int MEDIUM = 2;
public final static int SHORT = 3;
public final static int DEFAULT = MEDIUM;
public final static int ERA_FIELD = 0;
public final static int YEAR_FIELD = 1;
public final static int MONTH_FIELD = 2;
public final static int DATE_FIELD = 3;
public final static int HOUR_OF_DAY1_FIELD = 4;
public final static int HOUR_OF_DAY0_FIELD = 5;
public final static int MINUTE_FIELD = 6;
public final static int SECOND_FIELD = 7;
public final static int MILLISECOND_FIELD = 8;
public final static int DAY_OF_WEEK_FIELD = 9;
public final static int DAY_OF_YEAR_FIELD = 10;
public final static int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
public final static int WEEK_OF_YEAR_FIELD = 12;
public final static int WEEK_OF_MONTH_FIELD = 13;
public final static int AM_PM_FIELD = 14;
public final static int HOUR1_FIELD = 15;
public final static int HOUR0_FIELD = 16;
public final static int TIMEZONE_FIELD = 17;
protected DateFormat ()
{
}
public boolean equals (Object obj)
{
if (! (obj instanceof DateFormat))
return false;
DateFormat d = (DateFormat) obj;
return calendar.equals(d.calendar) && numberFormat.equals(d.numberFormat);
}
public final StringBuffer format (Object obj,
StringBuffer buf, FieldPosition pos)
{
if (obj instanceof Number)
return format (new Date(((Number) obj).longValue()), buf, pos);
return format ((Date) obj, buf, pos);
}
public final String format (Date date)
{
StringBuffer sb = new StringBuffer ();
format (date, sb, new FieldPosition (MONTH_FIELD));
return sb.toString();
}
public abstract StringBuffer format (Date date,
StringBuffer buf, FieldPosition pos);
public static Locale[] getAvailableLocales ()
{
return null; // FIXME
}
public Calendar getCalendar ()
{
return calendar;
}
private static final DateFormat computeInstance (int style, Locale loc,
boolean use_date,
boolean use_time)
{
ResourceBundle res;
try
{
res = ResourceBundle.getBundle("gnu.gcj.text.LocaleData", loc);
}
catch (MissingResourceException x)
{
res = null;
}
String pattern = null;
if (use_date)
{
String name, def;
switch (style)
{
case FULL:
name = "fullDateFormat";
def = "EEEE MMMM d, yyyy G";
break;
case LONG:
name = "longDateFormat";
def = "MMMM d, yyyy";
break;
case MEDIUM:
name = "mediumDateFormat";
def = "d-MMM-yy";
break;
case SHORT:
name = "shortDateFormat";
def = "M/d/yy";
break;
default:
throw new IllegalArgumentException ();
}
try
{
pattern = res == null ? def : res.getString(name);
}
catch (MissingResourceException x)
{
pattern = def;
}
}
if (use_time)
{
if (pattern == null)
pattern = "";
else
pattern += " ";
String name, def;
switch (style)
{
case FULL:
name = "fullTimeFormat";
def = "h:mm:ss;S 'o''clock' a z";
break;
case LONG:
name = "longTimeFormat";
def = "h:mm:ss a z";
break;
case MEDIUM:
name = "mediumTimeFormat";
def = "h:mm:ss a";
break;
case SHORT:
name = "shortTimeFormat";
def = "h:mm a";
break;
default:
throw new IllegalArgumentException ();
}
String s;
try
{
s = res == null ? def : res.getString(name);
}
catch (MissingResourceException x)
{
s = def;
}
pattern += s;
}
return new SimpleDateFormat (pattern, loc);
}
public static final DateFormat getDateInstance ()
{
return getDateInstance (DEFAULT, Locale.getDefault());
}
public static final DateFormat getDateInstance (int style)
{
return getDateInstance (style, Locale.getDefault());
}
public static final DateFormat getDateInstance (int style, Locale loc)
{
return computeInstance (style, loc, true, false);
}
public static final DateFormat getDateTimeInstance ()
{
return getDateTimeInstance (DEFAULT, Locale.getDefault());
}
public static final DateFormat getDateTimeInstance (int style)
{
return getDateTimeInstance (style, Locale.getDefault());
}
public static final DateFormat getDateTimeInstance (int style, Locale loc)
{
return computeInstance (style, loc, true, true);
}
public static final DateFormat getInstance ()
{
// JCL book says SHORT.
return getDateTimeInstance (SHORT, Locale.getDefault());
}
public NumberFormat getNumberFormat ()
{
return numberFormat;
}
public static final DateFormat getTimeInstance ()
{
return getTimeInstance (DEFAULT, Locale.getDefault());
}
public static final DateFormat getTimeInstance (int style)
{
return getTimeInstance (style, Locale.getDefault());
}
public static final DateFormat getTimeInstance (int style, Locale loc)
{
return computeInstance (style, loc, false, true);
}
public TimeZone getTimeZone ()
{
return calendar.getTimeZone();
}
public int hashCode ()
{
int hash = calendar.hashCode();
if (numberFormat != null)
hash ^= numberFormat.hashCode();
return hash;
}
public boolean isLenient ()
{
return calendar.isLenient();
}
public Date parse (String source) throws ParseException
{
ParsePosition pos = new ParsePosition(0);
Date result = parse (source, pos);
if (result == null)
{
int index = pos.getErrorIndex();
if (index < 0)
index = pos.getIndex();
throw new ParseException("invalid Date syntax", index);
}
return result;
}
public abstract Date parse (String source, ParsePosition pos);
public Object parseObject (String source, ParsePosition pos)
{
return parse(source, pos);
}
public void setCalendar (Calendar calendar)
{
this.calendar = calendar;
}
public void setLenient (boolean lenient)
{
calendar.setLenient(lenient);
}
public void setNumberFormat (NumberFormat numberFormat)
{
this.numberFormat = numberFormat;
}
public void setTimeZone (TimeZone timeZone)
{
calendar.setTimeZone(timeZone);
}
}

View file

@ -0,0 +1,265 @@
/* Copyright (C) 1998, 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* @author Per Bothner <bothner@cygnus.com>
* @date October 24, 1998.
*/
/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3.
* Status: Believed complete and correct.
*/
public class DateFormatSymbols extends Object
implements java.io.Serializable, Cloneable
{
private String[] ampms;
private String[] eras;
private String localPatternChars;
private String[] months;
private String[] shortMonths;
private String[] shortWeekdays;
private String[] weekdays;
private String[][] zoneStrings;
private static final String[] ampmsDefault = {"AM", "PM" };
private static final String[] erasDefault = {"BC", "AD" };
// localPatternCharsDefault is used by SimpleDateFormat.
protected static final String localPatternCharsDefault
= "GyMdkHmsSEDFwWahKz";
private static final String[] monthsDefault = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December", ""
};
private static final String[] shortMonthsDefault = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""
};
private static final String[] shortWeekdaysDefault = {
"", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
private static final String[] weekdaysDefault = {
"", "Sunday", "Monday", "Tuesday",
"Wednesday", "Thursday", "Friday", "Saturday"
};
private static String[][] zoneStringsDefault = {
{ "PST", "Pacific Standard Time", "PST",
/**/ "Pacific Daylight Time", "PDT", "San Francisco" },
{ "MST", "Mountain Standard Time", "MST",
/**/ "Mountain Daylight Time", "MDT", "Denver" },
{ "PNT", "Mountain Standard Time", "MST",
/**/ "Mountain Standard Time", "MST", "Phoenix" },
{ "CST", "Central Standard Time", "CST",
/**/ "Central Daylight Time", "CDT", "Chicago" },
{ "EST", "Eastern Standard Time", "EST",
/**/ "Eastern Daylight Time", "EDT", "Boston" },
{ "IET", "Eastern Standard Time", "EST",
/**/ "Eastern Standard Time", "EST", "Indianapolis" },
{ "PRT", "Atlantic Standard Time", "AST",
/**/ "Atlantic Daylight Time", "ADT", "Halifax" },
{ "HST", "Hawaii Standard Time", "HST",
/**/ "Hawaii Daylight Time", "HDT", "Honolulu" },
{ "AST", "Alaska Standard Time", "AST",
/**/ "Alaska Daylight Time", "ADT", "Anchorage" }
};
private final Object safeGetResource (ResourceBundle res,
String key, Object def)
{
if (res != null)
{
try
{
return res.getObject(key);
}
catch (MissingResourceException x)
{
}
}
return def;
}
public DateFormatSymbols (Locale locale)
{
ResourceBundle res;
try
{
res = ResourceBundle.getBundle("gnu.gcj.text.LocaleData", locale);
}
catch (MissingResourceException x)
{
res = null;
}
ampms = (String[]) safeGetResource (res, "ampm", ampmsDefault);
eras = (String[]) safeGetResource (res, "eras", erasDefault);
localPatternChars = (String) safeGetResource (res, "datePatternChars",
localPatternCharsDefault);
months = (String[]) safeGetResource (res, "months", monthsDefault);
shortMonths = (String[]) safeGetResource (res, "shortMonths",
shortMonthsDefault);
shortWeekdays = (String[]) safeGetResource (res, "shortWeekdays",
shortWeekdaysDefault);
weekdays = (String[]) safeGetResource (res, "weekdays", weekdaysDefault);
zoneStrings = (String[][]) safeGetResource (res, "zoneStrings",
zoneStringsDefault);
}
public DateFormatSymbols ()
{
this (Locale.getDefault());
}
public String[] getAmPmStrings()
{
return ampms;
}
public String[] getEras()
{
return eras;
}
public String getLocalPatternChars()
{
return localPatternChars;
}
public String[] getMonths ()
{
return months;
}
public String[] getShortMonths ()
{
return shortMonths;
}
public String[] getShortWeekdays ()
{
return shortWeekdays;
}
public String[] getWeekdays ()
{
return weekdays;
}
public String[] [] getZoneStrings ()
{
return zoneStrings;
}
public void setAmPmStrings (String[] value)
{
ampms = value;
}
public void setEras (String[] value)
{
eras = value;
}
public void setLocalPatternChars (String value)
{
localPatternChars = value;
}
public void setMonths (String[] value)
{
months = value;
}
public void setShortMonths (String[] value)
{
shortMonths = value;
}
public void setShortWeekdays (String[] value)
{
shortWeekdays = value;
}
public void setWeekdays (String[] value)
{
weekdays = value;
}
public void setZoneStrings (String[][] value)
{
zoneStrings = value;
}
/* Does a "deep" equality test - recurses into arrays. */
protected static boolean equals (Object x, Object y)
{
if (x == y)
return true;
if (x == null || y == null)
return false;
if (! (x instanceof Object[]) || ! (y instanceof Object[]))
return x.equals(y);
Object[] xa = (Object[]) x;
Object[] ya = (Object[]) y;
if (xa.length != ya.length)
return false;
for (int i = xa.length; --i >= 0; )
{
if (! equals(xa[i], ya[i]))
return false;
}
return true;
}
private static int hashCode (Object x)
{
if (x == null)
return 0;
if (! (x instanceof Object[]))
return x.hashCode();
Object[] xa = (Object[]) x;
int hash = 0;
for (int i = 0; i < xa.length; i++)
hash = 37 * hashCode(xa[i]);
return hash;
}
public boolean equals (Object obj)
{
if (obj == null || ! (obj instanceof DateFormatSymbols))
return false;
DateFormatSymbols other = (DateFormatSymbols) obj;
return (equals(ampms, other.ampms)
&& equals(eras, other.eras)
&& equals(localPatternChars, other.localPatternChars)
&& equals(months, other.months)
&& equals(shortMonths, other.shortMonths)
&& equals(shortWeekdays, other.shortWeekdays)
&& equals(weekdays, other.weekdays)
&& equals(zoneStrings, other.zoneStrings));
}
public int hashCode ()
{
return (hashCode(ampms)
^ hashCode(eras)
^ hashCode(localPatternChars)
^ hashCode(months)
^ hashCode(shortMonths)
^ hashCode(shortWeekdays)
^ hashCode(weekdays)
^ hashCode(zoneStrings));
}
}

View file

@ -0,0 +1,983 @@
// DecimalFormat.java - Localized number formatting.
/* Copyright (C) 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* @author Tom Tromey <tromey@cygnus.com>
* @date March 4, 1999
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 from http://www.javasoft.com.
* Status: Believed complete and correct to 1.2, except serialization.
* Note however that the docs are very unclear about how format parsing
* should work. No doubt there are problems here.
*/
public class DecimalFormat extends NumberFormat
{
// This is a helper for applyPatternWithSymbols. It reads a prefix
// or a suffix. It can cause some side-effects.
private final int scanFix (String pattern, int index, StringBuffer buf,
String patChars, DecimalFormatSymbols syms,
boolean is_suffix)
throws ParseException
{
int len = pattern.length();
buf.setLength(0);
boolean multiplierSet = false;
while (index < len)
{
char c = pattern.charAt(index);
if (c == '\'' && index + 1 < len
&& pattern.charAt(index + 1) == '\'')
{
buf.append(c);
++index;
}
else if (c == '\'' && index + 2 < len
&& pattern.charAt(index + 2) == '\'')
{
buf.append(pattern.charAt(index + 1));
index += 2;
}
else if (c == '\u00a4')
{
if (index + 1 < len && pattern.charAt(index + 1) == '\u00a4')
{
buf.append(syms.getInternationalCurrencySymbol());
++index;
}
else
buf.append(syms.getCurrencySymbol());
}
else if (is_suffix && c == syms.getPercent())
{
if (multiplierSet)
throw new ParseException ("multiplier already set", index);
multiplierSet = true;
multiplier = 100;
buf.append(c);
}
else if (is_suffix && c == syms.getPerMill())
{
if (multiplierSet)
throw new ParseException ("multiplier already set", index);
multiplierSet = true;
multiplier = 1000;
buf.append(c);
}
else if (patChars.indexOf(c) != -1)
{
// This is a pattern character.
break;
}
else
buf.append(c);
++index;
}
return index;
}
// A helper which reads a number format.
private final int scanFormat (String pattern, int index,
String patChars, DecimalFormatSymbols syms,
boolean is_positive)
throws ParseException
{
int max = pattern.length();
int countSinceGroup = 0;
int zeroCount = 0;
boolean saw_group = false;
//
// Scan integer part.
//
while (index < max)
{
char c = pattern.charAt(index);
if (c == syms.getDigit())
{
if (zeroCount > 0)
throw new ParseException ("digit mark following zero", index);
++countSinceGroup;
}
else if (c == syms.getZeroDigit())
{
++zeroCount;
++countSinceGroup;
}
else if (c == syms.getGroupingSeparator())
{
countSinceGroup = 0;
saw_group = true;
}
else
break;
++index;
}
// We can only side-effect when parsing the positive format.
if (is_positive)
{
groupingUsed = saw_group;
groupingSize = (byte) countSinceGroup;
minimumIntegerDigits = zeroCount;
}
// Early termination.
if (index == max || pattern.charAt(index) == syms.getGroupingSeparator())
{
if (is_positive)
decimalSeparatorAlwaysShown = false;
return index;
}
if (pattern.charAt(index) == syms.getDecimalSeparator())
{
++index;
//
// Scan fractional part.
//
int hashCount = 0;
zeroCount = 0;
while (index < max)
{
char c = pattern.charAt(index);
if (c == syms.getZeroDigit())
{
if (hashCount > 0)
throw new ParseException ("zero mark following digit",
index);
++zeroCount;
}
else if (c == syms.getDigit())
{
++hashCount;
}
else if (c != syms.getExponential()
&& c != syms.getPatternSeparator()
&& patChars.indexOf(c) != -1)
throw new ParseException ("unexpected special character",
index);
else
break;
++index;
}
if (is_positive)
{
maximumFractionDigits = hashCount + zeroCount;
minimumFractionDigits = zeroCount;
}
if (index == max)
return index;
}
if (pattern.charAt(index) == syms.getExponential())
{
//
// Scan exponential format.
//
zeroCount = 0;
++index;
while (index < max)
{
char c = pattern.charAt(index);
if (c == syms.getZeroDigit())
++zeroCount;
else if (c == syms.getDigit())
{
if (zeroCount > 0)
throw new
ParseException ("digit mark following zero in exponent",
index);
}
else if (patChars.indexOf(c) != -1)
throw new ParseException ("unexpected special character",
index);
else
break;
++index;
}
if (is_positive)
{
useExponentialNotation = true;
minExponentDigits = (byte) zeroCount;
}
}
return index;
}
// This helper function creates a string consisting of all the
// characters which can appear in a pattern and must be quoted.
private final String patternChars (DecimalFormatSymbols syms)
{
StringBuffer buf = new StringBuffer ();
buf.append(syms.getDecimalSeparator());
buf.append(syms.getDigit());
buf.append(syms.getExponential());
buf.append(syms.getGroupingSeparator());
// Adding this one causes pattern application to fail.
// Of course, omitting is causes toPattern to fail.
// ... but we already have bugs there. FIXME.
// buf.append(syms.getMinusSign());
buf.append(syms.getPatternSeparator());
buf.append(syms.getPercent());
buf.append(syms.getPerMill());
buf.append(syms.getZeroDigit());
buf.append('\u00a4');
return buf.toString();
}
private final void applyPatternWithSymbols (String pattern,
DecimalFormatSymbols syms)
throws ParseException
{
// Initialize to the state the parser expects.
negativePrefix = "";
negativeSuffix = "";
positivePrefix = "";
positiveSuffix = "";
decimalSeparatorAlwaysShown = false;
groupingSize = 0;
minExponentDigits = 0;
multiplier = 1;
useExponentialNotation = false;
groupingUsed = false;
maximumFractionDigits = 0;
maximumIntegerDigits = 309;
minimumFractionDigits = 0;
minimumIntegerDigits = 1;
StringBuffer buf = new StringBuffer ();
String patChars = patternChars (syms);
int max = pattern.length();
int index = scanFix (pattern, 0, buf, patChars, syms, false);
positivePrefix = buf.toString();
index = scanFormat (pattern, index, patChars, syms, true);
index = scanFix (pattern, index, buf, patChars, syms, true);
positiveSuffix = buf.toString();
if (index == pattern.length())
{
// No negative info.
negativePrefix = null;
negativeSuffix = null;
}
else
{
if (pattern.charAt(index) != syms.getPatternSeparator())
throw new ParseException ("separator character expected", index);
index = scanFix (pattern, index + 1, buf, patChars, syms, false);
negativePrefix = buf.toString();
// We parse the negative format for errors but we don't let
// it side-effect this object.
index = scanFormat (pattern, index, patChars, syms, false);
index = scanFix (pattern, index, buf, patChars, syms, true);
negativeSuffix = buf.toString();
if (index != pattern.length())
throw new ParseException ("end of pattern expected", index);
}
}
public void applyLocalizedPattern (String pattern) throws ParseException
{
applyPatternWithSymbols (pattern, symbols);
}
public void applyPattern (String pattern) throws ParseException
{
applyPatternWithSymbols (pattern, nonLocalizedSymbols);
}
public Object clone ()
{
return new DecimalFormat (this);
}
private DecimalFormat (DecimalFormat dup)
{
decimalSeparatorAlwaysShown = dup.decimalSeparatorAlwaysShown;
groupingSize = dup.groupingSize;
minExponentDigits = dup.minExponentDigits;
multiplier = dup.multiplier;
negativePrefix = dup.negativePrefix;
negativeSuffix = dup.negativeSuffix;
positivePrefix = dup.positivePrefix;
positiveSuffix = dup.positiveSuffix;
symbols = (DecimalFormatSymbols) dup.symbols.clone();
useExponentialNotation = dup.useExponentialNotation;
}
public DecimalFormat ()
{
this ("#,##0.###");
}
public DecimalFormat (String pattern)
{
this (pattern, new DecimalFormatSymbols ());
}
public DecimalFormat (String pattern, DecimalFormatSymbols symbols)
{
this.symbols = symbols;
// The docs imply that the constructor turns a ParseException
// into an IllegalArgumentException.
try
{
applyPattern (pattern);
}
catch (ParseException x)
{
throw new IllegalArgumentException (x.getMessage());
}
}
private final boolean equals (String s1, String s2)
{
if (s1 == null || s2 == null)
return s1 == s2;
return s1.equals(s2);
}
public boolean equals (Object obj)
{
if (! (obj instanceof DecimalFormat))
return false;
DecimalFormat dup = (DecimalFormat) obj;
return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown
&& groupingSize == dup.groupingSize
&& minExponentDigits == dup.minExponentDigits
&& multiplier == dup.multiplier
&& equals(negativePrefix, dup.negativePrefix)
&& equals(negativeSuffix, dup.negativeSuffix)
&& equals(positivePrefix, dup.positivePrefix)
&& equals(positiveSuffix, dup.positiveSuffix)
&& symbols.equals(dup.symbols)
&& useExponentialNotation == dup.useExponentialNotation);
}
public StringBuffer format (double number, StringBuffer dest,
FieldPosition fieldPos)
{
// A very special case.
if (Double.isNaN(number))
{
dest.append(symbols.getNaN());
if (fieldPos != null && fieldPos.getField() == INTEGER_FIELD)
{
int index = dest.length();
fieldPos.setBeginIndex(index - symbols.getNaN().length());
fieldPos.setEndIndex(index);
}
return dest;
}
boolean is_neg = number < 0;
if (is_neg)
{
if (negativePrefix != null)
dest.append(negativePrefix);
else
{
dest.append(symbols.getMinusSign());
dest.append(positivePrefix);
}
number = - number;
}
else
dest.append(positivePrefix);
int integerBeginIndex = dest.length();
int integerEndIndex = 0;
if (Double.isInfinite (number))
{
dest.append(symbols.getInfinity());
integerEndIndex = dest.length();
}
else
{
number *= multiplier;
// Compute exponent.
long exponent = 0;
double baseNumber;
if (useExponentialNotation)
{
exponent = (long) (Math.log(number) / Math.log(10));
if (minimumIntegerDigits > 0)
exponent -= minimumIntegerDigits - 1;
baseNumber = (long) (number / Math.pow(10.0, exponent));
}
else
baseNumber = number;
// Round to the correct number of digits.
baseNumber += 5 * Math.pow(10.0, - maximumFractionDigits - 1);
int index = dest.length();
double intPart = Math.floor(baseNumber);
int count = 0;
while (count < maximumIntegerDigits
&& (intPart > 0 || count < minimumIntegerDigits))
{
long dig = (long) (intPart % 10);
intPart = Math.floor(intPart / 10);
// Append group separator if required.
if (groupingUsed && count > 0 && count % groupingSize == 0)
dest.insert(index, symbols.getGroupingSeparator());
dest.insert(index, (char) (symbols.getZeroDigit() + dig));
++count;
}
integerEndIndex = dest.length();
int decimal_index = integerEndIndex;
int consecutive_zeros = 0;
int total_digits = 0;
// Strip integer part from NUMBER.
double fracPart = baseNumber - Math.floor(baseNumber);
for (count = 0;
count < maximumFractionDigits
&& (fracPart != 0 || count < minimumFractionDigits);
++count)
{
++total_digits;
fracPart *= 10;
long dig = (long) fracPart;
if (dig == 0)
++consecutive_zeros;
else
consecutive_zeros = 0;
dest.append((char) (symbols.getZeroDigit() + dig));
// Strip integer part from FRACPART.
fracPart = fracPart - Math.floor (fracPart);
}
// Strip extraneous trailing `0's. We can't always detect
// these in the loop.
int extra_zeros = Math.min (consecutive_zeros,
total_digits - minimumFractionDigits);
if (extra_zeros > 0)
{
dest.setLength(dest.length() - extra_zeros);
total_digits -= extra_zeros;
}
// If required, add the decimal symbol.
if (decimalSeparatorAlwaysShown
|| total_digits > 0)
{
dest.insert(decimal_index, symbols.getDecimalSeparator());
if (fieldPos != null && fieldPos.getField() == FRACTION_FIELD)
{
fieldPos.setBeginIndex(decimal_index + 1);
fieldPos.setEndIndex(dest.length());
}
}
// Finally, print the exponent.
if (useExponentialNotation)
{
dest.append(symbols.getExponential());
dest.append(exponent < 0 ? '-' : '+');
index = dest.length();
for (count = 0;
exponent > 0 || count < minExponentDigits;
++count)
{
long dig = exponent % 10;
exponent /= 10;
dest.insert(index, (char) (symbols.getZeroDigit() + dig));
}
}
}
if (fieldPos != null && fieldPos.getField() == INTEGER_FIELD)
{
fieldPos.setBeginIndex(integerBeginIndex);
fieldPos.setEndIndex(integerEndIndex);
}
dest.append((is_neg && negativeSuffix != null)
? negativeSuffix
: positiveSuffix);
return dest;
}
public StringBuffer format (long number, StringBuffer dest,
FieldPosition fieldPos)
{
// If using exponential notation, we just format as a double.
if (useExponentialNotation)
return format ((double) number, dest, fieldPos);
boolean is_neg = number < 0;
if (is_neg)
{
if (negativePrefix != null)
dest.append(negativePrefix);
else
{
dest.append(symbols.getMinusSign());
dest.append(positivePrefix);
}
number = - number;
}
else
dest.append(positivePrefix);
int integerBeginIndex = dest.length();
int index = dest.length();
int count = 0;
while (count < maximumIntegerDigits
&& (number > 0 || count < minimumIntegerDigits))
{
long dig = number % 10;
number /= 10;
// NUMBER and DIG will be less than 0 if the original number
// was the most negative long.
if (dig < 0)
{
dig = - dig;
number = - number;
}
// Append group separator if required.
if (groupingUsed && count > 0 && count % groupingSize == 0)
dest.insert(index, symbols.getGroupingSeparator());
dest.insert(index, (char) (symbols.getZeroDigit() + dig));
++count;
}
if (fieldPos != null && fieldPos.getField() == INTEGER_FIELD)
{
fieldPos.setBeginIndex(integerBeginIndex);
fieldPos.setEndIndex(dest.length());
}
if (decimalSeparatorAlwaysShown || minimumFractionDigits > 0)
{
dest.append(symbols.getDecimalSeparator());
if (fieldPos != null && fieldPos.getField() == FRACTION_FIELD)
{
fieldPos.setBeginIndex(dest.length());
fieldPos.setEndIndex(dest.length() + minimumFractionDigits);
}
}
for (count = 0; count < minimumFractionDigits; ++count)
dest.append(symbols.getZeroDigit());
dest.append((is_neg && negativeSuffix != null)
? negativeSuffix
: positiveSuffix);
return dest;
}
public DecimalFormatSymbols getDecimalFormatSymbols ()
{
return symbols;
}
public int getGroupingSize ()
{
return groupingSize;
}
public int getMultiplier ()
{
return multiplier;
}
public String getNegativePrefix ()
{
return negativePrefix;
}
public String getNegativeSuffix ()
{
return negativeSuffix;
}
public String getPositivePrefix ()
{
return positivePrefix;
}
public String getPositiveSuffix ()
{
return positiveSuffix;
}
public int hashCode ()
{
int hash = (negativeSuffix.hashCode() ^ negativePrefix.hashCode()
^positivePrefix.hashCode() ^ positiveSuffix.hashCode());
// FIXME.
return hash;
}
public boolean isDecimalSeparatorAlwaysShown ()
{
return decimalSeparatorAlwaysShown;
}
public Number parse (String str, ParsePosition pos)
{
// Our strategy is simple: copy the text into a buffer,
// translating or omitting locale-specific information. Then
// let Double or Long convert the number for us.
boolean is_neg = false;
int index = pos.getIndex();
StringBuffer buf = new StringBuffer ();
// We have to check both prefixes, because one might be empty.
// We want to pick the longest prefix that matches.
boolean got_pos = str.startsWith(positivePrefix, index);
String np = (negativePrefix != null
? negativePrefix
: positivePrefix + symbols.getMinusSign());
boolean got_neg = str.startsWith(np, index);
if (got_pos && got_neg)
{
// By checking this way, we preserve ambiguity in the case
// where the negative format differs only in suffix. We
// check this again later.
if (np.length() > positivePrefix.length())
{
is_neg = true;
index += np.length();
}
else
index += positivePrefix.length();
}
else if (got_neg)
{
is_neg = true;
index += np.length();
}
else if (got_pos)
index += positivePrefix.length();
else
{
pos.setErrorIndex (index);
return null;
}
// FIXME: handle Inf and NaN.
// FIXME: do we have to respect minimum/maxmimum digit stuff?
// What about leading zeros? What about multiplier?
int start_index = index;
int max = str.length();
char zero = symbols.getZeroDigit();
int last_group = -1;
boolean int_part = true;
boolean exp_part = false;
for (; index < max; ++index)
{
char c = str.charAt(index);
// FIXME: what about grouping size?
if (groupingUsed && c == symbols.getGroupingSeparator())
{
if (last_group != -1
&& (index - last_group) % groupingSize != 0)
{
pos.setErrorIndex(index);
return null;
}
last_group = index;
}
else if (c >= zero && c <= zero + 9)
{
buf.append((char) (c - zero + '0'));
exp_part = false;
}
else if (parseIntegerOnly)
break;
else if (c == symbols.getDecimalSeparator())
{
if (last_group != -1
&& (index - last_group) % groupingSize != 0)
{
pos.setErrorIndex(index);
return null;
}
buf.append('.');
int_part = false;
}
else if (c == symbols.getExponential())
{
buf.append('E');
int_part = false;
exp_part = true;
}
else if (exp_part
&& (c == '+' || c == '-' || c == symbols.getMinusSign()))
{
// For exponential notation.
buf.append(c);
}
else
break;
}
if (index == start_index)
{
// Didn't see any digits.
pos.setErrorIndex(index);
return null;
}
// Check the suffix. We must do this before converting the
// buffer to a number to handle the case of a number which is
// the most negative Long.
boolean got_pos_suf = str.startsWith(positiveSuffix, index);
String ns = (negativePrefix == null ? positiveSuffix : negativeSuffix);
boolean got_neg_suf = str.startsWith(ns, index);
if (is_neg)
{
if (! got_neg_suf)
{
pos.setErrorIndex(index);
return null;
}
}
else if (got_pos && got_neg && got_neg_suf)
{
is_neg = true;
}
else if (got_pos != got_pos_suf && got_neg != got_neg_suf)
{
pos.setErrorIndex(index);
return null;
}
String suffix = is_neg ? ns : positiveSuffix;
if (is_neg)
buf.insert(0, '-');
String t = buf.toString();
Number result = null;
try
{
result = new Long (t);
}
catch (NumberFormatException x1)
{
try
{
result = new Double (t);
}
catch (NumberFormatException x2)
{
}
}
if (result == null)
{
pos.setErrorIndex(index);
return null;
}
pos.setIndex(index + suffix.length());
return result;
}
public void setDecimalFormatSymbols (DecimalFormatSymbols newSymbols)
{
symbols = newSymbols;
}
public void setDecimalSeparatorAlwaysShown (boolean newValue)
{
decimalSeparatorAlwaysShown = newValue;
}
public void setGroupingSize (int groupSize)
{
groupingSize = (byte) groupSize;
}
public void setMaximumFractionDigits (int newValue)
{
maximumFractionDigits = Math.min(newValue, 340);
}
public void setMaximumIntegerDigits (int newValue)
{
maximumIntegerDigits = Math.min(newValue, 309);
}
public void setMinimumFractionDigits (int newValue)
{
minimumFractionDigits = Math.min(newValue, 340);
}
public void setMinimumIntegerDigits (int newValue)
{
minimumIntegerDigits = Math.min(newValue, 309);
}
public void setMultiplier (int newValue)
{
multiplier = newValue;
}
public void setNegativePrefix (String newValue)
{
negativePrefix = newValue;
}
public void setNegativeSuffix (String newValue)
{
negativeSuffix = newValue;
}
public void setPositivePrefix (String newValue)
{
positivePrefix = newValue;
}
public void setPositiveSuffix (String newValue)
{
positiveSuffix = newValue;
}
private final void quoteFix (StringBuffer buf, String text, String patChars)
{
int len = text.length();
for (int index = 0; index < len; ++index)
{
char c = text.charAt(index);
if (patChars.indexOf(c) != -1)
{
buf.append('\'');
buf.append(c);
buf.append('\'');
}
else
buf.append(c);
}
}
private final String computePattern (DecimalFormatSymbols syms)
{
StringBuffer mainPattern = new StringBuffer ();
// We have to at least emit a zero for the minimum number of
// digits. Past that we need hash marks up to the grouping
// separator (and one beyond).
int total_digits = Math.max(minimumIntegerDigits,
groupingUsed ? groupingSize + 1: 0);
for (int i = 0; i < total_digits - minimumIntegerDigits; ++i)
mainPattern.append(syms.getDigit());
for (int i = total_digits - minimumIntegerDigits; i < total_digits; ++i)
mainPattern.append(syms.getZeroDigit());
// Inserting the gropuing operator afterwards is easier.
if (groupingUsed)
mainPattern.insert(mainPattern.length() - groupingSize,
syms.getGroupingSeparator());
// See if we need decimal info.
if (minimumFractionDigits > 0 || maximumFractionDigits > 0
|| decimalSeparatorAlwaysShown)
mainPattern.append(syms.getDecimalSeparator());
for (int i = 0; i < minimumFractionDigits; ++i)
mainPattern.append(syms.getZeroDigit());
for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i)
mainPattern.append(syms.getDigit());
if (useExponentialNotation)
{
mainPattern.append(syms.getExponential());
for (int i = 0; i < minExponentDigits; ++i)
mainPattern.append(syms.getZeroDigit());
if (minExponentDigits == 0)
mainPattern.append(syms.getDigit());
}
String main = mainPattern.toString();
String patChars = patternChars (syms);
mainPattern.setLength(0);
quoteFix (mainPattern, positivePrefix, patChars);
mainPattern.append(main);
quoteFix (mainPattern, positiveSuffix, patChars);
if (negativePrefix != null)
{
quoteFix (mainPattern, negativePrefix, patChars);
mainPattern.append(main);
quoteFix (mainPattern, negativeSuffix, patChars);
}
return mainPattern.toString();
}
public String toLocalizedPattern ()
{
return computePattern (symbols);
}
public String toPattern ()
{
return computePattern (nonLocalizedSymbols);
}
// These names are fixed by the serialization spec.
private boolean decimalSeparatorAlwaysShown;
private byte groupingSize;
private byte minExponentDigits;
private int multiplier;
private String negativePrefix;
private String negativeSuffix;
private String positivePrefix;
private String positiveSuffix;
private DecimalFormatSymbols symbols;
private boolean useExponentialNotation;
// The locale-independent pattern symbols happen to be the same as
// the US symbols.
private static final DecimalFormatSymbols nonLocalizedSymbols
= new DecimalFormatSymbols (Locale.US);
}

View file

@ -0,0 +1,293 @@
// DecimalFormatSymbols.java - Symbols used to format numbers.
/* Copyright (C) 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
import java.io.Serializable;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* @author Tom Tromey <tromey@cygnus.com>
* @date February 24, 1999
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 from http://www.javasoft.com.
* Status: Believed complete and correct to 1.2, except serialization.
*/
public final class DecimalFormatSymbols implements Cloneable, Serializable
{
public Object clone ()
{
return new DecimalFormatSymbols (this);
}
private DecimalFormatSymbols (DecimalFormatSymbols orig)
{
this.currencySymbol = orig.currencySymbol;
this.decimalSeparator = orig.decimalSeparator;
this.digit = orig.digit;
this.exponential = orig.exponential;
this.groupingSeparator = orig.groupingSeparator;
this.infinity = orig.infinity;
this.intlCurrencySymbol = orig.intlCurrencySymbol;
this.minusSign = orig.minusSign;
this.NaN = orig.NaN;
this.patternSeparator = orig.patternSeparator;
this.percent = orig.percent;
this.perMill = orig.perMill;
this.zeroDigit = orig.zeroDigit;
}
public DecimalFormatSymbols ()
{
this (Locale.getDefault());
}
private final String safeGetString (ResourceBundle bundle,
String name, String def)
{
if (bundle != null)
{
try
{
return bundle.getString(name);
}
catch (MissingResourceException x)
{
}
}
return def;
}
public final char safeGetChar (ResourceBundle bundle,
String name, char def)
{
String r = null;
if (bundle != null)
{
try
{
r = bundle.getString(name);
}
catch (MissingResourceException x)
{
}
}
if (r == null || r.length() < 1)
return def;
return r.charAt(0);
}
public DecimalFormatSymbols (Locale loc)
{
ResourceBundle res;
try
{
res = ResourceBundle.getBundle("gnu.gcj.text.LocaleData", loc);
}
catch (MissingResourceException x)
{
res = null;
}
currencySymbol = safeGetString (res, "currencySymbol", "$");
decimalSeparator = safeGetChar (res, "decimalSeparator", '.');
digit = safeGetChar (res, "digit", '#');
exponential = safeGetChar (res, "exponential", 'E');
groupingSeparator = safeGetChar (res, "groupingSeparator", ',');
infinity = safeGetString (res, "infinity", "\u221e");
// FIXME: default?
intlCurrencySymbol = safeGetString (res, "intlCurrencySymbol", "$");
minusSign = safeGetChar (res, "minusSign", '-');
NaN = safeGetString (res, "NaN", "\ufffd");
patternSeparator = safeGetChar (res, "patternSeparator", ';');
percent = safeGetChar (res, "percent", '%');
perMill = safeGetChar (res, "perMill", '\u2030');
zeroDigit = safeGetChar (res, "zeroDigit", '0');
}
public boolean equals (Object obj)
{
if (! (obj instanceof DecimalFormatSymbols))
return false;
DecimalFormatSymbols dfs = (DecimalFormatSymbols) obj;
return (currencySymbol.equals(dfs.currencySymbol)
&& decimalSeparator == dfs.decimalSeparator
&& digit == dfs.digit
&& exponential == dfs.exponential
&& groupingSeparator == dfs.groupingSeparator
&& infinity.equals(dfs.infinity)
&& intlCurrencySymbol.equals(dfs.intlCurrencySymbol)
&& minusSign == dfs.minusSign
&& NaN.equals(dfs.NaN)
&& patternSeparator == dfs.patternSeparator
&& percent == dfs.percent
&& perMill == dfs.perMill
&& zeroDigit == dfs.zeroDigit);
}
public String getCurrencySymbol ()
{
return currencySymbol;
}
public char getDecimalSeparator ()
{
return decimalSeparator;
}
public char getDigit ()
{
return digit;
}
// This is our own extension.
char getExponential ()
{
return exponential;
}
public char getGroupingSeparator ()
{
return groupingSeparator;
}
public String getInfinity ()
{
return infinity;
}
public String getInternationalCurrencySymbol ()
{
return intlCurrencySymbol;
}
public char getMinusSign ()
{
return minusSign;
}
public String getNaN ()
{
return NaN;
}
public char getPatternSeparator ()
{
return patternSeparator;
}
public char getPercent ()
{
return percent;
}
public char getPerMill ()
{
return perMill;
}
public char getZeroDigit ()
{
return zeroDigit;
}
public int hashCode ()
{
// Compute based on zero digit, grouping separator, and decimal
// separator -- JCL book. This probably isn't a very good hash
// code.
return zeroDigit << 16 + groupingSeparator << 8 + decimalSeparator;
}
public void setCurrenySymbol (String currency)
{
currencySymbol = currency;
}
public void setDecimalSeparator (char decimalSep)
{
decimalSeparator = decimalSep;
}
public void setDigit (char digit)
{
this.digit = digit;
}
// This is our own extension.
void setExponential (char exp)
{
exponential = exp;
}
public void setGroupingSeparator (char groupSep)
{
groupingSeparator = groupSep;
}
public void setInfinity (String infinity)
{
this.infinity = infinity;
}
public void setInternationalCurrencySymbol (String currency)
{
intlCurrencySymbol = currency;
}
public void setMinusSign (char minusSign)
{
this.minusSign = minusSign;
}
public void setNaN (String nan)
{
NaN = nan;
}
public void setPatternSeparator (char patternSep)
{
patternSeparator = patternSep;
}
public void setPercent (char percent)
{
this.percent = percent;
}
public void setPerMill (char perMill)
{
this.perMill = perMill;
}
public void setZeroDigit (char zeroDigit)
{
this.zeroDigit = zeroDigit;
}
// The names of the instance variables are fixed by the
// serialization spec.
private String currencySymbol;
private char decimalSeparator;
private char digit;
private char exponential;
private char groupingSeparator;
private String infinity;
private String intlCurrencySymbol;
private char minusSign;
private String NaN;
private char patternSeparator;
private char percent;
private char perMill;
private char zeroDigit;
}

View file

@ -0,0 +1,65 @@
/* Copyright (C) 1998, 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
/**
* @author Per Bothner <bothner@cygnus.com>
* @date October 25, 1998.
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 beta from http://www.javasoft.com.
* Status: Believed complete and correct.
* Includes JDK 1.2 methods.
*/
public class FieldPosition
{
int field;
int beginIndex;
int endIndex;
public FieldPosition (int field)
{
this.field = field;
}
public int getField ()
{
return field;
}
public int getBeginIndex ()
{
return beginIndex;
}
public int getEndIndex ()
{
return endIndex;
}
public void setBeginIndex (int index)
{
beginIndex = index;
}
public void setEndIndex (int index)
{
endIndex = index;
}
public boolean equals (Object obj)
{
if (! (obj instanceof FieldPosition))
return false;
FieldPosition other = (FieldPosition) obj;
return (field == other.field
&& beginIndex == other.beginIndex && endIndex == other.endIndex);
}
}

View file

@ -0,0 +1,51 @@
/* Copyright (C) 1998, 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
/**
* @author Per Bothner <bothner@cygnus.com>
* @date October 25, 1998.
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 beta from http://www.javasoft.com.
* Status: Believed complete and correct.
*/
public abstract class Format implements java.io.Serializable, Cloneable
{
public Format ()
{
}
public abstract StringBuffer format (Object obj,
StringBuffer sbuf, FieldPosition pos);
public final String format (Object obj)
{
StringBuffer sbuf = new StringBuffer();
format(obj, sbuf, new FieldPosition(0));
return sbuf.toString();
}
public abstract Object parseObject (String source, ParsePosition pos);
public Object parseObject (String source) throws ParseException
{
ParsePosition pos = new ParsePosition(0);
Object result = parseObject (source, pos);
if (result == null)
{
int index = pos.getErrorIndex();
if (index < 0)
index = pos.getIndex();
throw new ParseException("parseObject failed", index);
}
return result;
}
}

View file

@ -0,0 +1,543 @@
// MessageFormat.java - Localized message formatting.
/* Copyright (C) 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
import java.util.Date;
import java.util.Locale;
import java.util.Vector;
/**
* @author Tom Tromey <tromey@cygnus.com>
* @date March 3, 1999
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 from http://www.javasoft.com.
* Status: Believed complete and correct to 1.2, except serialization.
* and parsing.
*/
final class MessageFormatElement
{
// Argument number.
int argNumber;
// Formatter to be used. This is the format set by setFormat.
Format setFormat;
// Formatter to be used based on the type.
Format format;
// Argument will be checked to make sure it is an instance of this
// class.
Class formatClass;
// Formatter type.
String type;
// Formatter style.
String style;
// Text to follow this element.
String trailer;
// FIXME: shouldn't need this.
Class forName (String name)
{
try
{
return Class.forName (name);
}
catch (ClassNotFoundException x)
{
}
return null;
}
// Recompute the locale-based formatter.
void setLocale (Locale loc)
{
if (type == null)
;
else if (type.equals("number"))
{
// FIXME: named class literal.
// formatClass = Number.class;
formatClass = forName ("java.lang.Number");
if (style == null)
format = NumberFormat.getInstance(loc);
else if (style.equals("currency"))
format = NumberFormat.getCurrencyInstance(loc);
else if (style.equals("percent"))
format = NumberFormat.getPercentInstance(loc);
else if (style.equals("integer"))
{
NumberFormat nf = NumberFormat.getNumberInstance(loc);
nf.setMaximumFractionDigits(0);
nf.setGroupingUsed(false);
format = nf;
}
else
{
format = NumberFormat.getNumberInstance(loc);
DecimalFormat df = (DecimalFormat) format;
try
{
df.applyPattern(style);
}
catch (ParseException x)
{
throw new IllegalArgumentException (x.getMessage());
}
}
}
else if (type.equals("time") || type.equals("date"))
{
// FIXME: named class literal.
// formatClass = Date.class;
formatClass = forName ("java.util.Date");
int val = DateFormat.DEFAULT;
if (style == null)
;
if (style.equals("short"))
val = DateFormat.SHORT;
else if (style.equals("medium"))
val = DateFormat.MEDIUM;
else if (style.equals("long"))
val = DateFormat.LONG;
else if (style.equals("full"))
val = DateFormat.FULL;
if (type.equals("time"))
format = DateFormat.getTimeInstance(val, loc);
else
format = DateFormat.getDateInstance(val, loc);
if (style != null && val == DateFormat.DEFAULT)
{
SimpleDateFormat sdf = (SimpleDateFormat) format;
sdf.applyPattern(style);
}
}
else if (type.equals("choice"))
{
// FIXME: named class literal.
// formatClass = Number.class;
formatClass = forName ("java.lang.Number");
if (style == null)
throw new
IllegalArgumentException ("style required for choice format");
format = new ChoiceFormat (style);
}
}
}
public class MessageFormat extends Format
{
// Helper that returns the text up to the next format opener. The
// text is put into BUFFER. Returns index of character after end of
// string. Throws IllegalArgumentException on error.
private static final int scanString (String pat, int index,
StringBuffer buffer)
{
int max = pat.length();
buffer.setLength(0);
for (; index < max; ++index)
{
char c = pat.charAt(index);
if (c == '\'' && index + 2 < max && pat.charAt(index + 2) == '\'')
{
buffer.append(pat.charAt(index + 1));
index += 2;
}
else if (c == '\'' && index + 1 < max
&& pat.charAt(index + 1) == '\'')
{
buffer.append(c);
++index;
}
else if (c == '{')
break;
else if (c == '}')
throw new IllegalArgumentException ();
else
buffer.append(c);
}
return index;
}
// This helper retrieves a single part of a format element. Returns
// the index of the terminating character.
private static final int scanFormatElement (String pat, int index,
StringBuffer buffer,
char term)
{
int max = pat.length();
buffer.setLength(0);
int brace_depth = 1;
for (; index < max; ++index)
{
char c = pat.charAt(index);
if (c == '\'' && index + 2 < max && pat.charAt(index + 2) == '\'')
{
buffer.append(c);
buffer.append(pat.charAt(index + 1));
buffer.append(c);
index += 2;
}
else if (c == '\'' && index + 1 < max
&& pat.charAt(index + 1) == '\'')
{
buffer.append(c);
++index;
}
else if (c == '{')
{
buffer.append(c);
++brace_depth;
}
else if (c == '}')
{
if (--brace_depth == 0)
break;
buffer.append(c);
}
// Check for TERM after braces, because TERM might be `}'.
else if (c == term)
break;
else
buffer.append(c);
}
return index;
}
// This is used to parse a format element and whatever non-format
// text might trail it.
private static final int scanFormat (String pat, int index,
StringBuffer buffer, Vector elts,
Locale locale)
{
MessageFormatElement mfe = new MessageFormatElement ();
elts.addElement(mfe);
int max = pat.length();
// Skip the opening `{'.
++index;
// Fetch the argument number.
index = scanFormatElement (pat, index, buffer, ',');
try
{
mfe.argNumber = Integer.parseInt(buffer.toString());
}
catch (NumberFormatException nfx)
{
throw new IllegalArgumentException ();
}
// Extract the element format.
if (index < max && pat.charAt(index) == ',')
{
index = scanFormatElement (pat, index + 1, buffer, ',');
mfe.type = buffer.toString();
// Extract the style.
if (index < max && pat.charAt(index) == ',')
{
index = scanFormatElement (pat, index + 1, buffer, '}');
mfe.style = buffer.toString ();
}
}
// Advance past the last terminator.
if (index >= max || pat.charAt(index) != '}')
throw new IllegalArgumentException ();
++index;
// Now fetch trailing string.
index = scanString (pat, index, buffer);
mfe.trailer = buffer.toString ();
mfe.setLocale(locale);
return index;
}
public void applyPattern (String newPattern)
{
pattern = newPattern;
StringBuffer tempBuffer = new StringBuffer ();
int index = scanString (newPattern, 0, tempBuffer);
leader = tempBuffer.toString();
Vector elts = new Vector ();
while (index < newPattern.length())
index = scanFormat (newPattern, index, tempBuffer, elts, locale);
elements = new MessageFormatElement[elts.size()];
elts.copyInto(elements);
}
public Object clone ()
{
MessageFormat c = new MessageFormat ();
c.setLocale(locale);
c.applyPattern(pattern);
return (Object) c;
}
public boolean equals (Object obj)
{
if (! (obj instanceof MessageFormat))
return false;
MessageFormat mf = (MessageFormat) obj;
return (pattern.equals(mf.pattern)
&& locale.equals(mf.locale));
}
public static String format (String pattern, Object arguments[])
{
MessageFormat mf = new MessageFormat (pattern);
StringBuffer sb = new StringBuffer ();
FieldPosition fp = new FieldPosition (NumberFormat.INTEGER_FIELD);
return mf.format(arguments, sb, fp).toString();
}
public final StringBuffer format (Object arguments[], StringBuffer appendBuf,
FieldPosition ignore)
{
appendBuf.append(leader);
for (int i = 0; i < elements.length; ++i)
{
if (elements[i].argNumber >= arguments.length)
throw new IllegalArgumentException ();
Object thisArg = arguments[elements[i].argNumber];
Format formatter = null;
if (elements[i].setFormat != null)
formatter = elements[i].setFormat;
else if (elements[i].format != null)
{
if (elements[i].formatClass != null
&& ! elements[i].formatClass.isInstance(thisArg))
throw new IllegalArgumentException ();
formatter = elements[i].format;
}
else if (thisArg instanceof Number)
formatter = NumberFormat.getInstance(locale);
else if (thisArg instanceof Date)
formatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale);
else
appendBuf.append(thisArg);
if (formatter != null)
{
// Special-case ChoiceFormat.
if (formatter instanceof ChoiceFormat)
{
StringBuffer buf = new StringBuffer ();
// FIXME: don't actually know what is correct here.
// Can a sub-format refer to any argument, or just
// the single argument passed to it? Must test
// against JDK.
formatter.format(thisArg, buf, ignore);
MessageFormat mf = new MessageFormat ();
mf.setLocale(locale);
mf.applyPattern(buf.toString());
formatter = mf;
}
formatter.format(thisArg, appendBuf, ignore);
}
appendBuf.append(elements[i].trailer);
}
return appendBuf;
}
public final StringBuffer format (Object singleArg, StringBuffer appendBuf,
FieldPosition ignore)
{
Object[] args = new Object[1];
args[0] = singleArg;
return format (args, appendBuf, ignore);
}
public Format[] getFormats ()
{
Format[] f = new Format[elements.length];
for (int i = elements.length - 1; i >= 0; --i)
f[i] = elements[i].setFormat;
return f;
}
public Locale getLocale ()
{
return locale;
}
public int hashCode ()
{
// FIXME: not a very good hash.
return pattern.hashCode() + locale.hashCode();
}
private MessageFormat ()
{
}
public MessageFormat (String pattern)
{
applyPattern (pattern);
}
public Object[] parse (String sourceStr, ParsePosition pos)
{
// Check initial text.
int index = pos.getIndex();
if (! sourceStr.startsWith(leader, index))
{
pos.setErrorIndex(index);
return null;
}
index += leader.length();
Vector results = new Vector (elements.length, 1);
// Now check each format.
for (int i = 0; i < elements.length; ++i)
{
Format formatter = null;
if (elements[i].setFormat != null)
formatter = elements[i].setFormat;
else if (elements[i].format != null)
formatter = elements[i].format;
Object value = null;
if (formatter instanceof ChoiceFormat)
{
// We must special-case a ChoiceFormat because it might
// have recursive formatting.
ChoiceFormat cf = (ChoiceFormat) formatter;
String[] formats = (String[]) cf.getFormats();
double[] limits = (double[]) cf.getLimits();
MessageFormat subfmt = new MessageFormat ();
subfmt.setLocale(locale);
ParsePosition subpos = new ParsePosition (index);
int j;
for (j = 0; value == null && j < limits.length; ++j)
{
subfmt.applyPattern(formats[j]);
subpos.setIndex(index);
value = subfmt.parse(sourceStr, subpos);
}
if (value != null)
{
index = subpos.getIndex();
value = new Double (limits[j]);
}
}
else if (formatter != null)
{
pos.setIndex(index);
value = formatter.parseObject(sourceStr, pos);
if (value != null)
index = pos.getIndex();
}
else
{
// We have a String format. This can lose in a number
// of ways, but we give it a shot.
int next_index = sourceStr.indexOf(elements[i].trailer, index);
if (next_index == -1)
{
pos.setErrorIndex(index);
return null;
}
value = sourceStr.substring(index, next_index);
index = next_index;
}
if (value == null
|| ! sourceStr.startsWith(elements[i].trailer, index))
{
pos.setErrorIndex(index);
return null;
}
if (elements[i].argNumber >= results.size())
results.setSize(elements[i].argNumber + 1);
results.setElementAt(value, elements[i].argNumber);
index += elements[i].trailer.length();
}
Object[] r = new Object[results.size()];
results.copyInto(r);
return r;
}
public Object[] parse (String sourceStr) throws ParseException
{
ParsePosition pp = new ParsePosition (0);
Object[] r = parse (sourceStr, pp);
if (r == null)
throw new ParseException ("couldn't parse string", pp.getErrorIndex());
return r;
}
public Object parseObject (String sourceStr, ParsePosition pos)
{
return parse (sourceStr, pos);
}
public void setFormat (int variableNum, Format newFormat)
{
elements[variableNum].setFormat = newFormat;
}
public void setFormats (Format[] newFormats)
{
if (newFormats.length < elements.length)
throw new IllegalArgumentException ();
int len = Math.min(newFormats.length, elements.length);
for (int i = 0; i < len; ++i)
elements[i].setFormat = newFormats[i];
}
public void setLocale (Locale loc)
{
locale = loc;
if (elements != null)
{
for (int i = 0; i < elements.length; ++i)
elements[i].setLocale(loc);
}
}
public String toPattern ()
{
return pattern;
}
// The pattern string.
private String pattern;
// The locale.
private Locale locale;
// Variables.
private MessageFormatElement[] elements;
// Leader text.
private String leader;
}

View file

@ -0,0 +1,236 @@
/* Copyright (C) 1998, 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.MissingResourceException;
/**
* @author Tom Tromey <tromey@cygnus.com>
* @date March 4, 1999
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 from http://www.javasoft.com.
* Status: Believed complete and correct to 1.2, except serialization
* and getAvailableLocales.
*/
public abstract class NumberFormat extends Format implements Cloneable
{
public static final int INTEGER_FIELD = 0;
public static final int FRACTION_FIELD = 1;
public final String format (long number)
{
StringBuffer sbuf = new StringBuffer(50);
format (number, sbuf, null);
return sbuf.toString();
}
public final StringBuffer format (Object obj, StringBuffer sbuf,
FieldPosition pos)
{
return format(((Number) obj).doubleValue(), sbuf, pos);
}
public abstract StringBuffer format (double number,
StringBuffer sbuf, FieldPosition pos);
public abstract StringBuffer format (long number,
StringBuffer sbuf, FieldPosition pos);
public static Locale[] getAvailableLocales ()
{
// FIXME.
return null;
}
private static final NumberFormat computeInstance (Locale loc,
String resource,
String def)
{
ResourceBundle res;
try
{
res = ResourceBundle.getBundle("gnu.gcj.text.LocaleData", loc);
}
catch (MissingResourceException x)
{
res = null;
}
String fmt;
try
{
fmt = res == null ? def : res.getString(resource);
}
catch (MissingResourceException x)
{
fmt = def;
}
DecimalFormatSymbols dfs = new DecimalFormatSymbols (loc);
return new DecimalFormat (fmt, dfs);
}
public static final NumberFormat getCurrencyInstance ()
{
return getCurrencyInstance (Locale.getDefault());
}
public static NumberFormat getCurrencyInstance (Locale loc)
{
return computeInstance (loc, "currencyFormat", "$#,##0.00;($#,##0.00)");
}
public static final NumberFormat getInstance ()
{
return getInstance (Locale.getDefault());
}
public static NumberFormat getInstance (Locale loc)
{
// For now always return a number instance.
return getNumberInstance (loc);
}
public int getMaximumFractionDigits ()
{
return maximumFractionDigits;
}
public int getMaximumIntegerDigits ()
{
return maximumIntegerDigits;
}
public int getMinimumFractionDigits ()
{
return minimumFractionDigits;
}
public int getMinimumIntegerDigits ()
{
return minimumIntegerDigits;
}
public static final NumberFormat getNumberInstance ()
{
return getNumberInstance (Locale.getDefault());
}
public static NumberFormat getNumberInstance (Locale loc)
{
return computeInstance (loc, "numberFormat", "#,##0.###");
}
public static final NumberFormat getPercentInstance ()
{
return getPercentInstance (Locale.getDefault());
}
public static NumberFormat getPercentInstance (Locale loc)
{
return computeInstance (loc, "percentFormat", "#,##0%");
}
public int hashCode ()
{
int hash = super.hashCode();
hash ^= (maximumFractionDigits + maximumIntegerDigits
+ minimumFractionDigits + minimumIntegerDigits);
if (groupingUsed)
hash ^= 0xf0f0;
if (parseIntegerOnly)
hash ^= 0x0f0f;
return hash;
}
public boolean isGroupingUsed ()
{
return groupingUsed;
}
public boolean isParseIntegerOnly ()
{
return parseIntegerOnly;
}
public NumberFormat ()
{
}
public abstract Number parse (String sourceStr, ParsePosition pos);
public Number parse (String sourceStr) throws ParseException
{
ParsePosition pp = new ParsePosition (0);
Number r = parse (sourceStr, pp);
if (r == null)
{
int index = pp.getErrorIndex();
if (index < 0)
index = pp.getIndex();
throw new ParseException ("couldn't parse number", index);
}
return r;
}
public final Object parseObject (String sourceStr, ParsePosition pos)
{
return parse (sourceStr, pos);
}
public void setGroupingUsed (boolean newValue)
{
groupingUsed = newValue;
}
public void setMaximumFractionDigits (int newValue)
{
maximumFractionDigits = newValue;
}
public void setMaximumIntegerDigits (int newValue)
{
maximumIntegerDigits = newValue;
}
public void setMinimumFractionDigits (int newValue)
{
minimumFractionDigits = newValue;
}
public void setMinimumIntegerDigits (int newValue)
{
minimumIntegerDigits = newValue;
}
public void setParseIntegerOnly (boolean value)
{
parseIntegerOnly = value;
}
public final String format (double number)
{
StringBuffer sbuf = new StringBuffer(50);
format (number, sbuf, null);
return sbuf.toString();
}
// These field names are fixed by the serialization spec.
// FIXME: serialization spec also mentions `byte' versions of the
// min/max fields. We have no use for those, so for now they are
// omitted.
protected boolean groupingUsed;
protected int maximumFractionDigits;
protected int maximumIntegerDigits;
protected int minimumFractionDigits;
protected int minimumIntegerDigits;
protected boolean parseIntegerOnly;
}

View file

@ -0,0 +1,34 @@
/* Copyright (C) 1998, 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
/**
* @author Per Bothner <bothner@cygnus.com>
* @date October 25, 1998.
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 beta from http://www.javasoft.com.
* Status: Believed complete and correct.
*/
public class ParseException extends Exception
{
private int errorOffset;
public ParseException (String msg, int errorOffset)
{
super(msg);
this.errorOffset = errorOffset;
}
public int getErrorOffset ()
{
return errorOffset;
}
}

View file

@ -0,0 +1,59 @@
/* Copyright (C) 1998, 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
/**
* @author Per Bothner <bothner@cygnus.com>
* @date October 25, 1998.
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 beta from http://www.javasoft.com.
* Status: Believed complete and correct.
* Includes JDK 1.2 methods.
*/
public class ParsePosition
{
int index;
int errorIndex;
public ParsePosition (int index)
{
this.index = index;
errorIndex = -1;
}
public int getIndex ()
{
return index;
}
public void setIndex (int index)
{
this.index = index;
}
public int getErrorIndex ()
{
return errorIndex;
}
public void setErrorIndex (int ei)
{
errorIndex = ei;
}
public boolean equals (Object obj)
{
if (obj != null || ! (obj instanceof ParsePosition))
return false;
ParsePosition other = (ParsePosition) obj;
return index == other.index && errorIndex == other.errorIndex;
}
}

View file

@ -0,0 +1,522 @@
/* Copyright (C) 1998, 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
import java.util.*;
/**
* @author Per Bothner <bothner@cygnus.com>
* @date October 25, 1998.
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 beta from http://www.javasoft.com.
* Status: parse is not implemented.
*/
public class SimpleDateFormat extends DateFormat
{
private Date defaultCenturyStart;
private DateFormatSymbols formatData;
private String pattern;
public SimpleDateFormat ()
{
this("dd/MM/yy HH:mm", Locale.getDefault());
}
public SimpleDateFormat (String pattern)
{
this(pattern, Locale.getDefault());
}
public SimpleDateFormat (String pattern, Locale locale)
{
this.pattern = pattern;
this.calendar = Calendar.getInstance(locale);
this.numberFormat = NumberFormat.getInstance(locale);
numberFormat.setGroupingUsed(false);
this.formatData = new DateFormatSymbols (locale);
}
public SimpleDateFormat (String pattern, DateFormatSymbols formatData)
{
this.pattern = pattern;
this.formatData = formatData;
this.calendar = Calendar.getInstance();
this.numberFormat = NumberFormat.getInstance();
numberFormat.setGroupingUsed(false);
}
public Date get2DigitYearStart()
{
return defaultCenturyStart;
}
public void set2DigitYearStart(Date startDate)
{
defaultCenturyStart = startDate;
}
public DateFormatSymbols getDateFormatSymbols ()
{
return formatData;
}
public void setDateFormatSymbols (DateFormatSymbols value)
{
formatData = value;
}
public String toPattern ()
{
return pattern;
}
public void applyPattern (String pattern)
{
this.pattern = pattern;
}
private String applyLocalizedPattern (String pattern,
String oldChars, String newChars)
{
int len = pattern.length();
StringBuffer buf = new StringBuffer(len);
boolean quoted = false;
for (int i = 0; i < len; i++)
{
char ch = pattern.charAt(i);
if (ch == '\'')
quoted = ! quoted;
if (! quoted)
{
int j = oldChars.indexOf(ch);
if (j >= 0)
ch = newChars.charAt(j);
}
buf.append(ch);
}
return buf.toString();
}
public void applyLocalizedPattern (String pattern)
{
String localChars = formatData.getLocalPatternChars();
String standardChars = DateFormatSymbols.localPatternCharsDefault;
pattern = applyLocalizedPattern (pattern, localChars, standardChars);
applyPattern(pattern);
}
public String toLocalizedPattern ()
{
String localChars = formatData.getLocalPatternChars();
String standardChars = DateFormatSymbols.localPatternCharsDefault;
return applyLocalizedPattern (pattern, standardChars, localChars);
}
private final void append (StringBuffer buf, int value, int numDigits)
{
numberFormat.setMinimumIntegerDigits(numDigits);
numberFormat.format(value, buf, null);
}
public StringBuffer format (Date date, StringBuffer buf, FieldPosition pos)
{
Calendar calendar = (Calendar) this.calendar.clone();
calendar.setTime(date);
int len = pattern.length();
int quoteStart = -1;
for (int i = 0; i < len; i++)
{
char ch = pattern.charAt(i);
if (ch == '\'')
{
// We must do a little lookahead to see if we have two
// single quotes embedded in quoted text.
if (i < len - 1 && pattern.charAt(i + 1) == '\'')
{
++i;
buf.append(ch);
}
else
quoteStart = quoteStart < 0 ? i : -1;
}
// From JCL: any characters in the pattern that are not in
// the ranges of [a..z] and [A..Z] are treated as quoted
// text.
else if (quoteStart != -1
|| ((ch < 'a' || ch > 'z')
&& (ch < 'A' || ch > 'Z')))
buf.append(ch);
else
{
int first = i;
int value;
while (++i < len && pattern.charAt(i) == ch) ;
int count = i - first; // Number of repetions of ch in pattern.
int beginIndex = buf.length();
int field;
i--; // Skip all but last instance of ch in pattern.
switch (ch)
{
case 'd':
append(buf, calendar.get(Calendar.DATE), count);
field = DateFormat.DATE_FIELD;
break;
case 'D':
append(buf, calendar.get(Calendar.DAY_OF_YEAR), count);
field = DateFormat.DAY_OF_YEAR_FIELD;
break;
case 'F':
append(buf, calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH),count);
field = DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD;
break;
case 'E':
value = calendar.get(calendar.DAY_OF_WEEK);
buf.append(count <= 3 ? formatData.getShortWeekdays()[value]
: formatData.getWeekdays()[value]);
field = DateFormat.DAY_OF_WEEK_FIELD;
break;
case 'w':
append(buf, calendar.get(Calendar.WEEK_OF_YEAR), count);
field = DateFormat.WEEK_OF_YEAR_FIELD;
break;
case 'W':
append(buf, calendar.get(Calendar.WEEK_OF_MONTH), count);
field = DateFormat.WEEK_OF_MONTH_FIELD;
break;
case 'M':
value = calendar.get(Calendar.MONTH);
if (count <= 2)
append(buf, value + 1, count);
else
buf.append(count <= 3 ? formatData.getShortMonths()[value]
: formatData.getMonths()[value]);
field = DateFormat.MONTH_FIELD;
break;
case 'y':
value = calendar.get(Calendar.YEAR);
append(buf, count <= 2 ? value % 100 : value, count);
field = DateFormat.YEAR_FIELD;
break;
case 'K':
append(buf, calendar.get(Calendar.HOUR), count);
field = DateFormat.HOUR0_FIELD;
break;
case 'h':
value = ((calendar.get(Calendar.HOUR) + 11) % 12) + 1;
append(buf, value, count);
field = DateFormat.HOUR1_FIELD;
break;
case 'H':
append(buf, calendar.get(Calendar.HOUR_OF_DAY), count);
field = DateFormat.HOUR_OF_DAY0_FIELD;
break;
case 'k':
value = ((calendar.get(Calendar.HOUR_OF_DAY) + 23) % 24) + 1;
append(buf, value, count);
field = DateFormat.HOUR_OF_DAY1_FIELD;
break;
case 'm':
append(buf, calendar.get(Calendar.MINUTE), count);
field = DateFormat.MINUTE_FIELD;
break;
case 's':
append(buf, calendar.get(Calendar.SECOND), count);
field = DateFormat.SECOND_FIELD;
break;
case 'S':
append(buf, calendar.get(Calendar.MILLISECOND), count);
field = DateFormat.MILLISECOND_FIELD;
break;
case 'a':
value = calendar.get(calendar.AM_PM);
buf.append(formatData.getAmPmStrings()[value]);
field = DateFormat.AM_PM_FIELD;
break;
case 'z':
String zoneID = calendar.getTimeZone().getID();
String[][] zoneStrings = formatData.getZoneStrings();
int zoneCount = zoneStrings.length;
for (int j = 0; j < zoneCount; j++)
{
String[] strings = zoneStrings[j];
if (zoneID.equals(strings[0]))
{
j = count > 3 ? 2 : 1;
if (calendar.get(Calendar.DST_OFFSET) != 0)
j+=2;
zoneID = strings[j];
break;
}
}
buf.append(zoneID);
field = DateFormat.TIMEZONE_FIELD;
break;
default:
// Note that the JCL is actually somewhat
// contradictory here. It defines the pattern letters
// to be a particular list, but also says that a
// pattern containing an invalid pattern letter must
// throw an exception. It doesn't describe what an
// invalid pattern letter might be, so we just assume
// it is any letter in [a-zA-Z] not explicitly covered
// above.
throw new RuntimeException("bad format string");
}
if (pos != null && field == pos.getField())
{
pos.setBeginIndex(beginIndex);
pos.setEndIndex(buf.length());
}
}
}
return buf;
}
private final boolean expect (String source, ParsePosition pos,
char ch)
{
int x = pos.getIndex();
boolean r = x < source.length() && source.charAt(x) == ch;
if (r)
pos.setIndex(x + 1);
else
pos.setErrorIndex(x);
return r;
}
public Date parse (String source, ParsePosition pos)
{
int fmt_index = 0;
int fmt_max = pattern.length();
calendar.clear();
int quote_start = -1;
for (; fmt_index < fmt_max; ++fmt_index)
{
char ch = pattern.charAt(fmt_index);
if (ch == '\'')
{
int index = pos.getIndex();
if (fmt_index < fmt_max - 1
&& pattern.charAt(fmt_index + 1) == '\'')
{
if (! expect (source, pos, ch))
return null;
++fmt_index;
}
else
quote_start = quote_start < 0 ? fmt_index : -1;
continue;
}
if (quote_start != -1
|| ((ch < 'a' || ch > 'z')
&& (ch < 'A' || ch > 'Z')))
{
if (! expect (source, pos, ch))
return null;
continue;
}
// We've arrived at a potential pattern character in the
// pattern.
int first = fmt_index;
while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch)
;
int count = fmt_index - first;
--fmt_index;
// We can handle most fields automatically: most either are
// numeric or are looked up in a string vector. In some cases
// we need an offset. When numeric, `offset' is added to the
// resulting value. When doing a string lookup, offset is the
// initial index into the string array.
int calendar_field;
boolean is_numeric = true;
String[] match = null;
int offset = 0;
int zone_number = 0;
switch (ch)
{
case 'd':
calendar_field = Calendar.DATE;
break;
case 'D':
calendar_field = Calendar.DAY_OF_YEAR;
break;
case 'F':
calendar_field = Calendar.DAY_OF_WEEK_IN_MONTH;
break;
case 'E':
is_numeric = false;
offset = 1;
calendar_field = Calendar.DAY_OF_WEEK;
match = (count <= 3
? formatData.getShortWeekdays()
: formatData.getWeekdays());
break;
case 'w':
calendar_field = Calendar.WEEK_OF_YEAR;
break;
case 'W':
calendar_field = Calendar.WEEK_OF_MONTH;
break;
case 'M':
calendar_field = Calendar.MONTH;
if (count <= 2)
;
else
{
is_numeric = false;
match = (count <= 3
? formatData.getShortMonths()
: formatData.getMonths());
}
break;
case 'y':
calendar_field = Calendar.YEAR;
if (count <= 2)
offset = 1900;
break;
case 'K':
calendar_field = Calendar.HOUR;
break;
case 'h':
calendar_field = Calendar.HOUR;
offset = -1;
break;
case 'H':
calendar_field = Calendar.HOUR_OF_DAY;
break;
case 'k':
calendar_field = Calendar.HOUR_OF_DAY;
offset = -1;
break;
case 'm':
calendar_field = Calendar.MINUTE;
break;
case 's':
calendar_field = Calendar.SECOND;
break;
case 'S':
calendar_field = Calendar.MILLISECOND;
break;
case 'a':
is_numeric = false;
calendar_field = Calendar.AM_PM;
match = formatData.getAmPmStrings();
break;
case 'z':
// We need a special case for the timezone, because it
// uses a different data structure than the other cases.
is_numeric = false;
calendar_field = Calendar.DST_OFFSET;
String[][] zoneStrings = formatData.getZoneStrings();
int zoneCount = zoneStrings.length;
int index = pos.getIndex();
boolean found_zone = false;
for (int j = 0; j < zoneCount; j++)
{
String[] strings = zoneStrings[j];
int k;
for (k = 1; k < strings.length; ++k)
{
if (source.startsWith(strings[k], index))
break;
}
if (k != strings.length)
{
if (k > 2)
; // FIXME: dst.
zone_number = 0; // FIXME: dst.
// FIXME: raw offset to SimpleTimeZone const.
calendar.setTimeZone(new SimpleTimeZone (1, strings[0]));
pos.setIndex(index + strings[k].length());
break;
}
}
if (! found_zone)
{
pos.setErrorIndex(pos.getIndex());
return null;
}
break;
default:
pos.setErrorIndex(pos.getIndex());
return null;
}
// Compute the value we should assign to the field.
int value;
if (is_numeric)
{
numberFormat.setMinimumIntegerDigits(count);
Number n = numberFormat.parse(source, pos);
if (pos == null || ! (n instanceof Long))
return null;
value = n.intValue() + offset;
}
else if (match != null)
{
int index = pos.getIndex();
int i;
for (i = offset; i < match.length; ++i)
{
if (source.startsWith(match[i], index))
break;
}
if (i == match.length)
{
pos.setErrorIndex(index);
return null;
}
pos.setIndex(index + match[i].length());
value = i;
}
else
value = zone_number;
// Assign the value and move on.
try
{
calendar.set(calendar_field, value);
}
// FIXME: what exception is thrown on an invalid
// non-lenient set?
catch (IllegalArgumentException x)
{
pos.setErrorIndex(pos.getIndex());
return null;
}
}
return calendar.getTime();
}
public boolean equals (Object obj)
{
if (! (obj instanceof SimpleDateFormat) || ! super.equals(obj) )
return false;
SimpleDateFormat other = (SimpleDateFormat) obj;
return (DateFormatSymbols.equals(pattern, other.pattern)
&& DateFormatSymbols.equals(formatData, other.formatData)
&& DateFormatSymbols.equals(defaultCenturyStart,
other.defaultCenturyStart));
}
public int hashCode ()
{
int hash = super.hashCode();
if (pattern != null)
hash ^= pattern.hashCode();
return hash;
}
}

View file

@ -0,0 +1,142 @@
// StringCharacterIterator.java - Iterate over string of Unicode characters.
/* Copyright (C) 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
package java.text;
/**
* @author Tom Tromey <tromey@cygnus.com>
* @date February 22, 1999
*/
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 beta from http://www.javasoft.com.
* Status: Believed complete and correct to 1.1.
*/
public final class StringCharacterIterator implements CharacterIterator
{
public Object clone ()
{
return (Object) new StringCharacterIterator (text, begin, end, pos);
}
public char current ()
{
// This follows JDK 1.2 semantics and not 1.1 semantics.
// In 1.1 we would throw an exception if begin==end.
return (pos < end) ? text.charAt(pos) : CharacterIterator.DONE;
}
public boolean equals (Object obj)
{
if (! (obj instanceof StringCharacterIterator))
return false;
StringCharacterIterator sci = (StringCharacterIterator) obj;
// The spec says "the same text". We take this to mean equals,
// not ==.
return (pos == sci.pos
&& begin == sci.begin
&& end == sci.end
&& text.equals(sci.text));
}
public char first ()
{
pos = begin;
return current ();
}
public int getBeginIndex ()
{
return begin;
}
public int getEndIndex ()
{
return end;
}
public int getIndex ()
{
return pos;
}
public int hashCode ()
{
// FIXME: this is a terrible hash code. Find a better one.
return text.hashCode() + pos + begin + end;
}
public char last ()
{
pos = end;
return current ();
}
public char next ()
{
if (pos == end)
return CharacterIterator.DONE;
++pos;
return current ();
}
public char previous ()
{
if (pos == begin)
return CharacterIterator.DONE;
--pos;
return current ();
}
public char setIndex (int idx)
{
// In 1.1 we would throw an error if `idx == end'.
if (idx < begin || idx > end)
throw new IllegalArgumentException ();
pos = idx;
return current ();
}
public StringCharacterIterator (String text)
{
// FIXME: remove check for null once we have compiler/runtime
// support for NullPointerException.
this (text, 0, text == null ? 0 : text.length(), 0);
}
public StringCharacterIterator (String text, int pos)
{
// FIXME: remove check for null once we have compiler/runtime
// support for NullPointerException.
this (text, 0, text == null ? 0 : text.length(), pos);
}
public StringCharacterIterator (String text, int begin, int end, int pos)
{
if (text == null)
throw new NullPointerException ();
if (begin < 0 || begin > end || end > text.length()
// In 1.1 we would also throw if `pos == end'.
|| pos < begin || pos > end)
throw new IllegalArgumentException ();
this.text = text;
this.begin = begin;
this.end = end;
this.pos = pos;
}
// String to iterate over.
private String text;
// Current position.
private int pos;
// Start position in string.
private int begin;
// End position in string.
private int end;
}