summaryrefslogtreecommitdiffstats
path: root/libjava/classpath/java/text
diff options
context:
space:
mode:
authortromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>2005-07-16 00:30:23 +0000
committertromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>2005-07-16 00:30:23 +0000
commitc8875fb97fc03779a5bba09872227b1d08e5d52a (patch)
treea0b991cf5866ae1d616639b906ac001811d74508 /libjava/classpath/java/text
parentc40c1730800ed292b6db39a83d592476fa59623c (diff)
downloadppe42-gcc-c8875fb97fc03779a5bba09872227b1d08e5d52a.tar.gz
ppe42-gcc-c8875fb97fc03779a5bba09872227b1d08e5d52a.zip
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@102074 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/classpath/java/text')
-rw-r--r--libjava/classpath/java/text/Annotation.java113
-rw-r--r--libjava/classpath/java/text/AttributedCharacterIterator.java268
-rw-r--r--libjava/classpath/java/text/AttributedString.java425
-rw-r--r--libjava/classpath/java/text/AttributedStringIterator.java348
-rw-r--r--libjava/classpath/java/text/BreakIterator.java374
-rw-r--r--libjava/classpath/java/text/CharacterIterator.java144
-rw-r--r--libjava/classpath/java/text/ChoiceFormat.java503
-rw-r--r--libjava/classpath/java/text/CollationElementIterator.java467
-rw-r--r--libjava/classpath/java/text/CollationKey.java199
-rw-r--r--libjava/classpath/java/text/Collator.java400
-rw-r--r--libjava/classpath/java/text/DateFormat.java892
-rw-r--r--libjava/classpath/java/text/DateFormatSymbols.java525
-rw-r--r--libjava/classpath/java/text/DecimalFormat.java1435
-rw-r--r--libjava/classpath/java/text/DecimalFormatSymbols.java688
-rw-r--r--libjava/classpath/java/text/FieldPosition.java232
-rw-r--r--libjava/classpath/java/text/Format.java181
-rw-r--r--libjava/classpath/java/text/MessageFormat.java832
-rw-r--r--libjava/classpath/java/text/NumberFormat.java808
-rw-r--r--libjava/classpath/java/text/ParseException.java86
-rw-r--r--libjava/classpath/java/text/ParsePosition.java151
-rw-r--r--libjava/classpath/java/text/RuleBasedCollator.java1017
-rw-r--r--libjava/classpath/java/text/SimpleDateFormat.java1257
-rw-r--r--libjava/classpath/java/text/StringCharacterIterator.java356
-rw-r--r--libjava/classpath/java/text/class-dependencies.conf220
-rw-r--r--libjava/classpath/java/text/package.html47
25 files changed, 11968 insertions, 0 deletions
diff --git a/libjava/classpath/java/text/Annotation.java b/libjava/classpath/java/text/Annotation.java
new file mode 100644
index 00000000000..cecb44aaf0e
--- /dev/null
+++ b/libjava/classpath/java/text/Annotation.java
@@ -0,0 +1,113 @@
+/* Annotation.java -- Wrapper for a text attribute object
+ Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+/**
+ * This class is used as a wrapper for a text attribute object. Annotation
+ * objects are associated with a specific range of text. Changing either
+ * the text range or the underlying text invalidates the object.
+ *
+ * @version 0.0
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ */
+public class Annotation
+{
+
+/*
+ * Instance Variables
+ */
+
+/**
+ * This is the attribute object being wrappered
+ */
+private Object attrib;
+
+/*************************************************************************/
+
+/**
+ * Constructors
+ */
+
+/**
+ * This method initializes a new instance of <code>Annotation</code> to
+ * wrapper the specified text attribute object.
+ *
+ * @param attrib The text attribute <code>Object</code> to wrapper.
+ */
+public
+Annotation(Object attrib)
+{
+ this.attrib = attrib;
+}
+
+/*************************************************************************/
+
+/*
+ * Instance Variables
+ */
+
+/**
+ * This method returns the text attribute object this <code>Annotation</code>
+ * instance is wrappering.
+ *
+ * @return The text attribute object for this <code>Annotation</code>.
+ */
+public Object
+getValue()
+{
+ return(attrib);
+}
+
+/*************************************************************************/
+
+/**
+ * This method returns a <code>String</code> representation of this
+ * object.
+ *
+ * @return This object as a <code>String</code>.
+ */
+public String
+toString()
+{
+ return(getClass().getName() + "[value=" + attrib.toString() + "]");
+}
+
+} // class Annotation
+
diff --git a/libjava/classpath/java/text/AttributedCharacterIterator.java b/libjava/classpath/java/text/AttributedCharacterIterator.java
new file mode 100644
index 00000000000..e5686ba346d
--- /dev/null
+++ b/libjava/classpath/java/text/AttributedCharacterIterator.java
@@ -0,0 +1,268 @@
+/* AttributedCharacterIterator.java -- Iterate over attributes
+ Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.io.InvalidObjectException;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This interface extends the <code>CharacterIterator</code> interface
+ * in order to support iteration over character attributes as well as
+ * over the characters themselves.
+ * <p>
+ * In addition to attributes of specific characters, this interface
+ * supports the concept of the "attribute run", which is an attribute
+ * that is defined for a particular value across an entire range of
+ * characters or which is undefined over a range of characters.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ */
+public interface AttributedCharacterIterator extends CharacterIterator
+{
+ /**
+ * This class defines attribute keys that are used as text attributes.
+ */
+ public static class Attribute implements Serializable
+ {
+ private static final long serialVersionUID = -9142742483513960612L;
+
+ /**
+ * This is the attribute for the language of the text. The value of
+ * attributes of this key type are instances of <code>Locale</code>.
+ */
+ public static final Attribute LANGUAGE = new Attribute ("LANGUAGE");
+
+ /**
+ * This is the attribute for the reading form of text. This is used
+ * for storing pronunciation along with the written text for languages
+ * which need it. The value of attributes of this key type are
+ * instances of <code>Annotation</code> which wrappers a <code>String</code>.
+ */
+ public static final Attribute READING = new Attribute ("READING");
+
+ /**
+ * This is the attribute for input method segments. The value of attributes
+ * of this key type are instances of <code>Annotation</code> which wrapper
+ * a <code>String</code>.
+ */
+ public static final Attribute INPUT_METHOD_SEGMENT =
+ new Attribute ("INPUT_METHOD_SEGMENT");
+
+ /**
+ * This is the name of the attribute key
+ * @serial
+ */
+ private String name;
+
+ /**
+ * This method initializes a new instance of this class with the specified
+ * name.
+ *
+ * @param name The name of this attribute key.
+ */
+ protected Attribute (String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * This method returns the name of this attribute.
+ *
+ * @return The attribute name
+ */
+ protected String getName()
+ {
+ return name;
+ }
+
+ /**
+ * This method resolves an instance of <code>AttributedCharacterIterator.Attribute</code>
+ * that is being deserialized to one of the three pre-defined attribute
+ * constants. It does this by comparing the names of the attributes. The
+ * constant that the deserialized object resolves to is returned.
+ *
+ * @return The resolved contant value
+ *
+ * @exception InvalidObjectException If the object being deserialized cannot be resolved.
+ */
+ protected Object readResolve() throws InvalidObjectException
+ {
+ if (this.equals (READING))
+ return READING;
+
+ if (this.equals (LANGUAGE))
+ return LANGUAGE;
+
+ if (this.equals (INPUT_METHOD_SEGMENT))
+ return INPUT_METHOD_SEGMENT;
+
+ throw new InvalidObjectException ("Can't resolve Attribute: " + getName());
+ }
+
+ /**
+ * This method tests this object for equality against the specified object.
+ * The two objects will be considered equal if and only if:
+ * <ul>
+ * <li>The specified object is not <code>null</code>.
+ * <li>The specified object is an instance of <code>AttributedCharacterIterator.Attribute</code>.
+ * <li>The specified object has the same attribute name as this object.
+ * </ul>
+ *
+ * @param The <code>Object</code> to test for equality against this object.
+ *
+ * @return <code>true</code> if the specified object is equal to this one, <code>false</code> otherwise.
+ */
+ public final boolean equals (Object obj)
+ {
+ if (obj == this)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * This method returns a hash value for this object.
+ *
+ * @return A hash value for this object.
+ */
+ public final int hashCode()
+ {
+ return super.hashCode();
+ }
+
+ /**
+ * This method returns a <code>String</code> representation of this object.
+ *
+ * @return A <code>String</code> representation of this object.
+ */
+ public String toString()
+ {
+ return getClass().getName() + "(" + getName() + ")";
+ }
+
+ } // Inner class Attribute
+
+ /**
+ * This method returns a list of all keys that are defined for the
+ * text range. This can be an empty list if no attributes are defined.
+ *
+ * @return A list of keys
+ */
+ Set getAllAttributeKeys();
+
+ /**
+ * This method returns a <code>Map</code> of the attributed defined for
+ * the current character.
+ *
+ * @return A <code>Map</code> of the attributes for the current character.
+ */
+ Map getAttributes();
+
+ /**
+ * This method returns the value of the specified attribute for the
+ * current character. If the attribute is not defined for the current
+ * character, <code>null</code> is returned.
+ *
+ * @param attrib The attribute to retrieve the value of.
+ *
+ * @return The value of the specified attribute
+ */
+ Object getAttribute (AttributedCharacterIterator.Attribute attrib);
+
+ /**
+ * This method returns the index of the first character in the run that
+ * contains all attributes defined for the current character.
+ *
+ * @return The start index of the run
+ */
+ int getRunStart();
+
+ /**
+ * This method returns the index of the first character in the run that
+ * contains all attributes in the specified <code>Set</code> defined for
+ * the current character.
+ *
+ * @param attribs The <code>Set</code> of attributes.
+ *
+ * @return The start index of the run.
+ */
+ int getRunStart (Set attribs);
+
+ /**
+ * This method returns the index of the first character in the run that
+ * contains the specified attribute defined for the current character.
+ *
+ * @param attrib The attribute.
+ *
+ * @return The start index of the run.
+ */
+ int getRunStart (AttributedCharacterIterator.Attribute attrib);
+
+ /**
+ * This method returns the index of the character after the end of the run
+ * that contains all attributed defined for the current character.
+ *
+ * @return The end index of the run.
+ */
+ int getRunLimit();
+
+ /**
+ * This method returns the index of the character after the end of the run
+ * that contains all attributes in the specified <code>Set</code> defined
+ * for the current character.
+ *
+ * @param attribs The <code>Set</code> of attributes.
+ *
+ * @return The end index of the run.
+ */
+ int getRunLimit (Set attribs);
+
+ /**
+ * This methods returns the index of the character after the end of the run
+ * that contains the specified attribute defined for the current character.
+ *
+ * @param attrib The attribute.
+ *
+ * @return The end index of the run.
+ */
+ int getRunLimit (AttributedCharacterIterator.Attribute attrib);
+
+} // interface AttributedCharacterIterator
diff --git a/libjava/classpath/java/text/AttributedString.java b/libjava/classpath/java/text/AttributedString.java
new file mode 100644
index 00000000000..b9ced8f5ac2
--- /dev/null
+++ b/libjava/classpath/java/text/AttributedString.java
@@ -0,0 +1,425 @@
+/* AttributedString.java -- Models text with attributes
+ Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class models a <code>String</code> with attributes over various
+ * subranges of the string. It allows applications to access this
+ * information via the <code>AttributedCharcterIterator</code> interface.
+ *
+ * @version 0.0
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ */
+public class AttributedString
+{
+
+/*************************************************************************/
+
+/*
+ * Inner Classes
+ */
+
+/**
+ * This class contains the attributes and ranges of text over which
+ * that attributes apply.
+ */
+final class AttributeRange
+{
+
+/*
+ * Instance Variables
+ */
+
+/**
+ * A Map of the attributes
+ */
+Map attribs;
+
+/**
+ * The beginning index of the attributes
+ */
+int begin_index;
+
+/**
+ * The ending index of the attributes
+ */
+int end_index;
+
+/*************************************************************************/
+
+/*
+ * Constructors
+ */
+
+AttributeRange(Map attribs, int begin_index, int end_index)
+{
+ this.attribs = attribs;
+ this.begin_index = begin_index;
+ this.end_index = end_index;
+}
+
+} // Inner class AttributeRange
+
+/*************************************************************************/
+
+/*
+ * Instance Variables
+ */
+
+/**
+ * This object holds the string we are representing.
+ */
+private StringCharacterIterator sci;
+
+/**
+ * This is the attribute information
+ */
+private AttributeRange[] attribs;
+
+/*************************************************************************/
+
+/*
+ * Constructors
+ */
+
+/**
+ * This method initializes a new instance of <code>AttributedString</code>
+ * that represents the specified <code>String</code> with no attributes.
+ *
+ * @param str The <code>String</code> to be attributed.
+ */
+public
+AttributedString(String str)
+{
+ sci = new StringCharacterIterator(str);
+ attribs = new AttributeRange[0];
+}
+
+/*************************************************************************/
+
+/**
+ * This method initializes a new instance of <code>AttributedString</code>
+ * that represents that specified <code>String</code> with the specified
+ * attributes over the entire length of the <code>String</code>.
+ *
+ * @param str The <code>String</code> to be attributed.
+ * @param attributes The attribute list.
+ */
+public
+AttributedString(String str, Map attributes)
+{
+ this(str);
+
+ attribs = new AttributeRange[1];
+ attribs[0] = new AttributeRange(attributes, 0, str.length());
+}
+
+/*************************************************************************/
+
+/**
+ * This method initializes a new instance of <code>AttributedString</code>
+ * that will use the text and attribute information from the specified
+ * <code>AttributedCharacterIterator</code>.
+ *
+ * @param aci The <code>AttributedCharacterIterator</code> containing the text and attribute information.
+ */
+public
+AttributedString(AttributedCharacterIterator aci)
+{
+ this(aci, aci.getBeginIndex(), aci.getEndIndex(), null);
+}
+
+/*************************************************************************/
+
+/**
+ * This method initializes a new instance of <code>AttributedString</code>
+ * that will use the text and attribute information from the specified
+ * subrange of the specified <code>AttributedCharacterIterator</code>.
+ *
+ * @param aci The <code>AttributedCharacterIterator</code> containing the text and attribute information.
+ * @param begin_index The beginning index of the text subrange.
+ * @param end_index The ending index of the text subrange.
+ */
+public
+AttributedString(AttributedCharacterIterator aci, int begin_index,
+ int end_index)
+{
+ this(aci, begin_index, end_index, null);
+}
+
+/*************************************************************************/
+
+/**
+ * This method initializes a new instance of <code>AttributedString</code>
+ * that will use the text and attribute information from the specified
+ * subrange of the specified <code>AttributedCharacterIterator</code>.
+ * Only attributes from the source iterator that are present in the
+ * specified array of attributes will be included in the attribute list
+ * for this object.
+ *
+ * @param aci The <code>AttributedCharacterIterator</code> containing the text and attribute information.
+ * @param begin_index The beginning index of the text subrange.
+ * @param end_index The ending index of the text subrange.
+ * @param attributes A list of attributes to include from the iterator, or <code>null</code> to include all attributes.
+ */
+public
+AttributedString(AttributedCharacterIterator aci, int begin_index,
+ int end_index, AttributedCharacterIterator.Attribute[] attributes)
+{
+ // Validate some arguments
+ if ((begin_index < 0) || (end_index < begin_index))
+ throw new IllegalArgumentException("Bad index values");
+
+ StringBuffer sb = new StringBuffer("");
+
+ // Get the valid attribute list
+ Set all_attribs = aci.getAllAttributeKeys();
+ if (attributes != null)
+ all_attribs.retainAll(Arrays.asList(attributes));
+
+ // Loop through and extract the attributes
+ char c = aci.setIndex(begin_index);
+
+ ArrayList accum = new ArrayList();
+ do
+ {
+ sb.append(c);
+
+ Iterator iter = all_attribs.iterator();
+ while(iter.hasNext())
+ {
+ Object obj = iter.next();
+
+ // What should we do if this is not true?
+ if (!(obj instanceof AttributedCharacterIterator.Attribute))
+ continue;
+
+ AttributedCharacterIterator.Attribute attrib =
+ (AttributedCharacterIterator.Attribute)obj;
+
+ // Make sure the attribute is defined.
+ int rl = aci.getRunLimit(attrib);
+ if (rl == -1)
+ continue;
+ if (rl > end_index)
+ rl = end_index;
+ rl -= begin_index;
+
+ // Check to see if we already processed this one
+ int rs = aci.getRunStart(attrib);
+ if ((rs < aci.getIndex()) && (aci.getIndex() != begin_index))
+ continue;
+
+ // If the attribute run starts before the beginning index, we
+ // need to junk it if it is an Annotation.
+ Object attrib_obj = aci.getAttribute(attrib);
+ if (rs < begin_index)
+ {
+ if (attrib_obj instanceof Annotation)
+ continue;
+
+ rs = begin_index;
+ }
+ else
+ {
+ rs -= begin_index;
+ }
+
+ // Create a map object. Yes this will only contain one attribute
+ Map new_map = new Hashtable();
+ new_map.put(attrib, attrib_obj);
+
+ // Add it to the attribute list.
+ accum.add(new AttributeRange(new_map, rs, rl));
+ }
+
+ c = aci.next();
+ }
+ while(c != CharacterIterator.DONE);
+
+ attribs = new AttributeRange[accum.size()];
+ attribs = (AttributeRange[]) accum.toArray(attribs);
+
+ sci = new StringCharacterIterator(sb.toString());
+}
+
+/*************************************************************************/
+
+/*
+ * Instance Methods
+ */
+
+/**
+ * This method adds a new attribute that will cover the entire string.
+ *
+ * @param attrib The attribute to add.
+ * @param value The value of the attribute.
+ */
+public void
+addAttribute(AttributedCharacterIterator.Attribute attrib, Object value)
+{
+ addAttribute(attrib, value, 0, sci.getEndIndex());
+}
+
+/*************************************************************************/
+
+/**
+ * This method adds a new attribute that will cover the specified subrange
+ * of the string.
+ *
+ * @param attrib The attribute to add.
+ * @param value The value of the attribute, which may be null.
+ * @param begin_index The beginning index of the subrange.
+ * @param end_index The ending index of the subrange.
+ *
+ * @exception IllegalArgumentException If attribute is <code>null</code> or the subrange is not valid.
+ */
+public void
+addAttribute(AttributedCharacterIterator.Attribute attrib, Object value,
+ int begin_index, int end_index)
+{
+ if (attrib == null)
+ throw new IllegalArgumentException("null attribute");
+
+ HashMap hm = new HashMap();
+ hm.put(attrib, value);
+
+ addAttributes(hm, begin_index, end_index);
+}
+
+/*************************************************************************/
+
+/**
+ * This method adds all of the attributes in the specified list to the
+ * specified subrange of the string.
+ *
+ * @param attributes The list of attributes.
+ * @param begin_index The beginning index.
+ * @param end_index The ending index
+ *
+ * @param IllegalArgumentException If the list is <code>null</code> or the subrange is not valid.
+ */
+public void
+addAttributes(Map attributes, int begin_index, int end_index)
+{
+ if (attributes == null)
+ throw new IllegalArgumentException("null attribute");
+
+ if ((begin_index < 0) || (end_index > sci.getEndIndex()) ||
+ (end_index < begin_index))
+ throw new IllegalArgumentException("bad range");
+
+ AttributeRange[] new_list = new AttributeRange[attribs.length + 1];
+ System.arraycopy(attribs, 0, new_list, 0, attribs.length);
+ attribs = new_list;
+ attribs[attribs.length - 1] = new AttributeRange(attributes, begin_index,
+ end_index);
+}
+
+/*************************************************************************/
+
+/**
+ * This method returns an <code>AttributedCharacterIterator</code> that
+ * will iterate over the entire string.
+ *
+ * @return An <code>AttributedCharacterIterator</code> for the entire string.
+ */
+public AttributedCharacterIterator
+getIterator()
+{
+ return(new AttributedStringIterator(sci, attribs, 0, sci.getEndIndex(), null));
+}
+
+/*************************************************************************/
+
+/**
+ * This method returns an <code>AttributedCharacterIterator</code> that
+ * will iterate over the entire string. This iterator will return information
+ * about the list of attributes in the specified array. Attributes not in
+ * the array may or may not be returned by the iterator. If the specified
+ * array is <code>null</code>, all attributes will be returned.
+ *
+ * @param attributes A list of attributes to include in the returned iterator.
+ *
+ * @return An <code>AttributedCharacterIterator</code> for this string.
+ */
+public AttributedCharacterIterator
+getIterator(AttributedCharacterIterator.Attribute[] attributes)
+{
+ return(getIterator(attributes, 0, sci.getEndIndex()));
+}
+
+/*************************************************************************/
+
+/**
+ * This method returns an <code>AttributedCharacterIterator</code> that
+ * will iterate over the specified subrange. This iterator will return information
+ * about the list of attributes in the specified array. Attributes not in
+ * the array may or may not be returned by the iterator. If the specified
+ * array is <code>null</code>, all attributes will be returned.
+ *
+ * @param attributes A list of attributes to include in the returned iterator.
+ * @param begin_index The beginning index of the subrange.
+ * @param end_index The ending index of the subrange.
+ *
+ * @return An <code>AttributedCharacterIterator</code> for this string.
+ */
+public AttributedCharacterIterator
+getIterator(AttributedCharacterIterator.Attribute[] attributes,
+ int begin_index, int end_index)
+{
+ if ((begin_index < 0) || (end_index > sci.getEndIndex()) ||
+ (end_index < begin_index))
+ throw new IllegalArgumentException("bad range");
+
+ return(new AttributedStringIterator(sci, attribs, begin_index, end_index,
+ attributes));
+}
+
+} // class AttributedString
+
diff --git a/libjava/classpath/java/text/AttributedStringIterator.java b/libjava/classpath/java/text/AttributedStringIterator.java
new file mode 100644
index 00000000000..7d7bf270cef
--- /dev/null
+++ b/libjava/classpath/java/text/AttributedStringIterator.java
@@ -0,0 +1,348 @@
+/* AttributedStringIterator.java -- Class to iterate over AttributedString
+ Copyright (C) 1998, 1999, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class implements the AttributedCharacterIterator interface. It
+ * is used by AttributedString.getIterator().
+ *
+ * @version 0.0
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ */
+class AttributedStringIterator implements AttributedCharacterIterator
+{
+
+/*************************************************************************/
+
+/**
+ * Instance Variables
+ */
+
+/**
+ * The character iterator containing the text
+ */
+private CharacterIterator ci;
+
+/**
+ * The list of attributes and ranges
+ */
+private AttributedString.AttributeRange[] attribs;
+
+/**
+ * The list of attributes that the user is interested in. We may,
+ * at our option, not return any other attributes.
+ */
+private AttributedCharacterIterator.Attribute[] restricts;
+
+/*************************************************************************/
+
+/*
+ * Constructors
+ */
+
+AttributedStringIterator(StringCharacterIterator sci,
+ AttributedString.AttributeRange[] attribs,
+ int begin_index, int end_index,
+ AttributedCharacterIterator.Attribute[] restricts)
+{
+ this.ci = new StringCharacterIterator(sci, begin_index, end_index);
+ this.attribs = attribs;
+ this.restricts = restricts;
+}
+
+/*************************************************************************/
+
+/*
+ * Instance Methods
+ */
+
+// First we have a bunch of stupid redirects. If StringCharacterIterator
+// weren't final, I just would have extended that for this class. Alas, no.
+
+public Object
+clone()
+{
+ return(ci.clone());
+}
+
+public char
+current()
+{
+ return(ci.current());
+}
+
+public char
+next()
+{
+ return(ci.next());
+}
+
+public char
+previous()
+{
+ return(ci.previous());
+}
+
+public char
+first()
+{
+ return(ci.first());
+}
+
+public char
+last()
+{
+ return(ci.last());
+}
+
+public int
+getIndex()
+{
+ return(ci.getIndex());
+}
+
+public char
+setIndex(int index)
+{
+ return(ci.setIndex(index));
+}
+
+public int
+getBeginIndex()
+{
+ return(ci.getBeginIndex());
+}
+
+public int
+getEndIndex()
+{
+ return(ci.getEndIndex());
+}
+
+/*
+ * Here is where the AttributedCharacterIterator methods start.
+ */
+
+/*************************************************************************/
+
+/**
+ * Returns a list of all the attribute keys that are defined anywhere
+ * on this string.
+ */
+public Set
+getAllAttributeKeys()
+{
+ HashSet s = new HashSet();
+ if (attribs == null)
+ return(s);
+
+ for (int i = 0; i < attribs.length; i++)
+ {
+ if (attribs[i].begin_index > getEndIndex()
+ || attribs[i].end_index <= getBeginIndex())
+ continue;
+
+ Set key_set = attribs[i].attribs.keySet();
+ Iterator iter = key_set.iterator();
+ while (iter.hasNext())
+ {
+ s.add(iter.next());
+ }
+ }
+
+ return(s);
+}
+
+/*************************************************************************/
+
+/**
+ * Various methods that determine how far the run extends for various
+ * attribute combinations.
+ */
+
+public int
+getRunLimit()
+{
+ return(getRunLimit(getAttributes().keySet()));
+}
+
+public int
+getRunLimit(AttributedCharacterIterator.Attribute attrib)
+{
+ HashSet s = new HashSet();
+ s.add(attrib);
+
+ return(getRunLimit(s));
+}
+
+public synchronized int
+getRunLimit(Set attribute_set)
+{
+ boolean hit = false;
+ int runLimit = ci.getEndIndex ();
+ int pos = ci.getIndex ();
+
+ for (int i = 0; i < attribs.length; ++i)
+ {
+ if (pos >= attribs[i].begin_index &&
+ pos < attribs[i].end_index)
+ {
+ Iterator iter = attribute_set.iterator();
+ while(iter.hasNext())
+ if (attribs[i].attribs.containsKey(iter.next()))
+ {
+ hit = true;
+ runLimit = Math.min(runLimit, attribs[i].end_index);
+ }
+ }
+ }
+ if (hit)
+ return runLimit;
+ else
+ return ci.getEndIndex();
+}
+
+/*************************************************************************/
+
+/**
+ * Various methods that determine where the run begins for various
+ * attribute combinations.
+ */
+
+public int
+getRunStart()
+{
+ return(getRunStart(getAttributes().keySet()));
+}
+
+public int
+getRunStart(AttributedCharacterIterator.Attribute attrib)
+{
+ HashSet s = new HashSet();
+ s.add(attrib);
+
+ return(getRunStart(s));
+}
+
+public int
+getRunStart(Set attribute_set)
+{
+ boolean hit = false;
+ int runBegin = 0;
+ int pos = ci.getIndex ();
+
+ for (int i = 0; i < attribs.length; ++i)
+ {
+ if (pos >= attribs[i].begin_index &&
+ pos <= attribs[i].end_index)
+ {
+ Iterator iter = attribute_set.iterator();
+ while(iter.hasNext())
+ if (attribs[i].attribs.containsKey(iter.next()))
+ {
+ hit = true;
+ runBegin = Math.max(runBegin, attribs[i].begin_index);
+ }
+ }
+ }
+ if (hit)
+ return runBegin;
+ else
+ return -1;
+}
+
+/*************************************************************************/
+
+public Object
+getAttribute(AttributedCharacterIterator.Attribute attrib)
+{
+ if (attribs == null)
+ return(null);
+
+ for (int i = 0; i < attribs.length; i++)
+ {
+ Set key_set = attribs[i].attribs.keySet();
+ Iterator iter = key_set.iterator();
+ while (iter.hasNext())
+ {
+ Object obj = iter.next();
+
+ // Check for attribute match and range match
+ if (obj.equals(attrib))
+ if ((ci.getIndex() >= attribs[i].begin_index) &&
+ (ci.getIndex() < attribs[i].end_index))
+ return(attribs[i].attribs.get(obj));
+ }
+ }
+
+ return(null);
+}
+
+/*************************************************************************/
+
+/**
+ * Return a list of all the attributes and values defined for this
+ * character
+ */
+public Map
+getAttributes()
+{
+ HashMap m = new HashMap();
+ if (attribs == null)
+ return(m);
+
+ for (int i = 0; i < attribs.length; i++)
+ {
+ if ((ci.getIndex() >= attribs[i].begin_index) &&
+ (ci.getIndex() < attribs[i].end_index))
+ m.putAll(attribs[i].attribs);
+ }
+
+ return(m);
+}
+
+} // class AttributedStringIterator
+
diff --git a/libjava/classpath/java/text/BreakIterator.java b/libjava/classpath/java/text/BreakIterator.java
new file mode 100644
index 00000000000..d021dceba73
--- /dev/null
+++ b/libjava/classpath/java/text/BreakIterator.java
@@ -0,0 +1,374 @@
+/* BreakIterator.java -- Breaks text into elements
+ Copyright (C) 1998, 1999, 2001, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * This class iterates over text elements such as words, lines, sentences,
+ * and characters. It can only iterate over one of these text elements at
+ * a time. An instance of this class configured for the desired iteration
+ * type is created by calling one of the static factory methods, not
+ * by directly calling a constructor.
+ *
+ * The standard iterators created by the factory methods in this
+ * class will be valid upon creation. That is, their methods will
+ * not cause exceptions if called before you call setText().
+ *
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Aaron M. Renn (arenn@urbanophile.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
+{
+ /**
+ * This value is returned by the <code>next()</code> and
+ * <code>previous</code> in order to indicate that the end of the
+ * text has been reached.
+ */
+ // The value was discovered by writing a test program.
+ public static final int DONE = -1;
+
+ /**
+ * This method initializes a new instance of <code>BreakIterator</code>.
+ * This protected constructor is available to subclasses as a default
+ * no-arg superclass constructor.
+ */
+ protected BreakIterator ()
+ {
+ }
+
+ /**
+ * Create a clone of this object.
+ */
+ public Object clone ()
+ {
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * This method returns the index of the current text element boundary.
+ *
+ * @return The current text boundary.
+ */
+ public abstract int current ();
+
+ /**
+ * This method returns the first text element boundary in the text being
+ * iterated over.
+ *
+ * @return The first text boundary.
+ */
+ public abstract int first ();
+
+ /**
+ * This methdod returns the offset of the text element boundary following
+ * the specified offset.
+ *
+ * @param offset The text index from which to find the next text boundary.
+ *
+ * @param The next text boundary following the specified index.
+ */
+ public abstract int following (int pos);
+
+ /**
+ * This method returns a list of locales for which instances of
+ * <code>BreakIterator</code> are available.
+ *
+ * @return A list of available locales
+ */
+ public static synchronized Locale[] getAvailableLocales ()
+ {
+ Locale[] l = new Locale[1];
+ l[0] = Locale.US;
+ return l;
+ }
+
+ private static BreakIterator getInstance (String type, Locale loc)
+ {
+ String className;
+ try
+ {
+ ResourceBundle res
+ = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+ loc, ClassLoader.getSystemClassLoader());
+ 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;
+ }
+ }
+
+ /**
+ * This method returns an instance of <code>BreakIterator</code> that will
+ * iterate over characters as defined in the default locale.
+ *
+ * @return A <code>BreakIterator</code> instance for the default locale.
+ */
+ public static BreakIterator getCharacterInstance ()
+ {
+ return getCharacterInstance (Locale.getDefault());
+ }
+
+ /**
+ * This method returns an instance of <code>BreakIterator</code> that will
+ * iterate over characters as defined in the specified locale. If the
+ * desired locale is not available, the default locale is used.
+ *
+ * @param locale The desired locale.
+ *
+ * @return A <code>BreakIterator</code> instance for the default locale.
+ */
+ public static BreakIterator getCharacterInstance (Locale loc)
+ {
+ BreakIterator r = getInstance ("CharacterIterator", loc);
+ if (r == null)
+ r = new gnu.java.text.CharacterBreakIterator ();
+ return r;
+ }
+
+ /**
+ * This method returns an instance of <code>BreakIterator</code> that will
+ * iterate over line breaks as defined in the default locale.
+ *
+ * @return A <code>BreakIterator</code> instance for the default locale.
+ */
+ public static BreakIterator getLineInstance ()
+ {
+ return getLineInstance (Locale.getDefault());
+ }
+
+ /**
+ * This method returns an instance of <code>BreakIterator</code> that will
+ * iterate over line breaks as defined in the specified locale. If the
+ * desired locale is not available, the default locale is used.
+ *
+ * @param locale The desired locale.
+ *
+ * @return A <code>BreakIterator</code> instance for the default locale.
+ */
+ public static BreakIterator getLineInstance (Locale loc)
+ {
+ BreakIterator r = getInstance ("LineIterator", loc);
+ if (r == null)
+ r = new gnu.java.text.LineBreakIterator ();
+ return r;
+ }
+
+ /**
+ * This method returns an instance of <code>BreakIterator</code> that will
+ * iterate over sentences as defined in the default locale.
+ *
+ * @return A <code>BreakIterator</code> instance for the default locale.
+ */
+ public static BreakIterator getSentenceInstance ()
+ {
+ return getSentenceInstance (Locale.getDefault());
+ }
+
+ /**
+ * This method returns an instance of <code>BreakIterator</code> that will
+ * iterate over sentences as defined in the specified locale. If the
+ * desired locale is not available, the default locale is used.
+ *
+ * @param locale The desired locale.
+ *
+ * @return A <code>BreakIterator</code> instance for the default locale.
+ */
+ public static BreakIterator getSentenceInstance (Locale loc)
+ {
+ BreakIterator r = getInstance ("SentenceIterator", loc);
+ if (r == null)
+ r = new gnu.java.text.SentenceBreakIterator ();
+ return r;
+ }
+
+ /**
+ * This method returns the text this object is iterating over as a
+ * <code>CharacterIterator</code>.
+ *
+ * @param The text being iterated over.
+ */
+ public abstract CharacterIterator getText ();
+
+ /**
+ * This method returns an instance of <code>BreakIterator</code> that will
+ * iterate over words as defined in the default locale.
+ *
+ * @return A <code>BreakIterator</code> instance for the default locale.
+ */
+ public static BreakIterator getWordInstance ()
+ {
+ return getWordInstance (Locale.getDefault());
+ }
+
+ /**
+ * This method returns an instance of <code>BreakIterator</code> that will
+ * iterate over words as defined in the specified locale. If the
+ * desired locale is not available, the default locale is used.
+ *
+ * @param locale The desired locale.
+ *
+ * @return A <code>BreakIterator</code> instance for the default locale.
+ */
+ public static BreakIterator getWordInstance (Locale loc)
+ {
+ BreakIterator r = getInstance ("WordIterator", loc);
+ if (r == null)
+ r = new gnu.java.text.WordBreakIterator ();
+ return r;
+ }
+
+ /**
+ * This method tests whether or not the specified position is a text
+ * element boundary.
+ *
+ * @param offset The text position to test.
+ *
+ * @return <code>true</code> if the position is a boundary,
+ * <code>false</code> otherwise.
+ */
+ public boolean isBoundary (int pos)
+ {
+ if (pos == 0)
+ return true;
+ return following (pos - 1) == pos;
+ }
+
+ /**
+ * This method returns the last text element boundary in the text being
+ * iterated over.
+ *
+ * @return The last text boundary.
+ */
+ public abstract int last ();
+
+ /**
+ * This method returns the text element boundary following the current
+ * text position.
+ *
+ * @return The next text boundary.
+ */
+ public abstract int next ();
+
+ /**
+ * This method returns the n'th text element boundary following the current
+ * text position.
+ *
+ * @param n The number of text element boundaries to skip.
+ *
+ * @return The next text boundary.
+ */
+ public abstract int next (int n);
+
+ /**
+ * This methdod returns the offset of the text element boundary preceding
+ * the specified offset.
+ *
+ * @param offset The text index from which to find the preceding
+ * text boundary.
+ *
+ * @returns The next text boundary preceding the specified index.
+ */
+ public int preceding (int pos)
+ {
+ if (following (pos) == DONE)
+ last ();
+ while (previous () >= pos)
+ ;
+ return current ();
+ }
+
+ /**
+ * This method returns the text element boundary preceding the current
+ * text position.
+ *
+ * @return The previous text boundary.
+ */
+ public abstract int previous ();
+
+ /**
+ * This method sets the text string to iterate over.
+ *
+ * @param str The <code>String</code> to iterate over.
+ */
+ public void setText (String newText)
+ {
+ setText (new StringCharacterIterator (newText));
+ }
+
+ /**
+ * This method sets the text to iterate over from the specified
+ * <code>CharacterIterator</code>.
+ *
+ * @param ci The desired <code>CharacterIterator</code>.
+ */
+ public abstract void setText (CharacterIterator newText);
+}
diff --git a/libjava/classpath/java/text/CharacterIterator.java b/libjava/classpath/java/text/CharacterIterator.java
new file mode 100644
index 00000000000..6b3f951d1fb
--- /dev/null
+++ b/libjava/classpath/java/text/CharacterIterator.java
@@ -0,0 +1,144 @@
+/* CharacterIterator.java -- Iterate over a character range
+ Copyright (C) 1998, 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+/**
+ * This interface defines a mechanism for iterating over a range of
+ * characters. For a given range of text, a beginning and ending index,
+ * as well as a current index are defined. These values can be queried
+ * by the methods in this interface. Additionally, various methods allow
+ * the index to be set.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ */
+public interface CharacterIterator extends Cloneable
+{
+ /**
+ * This is a special constant value that is returned when the beginning or
+ * end of the character range has been reached.
+ */
+ char DONE = '\uFFFF';
+
+ /**
+ * This method returns the character at the current index position
+ *
+ * @return The character at the current index position.
+ */
+ char current();
+
+ /**
+ * This method increments the current index and then returns the character
+ * at the new index value. If the index is already at <code>getEndIndex() - 1</code>,
+ * it will not be incremented.
+ *
+ * @return The character at the position of the incremented index value,
+ * or <code>DONE</code> if the index has reached getEndIndex() - 1
+ */
+ char next();
+
+ /**
+ * This method decrements the current index and then returns the character
+ * at the new index value. If the index value is already at the beginning
+ * index, it will not be decremented.
+ *
+ * @return The character at the position of the decremented index value,
+ * or <code>DONE</code> if index was already equal to the beginning index value.
+ */
+ char previous();
+
+ /**
+ * This method sets the index value to the beginning of the range and returns
+ * the character there.
+ *
+ * @return The character at the beginning of the range, or <code>DONE</code> if the range is empty.
+ */
+ char first();
+
+ /**
+ * This method sets the index value to <code>getEndIndex() - 1</code> and
+ * returns the character there. If the range is empty, then the index value
+ * will be set equal to the beginning index.
+ *
+ * @return The character at the end of the range, or <code>DONE</code> if the range is empty.
+ */
+ char last();
+
+ /**
+ * This method returns the current value of the index.
+ *
+ * @return The current index value
+ */
+ int getIndex();
+
+ /**
+ * This method sets the value of the index to the specified value, then
+ * returns the character at that position.
+ *
+ * @param index The new index value.
+ *
+ * @return The character at the new index value or <code>DONE</code> if the index value is equal to <code>getEndIndex</code>.
+ */
+ char setIndex (int index) throws IllegalArgumentException;
+
+ /**
+ * This method returns the character position of the first character in the
+ * range.
+ *
+ * @return The index of the first character in the range.
+ */
+ int getBeginIndex();
+
+ /**
+ * This method returns the character position of the end of the text range.
+ * This will actually be the index of the first character following the
+ * end of the range. In the event the text range is empty, this will be
+ * equal to the first character in the range.
+ *
+ * @return The index of the end of the range.
+ */
+ int getEndIndex();
+
+ /**
+ * This method creates a copy of this <code>CharacterIterator</code>.
+ *
+ * @return A copy of this <code>CharacterIterator</code>.
+ */
+ Object clone();
+
+} // interface CharacterIterator
diff --git a/libjava/classpath/java/text/ChoiceFormat.java b/libjava/classpath/java/text/ChoiceFormat.java
new file mode 100644
index 00000000000..23c8a8c3af0
--- /dev/null
+++ b/libjava/classpath/java/text/ChoiceFormat.java
@@ -0,0 +1,503 @@
+/* ChoiceFormat.java -- Format over a range of numbers
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005
+ Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.util.Vector;
+
+/**
+ * This class allows a format to be specified based on a range of numbers.
+ * To use this class, first specify two lists of formats and range terminators.
+ * These lists must be arrays of equal length. The format of index
+ * <code>i</code> will be selected for value <code>X</code> if
+ * <code>terminator[i] &lt;= X &lt; limit[i + 1]</code>. If the value X is not
+ * included in any range, then either the first or last format will be
+ * used depending on whether the value X falls outside the range.
+ * <p>
+ * This sounds complicated, but that is because I did a poor job of
+ * explaining it. Consider the following example:
+ * <p>
+ *
+<pre>terminators = { 1, ChoiceFormat.nextDouble(1) }
+formats = { "file", "files" }</pre>
+ *
+ * <p>
+ * In this case if the actual number tested is one or less, then the word
+ * "file" is used as the format value. If the number tested is greater than
+ * one, then "files" is used. This allows plurals to be handled
+ * gracefully. Note the use of the method <code>nextDouble</code>. This
+ * method selects the next highest double number than its argument. This
+ * effectively makes any double greater than 1.0 cause the "files" string
+ * to be selected. (Note that all terminator values are specified as
+ * doubles.
+ * <p>
+ * Note that in order for this class to work properly, the range terminator
+ * array must be sorted in ascending order and the format string array
+ * must be the same length as the terminator array.
+ *
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Aaron M. Renn (arenn@urbanophile.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
+{
+ /**
+ * This method sets new range terminators and format strings for this
+ * object based on the specified pattern. This pattern is of the form
+ * "term#string|term#string...". For example "1#Sunday|2#Monday|#Tuesday".
+ *
+ * @param pattern The pattern of terminators and format strings.
+ *
+ * @exception IllegalArgumentException If the pattern is not valid
+ */
+ public void applyPattern (String newPattern)
+ {
+ // 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.
+ 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;
+ }
+
+ choiceFormats = new String[stringVec.size()];
+ stringVec.copyInto(choiceFormats);
+
+ choiceLimits = new double[limitVec.size()];
+ for (int i = 0; i < choiceLimits.length; ++i)
+ {
+ Double d = (Double) limitVec.elementAt(i);
+ choiceLimits[i] = d.doubleValue();
+ }
+ }
+
+ /**
+ * This method initializes a new instance of <code>ChoiceFormat</code> that
+ * generates its range terminator and format string arrays from the
+ * specified pattern. This pattern is of the form
+ * "term#string|term#string...". For example "1#Sunday|2#Monday|#Tuesday".
+ * This is the same pattern type used by the <code>applyPattern</code>
+ * method.
+ *
+ * @param pattern The pattern of terminators and format strings.
+ *
+ * @exception IllegalArgumentException If the pattern is not valid
+ */
+ public ChoiceFormat (String newPattern)
+ {
+ super ();
+ applyPattern (newPattern);
+ }
+
+ /**
+ * This method initializes a new instance of <code>ChoiceFormat</code> that
+ * will use the specified range terminators and format strings.
+ *
+ * @param choiceLimits The array of range terminators
+ * @param choiceFormats The array of format strings
+ */
+ public ChoiceFormat (double[] choiceLimits, String[] choiceFormats)
+ {
+ super ();
+ setChoices (choiceLimits, choiceFormats);
+ }
+
+ /**
+ * This method tests this object for equality with the specified
+ * object. This will be true if and only if:
+ * <ul>
+ * <li>The specified object is not <code>null</code>.</li>
+ * <li>The specified object is an instance of <code>ChoiceFormat</code>.</li>
+ * <li>The termination ranges and format strings are identical to
+ * this object's. </li>
+ * </ul>
+ *
+ * @param obj The object to test for equality against.
+ *
+ * @return <code>true</code> if the specified object is equal to
+ * this one, <code>false</code> otherwise.
+ */
+ public boolean equals (Object obj)
+ {
+ if (! (obj instanceof ChoiceFormat))
+ return false;
+ ChoiceFormat cf = (ChoiceFormat) obj;
+ if (choiceLimits.length != cf.choiceLimits.length)
+ return false;
+ for (int i = choiceLimits.length - 1; i >= 0; --i)
+ {
+ if (choiceLimits[i] != cf.choiceLimits[i]
+ || !choiceFormats[i].equals(cf.choiceFormats[i]))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * This method appends the appropriate format string to the specified
+ * <code>StringBuffer</code> based on the supplied <code>long</code>
+ * argument.
+ *
+ * @param number The number used for determine (based on the range
+ * terminators) which format string to append.
+ * @param sb The <code>StringBuffer</code> to append the format string to.
+ * @param status Unused.
+ *
+ * @return The <code>StringBuffer</code> with the format string appended.
+ */
+ public StringBuffer format (long num, StringBuffer appendBuf,
+ FieldPosition pos)
+ {
+ return format ((double) num, appendBuf, pos);
+ }
+
+ /**
+ * This method appends the appropriate format string to the specified
+ * <code>StringBuffer</code> based on the supplied <code>double</code>
+ * argument.
+ *
+ * @param number The number used for determine (based on the range
+ * terminators) which format string to append.
+ * @param sb The <code>StringBuffer</code> to append the format string to.
+ * @param status Unused.
+ *
+ * @return The <code>StringBuffer</code> with the format string appended.
+ */
+ public StringBuffer format (double num, StringBuffer appendBuf,
+ FieldPosition pos)
+ {
+ if (choiceLimits.length == 0)
+ return appendBuf;
+
+ int index = 0;
+ if (! Double.isNaN(num) && num >= choiceLimits[0])
+ {
+ for (; index < choiceLimits.length - 1; ++index)
+ {
+ if (choiceLimits[index] <= num && num < choiceLimits[index + 1])
+ break;
+ }
+ }
+
+ return appendBuf.append(choiceFormats[index]);
+ }
+
+ /**
+ * This method returns the list of format strings in use.
+ *
+ * @return The list of format objects.
+ */
+ public Object[] getFormats ()
+ {
+ return (Object[]) choiceFormats.clone();
+ }
+
+ /**
+ * This method returns the list of range terminators in use.
+ *
+ * @return The list of range terminators.
+ */
+ public double[] getLimits ()
+ {
+ return (double[]) choiceLimits.clone();
+ }
+
+ /**
+ * This method returns a hash value for this object
+ *
+ * @return A hash value for this object.
+ */
+ public int hashCode ()
+ {
+ int hash = 0;
+ for (int i = 0; i < choiceLimits.length; ++i)
+ {
+ long v = Double.doubleToLongBits(choiceLimits[i]);
+ hash ^= (v ^ (v >>> 32));
+ hash ^= choiceFormats[i].hashCode();
+ }
+ return hash;
+ }
+
+ /**
+ * This method returns the lowest possible double greater than the
+ * specified double. If the specified double value is equal to
+ * <code>Double.NaN</code> then that is the value returned.
+ *
+ * @param d The specified double
+ *
+ * @return The lowest double value greater than the specified double.
+ */
+ public static final double nextDouble (double d)
+ {
+ return nextDouble (d, true);
+ }
+
+ /**
+ * This method returns a double that is either the next highest double
+ * or next lowest double compared to the specified double depending on the
+ * value of the passed boolean parameter. If the boolean parameter is
+ * <code>true</code>, then the lowest possible double greater than the
+ * specified double will be returned. Otherwise the highest possible
+ * double less than the specified double will be returned.
+ *
+ * @param d The specified double
+ * @param positive <code>true</code> to return the next highest
+ * double, <code>false</code> otherwise.
+ *
+ * @return The next highest or lowest double value.
+ */
+ public static 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);
+ }
+
+ /**
+ * I'm not sure what this method is really supposed to do, as it is
+ * not documented.
+ */
+ public Number parse (String sourceStr, ParsePosition pos)
+ {
+ int index = pos.getIndex();
+ for (int i = 0; i < choiceLimits.length; ++i)
+ {
+ if (sourceStr.startsWith(choiceFormats[i], index))
+ {
+ pos.setIndex(index + choiceFormats[i].length());
+ return new Double (choiceLimits[i]);
+ }
+ }
+ pos.setErrorIndex(index);
+ return new Double (Double.NaN);
+ }
+
+ /**
+ * This method returns the highest possible double less than the
+ * specified double. If the specified double value is equal to
+ * <code>Double.NaN</code> then that is the value returned.
+ *
+ * @param d The specified double
+ *
+ * @return The highest double value less than the specified double.
+ */
+ public static final double previousDouble (double d)
+ {
+ return nextDouble (d, false);
+ }
+
+ /**
+ * This method sets new range terminators and format strings for this
+ * object.
+ *
+ * @param choiceLimits The new range terminators
+ * @param choiceFormats The new choice formats
+ */
+ public void setChoices (double[] choiceLimits, String[] choiceFormats)
+ {
+ if (choiceLimits == null || choiceFormats == null)
+ throw new NullPointerException ();
+ if (choiceLimits.length != choiceFormats.length)
+ throw new IllegalArgumentException ();
+ this.choiceFormats = (String[]) choiceFormats.clone();
+ this.choiceLimits = (double[]) choiceLimits.clone();
+ }
+
+ private 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);
+ }
+ }
+
+ /**
+ * This method returns the range terminator list and format string list
+ * as a <code>String</code> suitable for using with the
+ * <code>applyPattern</code> method.
+ *
+ * @return A pattern string for this object
+ */
+ public String toPattern ()
+ {
+ StringBuffer result = new StringBuffer ();
+ for (int i = 0; i < choiceLimits.length; ++i)
+ {
+ result.append(choiceLimits[i]);
+ result.append('#');
+ quoteString (result, choiceFormats[i]);
+ }
+ return result.toString();
+ }
+
+ /**
+ * This is the list of format strings. Note that this variable is
+ * specified by the serialization spec of this class.
+ */
+ private String[] choiceFormats;
+
+ /**
+ * This is the list of range terminator values. Note that this variable is
+ * specified by the serialization spec of this class.
+ */
+ private double[] choiceLimits;
+
+ // 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;
+
+ private static final long serialVersionUID = 1795184449645032964L;
+}
diff --git a/libjava/classpath/java/text/CollationElementIterator.java b/libjava/classpath/java/text/CollationElementIterator.java
new file mode 100644
index 00000000000..60b148ef7c1
--- /dev/null
+++ b/libjava/classpath/java/text/CollationElementIterator.java
@@ -0,0 +1,467 @@
+/* CollationElementIterator.java -- Walks through collation elements
+ Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004 Free Software Foundation
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.util.ArrayList;
+
+/* 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 JDK 1.1.
+ */
+
+/**
+ * This class walks through the character collation elements of a
+ * <code>String</code> as defined by the collation rules in an instance of
+ * <code>RuleBasedCollator</code>. There is no public constructor for
+ * this class. An instance is created by calling the
+ * <code>getCollationElementIterator</code> method on
+ * <code>RuleBasedCollator</code>.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Guilhem Lavaux (guilhem.lavaux@free.fr)
+ */
+public final class CollationElementIterator
+{
+ /**
+ * This is a constant value that is returned to indicate that the end of
+ * the string was encountered.
+ */
+ public static final int NULLORDER = -1;
+
+ /**
+ * This is the RuleBasedCollator this object was created from.
+ */
+ RuleBasedCollator collator;
+
+ /**
+ * This is the String that is being iterated over.
+ */
+ String text;
+
+ /**
+ * This is the index into the collation decomposition where we are currently scanning.
+ */
+ int index;
+
+ /**
+ * This is the index into the String where we are currently scanning.
+ */
+ int textIndex;
+
+ /**
+ * Array containing the collation decomposition of the
+ * text given to the constructor.
+ */
+ private RuleBasedCollator.CollationElement[] text_decomposition;
+
+ /**
+ * Array containing the index of the specified block.
+ */
+ private int[] text_indexes;
+
+ /**
+ * This method initializes a new instance of <code>CollationElementIterator</code>
+ * to iterate over the specified <code>String</code> using the rules in the
+ * specified <code>RuleBasedCollator</code>.
+ *
+ * @param collator The <code>RuleBasedCollation</code> used for calculating collation values
+ * @param text The <code>String</code> to iterate over.
+ */
+ CollationElementIterator(RuleBasedCollator collator, String text)
+ {
+ this.collator = collator;
+
+ setText (text);
+ }
+
+ RuleBasedCollator.CollationElement nextBlock()
+ {
+ if (index >= text_decomposition.length)
+ return null;
+
+ RuleBasedCollator.CollationElement e = text_decomposition[index];
+
+ textIndex = text_indexes[index+1];
+
+ index++;
+
+ return e;
+ }
+
+ RuleBasedCollator.CollationElement previousBlock()
+ {
+ if (index == 0)
+ return null;
+
+ index--;
+ RuleBasedCollator.CollationElement e = text_decomposition[index];
+
+ textIndex = text_indexes[index+1];
+
+ return e;
+ }
+
+ /**
+ * This method returns the collation ordering value of the next character sequence
+ * in the string (it may be an extended character following collation rules).
+ * This method will return <code>NULLORDER</code> if the
+ * end of the string was reached.
+ *
+ * @return The collation ordering value.
+ */
+ public int next()
+ {
+ RuleBasedCollator.CollationElement e = nextBlock();
+
+ if (e == null)
+ return NULLORDER;
+
+ return e.getValue();
+ }
+
+ /**
+ * This method returns the collation ordering value of the previous character
+ * in the string. This method will return <code>NULLORDER</code> if the
+ * beginning of the string was reached.
+ *
+ * @return The collation ordering value.
+ */
+ public int previous()
+ {
+ RuleBasedCollator.CollationElement e = previousBlock();
+
+ if (e == null)
+ return NULLORDER;
+
+ return e.getValue();
+ }
+
+ /**
+ * This method returns the primary order value for the given collation
+ * value.
+ *
+ * @param value The collation value returned from <code>next()</code> or <code>previous()</code>.
+ *
+ * @return The primary order value of the specified collation value. This is the high 16 bits.
+ */
+ public static int primaryOrder(int order)
+ {
+ // From the JDK 1.2 spec.
+ return order >>> 16;
+ }
+
+ /**
+ * This method resets the internal position pointer to read from the
+ * beginning of the <code>String</code> again.
+ */
+ public void reset()
+ {
+ index = 0;
+ textIndex = 0;
+ }
+
+ /**
+ * This method returns the secondary order value for the given collation
+ * value.
+ *
+ * @param value The collation value returned from <code>next()</code> or <code>previous()</code>.
+ *
+ * @return The secondary order value of the specified collation value. This is the bits 8-15.
+ */
+ public static short secondaryOrder(int order)
+ {
+ // From the JDK 1.2 spec.
+ return (short) ((order >>> 8) & 255);
+ }
+
+ /**
+ * This method returns the tertiary order value for the given collation
+ * value.
+ *
+ * @param value The collation value returned from <code>next()</code> or <code>previous()</code>.
+ *
+ * @return The tertiary order value of the specified collation value. This is the low eight bits.
+ */
+ public static short tertiaryOrder(int order)
+ {
+ // From the JDK 1.2 spec.
+ return (short) (order & 255);
+ }
+
+ /**
+ * This method sets the <code>String</code> that it is iterating over
+ * to the specified <code>String</code>.
+ *
+ * @param text The new <code>String</code> to iterate over.
+ *
+ * @since 1.2
+ */
+ public void setText(String text)
+ {
+ int idx = 0;
+ int idx_idx = 0;
+ int alreadyExpanded = 0;
+ int idxToMove = 0;
+
+ this.text = text;
+ this.index = 0;
+
+ String work_text = text.intern();
+
+ ArrayList a_element = new ArrayList();
+ ArrayList a_idx = new ArrayList();
+
+ // Build element collection ordered as they come in "text".
+ while (idx < work_text.length())
+ {
+ String key, key_old;
+
+ Object object = null;
+ int p = 1;
+
+ // IMPROVE: use a TreeMap with a prefix-ordering rule.
+ key_old = key = null;
+ do
+ {
+ if (object != null)
+ key_old = key;
+ key = work_text.substring (idx, idx+p);
+ object = collator.prefix_tree.get (key);
+ if (object != null && idx < alreadyExpanded)
+ {
+ RuleBasedCollator.CollationElement prefix = (RuleBasedCollator.CollationElement)object;
+ if (prefix.expansion != null &&
+ prefix.expansion.startsWith(work_text.substring(0, idx)))
+ {
+ object = null;
+ key = key_old;
+ }
+ }
+ p++;
+ }
+ while (idx+p <= work_text.length());
+
+ if (object == null)
+ key = key_old;
+
+ RuleBasedCollator.CollationElement prefix =
+ (RuleBasedCollator.CollationElement) collator.prefix_tree.get (key);
+
+ /*
+ * First case: There is no such sequence in the database.
+ * We will have to build one from the context.
+ */
+ if (prefix == null)
+ {
+ /*
+ * We are dealing with sequences in an expansion. They
+ * are treated as accented characters (tertiary order).
+ */
+ if (alreadyExpanded > 0)
+ {
+ RuleBasedCollator.CollationElement e =
+ collator.getDefaultAccentedElement (work_text.charAt (idx));
+
+ a_element.add (e);
+ a_idx.add (new Integer(idx_idx));
+ idx++;
+ alreadyExpanded--;
+ if (alreadyExpanded == 0)
+ {
+ /* There is not any characters left in the expansion set.
+ * We can increase the pointer in the source string.
+ */
+ idx_idx += idxToMove;
+ idxToMove = 0;
+ }
+ else
+ idx_idx++;
+ }
+ else
+ {
+ /* This is a normal character. */
+ RuleBasedCollator.CollationElement e =
+ collator.getDefaultElement (work_text.charAt (idx));
+ Integer i_ref = new Integer(idx_idx);
+
+ /* Don't forget to mark it as a special sequence so the
+ * string can be ordered.
+ */
+ a_element.add (RuleBasedCollator.SPECIAL_UNKNOWN_SEQ);
+ a_idx.add (i_ref);
+ a_element.add (e);
+ a_idx.add (i_ref);
+ idx_idx++;
+ idx++;
+ }
+ continue;
+ }
+
+ /*
+ * Second case: Here we have found a matching sequence.
+ * Here we have an expansion string prepend it to the "work text" and
+ * add the corresponding sorting element. We must also mark
+ */
+ if (prefix.expansion != null)
+ {
+ work_text = prefix.expansion
+ + work_text.substring (idx+prefix.key.length());
+ idx = 0;
+ a_element.add (prefix);
+ a_idx.add (new Integer(idx_idx));
+ if (alreadyExpanded == 0)
+ idxToMove = prefix.key.length();
+ alreadyExpanded += prefix.expansion.length()-prefix.key.length();
+ }
+ else
+ {
+ /* Third case: the simplest. We have got the prefix and it
+ * has not to be expanded.
+ */
+ a_element.add (prefix);
+ a_idx.add (new Integer(idx_idx));
+ idx += prefix.key.length();
+ /* If the sequence is in an expansion, we must decrease the
+ * counter.
+ */
+ if (alreadyExpanded > 0)
+ {
+ alreadyExpanded -= prefix.key.length();
+ if (alreadyExpanded == 0)
+ {
+ idx_idx += idxToMove;
+ idxToMove = 0;
+ }
+ }
+ else
+ idx_idx += prefix.key.length();
+ }
+ }
+
+ text_decomposition = (RuleBasedCollator.CollationElement[])
+ a_element.toArray(new RuleBasedCollator.CollationElement[a_element.size()]);
+ text_indexes = new int[a_idx.size()+1];
+ for (int i = 0; i < a_idx.size(); i++)
+ {
+ text_indexes[i] = ((Integer)a_idx.get(i)).intValue();
+ }
+ text_indexes[a_idx.size()] = text.length();
+ }
+
+ /**
+ * This method sets the <code>String</code> that it is iterating over
+ * to the <code>String</code> represented by the specified
+ * <code>CharacterIterator</code>.
+ *
+ * @param source The <code>CharacterIterator</code> containing the new
+ * <code>String</code> to iterate over.
+ */
+ public void setText(CharacterIterator source)
+ {
+ StringBuffer expand = new StringBuffer();
+
+ // For now assume we read from the beginning of the string.
+ for (char c = source.first();
+ c != CharacterIterator.DONE;
+ c = source.next())
+ expand.append(c);
+
+ setText(expand.toString());
+ }
+
+ /**
+ * This method returns the current offset into the <code>String</code>
+ * that is being iterated over.
+ *
+ * @return The iteration index position.
+ *
+ * @since 1.2
+ */
+ public int getOffset()
+ {
+ return textIndex;
+ }
+
+ /**
+ * This method sets the iteration index position into the current
+ * <code>String</code> to the specified value. This value must not
+ * be negative and must not be greater than the last index position
+ * in the <code>String</code>.
+ *
+ * @param offset The new iteration index position.
+ *
+ * @exception IllegalArgumentException If the new offset is not valid.
+ */
+ public void setOffset(int offset)
+ {
+ if (offset < 0)
+ throw new IllegalArgumentException("Negative offset: " + offset);
+
+ if (offset > (text.length() - 1))
+ throw new IllegalArgumentException("Offset too large: " + offset);
+
+ for (index = 0; index < text_decomposition.length; index++)
+ {
+ if (offset <= text_indexes[index])
+ break;
+ }
+ /*
+ * As text_indexes[0] == 0, we should not have to take care whether index is
+ * greater than 0. It is always.
+ */
+ if (text_indexes[index] == offset)
+ textIndex = offset;
+ else
+ textIndex = text_indexes[index-1];
+ }
+
+ /**
+ * This method returns the maximum length of any expansion sequence that
+ * ends with the specified collation order value. (Whatever that means).
+ *
+ * @param value The collation order value
+ *
+ * @param The maximum length of an expansion sequence.
+ */
+ public int getMaxExpansion(int value)
+ {
+ return 1;
+ }
+}
diff --git a/libjava/classpath/java/text/CollationKey.java b/libjava/classpath/java/text/CollationKey.java
new file mode 100644
index 00000000000..f7e3a2476de
--- /dev/null
+++ b/libjava/classpath/java/text/CollationKey.java
@@ -0,0 +1,199 @@
+/* CollationKey.java -- Precomputed collation value
+ Copyright (C) 1998, 1999, 2000, 2003, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+/* 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.
+ */
+
+/**
+ * This class represents a pre-computed series of bits representing a
+ * <code>String</code> for under a particular <code>Collator</code>. This
+ * value may be compared bitwise against another <code>CollationKey</code>
+ * representing a different <code>String</code> under the same
+ * <code>Collator</code> in a manner than is usually more efficient than
+ * using the raw <code>Collator</code> compare methods. There is overhead
+ * associated with calculating this value, so it is generally not
+ * advisable to compute <code>CollationKey</code>'s unless multiple
+ * comparisons against a <code>String</code> will be done. (For example,
+ * in a sort routine).
+ * <p>
+ * This class cannot be instantiated directly. Instead, a
+ * <code>CollationKey</code> is created by calling the
+ * <code>getCollationKey</code> method on an instance of <code>Collator</code>.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @date March 25, 1999
+ */
+public final class CollationKey implements Comparable
+{
+ /**
+ * This is the <code>Collator</code> this object was created from.
+ */
+ private Collator collator;
+
+ /**
+ * This is the <code>String</code> this object represents.
+ */
+ private String originalText;
+
+ /**
+ * This is the bit value for this key.
+ */
+ private byte[] key;
+
+ CollationKey (Collator collator, String originalText, byte[] key)
+ {
+ this.collator = collator;
+ this.originalText = originalText;
+ this.key = key;
+ }
+
+ /**
+ * This method compares the specified object to this one. An integer is
+ * returned which indicates whether the specified object is less than,
+ * greater than, or equal to this object.
+ *
+ * @param ck The <code>CollationKey</code> to compare against this one.
+ *
+ * @return A negative integer if this object is less than the specified object, 0 if it is equal or a positive integer if it is greater than the specified object.
+ */
+ public int compareTo (CollationKey ck)
+ {
+ int max = Math.min (key.length, ck.key.length);
+
+ for (int i = 0; i < max; ++i)
+ {
+ if (key[i] != ck.key[i])
+ return key[i] - ck.key[i];
+ }
+
+ return key.length - ck.key.length;
+ }
+
+ /**
+ * This method compares the specified object to this one. The specified
+ * object must be an instance of <code>CollationKey</code> or an exception
+ * will be thrown. An integer is returned which indicates whether the
+ * specified object is less than, greater than, or equal to this object.
+ *
+ * @param obj The <code>Object</code> to compare against this one.
+ *
+ * @return A negative integer if this object is less than the specified object, 0 if it is equal or a positive integer if it is greater than the specified object.
+ */
+ public int compareTo (Object obj)
+ {
+ return compareTo ((CollationKey) obj);
+ }
+
+ /**
+ * This method tests the specified <code>Object</code> for equality with
+ * this object. This will be true if and only if:
+ * <p>
+ * <ul>
+ * <li>The specified object must not be <code>null</code></li>
+ * <li>The specified object is an instance of <code>CollationKey</code>.</li>
+ * <li>The specified object was created from the same <code>Collator</code>
+ * as this object.</li>
+ * <li>The specified object has the same source string and bit key as
+ * this object.</li>
+ * </ul>
+ *
+ * @param obj The <code>Object</code> to test for equality.
+ *
+ * @return <code>true</code> if the specified object is equal to this one, <code>false</code> otherwise.
+ */
+ public boolean equals (Object obj)
+ {
+ if (! (obj instanceof CollationKey))
+ return false;
+
+ CollationKey ck = (CollationKey) obj;
+
+ if (ck.collator != collator)
+ return false;
+
+ if (!ck.getSourceString ().equals (getSourceString ()))
+ return false;
+
+ if (!ck.toByteArray ().equals (toByteArray ()))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * This method returns the <code>String</code> that this object was created
+ * from.
+ *
+ * @return The source <code>String</code> for this object.
+ */
+ public String getSourceString()
+ {
+ return originalText;
+ }
+
+ /**
+ * This method returns a hash value for this object. The hash value
+ * returned will be the hash code of the bit key so that identical bit
+ * keys will return the same value.
+ *
+ * @return A hash value for this object.
+ */
+ public int hashCode()
+ {
+ // We just follow BitSet instead of thinking up something new.
+ long h = originalText.hashCode();
+ for (int i = key.length - 1; i >= 0; --i)
+ h ^= key[i] * (i + 1);
+ return (int) ((h >> 32) ^ h);
+ }
+
+ /**
+ * This method returns the collation bit sequence as a byte array.
+ *
+ * @param A byte array containing the collation bit sequence.
+ */
+ public byte[] toByteArray()
+ {
+ return key;
+ }
+}
diff --git a/libjava/classpath/java/text/Collator.java b/libjava/classpath/java/text/Collator.java
new file mode 100644
index 00000000000..633bc672527
--- /dev/null
+++ b/libjava/classpath/java/text/Collator.java
@@ -0,0 +1,400 @@
+/* Collator.java -- Perform locale dependent String comparisons.
+ Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.util.Comparator;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * This class is the abstract superclass of classes which perform
+ * locale dependent <code>String</code> comparisons. A caller requests
+ * an instance of <code>Collator</code> for a particular locale using
+ * the <code>getInstance()</code> static method in this class. That method
+ * will return a locale specific subclass of <code>Collator</code> which
+ * can be used to perform <code>String</code> comparisons for that locale.
+ * If a subclass of <code>Collator</code> cannot be located for a particular
+ * locale, a default instance for the current locale will be returned.
+ *
+ * In addition to setting the correct locale, there are two additional
+ * settings that can be adjusted to affect <code>String</code> comparisons:
+ * strength and decomposition. The strength value determines the level
+ * of signficance of character differences required for them to sort
+ * differently. (For example, whether or not capital letters are considered
+ * different from lower case letters). The decomposition value affects how
+ * variants of the same character are treated for sorting purposes. (For
+ * example, whether or not an accent is signficant or not). These settings
+ * are described in detail in the documentation for the methods and values
+ * that are related to them.
+ *
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @date March 18, 1999
+ */
+/* Written using "Java Class Libraries", 2nd edition, plus online
+ * API docs for JDK 1.2 from http://www.javasoft.com.
+ * Status: Mostly complete, but parts stubbed out. Look for FIXME.
+ */
+public abstract class Collator implements Comparator, Cloneable
+{
+ /**
+ * This constant is a strength value which indicates that only primary
+ * differences between characters will be considered signficant. As an
+ * example, two completely different English letters such as 'a' and 'b'
+ * are considered to have a primary difference.
+ */
+ public static final int PRIMARY = 0;
+
+ /**
+ * This constant is a strength value which indicates that only secondary
+ * or primary differences between characters will be considered
+ * significant. An example of a secondary difference between characters
+ * are instances of the same letter with different accented forms.
+ */
+ public static final int SECONDARY = 1;
+
+ /**
+ * This constant is a strength value which indicates that tertiary,
+ * secondary, and primary differences will be considered during sorting.
+ * An example of a tertiary difference is capitalization of a given letter.
+ * This is the default value for the strength setting.
+ */
+ public static final int TERTIARY = 2;
+
+ /**
+ * This constant is a strength value which indicates that any difference
+ * at all between character values are considered significant.
+ */
+ public static final int IDENTICAL = 3;
+
+ /**
+ * This constant indicates that accented characters won't be decomposed
+ * when performing comparisons. This will yield the fastest results, but
+ * will only work correctly in call cases for languages which do not
+ * use accents such as English.
+ */
+ public static final int NO_DECOMPOSITION = 0;
+
+ /**
+ * This constant indicates that only characters which are canonical variants
+ * in Unicode 2.0 will be decomposed prior to performing comparisons. This
+ * will cause accented languages to be sorted correctly. This is the
+ * default decomposition value.
+ */
+ public static final int CANONICAL_DECOMPOSITION = 1;
+
+ /**
+ * This constant indicates that both canonical variants and compatibility
+ * variants in Unicode 2.0 will be decomposed prior to performing
+ * comparisons. This is the slowest mode, but is required to get the
+ * correct sorting for certain languages with certain special formats.
+ */
+ public static final int FULL_DECOMPOSITION = 2;
+
+ /**
+ * This method initializes a new instance of <code>Collator</code> to have
+ * the default strength (TERTIARY) and decomposition
+ * (CANONICAL_DECOMPOSITION) settings. This constructor is protected and
+ * is for use by subclasses only. Non-subclass callers should use the
+ * static <code>getInstance()</code> methods of this class to instantiate
+ * <code>Collation</code> objects for the desired locale.
+ */
+ protected Collator ()
+ {
+ strength = TERTIARY;
+ decmp = CANONICAL_DECOMPOSITION;
+ }
+
+ /**
+ * This method compares the two <code>String</code>'s and returns an
+ * integer indicating whether or not the first argument is less than,
+ * equal to, or greater than the second argument. The comparison is
+ * performed according to the rules of the locale for this
+ * <code>Collator</code> and the strength and decomposition rules in
+ * effect.
+ *
+ * @param str1 The first object to compare
+ * @param str2 The second object to compare
+ *
+ * @return A negative integer if str1 &lt; str2, 0 if str1 == str2, or
+ * a positive integer if str1 &gt; str2.
+ */
+ public abstract int compare (String source, String target);
+
+ /**
+ * This method compares the two <code>Object</code>'s and returns an
+ * integer indicating whether or not the first argument is less than,
+ * equal to, or greater than the second argument. These two objects
+ * must be <code>String</code>'s or an exception will be thrown.
+ *
+ * @param obj1 The first object to compare
+ * @param obj2 The second object to compare
+ *
+ * @return A negative integer if obj1 &lt; obj2, 0 if obj1 == obj2, or
+ * a positive integer if obj1 &gt; obj2.
+ *
+ * @exception ClassCastException If the arguments are not instances
+ * of <code>String</code>.
+ */
+ public int compare (Object o1, Object o2)
+ {
+ return compare ((String) o1, (String) o2);
+ }
+
+ /**
+ * This method tests the specified object for equality against this
+ * object. This will be true if and only if the following conditions are
+ * met:
+ * <ul>
+ * <li>The specified object is not <code>null</code>.</li>
+ * <li>The specified object is an instance of <code>Collator</code>.</li>
+ * <li>The specified object has the same strength and decomposition
+ * settings as this object.</li>
+ * </ul>
+ *
+ * @param obj The <code>Object</code> to test for equality against
+ * this object.
+ *
+ * @return <code>true</code> if the specified object is equal to
+ * this one, <code>false</code> otherwise.
+ */
+ public boolean equals (Object obj)
+ {
+ if (! (obj instanceof Collator))
+ return false;
+ Collator c = (Collator) obj;
+ return decmp == c.decmp && strength == c.strength;
+ }
+
+ /**
+ * This method tests whether the specified <code>String</code>'s are equal
+ * according to the collation rules for the locale of this object and
+ * the current strength and decomposition settings.
+ *
+ * @param str1 The first <code>String</code> to compare
+ * @param str2 The second <code>String</code> to compare
+ *
+ * @return <code>true</code> if the two strings are equal,
+ * <code>false</code> otherwise.
+ */
+ public boolean equals (String source, String target)
+ {
+ return compare (source, target) == 0;
+ }
+
+ /**
+ * This method returns a copy of this <code>Collator</code> object.
+ *
+ * @return A duplicate of this object.
+ */
+ public Object clone ()
+ {
+ try
+ {
+ return super.clone ();
+ }
+ catch (CloneNotSupportedException _)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * This method returns an array of <code>Locale</code> objects which is
+ * the list of locales for which <code>Collator</code> objects exist.
+ *
+ * @return The list of locales for which <code>Collator</code>'s exist.
+ */
+ public static synchronized Locale[] getAvailableLocales ()
+ {
+ // FIXME
+ Locale[] l = new Locale[1];
+ l[0] = Locale.US;
+ return l;
+ }
+
+ /**
+ * This method transforms the specified <code>String</code> into a
+ * <code>CollationKey</code> for faster comparisons. This is useful when
+ * comparisons against a string might be performed multiple times, such
+ * as during a sort operation.
+ *
+ * @param str The <code>String</code> to convert.
+ *
+ * @return A <code>CollationKey</code> for the specified <code>String</code>.
+ */
+ public abstract CollationKey getCollationKey (String source);
+
+ /**
+ * This method returns the current decomposition setting for this
+ * object. This * will be one of NO_DECOMPOSITION,
+ * CANONICAL_DECOMPOSITION, or * FULL_DECOMPOSITION. See the
+ * documentation for those constants for an * explanation of this
+ * setting.
+ *
+ * @return The current decomposition setting.
+ */
+ public synchronized int getDecomposition ()
+ {
+ return decmp;
+ }
+
+ /**
+ * This method returns an instance of <code>Collator</code> for the
+ * default locale.
+ *
+ * @return A <code>Collator</code> for the default locale.
+ */
+ public static Collator getInstance ()
+ {
+ return getInstance (Locale.getDefault());
+ }
+
+ /**
+ * This method returns an instance of <code>Collator</code> for the
+ * specified locale. If no <code>Collator</code> exists for the desired
+ * locale, a <code>Collator</code> for the default locale will be returned.
+ *
+ * @param locale The desired localed to load a <code>Collator</code> for.
+ *
+ * @return A <code>Collator</code> for the requested locale
+ */
+ public static Collator getInstance (Locale loc)
+ {
+ ResourceBundle res;
+ String pattern;
+ try
+ {
+ res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+ loc, ClassLoader.getSystemClassLoader());
+ pattern = res.getString("collation_rules");
+ }
+ catch (MissingResourceException x)
+ {
+ pattern = "<0<1<2<3<4<5<6<7<8<9<A,a<b,B<c,C<d,D<e,E<f,F<g,G<h,H<i,I<j,J<k,K" +
+ "<l,L<m,M<n,N<o,O<p,P<q,Q<r,R<s,S<t,T<u,U<v,V<w,W<x,X<y,Y<z,Z";
+ }
+ try
+ {
+ return new RuleBasedCollator (pattern);
+ }
+ catch (ParseException x)
+ {
+ throw (InternalError)new InternalError().initCause(x);
+ }
+ }
+
+ /**
+ * This method returns the current strength setting for this object. This
+ * will be one of PRIMARY, SECONDARY, TERTIARY, or IDENTICAL. See the
+ * documentation for those constants for an explanation of this setting.
+ *
+ * @return The current strength setting.
+ */
+ public synchronized int getStrength ()
+ {
+ return strength;
+ }
+
+ /**
+ * This method returns a hash code value for this object.
+ *
+ * @return A hash value for this object.
+ */
+ public abstract int hashCode ();
+
+ /**
+ * This method sets the decomposition setting for this object to the
+ * specified value. This must be one of NO_DECOMPOSITION,
+ * CANONICAL_DECOMPOSITION, or FULL_DECOMPOSITION. Otherwise an
+ * exception will be thrown. See the documentation for those
+ * contants for an explanation of this setting.
+ *
+ * @param decmp The new decomposition setting.
+ *
+ * @exception IllegalArgumentException If the requested
+ * decomposition setting is not valid.
+ */
+ public synchronized void setDecomposition (int mode)
+ {
+ if (mode != NO_DECOMPOSITION
+ && mode != CANONICAL_DECOMPOSITION
+ && mode != FULL_DECOMPOSITION)
+ throw new IllegalArgumentException ();
+ decmp = mode;
+ }
+
+ /**
+ * This method sets the strength setting for this object to the specified
+ * value. This must be one of PRIMARY, SECONDARY, TERTIARY, or IDENTICAL.
+ * Otherwise an exception is thrown. See the documentation for these
+ * constants for an explanation of this setting.
+ *
+ * @param strength The new strength setting.
+ *
+ * @exception IllegalArgumentException If the requested strength
+ * setting value is not valid.
+ */
+ public synchronized void setStrength (int strength)
+ {
+ if (strength != PRIMARY && strength != SECONDARY
+ && strength != TERTIARY && strength != IDENTICAL)
+ throw new IllegalArgumentException ();
+ this.strength = strength;
+ }
+
+ // Decompose a single character and append results to the buffer.
+ // FIXME: for libgcj this is a native method which handles
+ // decomposition. For Classpath, for now, it does nothing.
+ final void decomposeCharacter (char c, StringBuffer buf)
+ {
+ buf.append (c);
+ }
+
+ /**
+ * This is the current collation decomposition setting.
+ */
+ int decmp;
+
+ /**
+ * This is the current collation strength setting.
+ */
+ int strength;
+}
diff --git a/libjava/classpath/java/text/DateFormat.java b/libjava/classpath/java/text/DateFormat.java
new file mode 100644
index 00000000000..f6dfceb14e8
--- /dev/null
+++ b/libjava/classpath/java/text/DateFormat.java
@@ -0,0 +1,892 @@
+/* DateFormat.java -- Class for formatting/parsing date/times
+ Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005
+ Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.io.InvalidObjectException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.TimeZone;
+
+/**
+ * @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 static final int FULL = 0;
+ public static final int LONG = 1;
+ public static final int MEDIUM = 2;
+ public static final int SHORT = 3;
+ public static final int DEFAULT = MEDIUM;
+
+ /* These constants need to have these exact values. They
+ * correspond to index positions within the localPatternChars
+ * string for a given locale. Each locale may specify its
+ * own character for a particular field, but the position
+ * of these characters must correspond to an appropriate field
+ * number (as listed below), in order for their meaning to
+ * be determined. For example, the US locale uses
+ * the string "GyMdkHmsSEDFwWahKzYeugAZ", where 'G' is the character
+ * for era, 'y' for year, and so on down to 'Z' for time zone.
+ */
+ /**
+ * Represents the position of the era
+ * pattern character in the array of
+ * localized pattern characters.
+ * For example, 'AD' is an era used
+ * in the Gregorian calendar system.
+ * In the U.S. locale, this is 'G'.
+ */
+ public static final int ERA_FIELD = 0;
+ /**
+ * Represents the position of the year
+ * pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'y'.
+ */
+ public static final int YEAR_FIELD = 1;
+ /**
+ * Represents the position of the month
+ * pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'M'.
+ */
+ public static final int MONTH_FIELD = 2;
+ /**
+ * Represents the position of the date
+ * or day of the month pattern character
+ * in the array of localized pattern
+ * characters. In the U.S. locale,
+ * this is 'd'.
+ */
+ public static final int DATE_FIELD = 3;
+ /**
+ * Represents the position of the 24
+ * hour pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'k'.
+ * This field numbers hours from 1 to 24.
+ */
+ public static final int HOUR_OF_DAY1_FIELD = 4;
+ /**
+ * Represents the position of the 24
+ * hour pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'H'.
+ * This field numbers hours from 0 to 23.
+ */
+ public static final int HOUR_OF_DAY0_FIELD = 5;
+ /**
+ * Represents the position of the minute
+ * pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'm'.
+ */
+ public static final int MINUTE_FIELD = 6;
+ /**
+ * Represents the position of the second
+ * pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 's'.
+ */
+ public static final int SECOND_FIELD = 7;
+ /**
+ * Represents the position of the millisecond
+ * pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'S'.
+ */
+ public static final int MILLISECOND_FIELD = 8;
+ /**
+ * Represents the position of the day of the
+ * week pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'E'.
+ */
+ public static final int DAY_OF_WEEK_FIELD = 9;
+ /**
+ * Represents the position of the day of the
+ * year pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'D'.
+ */
+ public static final int DAY_OF_YEAR_FIELD = 10;
+ /**
+ * Represents the position of the day of the
+ * week in the month pattern character in the
+ * array of localized pattern characters.
+ * In the U.S. locale, this is 'F'.
+ */
+ public static final int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
+ /**
+ * Represents the position of the week of the
+ * year pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'w'.
+ */
+ public static final int WEEK_OF_YEAR_FIELD = 12;
+ /**
+ * Represents the position of the week of the
+ * month pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'W'.
+ */
+ public static final int WEEK_OF_MONTH_FIELD = 13;
+ /**
+ * Represents the position of the am/pm
+ * pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'a'.
+ */
+ public static final int AM_PM_FIELD = 14;
+ /**
+ * Represents the position of the 12
+ * hour pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'h'.
+ * This field numbers hours from 1 to 12.
+ */
+ public static final int HOUR1_FIELD = 15;
+ /**
+ * Represents the position of the 12
+ * hour pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'K'.
+ * This field numbers hours from 0 to 11.
+ */
+ public static final int HOUR0_FIELD = 16;
+ /**
+ * Represents the position of the generic
+ * timezone pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'z'.
+ */
+ public static final int TIMEZONE_FIELD = 17;
+ /**
+ * Represents the position of the ISO year
+ * pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'Y'.
+ * This is a GNU extension in accordance with
+ * the CLDR data used. This value may
+ * differ from the normal year value.
+ */
+ public static final int ISO_YEAR_FIELD = 18;
+ /**
+ * Represents the position of the localized
+ * day of the week pattern character in the
+ * array of localized pattern characters.
+ * In the U.S. locale, this is 'e'.
+ * This is a GNU extension in accordance with
+ * the CLDR data used. This value only
+ * differs from the day of the week with
+ * numeric formatting, in which case the
+ * locale's first day of the week is used.
+ */
+ public static final int LOCALIZED_DAY_OF_WEEK_FIELD = 19;
+ /**
+ * Represents the position of the extended year
+ * pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'u'.
+ * This is a GNU extension in accordance with
+ * the CLDR data used. This value modifies
+ * the year value, so as to incorporate the era.
+ * For example, in the Gregorian calendar system,
+ * the extended year is negative instead of being
+ * marked as BC.
+ */
+ public static final int EXTENDED_YEAR_FIELD = 20;
+ /**
+ * Represents the position of the modified Julian
+ * day pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'g'.
+ * This is a GNU extension in accordance with
+ * the CLDR data used. This value differs
+ * from the standard Julian day in that days
+ * are marked from midnight onwards rather than
+ * noon, and the local time zone affects the value.
+ * In simple terms, it can be thought of as all
+ * the date fields represented as a single number.
+ */
+ public static final int MODIFIED_JULIAN_DAY_FIELD = 21;
+ /**
+ * Represents the position of the millisecond
+ * in the day pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'A'.
+ * This is a GNU extension in accordance with
+ * the CLDR data used. This value represents
+ * all the time fields (excluding the time zone)
+ * numerically, giving the number of milliseconds
+ * into the day (e.g. 10 in the morning would
+ * be 10 * 60 * 60 * 1000). Any daylight savings
+ * offset also affects this value.
+ */
+ public static final int MILLISECOND_IN_DAY_FIELD = 22;
+ /**
+ * Represents the position of the RFC822
+ * timezone pattern character in the array of
+ * localized pattern characters.
+ * In the U.S. locale, this is 'Z'.
+ * This is a GNU extension in accordance with
+ * the CLDR data used. The value is the offset
+ * of the current time from GMT e.g. -0500 would
+ * be five hours prior to GMT.
+ */
+ public static final int RFC822_TIMEZONE_FIELD = 23;
+
+ public static class Field extends Format.Field
+ {
+ static final long serialVersionUID = 7441350119349544720L;
+
+ private int calendarField;
+
+ public static final DateFormat.Field ERA
+ = new Field("era", Calendar.ERA);
+ public static final DateFormat.Field YEAR
+ = new Field("year", Calendar.YEAR);
+ public static final DateFormat.Field MONTH
+ = new Field("month", Calendar.MONTH);
+ public static final DateFormat.Field DAY_OF_MONTH
+ = new Field("day of month", Calendar.DAY_OF_MONTH);
+ public static final DateFormat.Field HOUR_OF_DAY1
+ = new Field("hour of day 1", Calendar.HOUR_OF_DAY);
+ public static final DateFormat.Field HOUR_OF_DAY0
+ = new Field("hour of day 0", Calendar.HOUR_OF_DAY);
+ public static final DateFormat.Field MINUTE
+ = new Field("minute", Calendar.MINUTE);
+ public static final DateFormat.Field SECOND
+ = new Field("second", Calendar.SECOND);
+ public static final DateFormat.Field MILLISECOND
+ = new Field("millisecond", Calendar.MILLISECOND);
+ public static final DateFormat.Field DAY_OF_WEEK
+ = new Field("day of week", Calendar.DAY_OF_WEEK);
+ public static final DateFormat.Field DAY_OF_YEAR
+ = new Field("day of year", Calendar.DAY_OF_YEAR);
+ public static final DateFormat.Field DAY_OF_WEEK_IN_MONTH
+ = new Field("day of week in month", Calendar.DAY_OF_WEEK_IN_MONTH);
+ public static final DateFormat.Field WEEK_OF_YEAR
+ = new Field("week of year", Calendar.WEEK_OF_YEAR);
+ public static final DateFormat.Field WEEK_OF_MONTH
+ = new Field("week of month", Calendar.WEEK_OF_MONTH);
+ public static final DateFormat.Field AM_PM
+ = new Field("am/pm", Calendar.AM_PM);
+ public static final DateFormat.Field HOUR1
+ = new Field("hour1", Calendar.HOUR);
+ public static final DateFormat.Field HOUR0
+ = new Field("hour0", Calendar.HOUR);
+ public static final DateFormat.Field TIME_ZONE
+ = new Field("timezone", Calendar.ZONE_OFFSET);
+ public static final DateFormat.Field ISO_YEAR
+ = new Field("iso year", Calendar.YEAR);
+ public static final DateFormat.Field LOCALIZED_DAY_OF_WEEK
+ = new Field("localized day of week", Calendar.DAY_OF_WEEK);
+ public static final DateFormat.Field EXTENDED_YEAR
+ = new Field("extended year", Calendar.YEAR);
+ public static final DateFormat.Field MODIFIED_JULIAN_DAY
+ = new Field("julian day", -1);
+ public static final DateFormat.Field MILLISECOND_IN_DAY
+ = new Field("millisecond in day", -1);
+ public static final DateFormat.Field RFC822_TIME_ZONE
+ = new Field("rfc822 timezone", Calendar.ZONE_OFFSET);
+
+ static final DateFormat.Field[] allFields =
+ {
+ ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY1,
+ HOUR_OF_DAY0, MINUTE, SECOND, MILLISECOND,
+ DAY_OF_WEEK, DAY_OF_YEAR, DAY_OF_WEEK_IN_MONTH,
+ WEEK_OF_YEAR, WEEK_OF_MONTH, AM_PM, HOUR1, HOUR0,
+ TIME_ZONE, ISO_YEAR, LOCALIZED_DAY_OF_WEEK,
+ EXTENDED_YEAR, MODIFIED_JULIAN_DAY, MILLISECOND_IN_DAY,
+ RFC822_TIME_ZONE
+ };
+
+ // For deserialization
+ private Field()
+ {
+ super("");
+ }
+
+ protected Field(String name, int calendarField)
+ {
+ super(name);
+ this.calendarField = calendarField;
+ }
+
+ public int getCalendarField()
+ {
+ return calendarField;
+ }
+
+ public static Field ofCalendarField(int calendarField)
+ {
+ if (calendarField >= allFields.length || calendarField < 0)
+ throw new IllegalArgumentException("no such calendar field ("
+ + calendarField + ")");
+
+ return allFields[calendarField];
+ }
+
+ protected Object readResolve() throws InvalidObjectException
+ {
+ String s = getName();
+
+ for (int i=0;i<allFields.length;i++)
+ if (s.equals(allFields[i].getName()))
+ return allFields[i];
+
+ throw new InvalidObjectException("no such DateFormat field called " + s);
+ }
+ }
+
+ /**
+ * This method initializes a new instance of <code>DateFormat</code>.
+ */
+ protected DateFormat ()
+ {
+ }
+
+ /**
+ * This method tests this object for equality against the specified object.
+ * The two objects will be considered equal if an only if the specified
+ * object:
+ * <P>
+ * <ul>
+ * <li>Is not <code>null</code>.</li>
+ * <li>Is an instance of <code>DateFormat</code>.</li>
+ * <li>Has the same numberFormat field value as this object.</li>
+ * </ul>
+ *
+ * @param obj The object to test for equality against.
+ *
+ * @return <code>true</code> if the specified object is equal to this object,
+ * <code>false</code> otherwise.
+ */
+ public boolean equals (Object obj)
+ {
+ if (!(obj instanceof DateFormat))
+ return false;
+
+ DateFormat d = (DateFormat) obj;
+
+ return numberFormat.equals(d.numberFormat);
+ }
+
+ /**
+ * This method returns a copy of this object.
+ *
+ * @return A copy of this object.
+ */
+ public Object clone ()
+ {
+ // We know the superclass just call's Object's generic cloner.
+ return super.clone ();
+ }
+
+ /**
+ * This method formats the specified <code>Object</code> into a date string
+ * and appends it to the specified <code>StringBuffer</code>.
+ * The specified object must be an instance of <code>Number</code> or
+ * <code>Date</code> or an <code>IllegalArgumentException</code> will be
+ * thrown.
+ *
+ * @param obj The <code>Object</code> to format.
+ * @param toAppendTo The <code>StringBuffer</code> to append the resultant
+ * <code>String</code> to.
+ * @param fieldPosition Is updated to the start and end index of the
+ * specified field.
+ *
+ * @return The <code>StringBuffer</code> supplied on input, with the
+ * formatted date/time appended.
+ */
+ public final StringBuffer format (Object obj,
+ StringBuffer buf, FieldPosition pos)
+ {
+ if (obj instanceof Number)
+ obj = new Date(((Number) obj).longValue());
+ else if (! (obj instanceof Date))
+ throw new IllegalArgumentException
+ ("Cannot format given Object as a Date");
+
+ return format ((Date) obj, buf, pos);
+ }
+
+ /**
+ * Formats the date argument according to the pattern specified.
+ *
+ * @param date The formatted date.
+ */
+ public final String format (Date date)
+ {
+ StringBuffer sb = new StringBuffer ();
+ format (date, sb, new FieldPosition (MONTH_FIELD));
+ return sb.toString();
+ }
+
+ /**
+ * This method formats a <code>Date</code> into a string and appends it
+ * to the specified <code>StringBuffer</code>.
+ *
+ * @param date The <code>Date</code> value to format.
+ * @param toAppendTo The <code>StringBuffer</code> to append the resultant
+ * <code>String</code> to.
+ * @param fieldPosition Is updated to the start and end index of the
+ * specified field.
+ *
+ * @return The <code>StringBuffer</code> supplied on input, with the
+ * formatted date/time appended.
+ */
+ public abstract StringBuffer format (Date date,
+ StringBuffer buf, FieldPosition pos);
+
+ /**
+ * This method returns a list of available locales supported by this
+ * class.
+ */
+ public static Locale[] getAvailableLocales()
+ {
+ return Locale.getAvailableLocales();
+ }
+
+ /**
+ * This method returns the <code>Calendar</code> object being used by
+ * this object to parse/format datetimes.
+ *
+ * @return The <code>Calendar</code> being used by this object.
+ *
+ * @see java.util.Calendar
+ */
+ public Calendar getCalendar ()
+ {
+ return calendar;
+ }
+
+ private static DateFormat computeInstance (int style, Locale loc,
+ boolean use_date, boolean use_time)
+ {
+ return computeInstance (style, style, loc, use_date, use_time);
+ }
+
+ private static DateFormat computeInstance (int dateStyle, int timeStyle,
+ Locale loc, boolean use_date,
+ boolean use_time)
+ {
+ ResourceBundle res;
+ try
+ {
+ res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+ loc, ClassLoader.getSystemClassLoader());
+ }
+ catch (MissingResourceException x)
+ {
+ res = null;
+ }
+
+ String pattern = null;
+ if (use_date)
+ {
+ String name, def;
+ switch (dateStyle)
+ {
+ 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 (timeStyle)
+ {
+ 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);
+ }
+
+ /**
+ * This method returns an instance of <code>DateFormat</code> that will
+ * format using the default formatting style for dates.
+ *
+ * @return A new <code>DateFormat</code> instance.
+ */
+ public static final DateFormat getDateInstance ()
+ {
+ return getDateInstance (DEFAULT, Locale.getDefault());
+ }
+
+ /**
+ * This method returns an instance of <code>DateFormat</code> that will
+ * format using the specified formatting style for dates.
+ *
+ * @param style The type of formatting to perform.
+ *
+ * @return A new <code>DateFormat</code> instance.
+ */
+ public static final DateFormat getDateInstance (int style)
+ {
+ return getDateInstance (style, Locale.getDefault());
+ }
+
+ /**
+ * This method returns an instance of <code>DateFormat</code> that will
+ * format using the specified formatting style for dates. The specified
+ * localed will be used in place of the default.
+ *
+ * @param style The type of formatting to perform.
+ * @param aLocale The desired locale.
+ *
+ * @return A new <code>DateFormat</code> instance.
+ */
+ public static final DateFormat getDateInstance (int style, Locale loc)
+ {
+ return computeInstance (style, loc, true, false);
+ }
+
+ /**
+ * This method returns a new instance of <code>DateFormat</code> that
+ * formats both dates and times using the <code>SHORT</code> style.
+ *
+ * @return A new <code>DateFormat</code>instance.
+ */
+ public static final DateFormat getDateTimeInstance ()
+ {
+ return getDateTimeInstance (DEFAULT, DEFAULT, Locale.getDefault());
+ }
+
+ /**
+ * This method returns a new instance of <code>DateFormat</code> that
+ * formats both dates and times using the <code>DEFAULT</code> style.
+ *
+ * @return A new <code>DateFormat</code>instance.
+ */
+ public static final DateFormat getDateTimeInstance (int dateStyle,
+ int timeStyle)
+ {
+ return getDateTimeInstance (dateStyle, timeStyle, Locale.getDefault());
+ }
+
+ /**
+ * This method returns a new instance of <code>DateFormat</code> that
+ * formats both dates and times using the specified styles.
+ *
+ * @param dateStyle The desired style for date formatting.
+ * @param timeStyle The desired style for time formatting
+ *
+ * @return A new <code>DateFormat</code>instance.
+ */
+ public static final DateFormat getDateTimeInstance (int dateStyle,
+ int timeStyle,
+ Locale loc)
+ {
+ return computeInstance (dateStyle, timeStyle, loc, true, true);
+ }
+
+ /**
+ * This method returns a new instance of <code>DateFormat</code> that
+ * formats both dates and times using the <code>SHORT</code> style.
+ *
+ * @return A new <code>DateFormat</code>instance.
+ */
+ public static final DateFormat getInstance ()
+ {
+ // JCL book says SHORT.
+ return getDateTimeInstance (SHORT, SHORT, Locale.getDefault());
+ }
+
+ /**
+ * This method returns the <code>NumberFormat</code> object being used
+ * by this object to parse/format time values.
+ *
+ * @return The <code>NumberFormat</code> in use by this object.
+ */
+ public NumberFormat getNumberFormat ()
+ {
+ return numberFormat;
+ }
+
+ /**
+ * This method returns an instance of <code>DateFormat</code> that will
+ * format using the default formatting style for times.
+ *
+ * @return A new <code>DateFormat</code> instance.
+ */
+ public static final DateFormat getTimeInstance ()
+ {
+ return getTimeInstance (DEFAULT, Locale.getDefault());
+ }
+
+ /**
+ * This method returns an instance of <code>DateFormat</code> that will
+ * format using the specified formatting style for times.
+ *
+ * @param style The type of formatting to perform.
+ *
+ * @return A new <code>DateFormat</code> instance.
+ */
+ public static final DateFormat getTimeInstance (int style)
+ {
+ return getTimeInstance (style, Locale.getDefault());
+ }
+
+ /**
+ * This method returns an instance of <code>DateFormat</code> that will
+ * format using the specified formatting style for times. The specified
+ * localed will be used in place of the default.
+ *
+ * @param style The type of formatting to perform.
+ * @param aLocale The desired locale.
+ *
+ * @return A new <code>DateFormat</code> instance.
+ */
+ public static final DateFormat getTimeInstance (int style, Locale loc)
+ {
+ return computeInstance (style, loc, false, true);
+ }
+
+ /**
+ * This method returns the <code>TimeZone</code> object being used by
+ * this instance.
+ *
+ * @return The time zone in use.
+ */
+ public TimeZone getTimeZone ()
+ {
+ return calendar.getTimeZone();
+ }
+
+ /**
+ * This method returns a hash value for this object.
+ *
+ * @return A hash value for this object.
+ */
+ public int hashCode ()
+ {
+ if (numberFormat != null)
+ return numberFormat.hashCode();
+ else
+ return 0;
+ }
+
+ /**
+ * This method indicates whether or not the parsing of date and time
+ * values should be done in a lenient value.
+ *
+ * @return <code>true</code> if date/time parsing is lenient,
+ * <code>false</code> otherwise.
+ */
+ public boolean isLenient ()
+ {
+ return calendar.isLenient();
+ }
+
+ /**
+ * This method parses the specified date/time string.
+ *
+ * @param source The string to parse.
+ * @return The resultant date.
+ *
+ * @exception ParseException If the specified string cannot be parsed.
+ */
+ 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 in \""
+ + source + '\"', index);
+ }
+ return result;
+ }
+
+ /**
+ * This method parses the specified <code>String</code> into a
+ * <code>Date</code>. The <code>pos</code> argument contains the
+ * starting parse position on method entry and the ending parse
+ * position on method exit.
+ *
+ * @param text The string to parse.
+ * @param pos The starting parse position in entry, the ending parse
+ * position on exit.
+ *
+ * @return The parsed date, or <code>null</code> if the string cannot
+ * be parsed.
+ */
+ public abstract Date parse (String source, ParsePosition pos);
+
+ /**
+ * This method is identical to <code>parse(String, ParsePosition)</code>,
+ * but returns its result as an <code>Object</code> instead of a
+ * <code>Date</code>.
+ *
+ * @param source The string to parse.
+ * @param pos The starting parse position in entry, the ending parse
+ * position on exit.
+ *
+ * @return The parsed date, or <code>null</code> if the string cannot
+ * be parsed.
+ */
+ public Object parseObject (String source, ParsePosition pos)
+ {
+ return parse(source, pos);
+ }
+
+ /**
+ * This method specified the <code>Calendar</code> that should be used
+ * by this object to parse/format datetimes.
+ *
+ * @param The new <code>Calendar</code> for this object.
+ *
+ * @see java.util.Calendar
+ */
+ public void setCalendar (Calendar calendar)
+ {
+ this.calendar = calendar;
+ }
+
+ /**
+ * This method specifies whether or not this object should be lenient in
+ * the syntax it accepts while parsing date/time values.
+ *
+ * @param lenient <code>true</code> if parsing should be lenient,
+ * <code>false</code> otherwise.
+ */
+ public void setLenient (boolean lenient)
+ {
+ calendar.setLenient(lenient);
+ }
+
+ /**
+ * This method specifies the <code>NumberFormat</code> object that should
+ * be used by this object to parse/format times.
+ *
+ * @param The <code>NumberFormat</code> in use by this object.
+ */
+ public void setNumberFormat (NumberFormat numberFormat)
+ {
+ this.numberFormat = numberFormat;
+ }
+
+ /**
+ * This method sets the time zone that should be used by this object.
+ *
+ * @param The new time zone.
+ */
+ public void setTimeZone (TimeZone timeZone)
+ {
+ calendar.setTimeZone(timeZone);
+ }
+}
diff --git a/libjava/classpath/java/text/DateFormatSymbols.java b/libjava/classpath/java/text/DateFormatSymbols.java
new file mode 100644
index 00000000000..543a5c13a78
--- /dev/null
+++ b/libjava/classpath/java/text/DateFormatSymbols.java
@@ -0,0 +1,525 @@
+/* DateFormatSymbols.java -- Format over a range of numbers
+ Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+
+/**
+ * This class acts as container for locale specific date/time formatting
+ * information such as the days of the week and the months of the year.
+ * @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 implements java.io.Serializable, Cloneable
+{
+ String[] ampms;
+ String[] eras;
+ private String localPatternChars;
+ String[] months;
+ String[] shortMonths;
+ String[] shortWeekdays;
+ String[] weekdays;
+ private String[][] zoneStrings;
+
+ private static final long serialVersionUID = -5987973545549424702L;
+
+ // The order of these prefixes must be the same as in DateFormat
+ private static final String[] formatPrefixes =
+ {
+ "full", "long", "medium", "short"
+ };
+
+ // These are each arrays with a value for SHORT, MEDIUM, LONG, FULL,
+ // and DEFAULT (constants defined in java.text.DateFormat). While
+ // not part of the official spec, we need a way to get at locale-specific
+ // default formatting patterns. They are declared package scope so
+ // as to be easily accessible where needed (DateFormat, SimpleDateFormat).
+ transient String[] dateFormats;
+ transient String[] timeFormats;
+
+ private static String[] getStringArray(ResourceBundle res, String name)
+ {
+ return res.getString(name).split("\u00ae");
+ }
+
+ private String[][] getZoneStrings(ResourceBundle res)
+ {
+ try
+ {
+ int index = 0;
+ String data = res.getString("zoneStrings");
+ String[] zones = data.split("\u00a9");
+ String[][] array = new String[zones.length][];
+ for (int a = 0; a < zones.length; ++a)
+ array[a] = zones[a].split("\u00ae");
+ return array;
+ }
+ catch (MissingResourceException e)
+ {
+ return new String[0][];
+ }
+ }
+
+ private String[] formatsForKey(ResourceBundle res, String key)
+ {
+ String[] values = new String[formatPrefixes.length];
+
+ for (int i = 0; i < formatPrefixes.length; i++)
+ values[i] = res.getString(formatPrefixes[i] + key);
+
+ return values;
+ }
+
+ /**
+ * This method initializes a new instance of <code>DateFormatSymbols</code>
+ * by loading the date format information for the specified locale.
+ *
+ * @param locale The locale for which date formatting symbols should
+ * be loaded.
+ */
+ public DateFormatSymbols (Locale locale) throws MissingResourceException
+ {
+ ResourceBundle res
+ = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", locale,
+ ClassLoader.getSystemClassLoader());
+
+ ampms = getStringArray(res, "ampms");
+ eras = getStringArray(res, "eras");
+ localPatternChars = res.getString("localPatternChars");
+ months = getStringArray(res, "months");
+ shortMonths = getStringArray(res, "shortMonths");
+ shortWeekdays = getStringArray(res, "shortWeekdays");
+ weekdays = getStringArray(res, "weekdays");
+ zoneStrings = getZoneStrings(res);
+ dateFormats = formatsForKey(res, "DateFormat");
+ timeFormats = formatsForKey(res, "TimeFormat");
+ }
+
+ /**
+ * This method loads the format symbol information for the default
+ * locale.
+ */
+ public DateFormatSymbols () throws MissingResourceException
+ {
+ this (Locale.getDefault());
+ }
+
+ /**
+ * This method returns the list of strings used for displaying AM or PM.
+ * This is a two element <code>String</code> array indexed by
+ * <code>Calendar.AM</code> and <code>Calendar.PM</code>
+ *
+ * @return The list of AM/PM display strings.
+ */
+ public String[] getAmPmStrings()
+ {
+ return ampms;
+ }
+
+ /**
+ * This method returns the list of strings used for displaying eras
+ * (e.g., "BC" and "AD"). This is a two element <code>String</code>
+ * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
+ *
+ * @return The list of era disply strings.
+ */
+ public String[] getEras()
+ {
+ return eras;
+ }
+
+ /**
+ * This method returns the pattern character information for this
+ * object. This is an 18 character string that contains the characters
+ * that are used in creating the date formatting strings in
+ * <code>SimpleDateFormat</code>. The following are the character
+ * positions in the string and which format character they correspond
+ * to (the character in parentheses is the default value in the US English
+ * locale):
+ * <p>
+ * <ul>
+ * <li>0 - era (G)</li>
+ * <li>1 - year (y)</li>
+ * <li>2 - month (M)</li>
+ * <li>3 - day of month (d)</li>
+ * <li>4 - hour out of 12, from 1-12 (h)</li>
+ * <li>5 - hour out of 24, from 0-23 (H)</li>
+ * <li>6 - minute (m)</li>
+ * <li>7 - second (s)</li>
+ * <li>8 - millisecond (S)</li>
+ * <li>9 - date of week (E)</li>
+ * <li>10 - date of year (D)</li>
+ * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li>
+ * <li>12 - week in year (w)</li>
+ * <li>13 - week in month (W)</li>
+ * <li>14 - am/pm (a)</li>
+ * <li>15 - hour out of 24, from 1-24 (k)</li>
+ * <li>16 - hour out of 12, from 0-11 (K)</li>
+ * <li>17 - time zone (z)</li>
+ * </ul>
+ *
+ * @return The format patter characters
+ */
+ public String getLocalPatternChars()
+ {
+ return localPatternChars;
+ }
+
+ /**
+ * This method returns the list of strings used for displaying month
+ * names (e.g., "January" and "February"). This is a thirteen element
+ * string array indexed by <code>Calendar.JANUARY</code> through
+ * <code>Calendar.UNDECEMBER</code>. Note that there are thirteen
+ * elements because some calendars have thriteen months.
+ *
+ * @return The list of month display strings.
+ */
+ public String[] getMonths ()
+ {
+ return months;
+ }
+
+ /**
+ * This method returns the list of strings used for displaying abbreviated
+ * month names (e.g., "Jan" and "Feb"). This is a thirteen element
+ * <code>String</code> array indexed by <code>Calendar.JANUARY</code>
+ * through <code>Calendar.UNDECEMBER</code>. Note that there are thirteen
+ * elements because some calendars have thirteen months.
+ *
+ * @return The list of abbreviated month display strings.
+ */
+ public String[] getShortMonths ()
+ {
+ return shortMonths;
+ }
+
+ /**
+ * This method returns the list of strings used for displaying abbreviated
+ * weekday names (e.g., "Sun" and "Mon"). This is an eight element
+ * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
+ * through <code>Calendar.SATURDAY</code>. Note that the first element
+ * of this array is ignored.
+ *
+ * @return This list of abbreviated weekday display strings.
+ */
+ public String[] getShortWeekdays ()
+ {
+ return shortWeekdays;
+ }
+
+ /**
+ * This method returns the list of strings used for displaying weekday
+ * names (e.g., "Sunday" and "Monday"). This is an eight element
+ * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
+ * through <code>Calendar.SATURDAY</code>. Note that the first element
+ * of this array is ignored.
+ *
+ * @return This list of weekday display strings.
+ */
+ public String[] getWeekdays ()
+ {
+ return weekdays;
+ }
+
+ /**
+ * This method returns this list of localized timezone display strings.
+ * This is a two dimensional <code>String</code> array where each row in
+ * the array contains five values:
+ * <P>
+ * <ul>
+ * <li>0 - The non-localized time zone id string.</li>
+ * <li>1 - The long name of the time zone (standard time).</li>
+ * <li>2 - The short name of the time zone (standard time).</li>
+ * <li>3 - The long name of the time zone (daylight savings time).</li>
+ * <li>4 - the short name of the time zone (daylight savings time).</li>
+ * </ul>
+ *
+ * @return The list of time zone display strings.
+ */
+ public String[] [] getZoneStrings ()
+ {
+ return zoneStrings;
+ }
+
+ /**
+ * This method sets the list of strings used to display AM/PM values to
+ * the specified list.
+ * This is a two element <code>String</code> array indexed by
+ * <code>Calendar.AM</code> and <code>Calendar.PM</code>
+ *
+ * @param ampms The new list of AM/PM display strings.
+ */
+ public void setAmPmStrings (String[] value)
+ {
+ ampms = value;
+ }
+
+ /**
+ * This method sets the list of strings used to display time eras to
+ * to the specified list.
+ * This is a two element <code>String</code>
+ * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
+ *
+ * @param eras The new list of era disply strings.
+ */
+ public void setEras (String[] value)
+ {
+ eras = value;
+ }
+
+ /**
+ * This method sets the list of characters used to specific date/time
+ * formatting strings.
+ * This is an 18 character string that contains the characters
+ * that are used in creating the date formatting strings in
+ * <code>SimpleDateFormat</code>. The following are the character
+ * positions in the string and which format character they correspond
+ * to (the character in parentheses is the default value in the US English
+ * locale):
+ * <p>
+ * <ul>
+ * <li>0 - era (G)</li>
+ * <li>1 - year (y)</li>
+ * <li>2 - month (M)</li>
+ * <li>3 - day of month (d)</li>
+ * <li>4 - hour out of 12, from 1-12 (h)</li>
+ * <li>5 - hour out of 24, from 0-23 (H)</li>
+ * <li>6 - minute (m)</li>
+ * <li>7 - second (s)</li>
+ * <li>8 - millisecond (S)</li>
+ * <li>9 - date of week (E)</li>
+ * <li>10 - date of year (D)</li>
+ * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li>
+ * <li>12 - week in year (w)</li>
+ * <li>13 - week in month (W)</li>
+ * <li>14 - am/pm (a)</li>
+ * <li>15 - hour out of 24, from 1-24 (k)</li>
+ * <li>16 - hour out of 12, from 0-11 (K)</li>
+ * <li>17 - time zone (z)</li>
+ * </ul>
+ *
+ * @param localPatternChars The new format patter characters
+ */
+ public void setLocalPatternChars (String value)
+ {
+ localPatternChars = value;
+ }
+
+ /**
+ * This method sets the list of strings used to display month names.
+ * This is a thirteen element
+ * string array indexed by <code>Calendar.JANUARY</code> through
+ * <code>Calendar.UNDECEMBER</code>. Note that there are thirteen
+ * elements because some calendars have thriteen months.
+ *
+ * @param months The list of month display strings.
+ */
+ public void setMonths (String[] value)
+ {
+ months = value;
+ }
+
+ /**
+ * This method sets the list of strings used to display abbreviated month
+ * names.
+ * This is a thirteen element
+ * <code>String</code> array indexed by <code>Calendar.JANUARY</code>
+ * through <code>Calendar.UNDECEMBER</code>. Note that there are thirteen
+ * elements because some calendars have thirteen months.
+ *
+ * @param shortMonths The new list of abbreviated month display strings.
+ */
+ public void setShortMonths (String[] value)
+ {
+ shortMonths = value;
+ }
+
+ /**
+ * This method sets the list of strings used to display abbreviated
+ * weekday names.
+ * This is an eight element
+ * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
+ * through <code>Calendar.SATURDAY</code>. Note that the first element
+ * of this array is ignored.
+ *
+ * @param shortWeekdays This list of abbreviated weekday display strings.
+ */
+ public void setShortWeekdays (String[] value)
+ {
+ shortWeekdays = value;
+ }
+
+ /**
+ * This method sets the list of strings used to display weekday names.
+ * This is an eight element
+ * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
+ * through <code>Calendar.SATURDAY</code>. Note that the first element
+ * of this array is ignored.
+ *
+ * @param weekdays This list of weekday display strings.
+ */
+ public void setWeekdays (String[] value)
+ {
+ weekdays = value;
+ }
+
+ /**
+ * This method sets the list of display strings for time zones.
+ * This is a two dimensional <code>String</code> array where each row in
+ * the array contains five values:
+ * <P>
+ * <ul>
+ * <li>0 - The non-localized time zone id string.</li>
+ * <li>1 - The long name of the time zone (standard time).</li>
+ * <li>2 - The short name of the time zone (standard time).</li>
+ * <li>3 - The long name of the time zone (daylight savings time).</li>
+ * <li>4 - the short name of the time zone (daylight savings time).</li>
+ * </ul>
+ *
+ * @return The list of time zone display strings.
+ */
+ public void setZoneStrings (String[][] value)
+ {
+ zoneStrings = value;
+ }
+
+ /* Does a "deep" equality test - recurses into arrays. */
+ private 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;
+ }
+
+ /**
+ * This method tests a specified object for equality against this object.
+ * This will be true if and only if the specified object:
+ * <p>
+ * <ul>
+ * <li> Is not <code>null</code>.</li>
+ * <li> Is an instance of <code>DateFormatSymbols</code>.</li>
+ * <li> Contains identical formatting symbols to this object.</li>
+ * </ul>
+ *
+ * @param obj The <code>Object</code> to test for equality against.
+ *
+ * @return <code>true</code> if the specified object is equal to this one,
+ * <code>false</code> otherwise.
+ */
+ public boolean equals (Object obj)
+ {
+ if (! (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));
+ }
+
+ /**
+ * Returns a new copy of this object.
+ *
+ * @param A copy of this object
+ */
+ public Object clone ()
+ {
+ try
+ {
+ return super.clone ();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * This method returns a hash value for this object.
+ *
+ * @return A hash value for this object.
+ */
+ public int hashCode ()
+ {
+ return (hashCode(ampms)
+ ^ hashCode(eras)
+ ^ hashCode(localPatternChars)
+ ^ hashCode(months)
+ ^ hashCode(shortMonths)
+ ^ hashCode(shortWeekdays)
+ ^ hashCode(weekdays)
+ ^ hashCode(zoneStrings));
+ }
+}
diff --git a/libjava/classpath/java/text/DecimalFormat.java b/libjava/classpath/java/text/DecimalFormat.java
new file mode 100644
index 00000000000..6dadb0ce333
--- /dev/null
+++ b/libjava/classpath/java/text/DecimalFormat.java
@@ -0,0 +1,1435 @@
+/* DecimalFormat.java -- Formats and parses numbers
+ Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.text;
+
+import gnu.java.text.AttributedFormatBuffer;
+import gnu.java.text.FormatBuffer;
+import gnu.java.text.FormatCharacterIterator;
+import gnu.java.text.StringFormatBuffer;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.Currency;
+import java.util.HashMap;
+import java.util.Locale;
+
+/**
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @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.
+ * 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 int scanFix (String pattern, int index, FormatBuffer buf,
+ String patChars, DecimalFormatSymbols syms,
+ boolean is_suffix)
+ {
+ int len = pattern.length();
+ boolean quoteStarted = false;
+ buf.clear();
+
+ boolean multiplierSet = false;
+ while (index < len)
+ {
+ char c = pattern.charAt(index);
+
+ if (quoteStarted)
+ {
+ if (c == '\'')
+ quoteStarted = false;
+ else
+ buf.append(c);
+ index++;
+ continue;
+ }
+
+ if (c == '\'' && index + 1 < len
+ && pattern.charAt(index + 1) == '\'')
+ {
+ buf.append(c);
+ index++;
+ }
+ else if (c == '\'')
+ {
+ quoteStarted = true;
+ }
+ else if (c == '\u00a4')
+ {
+ /* Currency interpreted later */
+ buf.append(c);
+ }
+ else if (c == syms.getPercent())
+ {
+ if (multiplierSet)
+ throw new IllegalArgumentException ("multiplier already set " +
+ "- index: " + index);
+ multiplierSet = true;
+ multiplier = 100;
+ buf.append(c, NumberFormat.Field.PERCENT);
+ }
+ else if (c == syms.getPerMill())
+ {
+ if (multiplierSet)
+ throw new IllegalArgumentException ("multiplier already set " +
+ "- index: " + index);
+ multiplierSet = true;
+ multiplier = 1000;
+ buf.append(c, NumberFormat.Field.PERMILLE);
+ }
+ else if (patChars.indexOf(c) != -1)
+ {
+ // This is a pattern character.
+ break;
+ }
+ else
+ {
+ buf.append(c);
+ }
+ index++;
+ }
+
+ if (quoteStarted)
+ throw new IllegalArgumentException ("pattern is lacking a closing quote");
+
+ return index;
+ }
+
+ // A helper which reads a number format.
+ private int scanFormat (String pattern, int index, String patChars,
+ DecimalFormatSymbols syms, boolean is_positive)
+ {
+ 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 IllegalArgumentException ("digit mark following " +
+ "zero - index: " + 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 IllegalArgumentException ("zero mark " +
+ "following digit - index: " + index);
+ ++zeroCount;
+ }
+ else if (c == syms.getDigit())
+ {
+ ++hashCount;
+ }
+ else if (c != syms.getExponential()
+ && c != syms.getPatternSeparator()
+ && c != syms.getPercent()
+ && c != syms.getPerMill()
+ && patChars.indexOf(c) != -1)
+ throw new IllegalArgumentException ("unexpected special " +
+ "character - index: " + 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
+ IllegalArgumentException ("digit mark following zero " +
+ "in exponent - index: " +
+ index);
+ }
+ else if (patChars.indexOf(c) != -1)
+ throw new IllegalArgumentException ("unexpected special " +
+ "character - index: " +
+ index);
+ else
+ break;
+
+ ++index;
+ }
+
+ if (is_positive)
+ {
+ useExponentialNotation = true;
+ minExponentDigits = (byte) zeroCount;
+ }
+
+ maximumIntegerDigits = groupingSize;
+ groupingSize = 0;
+ if (maximumIntegerDigits > minimumIntegerDigits && maximumIntegerDigits > 0)
+ {
+ minimumIntegerDigits = 1;
+ exponentRound = maximumIntegerDigits;
+ }
+ else
+ exponentRound = 1;
+ }
+
+ return index;
+ }
+
+ // This helper function creates a string consisting of all the
+ // characters which can appear in a pattern and must be quoted.
+ private 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 void applyPatternWithSymbols(String pattern, DecimalFormatSymbols syms)
+ {
+ // 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 = MAXIMUM_INTEGER_DIGITS;
+ minimumFractionDigits = 0;
+ minimumIntegerDigits = 1;
+
+ AttributedFormatBuffer buf = new AttributedFormatBuffer ();
+ String patChars = patternChars (syms);
+
+ int max = pattern.length();
+ int index = scanFix (pattern, 0, buf, patChars, syms, false);
+ buf.sync();
+ positivePrefix = buf.getBuffer().toString();
+ positivePrefixRanges = buf.getRanges();
+ positivePrefixAttrs = buf.getAttributes();
+
+ index = scanFormat (pattern, index, patChars, syms, true);
+
+ index = scanFix (pattern, index, buf, patChars, syms, true);
+ buf.sync();
+ positiveSuffix = buf.getBuffer().toString();
+ positiveSuffixRanges = buf.getRanges();
+ positiveSuffixAttrs = buf.getAttributes();
+
+ if (index == pattern.length())
+ {
+ // No negative info.
+ negativePrefix = null;
+ negativeSuffix = null;
+ }
+ else
+ {
+ if (pattern.charAt(index) != syms.getPatternSeparator())
+ throw new IllegalArgumentException ("separator character " +
+ "expected - index: " + index);
+
+ index = scanFix (pattern, index + 1, buf, patChars, syms, false);
+ buf.sync();
+ negativePrefix = buf.getBuffer().toString();
+ negativePrefixRanges = buf.getRanges();
+ negativePrefixAttrs = buf.getAttributes();
+
+ // 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);
+ buf.sync();
+ negativeSuffix = buf.getBuffer().toString();
+ negativeSuffixRanges = buf.getRanges();
+ negativeSuffixAttrs = buf.getAttributes();
+
+ if (index != pattern.length())
+ throw new IllegalArgumentException ("end of pattern expected " +
+ "- index: " + index);
+ }
+ }
+
+ public void applyLocalizedPattern (String pattern)
+ {
+ // JCL p. 638 claims this throws a ParseException but p. 629
+ // contradicts this. Empirical tests with patterns of "0,###.0"
+ // and "#.#.#" corroborate the p. 629 statement that an
+ // IllegalArgumentException is thrown.
+ applyPatternWithSymbols (pattern, symbols);
+ }
+
+ public void applyPattern (String pattern)
+ {
+ // JCL p. 638 claims this throws a ParseException but p. 629
+ // contradicts this. Empirical tests with patterns of "0,###.0"
+ // and "#.#.#" corroborate the p. 629 statement that an
+ // IllegalArgumentException is thrown.
+ applyPatternWithSymbols (pattern, nonLocalizedSymbols);
+ }
+
+ public Object clone ()
+ {
+ DecimalFormat c = (DecimalFormat) super.clone ();
+ c.symbols = (DecimalFormatSymbols) symbols.clone ();
+ return c;
+ }
+
+ /**
+ * Constructs a <code>DecimalFormat</code> which uses the default
+ * pattern and symbols.
+ */
+ public DecimalFormat ()
+ {
+ this ("#,##0.###");
+ }
+
+ /**
+ * Constructs a <code>DecimalFormat</code> which uses the given
+ * pattern and the default symbols for formatting and parsing.
+ *
+ * @param pattern the non-localized pattern to use.
+ * @throws NullPointerException if any argument is null.
+ * @throws IllegalArgumentException if the pattern is invalid.
+ */
+ public DecimalFormat (String pattern)
+ {
+ this (pattern, new DecimalFormatSymbols ());
+ }
+
+ /**
+ * Constructs a <code>DecimalFormat</code> using the given pattern
+ * and formatting symbols. This construction method is used to give
+ * complete control over the formatting process.
+ *
+ * @param pattern the non-localized pattern to use.
+ * @param symbols the set of symbols used for parsing and formatting.
+ * @throws NullPointerException if any argument is null.
+ * @throws IllegalArgumentException if the pattern is invalid.
+ */
+ public DecimalFormat(String pattern, DecimalFormatSymbols symbols)
+ {
+ this.symbols = (DecimalFormatSymbols) symbols.clone();
+ applyPattern(pattern);
+ }
+
+ private boolean equals(String s1, String s2)
+ {
+ if (s1 == null || s2 == null)
+ return s1 == s2;
+ return s1.equals(s2);
+ }
+
+ /**
+ * Tests this instance for equality with an arbitrary object. This method
+ * returns <code>true</code> if:
+ * <ul>
+ * <li><code>obj</code> is not <code>null</code>;</li>
+ * <li><code>obj</code> is an instance of <code>DecimalFormat</code>;</li>
+ * <li>this instance and <code>obj</code> have the same attributes;</li>
+ * </ul>
+ *
+ * @param obj the object (<code>null</code> permitted).
+ *
+ * @return A boolean.
+ */
+ public boolean equals(Object obj)
+ {
+ if (! (obj instanceof DecimalFormat))
+ return false;
+ DecimalFormat dup = (DecimalFormat) obj;
+ return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown
+ && groupingUsed == dup.groupingUsed
+ && groupingSize == dup.groupingSize
+ && multiplier == dup.multiplier
+ && useExponentialNotation == dup.useExponentialNotation
+ && minExponentDigits == dup.minExponentDigits
+ && minimumIntegerDigits == dup.minimumIntegerDigits
+ && maximumIntegerDigits == dup.maximumIntegerDigits
+ && minimumFractionDigits == dup.minimumFractionDigits
+ && maximumFractionDigits == dup.maximumFractionDigits
+ && equals(negativePrefix, dup.negativePrefix)
+ && equals(negativeSuffix, dup.negativeSuffix)
+ && equals(positivePrefix, dup.positivePrefix)
+ && equals(positiveSuffix, dup.positiveSuffix)
+ && symbols.equals(dup.symbols));
+ }
+
+ private void formatInternal (double number, FormatBuffer dest,
+ FieldPosition fieldPos)
+ {
+ // A very special case.
+ if (Double.isNaN(number))
+ {
+ dest.append(symbols.getNaN());
+ if (fieldPos != null &&
+ (fieldPos.getField() == INTEGER_FIELD ||
+ fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
+ {
+ int index = dest.length();
+ fieldPos.setBeginIndex(index - symbols.getNaN().length());
+ fieldPos.setEndIndex(index);
+ }
+ return;
+ }
+
+ boolean is_neg = number < 0;
+ if (is_neg)
+ {
+ if (negativePrefix != null)
+ {
+ dest.append(substituteCurrency(negativePrefix, number),
+ negativePrefixRanges, negativePrefixAttrs);
+ }
+ else
+ {
+ dest.append(symbols.getMinusSign(), NumberFormat.Field.SIGN);
+ dest.append(substituteCurrency(positivePrefix, number),
+ positivePrefixRanges, positivePrefixAttrs);
+ }
+ number = - number;
+ }
+ else
+ {
+ dest.append(substituteCurrency(positivePrefix, number),
+ positivePrefixRanges, positivePrefixAttrs);
+ }
+ int integerBeginIndex = dest.length();
+ int integerEndIndex = 0;
+ int zeroStart = symbols.getZeroDigit() - '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.floor (Math.log(number) / Math.log(10));
+ exponent = exponent - (exponent % exponentRound);
+ if (minimumIntegerDigits > 0)
+ exponent -= minimumIntegerDigits - 1;
+ baseNumber = (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);
+ String intPart = Long.toString((long)Math.floor(baseNumber));
+ int count, groupPosition = intPart.length();
+
+ dest.setDefaultAttribute(NumberFormat.Field.INTEGER);
+
+ for (count = 0; count < minimumIntegerDigits-intPart.length(); count++)
+ dest.append(symbols.getZeroDigit());
+
+ for (count = 0;
+ count < maximumIntegerDigits && count < intPart.length();
+ count++)
+ {
+ int dig = intPart.charAt(count);
+
+ // Append group separator if required.
+ if (groupingUsed && count > 0 && groupingSize != 0 && groupPosition % groupingSize == 0)
+ {
+ dest.append(symbols.getGroupingSeparator(), NumberFormat.Field.GROUPING_SEPARATOR);
+ dest.setDefaultAttribute(NumberFormat.Field.INTEGER);
+ }
+ dest.append((char) (zeroStart + dig));
+
+ groupPosition--;
+ }
+ dest.setDefaultAttribute(null);
+
+ integerEndIndex = dest.length();
+
+ int decimal_index = integerEndIndex;
+ int consecutive_zeros = 0;
+ int total_digits = 0;
+
+ int localMaximumFractionDigits = maximumFractionDigits;
+
+ if (useExponentialNotation)
+ localMaximumFractionDigits += minimumIntegerDigits - count;
+
+ // Strip integer part from NUMBER.
+ double fracPart = baseNumber - Math.floor(baseNumber);
+
+ if ( ((fracPart != 0 || minimumFractionDigits > 0) && localMaximumFractionDigits > 0)
+ || decimalSeparatorAlwaysShown)
+ {
+ dest.append (symbols.getDecimalSeparator(), NumberFormat.Field.DECIMAL_SEPARATOR);
+ }
+
+ int fraction_begin = dest.length();
+ dest.setDefaultAttribute(NumberFormat.Field.FRACTION);
+ for (count = 0;
+ count < localMaximumFractionDigits
+ && (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.cutTail(extra_zeros);
+ total_digits -= extra_zeros;
+ if (total_digits == 0 && !decimalSeparatorAlwaysShown)
+ dest.cutTail(1);
+ }
+
+ if (fieldPos != null && fieldPos.getField() == FRACTION_FIELD)
+ {
+ fieldPos.setBeginIndex(fraction_begin);
+ fieldPos.setEndIndex(dest.length());
+ }
+
+ // Finally, print the exponent.
+ if (useExponentialNotation)
+ {
+ dest.append(symbols.getExponential(), NumberFormat.Field.EXPONENT_SYMBOL);
+ if (exponent < 0)
+ {
+ dest.append (symbols.getMinusSign (), NumberFormat.Field.EXPONENT_SIGN);
+ exponent = - exponent;
+ }
+ index = dest.length();
+ dest.setDefaultAttribute(NumberFormat.Field.EXPONENT);
+ String exponentString = Long.toString ((long) exponent);
+
+ for (count = 0; count < minExponentDigits-exponentString.length();
+ count++)
+ dest.append((char) symbols.getZeroDigit());
+
+ for (count = 0;
+ count < exponentString.length();
+ ++count)
+ {
+ int dig = exponentString.charAt(count);
+ dest.append((char) (zeroStart + dig));
+ }
+ }
+ }
+
+ if (fieldPos != null &&
+ (fieldPos.getField() == INTEGER_FIELD ||
+ fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
+ {
+ fieldPos.setBeginIndex(integerBeginIndex);
+ fieldPos.setEndIndex(integerEndIndex);
+ }
+
+ if (is_neg && negativeSuffix != null)
+ {
+ dest.append(substituteCurrency(negativeSuffix, number),
+ negativeSuffixRanges, negativeSuffixAttrs);
+ }
+ else
+ {
+ dest.append(substituteCurrency(positiveSuffix, number),
+ positiveSuffixRanges, positiveSuffixAttrs);
+ }
+ }
+
+ public StringBuffer format (double number, StringBuffer dest,
+ FieldPosition fieldPos)
+ {
+ formatInternal (number, new StringFormatBuffer(dest), fieldPos);
+ return dest;
+ }
+
+ public AttributedCharacterIterator formatToCharacterIterator (Object value)
+ {
+ AttributedFormatBuffer sbuf = new AttributedFormatBuffer();
+
+ if (value instanceof Number)
+ formatInternal(((Number) value).doubleValue(), sbuf, null);
+ else
+ throw new IllegalArgumentException
+ ("Cannot format given Object as a Number");
+
+ sbuf.sync();
+ return new FormatCharacterIterator(sbuf.getBuffer().toString(),
+ sbuf.getRanges(),
+ sbuf.getAttributes());
+ }
+
+ 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(substituteCurrency(negativePrefix, number));
+ else
+ {
+ dest.append(symbols.getMinusSign());
+ dest.append(substituteCurrency(positivePrefix, number));
+ }
+ number = - number;
+ }
+ else
+ dest.append(substituteCurrency(positivePrefix, number));
+
+ int integerBeginIndex = dest.length();
+ int index = dest.length();
+ int count = 0;
+
+ /* Handle percentages, etc. */
+ number *= multiplier;
+ 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 && groupingSize != 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)
+ ? substituteCurrency(negativeSuffix, number)
+ : substituteCurrency(positiveSuffix, number));
+ return dest;
+ }
+
+ /**
+ * Returns the currency corresponding to the currency symbol stored
+ * in the instance of <code>DecimalFormatSymbols</code> used by this
+ * <code>DecimalFormat</code>.
+ *
+ * @return A new instance of <code>Currency</code> if
+ * the currency code matches a known one, null otherwise.
+ */
+ public Currency getCurrency()
+ {
+ return symbols.getCurrency();
+ }
+
+ /**
+ * Returns a copy of the symbols used by this instance.
+ *
+ * @return A copy of the symbols.
+ */
+ public DecimalFormatSymbols getDecimalFormatSymbols()
+ {
+ return (DecimalFormatSymbols) symbols.clone();
+ }
+
+ 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;
+ }
+
+ /**
+ * Returns a hash code for this object.
+ *
+ * @return A hash code.
+ */
+ public int hashCode()
+ {
+ return toPattern().hashCode();
+ }
+
+ public boolean isDecimalSeparatorAlwaysShown ()
+ {
+ return decimalSeparatorAlwaysShown;
+ }
+
+ public Number parse (String str, ParsePosition pos)
+ {
+ /*
+ * Our strategy is simple: copy the text into separate buffers: one for the int part,
+ * one for the fraction part and for the exponential part.
+ * We translate or omit locale-specific information.
+ * If exponential is sufficiently big we merge the fraction and int part and
+ * remove the '.' and then we use Long to convert the number. In the other
+ * case, we use Double to convert the full number.
+ */
+
+ boolean is_neg = false;
+ int index = pos.getIndex();
+ StringBuffer int_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 digits?
+ // What about multiplier?
+
+ StringBuffer buf = int_buf;
+ StringBuffer frac_buf = null;
+ StringBuffer exp_buf = null;
+ int start_index = index;
+ int max = str.length();
+ int exp_index = -1;
+ int last = index + maximumIntegerDigits;
+
+ if (maximumFractionDigits > 0)
+ last += maximumFractionDigits + 1;
+
+ if (useExponentialNotation)
+ last += minExponentDigits + 1;
+
+ if (last > 0 && max > last)
+ max = last;
+
+ 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
+ && groupingSize != 0
+ && (index - last_group) % groupingSize != 0)
+ {
+ pos.setErrorIndex(index);
+ return null;
+ }
+ last_group = index+1;
+ }
+ else if (c >= zero && c <= zero + 9)
+ {
+ buf.append((char) (c - zero + '0'));
+ }
+ else if (parseIntegerOnly)
+ break;
+ else if (c == symbols.getDecimalSeparator())
+ {
+ if (last_group != -1
+ && groupingSize != 0
+ && (index - last_group) % groupingSize != 0)
+ {
+ pos.setErrorIndex(index);
+ return null;
+ }
+ buf = frac_buf = new StringBuffer();
+ frac_buf.append('.');
+ int_part = false;
+ }
+ else if (c == symbols.getExponential())
+ {
+ buf = exp_buf = new StringBuffer();
+ int_part = false;
+ exp_part = true;
+ exp_index = index+1;
+ }
+ 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;
+ }
+ else if (! got_pos_suf)
+ {
+ pos.setErrorIndex(index);
+ return null;
+ }
+
+ String suffix = is_neg ? ns : positiveSuffix;
+ long parsedMultiplier = 1;
+ boolean use_long;
+
+ if (is_neg)
+ int_buf.insert(0, '-');
+
+ // Now handle the exponential part if there is one.
+ if (exp_buf != null)
+ {
+ int exponent_value;
+
+ try
+ {
+ exponent_value = Integer.parseInt(exp_buf.toString());
+ }
+ catch (NumberFormatException x1)
+ {
+ pos.setErrorIndex(exp_index);
+ return null;
+ }
+
+ if (frac_buf == null)
+ {
+ // We only have to add some zeros to the int part.
+ // Build a multiplier.
+ for (int i = 0; i < exponent_value; i++)
+ int_buf.append('0');
+
+ use_long = true;
+ }
+ else
+ {
+ boolean long_sufficient;
+
+ if (exponent_value < frac_buf.length()-1)
+ {
+ int lastNonNull = -1;
+ /* We have to check the fraction buffer: it may only be full of '0'
+ * or be sufficiently filled with it to convert the number into Long.
+ */
+ for (int i = 1; i < frac_buf.length(); i++)
+ if (frac_buf.charAt(i) != '0')
+ lastNonNull = i;
+
+ long_sufficient = (lastNonNull < 0 || lastNonNull <= exponent_value);
+ }
+ else
+ long_sufficient = true;
+
+ if (long_sufficient)
+ {
+ for (int i = 1; i < frac_buf.length() && i < exponent_value; i++)
+ int_buf.append(frac_buf.charAt(i));
+ for (int i = frac_buf.length()-1; i < exponent_value; i++)
+ int_buf.append('0');
+ use_long = true;
+ }
+ else
+ {
+ /*
+ * A long type is not sufficient, we build the full buffer to
+ * be parsed by Double.
+ */
+ int_buf.append(frac_buf);
+ int_buf.append('E');
+ int_buf.append(exp_buf);
+ use_long = false;
+ }
+ }
+ }
+ else
+ {
+ if (frac_buf != null)
+ {
+ /* Check whether the fraction buffer contains only '0' */
+ int i;
+ for (i = 1; i < frac_buf.length(); i++)
+ if (frac_buf.charAt(i) != '0')
+ break;
+
+ if (i != frac_buf.length())
+ {
+ use_long = false;
+ int_buf.append(frac_buf);
+ }
+ else
+ use_long = true;
+ }
+ else
+ use_long = true;
+ }
+
+ String t = int_buf.toString();
+ Number result = null;
+ if (use_long)
+ {
+ try
+ {
+ result = new Long (t);
+ }
+ catch (NumberFormatException x1)
+ {
+ }
+ }
+ else
+ {
+ try
+ {
+ result = new Double (t);
+ }
+ catch (NumberFormatException x2)
+ {
+ }
+ }
+ if (result == null)
+ {
+ pos.setErrorIndex(index);
+ return null;
+ }
+
+ pos.setIndex(index + suffix.length());
+
+ return result;
+ }
+
+ /**
+ * Sets the <code>Currency</code> on the
+ * <code>DecimalFormatSymbols</code> used, which also sets the
+ * currency symbols on those symbols.
+ */
+ public void setCurrency(Currency currency)
+ {
+ symbols.setCurrency(currency);
+ }
+
+ /**
+ * Sets the symbols used by this instance. This method makes a copy of
+ * the supplied symbols.
+ *
+ * @param newSymbols the symbols (<code>null</code> not permitted).
+ */
+ public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols)
+ {
+ symbols = (DecimalFormatSymbols) newSymbols.clone();
+ }
+
+ public void setDecimalSeparatorAlwaysShown (boolean newValue)
+ {
+ decimalSeparatorAlwaysShown = newValue;
+ }
+
+ public void setGroupingSize (int groupSize)
+ {
+ groupingSize = (byte) groupSize;
+ }
+
+ public void setMaximumFractionDigits (int newValue)
+ {
+ super.setMaximumFractionDigits(Math.min(newValue, 340));
+ }
+
+ public void setMaximumIntegerDigits (int newValue)
+ {
+ super.setMaximumIntegerDigits(Math.min(newValue, 309));
+ }
+
+ public void setMinimumFractionDigits (int newValue)
+ {
+ super.setMinimumFractionDigits(Math.min(newValue, 340));
+ }
+
+ public void setMinimumIntegerDigits (int newValue)
+ {
+ super.setMinimumIntegerDigits(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 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 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: groupingSize);
+ 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);
+ }
+
+ private static final int MAXIMUM_INTEGER_DIGITS = 309;
+
+ // These names are fixed by the serialization spec.
+ private boolean decimalSeparatorAlwaysShown;
+ private byte groupingSize;
+ private byte minExponentDigits;
+ private int exponentRound;
+ private int multiplier;
+ private String negativePrefix;
+ private String negativeSuffix;
+ private String positivePrefix;
+ private String positiveSuffix;
+ private int[] negativePrefixRanges, positivePrefixRanges;
+ private HashMap[] negativePrefixAttrs, positivePrefixAttrs;
+ private int[] negativeSuffixRanges, positiveSuffixRanges;
+ private HashMap[] negativeSuffixAttrs, positiveSuffixAttrs;
+ private int serialVersionOnStream = 1;
+ private DecimalFormatSymbols symbols;
+ private boolean useExponentialNotation;
+ private static final long serialVersionUID = 864413376551465018L;
+
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException
+ {
+ stream.defaultReadObject();
+ if (serialVersionOnStream < 1)
+ {
+ useExponentialNotation = false;
+ serialVersionOnStream = 1;
+ }
+ }
+
+ // The locale-independent pattern symbols happen to be the same as
+ // the US symbols.
+ private static final DecimalFormatSymbols nonLocalizedSymbols
+ = new DecimalFormatSymbols (Locale.US);
+
+ /**
+ * <p>
+ * Substitutes the currency symbol into the given string,
+ * based on the value used. Currency symbols can either
+ * be a simple series of characters (e.g. '$'), which are
+ * simply used as is, or they can be of a more complex
+ * form:
+ * </p>
+ * <p>
+ * (lower bound)|(mid value)|(upper bound)
+ * </p>
+ * <p>
+ * where each bound has the syntax '(value)(# or <)(symbol)',
+ * to indicate the bounding value and the symbol used.
+ * </p>
+ * <p>
+ * The currency symbol replaces the currency specifier, '\u00a4',
+ * an unlocalised character, which thus is used as such in all formats.
+ * If this symbol occurs twice, the international currency code is used
+ * instead.
+ * </p>
+ *
+ * @param string The string containing the currency specifier, '\u00a4'.
+ * @param number the number being formatted.
+ * @return a string formatted for the correct currency.
+ */
+ private String substituteCurrency(String string, double number)
+ {
+ int index;
+ int length;
+ char currentChar;
+ StringBuffer buf;
+
+ index = 0;
+ length = string.length();
+ buf = new StringBuffer();
+
+ while (index < length)
+ {
+ currentChar = string.charAt(index);
+ if (string.charAt(index) == '\u00a4')
+ {
+ if ((index + 1) < length && string.charAt(index + 1) == '\u00a4')
+ {
+ buf.append(symbols.getInternationalCurrencySymbol());
+ index += 2;
+ }
+ else
+ {
+ String symbol;
+
+ symbol = symbols.getCurrencySymbol();
+ if (symbol.startsWith("="))
+ {
+ String[] bounds;
+ int[] boundValues;
+ String[] boundSymbols;
+
+ bounds = symbol.substring(1).split("\\|");
+ boundValues = new int[3];
+ boundSymbols = new String[3];
+ for (int a = 0; a < 3; ++a)
+ {
+ String[] bound;
+
+ bound = bounds[a].split("[#<]");
+ boundValues[a] = Integer.parseInt(bound[0]);
+ boundSymbols[a] = bound[1];
+ }
+ if (number <= boundValues[0])
+ {
+ buf.append(boundSymbols[0]);
+ }
+ else if (number >= boundValues[2])
+ {
+ buf.append(boundSymbols[2]);
+ }
+ else
+ {
+ buf.append(boundSymbols[1]);
+ }
+ ++index;
+ }
+ else
+ {
+ buf.append(symbol);
+ ++index;
+ }
+ }
+ }
+ else
+ {
+ buf.append(string.charAt(index));
+ ++index;
+ }
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/java/text/DecimalFormatSymbols.java b/libjava/classpath/java/text/DecimalFormatSymbols.java
new file mode 100644
index 00000000000..81ea0017822
--- /dev/null
+++ b/libjava/classpath/java/text/DecimalFormatSymbols.java
@@ -0,0 +1,688 @@
+/* DecimalFormatSymbols.java -- Format symbols used by DecimalFormat
+ Copyright (C) 1999, 2000, 2001, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.Currency;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * This class is a container for the symbols used by
+ * <code>DecimalFormat</code> to format numbers and currency
+ * for a particular locale. These are
+ * normally handled automatically, but an application can override
+ * values as desired using this class.
+ *
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @date February 24, 1999
+ * @see java.text.DecimalFormat
+ */
+/* 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.
+ */
+public final class DecimalFormatSymbols implements Cloneable, Serializable
+{
+ public Object clone ()
+ {
+ try
+ {
+ return super.clone ();
+ }
+ catch(CloneNotSupportedException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * This method initializes a new instance of
+ * <code>DecimalFormatSymbols</code> for the default locale.
+ */
+ public DecimalFormatSymbols ()
+ {
+ this (Locale.getDefault());
+ }
+
+ /**
+ * Retrieves a valid string, either using the supplied resource
+ * bundle or the default value.
+ *
+ * @param bundle the resource bundle to use to find the string.
+ * @param name key for the string in the resource bundle.
+ * @param def default value for the string.
+ */
+ private String safeGetString(ResourceBundle bundle,
+ String name, String def)
+ {
+ if (bundle != null)
+ {
+ try
+ {
+ return bundle.getString(name);
+ }
+ catch (MissingResourceException x)
+ {
+ }
+ }
+ return def;
+ }
+
+ private 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);
+ }
+
+ /**
+ * This method initializes a new instance of
+ * <code>DecimalFormatSymbols</code> for the specified locale.
+ * <strong>Note</strong>: if the locale does not have an associated
+ * <code>Currency</code> instance, the currency symbol and
+ * international currency symbol will be set to the strings "?"
+ * and "XXX" respectively. This generally happens with language
+ * locales (those with no specified country), such as
+ * <code>Locale.ENGLISH</code>.
+ *
+ * @param locale The local to load symbols for.
+ * @throws NullPointerException if the locale is null.
+ */
+ public DecimalFormatSymbols (Locale loc)
+ {
+ ResourceBundle res;
+
+ currency = Currency.getInstance("XXX");
+ currencySymbol = "?";
+ intlCurrencySymbol = "XXX";
+ try
+ {
+ res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+ loc, ClassLoader.getSystemClassLoader());
+ }
+ catch (MissingResourceException x)
+ {
+ res = null;
+ }
+ try
+ {
+ Currency localeCurrency = Currency.getInstance(loc);
+ if (localeCurrency != null)
+ {
+ setCurrency(localeCurrency);
+ }
+ }
+ catch(IllegalArgumentException exception)
+ {
+ /* Locale has an invalid currency */
+ }
+ decimalSeparator = safeGetChar (res, "decimalSeparator", '.');
+ digit = safeGetChar (res, "digit", '#');
+ exponential = safeGetChar (res, "exponential", 'E');
+ groupingSeparator = safeGetChar (res, "groupingSeparator", ',');
+ infinity = safeGetString (res, "infinity", "\u221e");
+ try
+ {
+ monetarySeparator = safeGetChar (res, "monetarySeparator", '.');
+ }
+ catch (MissingResourceException x)
+ {
+ monetarySeparator = decimalSeparator;
+ }
+ 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');
+ locale = loc;
+ }
+
+ /**
+ * This method this this object for equality against the specified object.
+ * This will be true if and only if the following criteria are met with
+ * regard to the specified object:
+ * <p>
+ * <ul>
+ * <li>It is not <code>null</code>.</li>
+ * <li>It is an instance of <code>DecimalFormatSymbols</code>.</li>
+ * <li>All of its symbols are identical to the symbols in this object.</li>
+ * </ul>
+ *
+ * @return <code>true</code> if the specified object is equal to this
+ * object, <code>false</code> otherwise.
+ */
+ 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
+ && monetarySeparator == dfs.monetarySeparator
+ && NaN.equals(dfs.NaN)
+ && patternSeparator == dfs.patternSeparator
+ && percent == dfs.percent
+ && perMill == dfs.perMill
+ && zeroDigit == dfs.zeroDigit);
+ }
+
+ /**
+ * Returns the currency corresponding to the currency symbol stored
+ * in this instance of <code>DecimalFormatSymbols</code>.
+ *
+ * @return An instance of <code>Currency</code> which matches
+ * the currency used, or null if there is no corresponding
+ * instance.
+ */
+ public Currency getCurrency ()
+ {
+ return currency;
+ }
+
+ /**
+ * This method returns the currency symbol in local format. For example,
+ * "$" for Canadian dollars.
+ *
+ * @return The currency symbol in local format.
+ */
+ public String getCurrencySymbol ()
+ {
+ return currencySymbol;
+ }
+
+ /**
+ * This method returns the character used as the decimal point.
+ *
+ * @return The character used as the decimal point.
+ */
+ public char getDecimalSeparator ()
+ {
+ return decimalSeparator;
+ }
+
+ /**
+ * This method returns the character used to represent a digit in a
+ * format pattern string.
+ *
+ * @return The character used to represent a digit in a format
+ * pattern string.
+ */
+ public char getDigit ()
+ {
+ return digit;
+ }
+
+ /**
+ * This method returns the character used to represent the exponential
+ * format. This is a GNU Classpath extension.
+ *
+ * @return the character used to represent an exponential in a format
+ * pattern string.
+ */
+ char getExponential ()
+ {
+ return exponential;
+ }
+
+ /**
+ * This method sets the character used to separate groups of digits. For
+ * example, the United States uses a comma (,) to separate thousands in
+ * a number.
+ *
+ * @return The character used to separate groups of digits.
+ */
+ public char getGroupingSeparator ()
+ {
+ return groupingSeparator;
+ }
+
+ /**
+ * This method returns the character used to represent infinity.
+ *
+ * @return The character used to represent infinity.
+ */
+ public String getInfinity ()
+ {
+ return infinity;
+ }
+
+ /**
+ * This method returns the ISO 4217 currency code for
+ * the currency used.
+ *
+ * @return the ISO 4217 currency code.
+ */
+ public String getInternationalCurrencySymbol ()
+ {
+ return intlCurrencySymbol;
+ }
+
+ /**
+ * This method returns the character used to represent the minus sign.
+ *
+ * @return The character used to represent the minus sign.
+ */
+ public char getMinusSign ()
+ {
+ return minusSign;
+ }
+
+ /**
+ * This method returns the character used to represent the decimal
+ * point for currency values.
+ *
+ * @return The decimal point character used in currency values.
+ */
+ public char getMonetaryDecimalSeparator ()
+ {
+ return monetarySeparator;
+ }
+
+ /**
+ * This method returns the string used to represent the NaN (not a number)
+ * value.
+ *
+ * @return The string used to represent NaN
+ */
+ public String getNaN ()
+ {
+ return NaN;
+ }
+
+ /**
+ * This method returns the character used to separate positive and negative
+ * subpatterns in a format pattern.
+ *
+ * @return The character used to separate positive and negative subpatterns
+ * in a format pattern.
+ */
+ public char getPatternSeparator ()
+ {
+ return patternSeparator;
+ }
+
+ /**
+ * This method returns the character used as the percent sign.
+ *
+ * @return The character used as the percent sign.
+ */
+ public char getPercent ()
+ {
+ return percent;
+ }
+
+ /**
+ * This method returns the character used as the per mille character.
+ *
+ * @return The per mille character.
+ */
+ public char getPerMill ()
+ {
+ return perMill;
+ }
+
+ /**
+ * This method returns the character used to represent the digit zero.
+ *
+ * @return The character used to represent the digit zero.
+ */
+ public char getZeroDigit ()
+ {
+ return zeroDigit;
+ }
+
+ /**
+ * This method returns a hash value for this object.
+ *
+ * @return A hash value for this object.
+ */
+ 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;
+ }
+
+ /**
+ * This method sets the currency symbol and ISO 4217 currency
+ * code to the values obtained from the supplied currency.
+ *
+ * @param currency the currency from which to obtain the values.
+ * @throws NullPointerException if the currency is null.
+ */
+ public void setCurrency (Currency currency)
+ {
+ intlCurrencySymbol = currency.getCurrencyCode();
+ currencySymbol = currency.getSymbol();
+ this.currency = currency;
+ }
+
+ /**
+ * This method sets the currency symbol to the specified value.
+ *
+ * @param currencySymbol The new currency symbol
+ */
+ public void setCurrencySymbol (String currency)
+ {
+ currencySymbol = currency;
+ }
+
+ /**
+ * This method sets the decimal point character to the specified value.
+ *
+ * @param decimalSeparator The new decimal point character
+ */
+ public void setDecimalSeparator (char decimalSep)
+ {
+ decimalSeparator = decimalSep;
+ }
+
+ /**
+ * This method sets the character used to represents a digit in a format
+ * string to the specified value.
+ *
+ * @param digit The character used to represent a digit in a format pattern.
+ */
+ public void setDigit (char digit)
+ {
+ this.digit = digit;
+ }
+
+ /**
+ * This method sets the exponential character used in the format string to
+ * the specified value. This is a GNU Classpath extension.
+ *
+ * @param exp the character used for the exponential in a format pattern.
+ */
+ void setExponential (char exp)
+ {
+ exponential = exp;
+ }
+
+ /**
+ * This method sets the character used to separate groups of digits.
+ *
+ * @param groupingSeparator The character used to separate groups of digits.
+ */
+ public void setGroupingSeparator (char groupSep)
+ {
+ groupingSeparator = groupSep;
+ }
+
+ /**
+ * This method sets the string used to represents infinity.
+ *
+ * @param infinity The string used to represent infinity.
+ */
+ public void setInfinity (String infinity)
+ {
+ this.infinity = infinity;
+ }
+
+ /**
+ * This method sets the international currency symbol to the
+ * specified value. If a valid <code>Currency</code> instance
+ * exists for the international currency code, then this is
+ * used for the currency attribute, and the currency symbol
+ * is set to the corresponding value from this instance.
+ * Otherwise, the currency attribute is set to null and the
+ * symbol is left unmodified.
+ *
+ * @param currencyCode The new international currency symbol.
+ */
+ public void setInternationalCurrencySymbol (String currencyCode)
+ {
+ intlCurrencySymbol = currencyCode;
+ try
+ {
+ currency = Currency.getInstance(currencyCode);
+ }
+ catch (IllegalArgumentException exception)
+ {
+ currency = null;
+ }
+ if (currency != null)
+ {
+ setCurrencySymbol(currency.getSymbol(locale));
+ }
+ }
+
+ /**
+ * This method sets the character used to represent the minus sign.
+ *
+ * @param minusSign The character used to represent the minus sign.
+ */
+ public void setMinusSign (char minusSign)
+ {
+ this.minusSign = minusSign;
+ }
+
+ /**
+ * This method sets the character used for the decimal point in currency
+ * values.
+ *
+ * @param monetarySeparator The decimal point character used in
+ * currency values.
+ */
+ public void setMonetaryDecimalSeparator (char decimalSep)
+ {
+ monetarySeparator = decimalSep;
+ }
+
+ /**
+ * This method sets the string used to represent the NaN (not a
+ * number) value.
+ *
+ * @param NaN The string used to represent NaN
+ */
+ public void setNaN (String nan)
+ {
+ NaN = nan;
+ }
+
+ /**
+ * This method sets the character used to separate positive and negative
+ * subpatterns in a format pattern.
+ *
+ * @param patternSeparator The character used to separate positive and
+ * negative subpatterns in a format pattern.
+ */
+ public void setPatternSeparator (char patternSep)
+ {
+ patternSeparator = patternSep;
+ }
+
+ /**
+ * This method sets the character used as the percent sign.
+ *
+ * @param percent The character used as the percent sign.
+ */
+ public void setPercent (char percent)
+ {
+ this.percent = percent;
+ }
+
+ /**
+ * This method sets the character used as the per mille character.
+ *
+ * @param perMill The per mille character.
+ */
+ public void setPerMill (char perMill)
+ {
+ this.perMill = perMill;
+ }
+
+ /**
+ * This method sets the character used to represent the digit zero.
+ *
+ * @param zeroDigit The character used to represent the digit zero.
+ */
+ public void setZeroDigit (char zeroDigit)
+ {
+ this.zeroDigit = zeroDigit;
+ }
+
+ /**
+ * @serial A string used for the local currency
+ */
+ private String currencySymbol;
+ /**
+ * @serial The <code>char</code> used to separate decimals in a number.
+ */
+ private char decimalSeparator;
+ /**
+ * @serial This is the <code>char</code> used to represent a digit in
+ * a format specification.
+ */
+ private char digit;
+ /**
+ * @serial This is the <code>char</code> used to represent the exponent
+ * separator in exponential notation.
+ */
+ private char exponential;
+ /**
+ * @serial This separates groups of thousands in numbers.
+ */
+ private char groupingSeparator;
+ /**
+ * @serial This string represents infinity.
+ */
+ private String infinity;
+ /**
+ * @serial This string represents the local currency in an international
+ * context, eg, "C$" for Canadian dollars.
+ */
+ private String intlCurrencySymbol;
+ /**
+ * @serial This is the character used to represent the minus sign.
+ */
+ private char minusSign;
+ /**
+ * @serial This character is used to separate decimals when formatting
+ * currency values.
+ */
+ private char monetarySeparator;
+ /**
+ * @serial This string is used the represent the Java NaN value for
+ * "not a number".
+ */
+ private String NaN;
+ /**
+ * @serial This is the character used to separate positive and negative
+ * subpatterns in a format pattern.
+ */
+ private char patternSeparator;
+ /**
+ * @serial This is the percent symbols
+ */
+ private char percent;
+ /**
+ * @serial This character is used for the mille percent sign.
+ */
+ private char perMill;
+ /**
+ * @serial This value represents the type of object being de-serialized.
+ * 0 indicates a pre-Java 1.1.6 version, 1 indicates 1.1.6 or later.
+ * 0 indicates a pre-Java 1.1.6 version, 1 indicates 1.1.6 or later,
+ * 2 indicates 1.4 or later
+ */
+ private int serialVersionOnStream = 2;
+ /**
+ * @serial This is the character used to represent 0.
+ */
+ private char zeroDigit;
+
+ /**
+ * @serial The locale of these currency symbols.
+ */
+ private Locale locale;
+
+ /**
+ * The currency used for the symbols in this instance.
+ * This is stored temporarily for efficiency reasons,
+ * as well as to ensure that the correct instance
+ * is restored from the currency code.
+ *
+ * @serial Ignored.
+ */
+ private transient Currency currency;
+
+ private static final long serialVersionUID = 5772796243397350300L;
+
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException
+ {
+ stream.defaultReadObject();
+ if (serialVersionOnStream < 1)
+ {
+ monetarySeparator = decimalSeparator;
+ exponential = 'E';
+ }
+ if (serialVersionOnStream < 2)
+ locale = Locale.getDefault();
+
+ serialVersionOnStream = 2;
+ }
+}
diff --git a/libjava/classpath/java/text/FieldPosition.java b/libjava/classpath/java/text/FieldPosition.java
new file mode 100644
index 00000000000..427c07e8e11
--- /dev/null
+++ b/libjava/classpath/java/text/FieldPosition.java
@@ -0,0 +1,232 @@
+/* FieldPosition.java -- Keeps track of field positions while formatting
+ Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+/**
+ * This class is used by the java.text formatting classes to track
+ * field positions. A field position is defined by an identifier value
+ * and begin and end index positions. The formatting classes in java.text
+ * typically define constant values for the field identifiers.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Per Bothner (bothner@cygnus.com)
+ */
+public class FieldPosition
+{
+ /**
+ * This is the field identifier value.
+ */
+ private int field_id;
+
+ /**
+ * This is the beginning index of the field.
+ */
+ private int begin;
+
+ /**
+ * This is the ending index of the field.
+ */
+ private int end;
+
+ /**
+ * This is the field attribute value.
+ */
+ private Format.Field field_attribute;
+
+ /**
+ * This method initializes a new instance of <code>FieldPosition</code>
+ * to have the specified field attribute. The attribute will be used as
+ * an id. It is formally equivalent to calling FieldPosition(field, -1).
+ *
+ * @param field The field format attribute.
+ */
+ public FieldPosition (Format.Field field)
+ {
+ this(field, -1);
+ }
+
+ /**
+ * This method initializes a new instance of <code>FieldPosition</code>
+ * to have the specified field attribute. The attribute will be used as
+ * an id is non null. The integer field id is only used if the Format.Field
+ * attribute is not used by the formatter.
+ *
+ * @param field The field format attribute.
+ * @param field_id The field identifier value.
+ */
+ public FieldPosition (Format.Field field, int field_id)
+ {
+ this.field_attribute = field;
+ this.field_id = field_id;
+ }
+
+ /**
+ * This method initializes a new instance of <code>FieldPosition</code> to
+ * have the specified field id.
+ *
+ * @param field_id The field identifier value.
+ */
+ public FieldPosition (int field_id)
+ {
+ this.field_id = field_id;
+ }
+
+ /**
+ * This method returns the field identifier value for this object.
+ *
+ * @return The field identifier.
+ */
+ public int getField ()
+ {
+ return field_id;
+ }
+
+ public Format.Field getFieldAttribute ()
+ {
+ return field_attribute;
+ }
+
+ /**
+ * This method returns the beginning index for this field.
+ *
+ * @return The beginning index.
+ */
+ public int getBeginIndex ()
+ {
+ return begin;
+ }
+
+ /**
+ * This method sets the beginning index of this field to the specified value.
+ *
+ * @param begin The new beginning index.
+ */
+ public void setBeginIndex (int begin)
+ {
+ this.begin = begin;
+ }
+
+ /**
+ * This method returns the ending index for the field.
+ *
+ * @return The ending index.
+ */
+ public int getEndIndex ()
+ {
+ return end;
+ }
+
+ /**
+ * This method sets the ending index of this field to the specified value.
+ *
+ * @param end The new ending index.
+ */
+ public void setEndIndex (int end)
+ {
+ this.end = end;
+ }
+
+ /**
+ * This method tests this object for equality against the specified object.
+ * The objects will be considered equal if and only if:
+ * <p>
+ * <ul>
+ * <li>The specified object is not <code>null</code>.
+ * <li>The specified object has the same class as this object.
+ * <li>The specified object has the same field identifier, field attribute
+ * and beginning and ending index as this object.
+ * </ul>
+ *
+ * @param obj The object to test for equality to this object.
+ *
+ * @return <code>true</code> if the specified object is equal to
+ * this object, <code>false</code> otherwise.
+ */
+ public boolean equals (Object obj)
+ {
+ if (this == obj)
+ return true;
+
+ if (obj == null || obj.getClass() != this.getClass())
+ return false;
+
+ FieldPosition fp = (FieldPosition) obj;
+ return (field_id == fp.field_id
+ && (field_attribute == fp.field_attribute
+ || (field_attribute != null
+ && field_attribute.equals(fp.field_attribute)))
+ && begin == fp.begin
+ && end == fp.end);
+ }
+
+
+ /**
+ * This method returns a hash value for this object
+ *
+ * @return A hash value for this object.
+ */
+ public int hashCode ()
+ {
+ int hash = 5;
+
+ hash = 31 * hash + field_id;
+ hash = 31 * hash + begin;
+ hash = 31 * hash + end;
+ hash = 31 * hash +
+ (null == field_attribute ? 0 : field_attribute.hashCode());
+
+ return hash;
+ }
+
+ /**
+ * This method returns a <code>String</code> representation of this
+ * object.
+ *
+ * @return A <code>String</code> representation of this object.
+ */
+ public String toString ()
+ {
+ return (getClass ().getName ()
+ + "[field=" + getField ()
+ + ",attribute=" + getFieldAttribute ()
+ + ",beginIndex=" + getBeginIndex ()
+ + ",endIndex=" + getEndIndex ()
+ + "]");
+ }
+}
diff --git a/libjava/classpath/java/text/Format.java b/libjava/classpath/java/text/Format.java
new file mode 100644
index 00000000000..38fda34ff64
--- /dev/null
+++ b/libjava/classpath/java/text/Format.java
@@ -0,0 +1,181 @@
+/* Format.java -- Abstract superclass for formatting/parsing strings.
+ Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import gnu.java.text.FormatCharacterIterator;
+
+import java.io.Serializable;
+
+/**
+ * This class is the abstract superclass of classes that format and parse
+ * data to/from <code>Strings</code>. It is guaranteed that any
+ * <code>String</code> produced by a concrete subclass of <code>Format</code>
+ * will be parseable by that same subclass.
+ * <p>
+ * In addition to implementing the abstract methods in this class, subclasses
+ * should provide static factory methods of the form
+ * <code>getInstance()</code> and <code>getInstance(Locale)</code> if the
+ * subclass loads different formatting/parsing schemes based on locale.
+ * These subclasses should also implement a static method called
+ * <code>getAvailableLocales()</code> which returns an array of
+ * available locales in the current runtime environment.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Per Bothner (bothner@cygnus.com)
+ */
+public abstract class Format implements Serializable, Cloneable
+{
+ /**
+ * For compatability with Sun's JDK 1.4.2 rev. 5
+ */
+ static final long serialVersionUID = -299282585814624189L;
+
+ public static class Field extends AttributedCharacterIterator.Attribute
+ {
+ static final long serialVersionUID = 276966692217360283L;
+
+ protected Field(String name)
+ {
+ super(name);
+ }
+ }
+
+ /**
+ * This method initializes a new instance of <code>Format</code>.
+ * It performs no actions, but acts as a default constructor for
+ * subclasses.
+ */
+ public Format ()
+ {
+ }
+
+ /**
+ * This method formats an <code>Object</code> into a <code>String</code>.
+ *
+ * @param obj The <code>Object</code> to format.
+ *
+ * @return The formatted <code>String</code>.
+ *
+ * @exception IllegalArgumentException If the <code>Object</code>
+ * cannot be formatted.
+ */
+ public final String format(Object obj) throws IllegalArgumentException
+ {
+ StringBuffer sb = new StringBuffer ();
+ format (obj, sb, new FieldPosition (0));
+ return sb.toString ();
+ }
+
+ /**
+ * This method formats an <code>Object</code> into a <code>String</code> and
+ * appends the <code>String</code> to a <code>StringBuffer</code>.
+ *
+ * @param obj The <code>Object</code> to format.
+ * @param sb The <code>StringBuffer</code> to append to.
+ * @param pos The desired <code>FieldPosition</code>, which is also
+ * updated by this call.
+ *
+ * @return The updated <code>StringBuffer</code>.
+ *
+ * @exception IllegalArgumentException If the <code>Object</code>
+ * cannot be formatted.
+ */
+ public abstract StringBuffer format (Object obj, StringBuffer sb,
+ FieldPosition pos)
+ throws IllegalArgumentException;
+
+ /**
+ * This method parses a <code>String</code> and converts the parsed
+ * contents into an <code>Object</code>.
+ *
+ * @param str The <code>String</code> to parse.
+ *
+ * @return The resulting <code>Object</code>.
+ *
+ * @exception ParseException If the <code>String</code> cannot be parsed.
+ */
+ public Object parseObject (String str) throws ParseException
+ {
+ ParsePosition pos = new ParsePosition(0);
+ Object result = parseObject (str, pos);
+ if (result == null)
+ {
+ int index = pos.getErrorIndex();
+ if (index < 0)
+ index = pos.getIndex();
+ throw new ParseException("parseObject failed", index);
+ }
+ return result;
+ }
+
+ /**
+ * This method parses a <code>String</code> and converts the parsed
+ * contents into an <code>Object</code>.
+ *
+ * @param str The <code>String</code> to parse.
+ * @param pos The starting parse index on input, the ending parse
+ * index on output.
+ *
+ * @return The parsed <code>Object</code>, or <code>null</code> in
+ * case of error.
+ */
+ public abstract Object parseObject (String str, ParsePosition pos);
+
+ public AttributedCharacterIterator formatToCharacterIterator(Object obj)
+ {
+ return new FormatCharacterIterator(format(obj), null, null);
+ }
+
+ /**
+ * Creates a copy of this object.
+ *
+ * @return The copied <code>Object</code>.
+ */
+ public Object clone ()
+ {
+ try
+ {
+ return super.clone ();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ return null;
+ }
+ }
+}
diff --git a/libjava/classpath/java/text/MessageFormat.java b/libjava/classpath/java/text/MessageFormat.java
new file mode 100644
index 00000000000..f7a9f1687a6
--- /dev/null
+++ b/libjava/classpath/java/text/MessageFormat.java
@@ -0,0 +1,832 @@
+/* MessageFormat.java - Localized message formatting.
+ Copyright (C) 1999, 2001, 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import gnu.java.text.FormatCharacterIterator;
+
+import java.io.InvalidObjectException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Vector;
+
+public class MessageFormat extends Format
+{
+ /**
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Jorge Aliss (jaliss@hotmail.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.
+ */
+ private static 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;
+
+ // Recompute the locale-based formatter.
+ void setLocale (Locale loc)
+ {
+ if (type == null)
+ ;
+ else if (type.equals("number"))
+ {
+ formatClass = java.lang.Number.class;
+
+ 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;
+ df.applyPattern(style);
+ }
+ }
+ else if (type.equals("time") || type.equals("date"))
+ {
+ formatClass = java.util.Date.class;
+
+ int val = DateFormat.DEFAULT;
+ boolean styleIsPattern = false;
+ if (style == null)
+ ;
+ else 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;
+ else
+ styleIsPattern = true;
+
+ if (type.equals("time"))
+ format = DateFormat.getTimeInstance(val, loc);
+ else
+ format = DateFormat.getDateInstance(val, loc);
+
+ if (styleIsPattern)
+ {
+ SimpleDateFormat sdf = (SimpleDateFormat) format;
+ sdf.applyPattern(style);
+ }
+ }
+ else if (type.equals("choice"))
+ {
+ formatClass = java.lang.Number.class;
+
+ if (style == null)
+ throw new
+ IllegalArgumentException ("style required for choice format");
+ format = new ChoiceFormat (style);
+ }
+ }
+ }
+
+ private static final long serialVersionUID = 6479157306784022952L;
+
+ public static class Field extends Format.Field
+ {
+ static final long serialVersionUID = 7899943957617360810L;
+
+ /**
+ * This is the attribute set for all characters produced
+ * by MessageFormat during a formatting.
+ */
+ public static final MessageFormat.Field ARGUMENT = new MessageFormat.Field("argument");
+
+ // For deserialization
+ private Field()
+ {
+ super("");
+ }
+
+ protected Field(String s)
+ {
+ super(s);
+ }
+
+ /**
+ * invoked to resolve the true static constant by
+ * comparing the deserialized object to know name.
+ *
+ * @return object constant
+ */
+ protected Object readResolve() throws InvalidObjectException
+ {
+ if (getName().equals(ARGUMENT.getName()))
+ return ARGUMENT;
+
+ throw new InvalidObjectException("no such MessageFormat field called " + getName());
+ }
+
+ }
+
+ // 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 int scanString(String pat, int index, StringBuffer buffer)
+ {
+ int max = pat.length();
+ buffer.setLength(0);
+ boolean quoted = false;
+ for (; index < max; ++index)
+ {
+ char c = pat.charAt(index);
+ if (quoted)
+ {
+ // In a quoted context, a single quote ends the quoting.
+ if (c == '\'')
+ quoted = false;
+ else
+ buffer.append(c);
+ }
+ // Check for '', which is a single quote.
+ else if (c == '\'' && index + 1 < max && pat.charAt(index + 1) == '\'')
+ {
+ buffer.append(c);
+ ++index;
+ }
+ else if (c == '\'')
+ {
+ // Start quoting.
+ quoted = true;
+ }
+ else if (c == '{')
+ break;
+ else
+ buffer.append(c);
+ }
+ // Note that we explicitly allow an unterminated quote. This is
+ // done for compatibility.
+ return index;
+ }
+
+ // This helper retrieves a single part of a format element. Returns
+ // the index of the terminating character.
+ private static int scanFormatElement(String pat, int index,
+ StringBuffer buffer, char term)
+ {
+ int max = pat.length();
+ buffer.setLength(0);
+ int brace_depth = 1;
+ boolean quoted = false;
+
+ for (; index < max; ++index)
+ {
+ char c = pat.charAt(index);
+ // First see if we should turn off quoting.
+ if (quoted)
+ {
+ if (c == '\'')
+ quoted = false;
+ // In both cases we fall through to inserting the
+ // character here.
+ }
+ // See if we have just a plain quote to insert.
+ else if (c == '\'' && index + 1 < max
+ && pat.charAt(index + 1) == '\'')
+ {
+ buffer.append(c);
+ ++index;
+ }
+ // See if quoting should turn on.
+ else if (c == '\'')
+ quoted = true;
+ else if (c == '{')
+ ++brace_depth;
+ else if (c == '}')
+ {
+ if (--brace_depth == 0)
+ break;
+ }
+ // Check for TERM after braces, because TERM might be `}'.
+ else if (c == term)
+ break;
+ // All characters, including opening and closing quotes, are
+ // inserted here.
+ buffer.append(c);
+ }
+ return index;
+ }
+
+ // This is used to parse a format element and whatever non-format
+ // text might trail it.
+ private static 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)
+ {
+ IllegalArgumentException iae = new IllegalArgumentException(pat);
+ iae.initCause(nfx);
+ throw iae;
+ }
+
+ // 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("Missing '}' at end of message format");
+ ++index;
+
+ // Now fetch trailing string.
+ index = scanString (pat, index, buffer);
+ mfe.trailer = buffer.toString ();
+
+ mfe.setLocale(locale);
+
+ return index;
+ }
+
+ /**
+ * Applies the specified pattern to this MessageFormat.
+ *
+ * @param aPattern The Pattern
+ */
+ 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);
+ }
+
+ /**
+ * Overrides Format.clone()
+ */
+ public Object clone ()
+ {
+ MessageFormat c = (MessageFormat) super.clone ();
+ c.elements = (MessageFormatElement[]) elements.clone ();
+ return c;
+ }
+
+ /**
+ * Overrides Format.equals(Object obj)
+ */
+ public boolean equals (Object obj)
+ {
+ if (! (obj instanceof MessageFormat))
+ return false;
+ MessageFormat mf = (MessageFormat) obj;
+ return (pattern.equals(mf.pattern)
+ && locale.equals(mf.locale));
+ }
+
+ /**
+ * A convinience method to format patterns.
+ *
+ * @param aPattern The pattern used when formatting.
+ * @param arguments The array containing the objects to be formatted.
+ */
+ public AttributedCharacterIterator formatToCharacterIterator (Object arguments)
+ {
+ Object[] arguments_array = (Object[])arguments;
+ FormatCharacterIterator iterator = new FormatCharacterIterator();
+
+ formatInternal(arguments_array, new StringBuffer(), null, iterator);
+
+ return iterator;
+ }
+
+ /**
+ * A convinience method to format patterns.
+ *
+ * @param aPattern The pattern used when formatting.
+ * @param arguments The array containing the objects to be formatted.
+ */
+ 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.formatInternal(arguments, sb, fp, null).toString();
+ }
+
+ /**
+ * Returns the pattern with the formatted objects.
+ *
+ * @param source The array containing the objects to be formatted.
+ * @param result The StringBuffer where the text is appened.
+ * @param fp A FieldPosition object (it is ignored).
+ */
+ public final StringBuffer format (Object arguments[], StringBuffer appendBuf,
+ FieldPosition fp)
+ {
+ return formatInternal(arguments, appendBuf, fp, null);
+ }
+
+ private StringBuffer formatInternal (Object arguments[],
+ StringBuffer appendBuf,
+ FieldPosition fp,
+ FormatCharacterIterator output_iterator)
+ {
+ appendBuf.append(leader);
+ if (output_iterator != null)
+ output_iterator.append(leader);
+
+ for (int i = 0; i < elements.length; ++i)
+ {
+ Object thisArg = null;
+ boolean unavailable = false;
+ if (arguments == null || elements[i].argNumber >= arguments.length)
+ unavailable = true;
+ else
+ thisArg = arguments[elements[i].argNumber];
+
+ AttributedCharacterIterator iterator = null;
+
+ Format formatter = null;
+
+ if (fp != null && i == fp.getField() && fp.getFieldAttribute() == Field.ARGUMENT)
+ fp.setBeginIndex(appendBuf.length());
+
+ if (unavailable)
+ appendBuf.append("{" + elements[i].argNumber + "}");
+ else
+ {
+ 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("Wrong format class");
+
+ 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 (fp != null && fp.getField() == i && fp.getFieldAttribute() == Field.ARGUMENT)
+ fp.setEndIndex(appendBuf.length());
+
+ if (formatter != null)
+ {
+ // Special-case ChoiceFormat.
+ if (formatter instanceof ChoiceFormat)
+ {
+ StringBuffer buf = new StringBuffer ();
+ formatter.format(thisArg, buf, fp);
+ MessageFormat mf = new MessageFormat ();
+ mf.setLocale(locale);
+ mf.applyPattern(buf.toString());
+ mf.format(arguments, appendBuf, fp);
+ }
+ else
+ {
+ if (output_iterator != null)
+ iterator = formatter.formatToCharacterIterator(thisArg);
+ else
+ formatter.format(thisArg, appendBuf, fp);
+ }
+
+ elements[i].format = formatter;
+ }
+
+ if (output_iterator != null)
+ {
+ HashMap hash_argument = new HashMap();
+ int position = output_iterator.getEndIndex();
+
+ hash_argument.put (MessageFormat.Field.ARGUMENT,
+ new Integer(elements[i].argNumber));
+
+
+ if (iterator != null)
+ {
+ output_iterator.append(iterator);
+ output_iterator.addAttributes(hash_argument, position,
+ output_iterator.getEndIndex());
+ }
+ else
+ output_iterator.append(thisArg.toString(), hash_argument);
+
+ output_iterator.append(elements[i].trailer);
+ }
+
+ appendBuf.append(elements[i].trailer);
+ }
+
+ return appendBuf;
+ }
+
+ /**
+ * Returns the pattern with the formatted objects. The first argument
+ * must be a array of Objects.
+ * This is equivalent to format((Object[]) objectArray, appendBuf, fpos)
+ *
+ * @param objectArray The object array to be formatted.
+ * @param appendBuf The StringBuffer where the text is appened.
+ * @param fpos A FieldPosition object (it is ignored).
+ */
+ public final StringBuffer format (Object objectArray, StringBuffer appendBuf,
+ FieldPosition fpos)
+ {
+ return format ((Object[])objectArray, appendBuf, fpos);
+ }
+
+ /**
+ * Returns an array with the Formats for
+ * the arguments.
+ */
+ 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;
+ }
+
+ /**
+ * Returns the locale.
+ */
+ public Locale getLocale ()
+ {
+ return locale;
+ }
+
+ /**
+ * Overrides Format.hashCode()
+ */
+ public int hashCode ()
+ {
+ // FIXME: not a very good hash.
+ return pattern.hashCode() + locale.hashCode();
+ }
+
+ private MessageFormat ()
+ {
+ }
+
+ /**
+ * Creates a new MessageFormat object with
+ * the specified pattern
+ *
+ * @param pattern The Pattern
+ */
+ public MessageFormat(String pattern)
+ {
+ this(pattern, Locale.getDefault());
+ }
+
+ /**
+ * Creates a new MessageFormat object with
+ * the specified pattern
+ *
+ * @param pattern The Pattern
+ * @param locale The Locale to use
+ *
+ * @since 1.4
+ */
+ public MessageFormat(String pattern, Locale locale)
+ {
+ this.locale = locale;
+ applyPattern (pattern);
+ }
+
+ /**
+ * Parse a string <code>sourceStr</code> against the pattern specified
+ * to the MessageFormat constructor.
+ *
+ * @param sourceStr the string to be parsed.
+ * @param pos the current parse position (and eventually the error position).
+ * @return the array of parsed objects sorted according to their argument number
+ * in the 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;
+ if (elements[i].trailer.length() > 0)
+ next_index = sourceStr.indexOf(elements[i].trailer, index);
+ else
+ next_index = sourceStr.length();
+ 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);
+ }
+
+ /**
+ * Sets the format for the argument at an specified
+ * index.
+ *
+ * @param index The index.
+ * @format The Format object.
+ */
+ public void setFormat (int variableNum, Format newFormat)
+ {
+ elements[variableNum].setFormat = newFormat;
+ }
+
+ /**
+ * Sets the formats for the arguments.
+ *
+ * @param formats An array of Format objects.
+ */
+ public void setFormats (Format[] newFormats)
+ {
+ if (newFormats.length < elements.length)
+ throw new IllegalArgumentException("Not enough format objects");
+
+ int len = Math.min(newFormats.length, elements.length);
+ for (int i = 0; i < len; ++i)
+ elements[i].setFormat = newFormats[i];
+ }
+
+ /**
+ * Sets the locale.
+ *
+ * @param locale A Locale
+ */
+ public void setLocale (Locale loc)
+ {
+ locale = loc;
+ if (elements != null)
+ {
+ for (int i = 0; i < elements.length; ++i)
+ elements[i].setLocale(loc);
+ }
+ }
+
+ /**
+ * Returns the pattern.
+ */
+ public String toPattern ()
+ {
+ return pattern;
+ }
+
+ /**
+ * Return the formatters used sorted by argument index. It uses the
+ * internal table to fill in this array: if a format has been
+ * set using <code>setFormat</code> or <code>setFormatByArgumentIndex</code>
+ * then it returns it at the right index. If not it uses the detected
+ * formatters during a <code>format</code> call. If nothing is known
+ * about that argument index it just puts null at that position.
+ * To get useful informations you may have to call <code>format</code>
+ * at least once.
+ *
+ * @return an array of formatters sorted by argument index.
+ */
+ public Format[] getFormatsByArgumentIndex()
+ {
+ int argNumMax = 0;
+ // First, find the greatest argument number.
+ for (int i=0;i<elements.length;i++)
+ if (elements[i].argNumber > argNumMax)
+ argNumMax = elements[i].argNumber;
+
+ Format[] formats = new Format[argNumMax];
+ for (int i=0;i<elements.length;i++)
+ {
+ if (elements[i].setFormat != null)
+ formats[elements[i].argNumber] = elements[i].setFormat;
+ else if (elements[i].format != null)
+ formats[elements[i].argNumber] = elements[i].format;
+ }
+ return formats;
+ }
+
+ /**
+ * Set the format to used using the argument index number.
+ *
+ * @param argumentIndex the argument index.
+ * @param newFormat the format to use for this argument.
+ */
+ public void setFormatByArgumentIndex(int argumentIndex,
+ Format newFormat)
+ {
+ for (int i=0;i<elements.length;i++)
+ {
+ if (elements[i].argNumber == argumentIndex)
+ elements[i].setFormat = newFormat;
+ }
+ }
+
+ /**
+ * Set the format for argument using a specified array of formatters
+ * which is sorted according to the argument index. If the number of
+ * elements in the array is fewer than the number of arguments only
+ * the arguments specified by the array are touched.
+ *
+ * @param newFormats array containing the new formats to set.
+ *
+ * @throws NullPointerException if newFormats is null
+ */
+ public void setFormatsByArgumentIndex(Format[] newFormats)
+ {
+ for (int i=0;i<newFormats.length;i++)
+ {
+ // Nothing better than that can exist here.
+ setFormatByArgumentIndex(i, newFormats[i]);
+ }
+ }
+
+ // The pattern string.
+ private String pattern;
+ // The locale.
+ private Locale locale;
+ // Variables.
+ private MessageFormatElement[] elements;
+ // Leader text.
+ private String leader;
+}
diff --git a/libjava/classpath/java/text/NumberFormat.java b/libjava/classpath/java/text/NumberFormat.java
new file mode 100644
index 00000000000..a2c3997cc52
--- /dev/null
+++ b/libjava/classpath/java/text/NumberFormat.java
@@ -0,0 +1,808 @@
+/* NumberFormat.java -- Formats and parses numbers
+ Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Currency;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * This is the abstract superclass of all classes which format and
+ * parse numeric values such as decimal numbers, integers, currency values,
+ * and percentages. These classes perform their parsing and formatting
+ * in a locale specific manner, accounting for such items as differing
+ * currency symbols and thousands separators.
+ * <p>
+ * To create an instance of a concrete subclass of <code>NumberFormat</code>,
+ * do not call a class constructor directly. Instead, use one of the
+ * static factory methods in this class such as
+ * <code>getCurrencyInstance</code>.
+ *
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Aaron M. Renn (arenn@urbanophile.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 getAvailableLocales.
+ */
+public abstract class NumberFormat extends Format implements Cloneable
+{
+ /**
+ * This is a constant used to create a <code>FieldPosition</code> object
+ * that will return the integer portion of a formatted number.
+ */
+ public static final int INTEGER_FIELD = 0;
+
+ /**
+ * This is a constant used to create a <code>FieldPosition</code> object
+ * that will return the fractional portion of a formatted number.
+ */
+ public static final int FRACTION_FIELD = 1;
+
+ public static class Field extends Format.Field
+ {
+ static final long serialVersionUID = 7494728892700160890L;
+
+ /**
+ * Attribute set to all characters containing digits of the integer
+ * part.
+ */
+ public static final NumberFormat.Field INTEGER
+ = new Field("integer");
+
+ /**
+ * Attribute set to all characters containing digits of the fractional
+ * part.
+ */
+ public static final NumberFormat.Field FRACTION
+ = new Field("fraction");
+
+ /**
+ * Attribute set to all characters containing digits of the exponential
+ * part.
+ */
+ public static final NumberFormat.Field EXPONENT
+ = new Field("exponent");
+
+ /**
+ * Attribute set to all characters containing a decimal separator.
+ */
+ public static final NumberFormat.Field DECIMAL_SEPARATOR
+ = new Field("decimal separator");
+
+ /**
+ * Attribute set to all characters containing a sign (plus or minus).
+ */
+ public static final NumberFormat.Field SIGN
+ = new Field("sign");
+
+ /**
+ * Attribute set to all characters containing a grouping separator (e.g.
+ * a comma, a white space,...).
+ */
+ public static final NumberFormat.Field GROUPING_SEPARATOR
+ = new Field("grouping separator");
+
+ /**
+ * Attribute set to all characters containing an exponential symbol (e.g.
+ * 'E')
+ */
+ public static final NumberFormat.Field EXPONENT_SYMBOL
+ = new Field("exponent symbol");
+
+ /**
+ * Attribute set to all characters containing a percent symbol (e.g. '%')
+ */
+ public static final NumberFormat.Field PERCENT
+ = new Field("percent");
+
+ /**
+ * Attribute set to all characters containing a permille symbol.
+ */
+ public static final NumberFormat.Field PERMILLE
+ = new Field("permille");
+
+ /**
+ * Attribute set to all characters containing the currency unit.
+ */
+ public static final NumberFormat.Field CURRENCY
+ = new Field("currency");
+
+ /**
+ * Attribute set to all characters containing the exponent sign.
+ */
+ public static final NumberFormat.Field EXPONENT_SIGN
+ = new Field("exponent sign");
+
+ /**
+ * Private fields to register all fields contained in this descriptor.
+ */
+ private static final NumberFormat.Field[] allFields =
+ {
+ INTEGER, FRACTION, EXPONENT, DECIMAL_SEPARATOR, SIGN,
+ GROUPING_SEPARATOR, EXPONENT_SYMBOL, PERCENT,
+ PERMILLE, CURRENCY, EXPONENT_SIGN
+ };
+
+ /**
+ * This constructor is only used by the deserializer. Without it,
+ * it would fail to construct a valid object.
+ */
+ private Field()
+ {
+ super("");
+ }
+
+ /**
+ * Create a Field instance with the specified field name.
+ *
+ * @param field_name Field name for the new Field instance.
+ */
+ protected Field(String field_name)
+ {
+ super (field_name);
+ }
+
+ /**
+ * This function is used by the deserializer to know which object
+ * to use when it encounters an encoded NumberFormat.Field in a
+ * serialization stream. If the stream is valid it should return
+ * one of the above field. In the other case we throw an exception.
+ *
+ * @return a valid official NumberFormat.Field instance.
+ *
+ * @throws InvalidObjectException if the field name is invalid.
+ */
+ protected Object readResolve() throws InvalidObjectException
+ {
+ String s = getName();
+ for (int i = 0; i < allFields.length; i++)
+ if (s.equals(allFields[i].getName()))
+ return allFields[i];
+
+ throw new InvalidObjectException("no such NumberFormat field called "
+ + s);
+ }
+ }
+
+ /**
+ * This method is a specialization of the format method that performs
+ * a simple formatting of the specified <code>long</code> number.
+ *
+ * @param number The <code>long</code> to format.
+ *
+ * @return The formatted number
+ */
+ 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)
+ {
+ if (obj instanceof Number)
+ return format(((Number) obj).doubleValue(), sbuf, pos);
+ else
+ throw new IllegalArgumentException
+ ("Cannot format given Object as a Number");
+ }
+
+ /**
+ * This method formats the specified <code>double</code> and appends it to
+ * a <code>StringBuffer</code>.
+ *
+ * @param number The <code>double</code> to format.
+ * @param sb The <code>StringBuffer</code> to append the formatted number to.
+ * @param pos The desired <code>FieldPosition</code>.
+ *
+ * @return The <code>StringBuffer</code> with the appended number.
+ */
+ public abstract StringBuffer format (double number,
+ StringBuffer sbuf, FieldPosition pos);
+
+ /**
+ * This method formats the specified <code>long</code> and appends it to
+ * a <code>StringBuffer</code>.
+ *
+ * @param number The <code>long</code> to format.
+ * @param sb The <code>StringBuffer</code> to append the formatted number to.
+ * @param pos The desired <code>FieldPosition</code>.
+ *
+ * @return The <code>StringBuffer</code> with the appended number.
+ */
+ public abstract StringBuffer format (long number,
+ StringBuffer sbuf, FieldPosition pos);
+
+ /**
+ * This method tests the specified object for equality against this object.
+ * This will be <code>true</code> if the following conditions are met:
+ * <p>
+ * <ul>
+ * <li>The specified object is not <code>null</code>.
+ * <li>The specified object is an instance of <code>NumberFormat</code>.
+ * </ul>
+ * <p>
+ * Since this method does not test much, it is highly advised that
+ * concrete subclasses override this method.
+ *
+ * @param obj The <code>Object</code> to test against equality with
+ * this object.
+ *
+ * @return <code>true</code> if the specified object is equal to
+ * this object, <code>false</code> otherwise.
+ */
+ public boolean equals (Object obj)
+ {
+ if (! (obj instanceof NumberFormat))
+ return false;
+ NumberFormat nf = (NumberFormat) obj;
+ return (groupingUsed == nf.groupingUsed
+ && maximumFractionDigits == nf.maximumFractionDigits
+ && maximumIntegerDigits == nf.maximumIntegerDigits
+ && minimumFractionDigits == nf.minimumFractionDigits
+ && minimumIntegerDigits == nf.minimumIntegerDigits
+ && parseIntegerOnly == nf.parseIntegerOnly);
+ }
+
+ /**
+ * This method returns a list of locales for which concrete instances
+ * of <code>NumberFormat</code> subclasses may be created.
+ *
+ * @return The list of available locales.
+ */
+ public static Locale[] getAvailableLocales ()
+ {
+ Locale[] list = new Locale[1];
+ list[0] = Locale.US;
+ return list;
+ }
+
+ private static NumberFormat computeInstance(Locale loc, String resource,
+ String def)
+ {
+ ResourceBundle res;
+ try
+ {
+ res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+ loc, ClassLoader.getSystemClassLoader());
+ }
+ 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);
+ }
+
+ /**
+ * This method returns an instance of <code>NumberFormat</code> suitable
+ * for formatting and parsing currency values in the default locale.
+ *
+ * @return An instance of <code>NumberFormat</code> for handling currencies.
+ */
+ public static final NumberFormat getCurrencyInstance ()
+ {
+ return getCurrencyInstance (Locale.getDefault());
+ }
+
+ /**
+ * This method returns an instance of <code>NumberFormat</code> suitable
+ * for formatting and parsing currency values in the specified locale.
+ *
+ * @return An instance of <code>NumberFormat</code> for handling currencies.
+ */
+ public static NumberFormat getCurrencyInstance (Locale loc)
+ {
+ NumberFormat format;
+
+ format = computeInstance (loc, "currencyFormat", "$#,##0.00;($#,##0.00)");
+ format.setMaximumFractionDigits(format.getCurrency().getDefaultFractionDigits());
+ return format;
+ }
+
+ /**
+ * This method returns a default instance for the default locale. This
+ * will be a concrete subclass of <code>NumberFormat</code>, but the
+ * actual class returned is dependent on the locale.
+ *
+ * @return An instance of the default <code>NumberFormat</code> class.
+ */
+ public static final NumberFormat getInstance ()
+ {
+ return getInstance (Locale.getDefault());
+ }
+
+ /**
+ * This method returns a default instance for the specified locale. This
+ * will be a concrete subclass of <code>NumberFormat</code>, but the
+ * actual class returned is dependent on the locale.
+ *
+ * @param locale The desired locale.
+ *
+ * @return An instance of the default <code>NumberFormat</code> class.
+ */
+ public static NumberFormat getInstance (Locale loc)
+ {
+ // For now always return a number instance.
+ return getNumberInstance (loc);
+ }
+
+ /**
+ * This method returns the maximum number of digits allowed in the fraction
+ * portion of a number.
+ *
+ * @return The maximum number of digits allowed in the fraction
+ * portion of a number.
+ */
+ public int getMaximumFractionDigits ()
+ {
+ return maximumFractionDigits;
+ }
+
+ /**
+ * This method returns the maximum number of digits allowed in the integer
+ * portion of a number.
+ *
+ * @return The maximum number of digits allowed in the integer
+ * portion of a number.
+ */
+ public int getMaximumIntegerDigits ()
+ {
+ return maximumIntegerDigits;
+ }
+
+ /**
+ * This method returns the minimum number of digits allowed in the fraction
+ * portion of a number.
+ *
+ * @return The minimum number of digits allowed in the fraction
+ * portion of a number.
+ */
+ public int getMinimumFractionDigits ()
+ {
+ return minimumFractionDigits;
+ }
+
+ /**
+ * This method returns the minimum number of digits allowed in the integer
+ * portion of a number.
+ *
+ * @return The minimum number of digits allowed in the integer
+ * portion of a number.
+ */
+ public int getMinimumIntegerDigits ()
+ {
+ return minimumIntegerDigits;
+ }
+
+ /**
+ * This method returns a default instance for the specified locale. This
+ * will be a concrete subclass of <code>NumberFormat</code>, but the
+ * actual class returned is dependent on the locale.
+ *
+ * @param locale The desired locale.
+ *
+ * @return An instance of the default <code>NumberFormat</code> class.
+ */
+ public static final NumberFormat getNumberInstance ()
+ {
+ return getNumberInstance (Locale.getDefault());
+ }
+
+ /**
+ * This method returns a general purpose number formatting and parsing
+ * class for the default locale. This will be a concrete subclass of
+ * <code>NumberFormat</code>, but the actual class returned is dependent
+ * on the locale.
+ *
+ * @return An instance of a generic number formatter for the default locale.
+ */
+ public static NumberFormat getNumberInstance (Locale loc)
+ {
+ return computeInstance (loc, "numberFormat", "#,##0.###");
+ }
+
+ /**
+ * This method returns an integer formatting and parsing class for the
+ * default locale. This will be a concrete subclass of <code>NumberFormat</code>,
+ * but the actual class returned is dependent on the locale.
+ *
+ * @return An instance of an integer number formatter for the default locale.
+ * @since 1.4
+ */
+ public static final NumberFormat getIntegerInstance()
+ {
+ return getIntegerInstance (Locale.getDefault());
+ }
+
+ /**
+ * This method returns an integer formatting and parsing class for the
+ * default locale. This will be a concrete subclass of <code>NumberFormat</code>,
+ * but the actual class returned is dependent on the locale.
+ *
+ * @param locale the desired locale.
+ *
+ * @return An instance of an integer number formatter for the desired locale.
+ * @since 1.4
+ */
+ public static NumberFormat getIntegerInstance(Locale locale)
+ {
+ NumberFormat format = computeInstance (locale, "numberFormat", "#,##0");
+ format.setMaximumFractionDigits(0);
+ format.setParseIntegerOnly (true);
+ return format;
+ }
+
+ /**
+ * This method returns an instance of <code>NumberFormat</code> suitable
+ * for formatting and parsing percentage values in the default locale.
+ *
+ * @return An instance of <code>NumberFormat</code> for handling percentages.
+ */
+ public static final NumberFormat getPercentInstance ()
+ {
+ return getPercentInstance (Locale.getDefault());
+ }
+
+ /**
+ * This method returns an instance of <code>NumberFormat</code> suitable
+ * for formatting and parsing percentage values in the specified locale.
+ *
+ * @param locale The desired locale.
+ *
+ * @return An instance of <code>NumberFormat</code> for handling percentages.
+ */
+ public static NumberFormat getPercentInstance (Locale loc)
+ {
+ return computeInstance (loc, "percentFormat", "#,##0%");
+ }
+
+ /**
+ * This method returns a hash value for this object.
+ *
+ * @return The hash code.
+ */
+ public int hashCode ()
+ {
+ int hash = super.hashCode();
+ hash ^= (maximumFractionDigits + maximumIntegerDigits
+ + minimumFractionDigits + minimumIntegerDigits);
+ if (groupingUsed)
+ hash ^= 0xf0f0;
+ if (parseIntegerOnly)
+ hash ^= 0x0f0f;
+ return hash;
+ }
+
+ /**
+ * This method tests whether or not grouping is in use. Grouping is
+ * a method of marking separations in numbers, such as thousand separators
+ * in the US English locale. The grouping positions and symbols are all
+ * locale specific. As an example, with grouping disabled, the number one
+ * million would appear as "1000000". With grouping enabled, this number
+ * might appear as "1,000,000". (Both of these assume the US English
+ * locale).
+ *
+ * @return <code>true</code> if grouping is enabled,
+ * <code>false</code> otherwise.
+ */
+ public boolean isGroupingUsed ()
+ {
+ return groupingUsed;
+ }
+
+ /**
+ * This method tests whether or not only integer values should be parsed.
+ * If this class is parsing only integers, parsing stops at the decimal
+ * point.
+ *
+ * @return <code>true</code> if only integers are parsed,
+ * <code>false</code> otherwise.
+ */
+ public boolean isParseIntegerOnly ()
+ {
+ return parseIntegerOnly;
+ }
+
+ /**
+ * This is a default constructor for use by subclasses.
+ */
+ public NumberFormat ()
+ {
+ }
+
+ /**
+ * This method parses the specified string into a <code>Number</code>. This
+ * will be a <code>Long</code> if possible, otherwise it will be a
+ * <code>Double</code>. If no number can be parsed, no exception is
+ * thrown. Instead, the parse position remains at its initial index.
+ *
+ * @param str The string to parse.
+ * @param pp The desired <code>ParsePosition</code>.
+ *
+ * @return The parsed <code>Number</code>
+ */
+ public abstract Number parse (String sourceStr, ParsePosition pos);
+
+ /**
+ * This method parses the specified string into a <code>Number</code>. This
+ * will be a <code>Long</code> if possible, otherwise it will be a
+ * <code>Double</code>. If no number can be parsed, an exception will be
+ * thrown.
+ *
+ * @param str The string to parse.
+ *
+ * @return The parsed <code>Number</code>
+ *
+ * @exception ParseException If no number can be parsed.
+ */
+ 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;
+ }
+
+ /**
+ * This method parses the specified string into an <code>Object</code>. This
+ * will be a <code>Long</code> if possible, otherwise it will be a
+ * <code>Double</code>. If no number can be parsed, no exception is
+ * thrown. Instead, the parse position remains at its initial index.
+ *
+ * @param str The string to parse.
+ * @param pp The desired <code>ParsePosition</code>.
+ *
+ * @return The parsed <code>Object</code>
+ */
+ public final Object parseObject (String sourceStr, ParsePosition pos)
+ {
+ return parse (sourceStr, pos);
+ }
+
+ /**
+ * This method sets the grouping behavior of this formatter. Grouping is
+ * a method of marking separations in numbers, such as thousand separators
+ * in the US English locale. The grouping positions and symbols are all
+ * locale specific. As an example, with grouping disabled, the number one
+ * million would appear as "1000000". With grouping enabled, this number
+ * might appear as "1,000,000". (Both of these assume the US English
+ * locale).
+ *
+ * @param groupingUsed <code>true</code> to enable grouping,
+ * <code>false</code> to disable it.
+ */
+ public void setGroupingUsed (boolean newValue)
+ {
+ groupingUsed = newValue;
+ }
+
+ /**
+ * This method sets the maximum number of digits allowed in the fraction
+ * portion of a number to the specified value. If this is less than the
+ * current minimum allowed digits, the minimum allowed digits value will
+ * be lowered to be equal to the new maximum allowed digits value.
+ *
+ * @param maximumFractionDigits The new maximum fraction digits value.
+ */
+ public void setMaximumFractionDigits (int newValue)
+ {
+ maximumFractionDigits = newValue;
+ if (getMinimumFractionDigits () > maximumFractionDigits)
+ setMinimumFractionDigits (maximumFractionDigits);
+ }
+
+ /**
+ * This method sets the maximum number of digits allowed in the integer
+ * portion of a number to the specified value. If this is less than the
+ * current minimum allowed digits, the minimum allowed digits value will
+ * be lowered to be equal to the new maximum allowed digits value.
+ *
+ * @param maximumIntegerDigits The new maximum integer digits value.
+ */
+ public void setMaximumIntegerDigits (int newValue)
+ {
+ maximumIntegerDigits = newValue;
+ if (getMinimumIntegerDigits () > maximumIntegerDigits)
+ setMinimumIntegerDigits (maximumIntegerDigits);
+ }
+
+ /**
+ * This method sets the minimum number of digits allowed in the fraction
+ * portion of a number to the specified value. If this is greater than the
+ * current maximum allowed digits, the maximum allowed digits value will
+ * be raised to be equal to the new minimum allowed digits value.
+ *
+ * @param minimumFractionDigits The new minimum fraction digits value.
+ */
+ public void setMinimumFractionDigits (int newValue)
+ {
+ minimumFractionDigits = newValue;
+ if (getMaximumFractionDigits () < minimumFractionDigits)
+ setMaximumFractionDigits (minimumFractionDigits);
+ }
+
+ /**
+ * This method sets the minimum number of digits allowed in the integer
+ * portion of a number to the specified value. If this is greater than the
+ * current maximum allowed digits, the maximum allowed digits value will
+ * be raised to be equal to the new minimum allowed digits value.
+ *
+ * @param minimumIntegerDigits The new minimum integer digits value.
+ */
+ public void setMinimumIntegerDigits (int newValue)
+ {
+ minimumIntegerDigits = newValue;
+ if (getMaximumIntegerDigits () < minimumIntegerDigits)
+ setMaximumIntegerDigits (minimumIntegerDigits);
+ }
+
+ /**
+ * This method sets the parsing behavior of this object to parse only
+ * integers or not.
+ *
+ * @param parseIntegerOnly <code>true</code> to parse only integers,
+ * <code>false</code> otherwise.
+ */
+ public void setParseIntegerOnly (boolean value)
+ {
+ parseIntegerOnly = value;
+ }
+
+ /**
+ * This method is a specialization of the format method that performs
+ * a simple formatting of the specified <code>double</code> number.
+ *
+ * @param number The <code>double</code> to format.
+ *
+ * @return The formatted number
+ */
+ 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.
+ boolean groupingUsed;
+ int maximumFractionDigits;
+ private byte maxFractionDigits;
+ int maximumIntegerDigits;
+ private byte maxIntegerDigits;
+ int minimumFractionDigits;
+ private byte minFractionDigits;
+ int minimumIntegerDigits;
+ private byte minIntegerDigits;
+ boolean parseIntegerOnly;
+ private int serialVersionOnStream;
+ private static final long serialVersionUID = -2308460125733713944L;
+
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException
+ {
+ stream.defaultReadObject();
+ if (serialVersionOnStream < 1)
+ {
+ maximumFractionDigits = maxFractionDigits;
+ maximumIntegerDigits = maxIntegerDigits;
+ minimumFractionDigits = minFractionDigits;
+ minimumIntegerDigits = minIntegerDigits;
+ serialVersionOnStream = 1;
+ }
+ }
+
+ private void writeObject(ObjectOutputStream stream) throws IOException
+ {
+ maxFractionDigits = maximumFractionDigits < Byte.MAX_VALUE ?
+ (byte) maximumFractionDigits : Byte.MAX_VALUE;
+ maxIntegerDigits = maximumIntegerDigits < Byte.MAX_VALUE ?
+ (byte) maximumIntegerDigits : Byte.MAX_VALUE;
+ minFractionDigits = minimumFractionDigits < Byte.MAX_VALUE ?
+ (byte) minimumFractionDigits : Byte.MAX_VALUE;
+ minIntegerDigits = minimumIntegerDigits < Byte.MAX_VALUE ?
+ (byte) minimumIntegerDigits : Byte.MAX_VALUE;
+ serialVersionOnStream = 1;
+ stream.defaultWriteObject();
+ }
+
+ /**
+ * Returns the currency used by this number format when formatting currency
+ * values.
+ *
+ * The default implementation throws UnsupportedOperationException.
+ *
+ * @return The used currency object, or null.
+ *
+ * @throws UnsupportedOperationException If the number format class doesn't
+ * implement currency formatting.
+ *
+ * @since 1.4
+ */
+ public Currency getCurrency()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Sets the currency used by this number format when formatting currency
+ * values.
+ *
+ * The default implementation throws UnsupportedOperationException.
+ *
+ * @param currency The new currency to be used by this number format.
+ *
+ * @throws NullPointerException If currenc is null.
+ * @throws UnsupportedOperationException If the number format class doesn't
+ * implement currency formatting.
+ *
+ * @since 1.4
+ */
+ public void setCurrency(Currency currency)
+ {
+ if (currency == null)
+ throw new NullPointerException("currency may not be null");
+
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/libjava/classpath/java/text/ParseException.java b/libjava/classpath/java/text/ParseException.java
new file mode 100644
index 00000000000..6d014effd2a
--- /dev/null
+++ b/libjava/classpath/java/text/ParseException.java
@@ -0,0 +1,86 @@
+/* ParseException.java -- an error occurred while parsing
+ Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+/**
+ * This exception is thrown when an unexpected error occurs during parsing.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Per Bothner (bothner@cygnus.com)
+ * @see Format
+ * @see FieldPosition
+ * @status updated to 1.4
+ */
+public class ParseException extends Exception
+{
+ /**
+ * Compatible with JDK 1.1+.
+ */
+ private static final long serialVersionUID = 2703218443322787634L;
+
+ /**
+ * This is the position where the error was encountered.
+ *
+ * @serial the zero-based offset in the string where the error occurred
+ */
+ private final int errorOffset;
+
+ /**
+ * This method initializes a new instance of <code>ParseException</code>
+ * with a detailed error message and a error position.
+ *
+ * @param msg the descriptive message describing the error
+ * @param offset the position where the error was encountered
+ */
+ public ParseException(String s, int offset)
+ {
+ super(s);
+ errorOffset = offset;
+ }
+
+ /**
+ * This method returns the position where the error occurred.
+ *
+ * @return the position where the error occurred
+ */
+ public int getErrorOffset()
+ {
+ return errorOffset;
+ }
+} // class ParseException
diff --git a/libjava/classpath/java/text/ParsePosition.java b/libjava/classpath/java/text/ParsePosition.java
new file mode 100644
index 00000000000..782f5e0eda2
--- /dev/null
+++ b/libjava/classpath/java/text/ParsePosition.java
@@ -0,0 +1,151 @@
+/* ParsePosition.java -- Keep track of position while parsing.
+ Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+/**
+ * This class is used to keep track of the current position during parsing
+ * operations.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Per Bothner (bothner@cygnus.com)
+ */
+public class ParsePosition
+{
+ /**
+ * This is the index of the current parse position.
+ */
+ private int index;
+
+ /**
+ * This is the index of the position where an error occurred during parsing.
+ */
+ private int error_index;
+
+ /**
+ * This method initializes a new instance of <code>ParsePosition</code> to
+ * have the specified initial index value.
+ *
+ * @param index The initial parsing index.
+ */
+ public ParsePosition (int index)
+ {
+ this.index = index;
+ error_index = -1;
+ }
+
+ /**
+ * This method returns the current parsing index.
+ *
+ * @return The current parsing index
+ */
+ public int getIndex ()
+ {
+ return index;
+ }
+
+ /**
+ * This method sets the current parsing index to the specified value.
+ *
+ * @param index The new parsing index.
+ */
+ public void setIndex (int index)
+ {
+ this.index = index;
+ }
+
+ /**
+ * This method returns the error index value. This value defaults to -1
+ * unless explicitly set to another value.
+ *
+ * @return The error index.
+ */
+ public int getErrorIndex ()
+ {
+ return error_index;
+ }
+
+ /**
+ * This method sets the error index to the specified value.
+ *
+ * @param error_index The new error index
+ */
+ public void setErrorIndex (int error_index)
+ {
+ this.error_index = error_index;
+ }
+
+ /**
+ * This method tests the specified object for equality with this
+ * object. The two objects will be considered equal if and only if
+ * all of the following conditions are met.
+ * <p>
+ * <ul>
+ * <li>The specified object is not <code>null</code>.</li>
+ * <li>The specified object is an instance of <code>ParsePosition</code>.</li>
+ * <li>The specified object has the same index and error index as
+ * this object.</li>
+ * </ul>
+ *
+ * @param obj The <code>Object</code> to test for equality against
+ * this object.
+ *
+ * @return <code>true</code> if the specified object is equal to
+ * this object, <code>false</code> otherwise.
+ */
+ public boolean equals (Object obj)
+ {
+ if (! (obj instanceof ParsePosition))
+ return false;
+
+ ParsePosition other = (ParsePosition) obj;
+ return index == other.index && error_index == other.error_index;
+ }
+
+ /**
+ * This method returns a <code>String</code> representation of this
+ * object.
+ *
+ * @return A <code>String</code> that represents this object.
+ */
+ public String toString ()
+ {
+ return (getClass ().getName () + "[index=" + getIndex ()
+ + ",errorIndex=" + getErrorIndex () + "]");
+ }
+}
diff --git a/libjava/classpath/java/text/RuleBasedCollator.java b/libjava/classpath/java/text/RuleBasedCollator.java
new file mode 100644
index 00000000000..ae84a41032d
--- /dev/null
+++ b/libjava/classpath/java/text/RuleBasedCollator.java
@@ -0,0 +1,1017 @@
+/* RuleBasedCollator.java -- Concrete Collator Class
+ Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/* 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
+ */
+
+/**
+ * This class is a concrete subclass of <code>Collator</code> suitable
+ * for string collation in a wide variety of languages. An instance of
+ * this class is normally returned by the <code>getInstance</code> method
+ * of <code>Collator</code> with rules predefined for the requested
+ * locale. However, an instance of this class can be created manually
+ * with any desired rules.
+ * <p>
+ * Rules take the form of a <code>String</code> with the following syntax
+ * <ul>
+ * <li> Modifier: '@'</li>
+ * <li> Relation: '&lt;' | ';' | ',' | '=' : &lt;text&gt;</li>
+ * <li> Reset: '&amp;' : &lt;text&gt;</li>
+ * </ul>
+ * The modifier character indicates that accents sort backward as is the
+ * case with French. The modifier applies to all rules <b>after</b>
+ * the modifier but before the next primary sequence. If placed at the end
+ * of the sequence if applies to all unknown accented character.
+ * The relational operators specify how the text
+ * argument relates to the previous term. The relation characters have
+ * the following meanings:
+ * <ul>
+ * <li>'&lt;' - The text argument is greater than the prior term at the primary
+ * difference level.</li>
+ * <li>';' - The text argument is greater than the prior term at the secondary
+ * difference level.</li>
+ * <li>',' - The text argument is greater than the prior term at the tertiary
+ * difference level.</li>
+ * <li>'=' - The text argument is equal to the prior term</li>
+ * </ul>
+ * <p>
+ * As for the text argument itself, this is any sequence of Unicode
+ * characters not in the following ranges: 0x0009-0x000D, 0x0020-0x002F,
+ * 0x003A-0x0040, 0x005B-0x0060, and 0x007B-0x007E. If these characters are
+ * desired, they must be enclosed in single quotes. If any whitespace is
+ * encountered, it is ignored. (For example, "a b" is equal to "ab").
+ * <p>
+ * The reset operation inserts the following rule at the point where the
+ * text argument to it exists in the previously declared rule string. This
+ * makes it easy to add new rules to an existing string by simply including
+ * them in a reset sequence at the end. Note that the text argument, or
+ * at least the first character of it, must be present somewhere in the
+ * previously declared rules in order to be inserted properly. If this
+ * is not satisfied, a <code>ParseException</code> will be thrown.
+ * <p>
+ * This system of configuring <code>RuleBasedCollator</code> is needlessly
+ * complex and the people at Taligent who developed it (along with the folks
+ * at Sun who accepted it into the Java standard library) deserve a slow
+ * and agonizing death.
+ * <p>
+ * Here are a couple of example of rule strings:
+ * <p>
+ * "&lt; a &lt; b &lt; c" - This string says that a is greater than b which is
+ * greater than c, with all differences being primary differences.
+ * <p>
+ * "&lt; a,A &lt; b,B &lt; c,C" - This string says that 'A' is greater than 'a' with
+ * a tertiary strength comparison. Both 'b' and 'B' are greater than 'a' and
+ * 'A' during a primary strength comparison. But 'B' is greater than 'b'
+ * under a tertiary strength comparison.
+ * <p>
+ * "&lt; a &lt; c &amp; a &lt; b " - This sequence is identical in function to the
+ * "&lt; a &lt; b &lt; c" rule string above. The '&amp;' reset symbol indicates that
+ * the rule "&lt; b" is to be inserted after the text argument "a" in the
+ * previous rule string segment.
+ * <p>
+ * "&lt; a &lt; b &amp; y &lt; z" - This is an error. The character 'y' does not appear
+ * anywhere in the previous rule string segment so the rule following the
+ * reset rule cannot be inserted.
+ * <p>
+ * "&lt; a &amp; A @ &lt; e &amp; E &lt; f&amp; F" - This sequence is equivalent to the following
+ * "&lt; a &amp; A &lt; E &amp; e &lt; f &amp; F".
+ * <p>
+ * For a description of the various comparison strength types, see the
+ * documentation for the <code>Collator</code> class.
+ * <p>
+ * As an additional complication to this already overly complex rule scheme,
+ * if any characters precede the first rule, these characters are considered
+ * ignorable. They will be treated as if they did not exist during
+ * comparisons. For example, "- &lt; a &lt; b ..." would make '-' an ignorable
+ * character such that the strings "high-tech" and "hightech" would
+ * be considered identical.
+ * <p>
+ * A <code>ParseException</code> will be thrown for any of the following
+ * conditions:
+ * <ul>
+ * <li>Unquoted punctuation characters in a text argument.</li>
+ * <li>A relational or reset operator not followed by a text argument</li>
+ * <li>A reset operator where the text argument is not present in
+ * the previous rule string section.</li>
+ * </ul>
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Guilhem Lavaux (guilhem@kaffe.org)
+ */
+public class RuleBasedCollator extends Collator
+{
+ /**
+ * This class describes what rank has a character (or a sequence of characters)
+ * in the lexicographic order. Each element in a rule has a collation element.
+ */
+ static final class CollationElement
+ {
+ String key;
+ int primary;
+ short secondary;
+ short tertiary;
+ short equality;
+ boolean ignore;
+ String expansion;
+
+ CollationElement(String key, int primary, short secondary, short tertiary,
+ short equality, String expansion, boolean ignore)
+ {
+ this.key = key;
+ this.primary = primary;
+ this.secondary = secondary;
+ this.tertiary = tertiary;
+ this.equality = equality;
+ this.ignore = ignore;
+ this.expansion = expansion;
+ }
+
+ int getValue()
+ {
+ return (primary << 16) + (secondary << 8) + tertiary;
+ }
+ }
+
+ /**
+ * Basic collation instruction (internal format) to build the series of
+ * collation elements. It contains an instruction which specifies the new
+ * state of the generator. The sequence of instruction should not contain
+ * RESET (it is used by
+ * {@link #mergeRules(int,java.lang.String,java.util.ArrayList,java.util.ArrayList)})
+ * as a temporary state while merging two sets of instructions.
+ */
+ static final class CollationSorter
+ {
+ static final int GREATERP = 0;
+ static final int GREATERS = 1;
+ static final int GREATERT = 2;
+ static final int EQUAL = 3;
+ static final int RESET = 4;
+ static final int INVERSE_SECONDARY = 5;
+
+ int comparisonType;
+ String textElement;
+ int hashText;
+ int offset;
+ boolean ignore;
+
+ String expansionOrdering;
+ }
+
+ /**
+ * This the the original rule string.
+ */
+ private String rules;
+
+ /**
+ * This is the table of collation element values
+ */
+ private Object[] ce_table;
+
+ /**
+ * Quick-prefix finder.
+ */
+ HashMap prefix_tree;
+
+ /**
+ * This is the value of the last sequence entered into
+ * <code>ce_table</code>. It is used to compute the
+ * ordering value of unspecified character.
+ */
+ private int last_primary_value;
+
+ /**
+ * This is the value of the last secondary sequence of the
+ * primary 0, entered into
+ * <code>ce_table</code>. It is used to compute the
+ * ordering value of an unspecified accented character.
+ */
+ private int last_tertiary_value;
+
+ /**
+ * This variable is true if accents need to be sorted
+ * in the other direction.
+ */
+ private boolean inverseAccentComparison;
+
+ /**
+ * This collation element is special to unknown sequence.
+ * The JDK uses it to mark and sort the characters which has
+ * no collation rules.
+ */
+ static final CollationElement SPECIAL_UNKNOWN_SEQ =
+ new CollationElement("", (short) 32767, (short) 0, (short) 0,
+ (short) 0, null, false);
+
+ /**
+ * This method initializes a new instance of <code>RuleBasedCollator</code>
+ * with the specified collation rules. Note that an application normally
+ * obtains an instance of <code>RuleBasedCollator</code> by calling the
+ * <code>getInstance</code> method of <code>Collator</code>. That method
+ * automatically loads the proper set of rules for the desired locale.
+ *
+ * @param rules The collation rule string.
+ *
+ * @exception ParseException If the rule string contains syntax errors.
+ */
+ public RuleBasedCollator(String rules) throws ParseException
+ {
+ if (rules.equals(""))
+ throw new ParseException("empty rule set", 0);
+
+ this.rules = rules;
+
+ buildCollationVector(parseString(rules));
+ buildPrefixAccess();
+ }
+
+ /**
+ * This method returns the number of common characters at the beginning
+ * of the string of the two parameters.
+ *
+ * @param prefix A string considered as a prefix to test against
+ * the other string.
+ * @param s A string to test the prefix against.
+ * @return The number of common characters.
+ */
+ static int findPrefixLength(String prefix, String s)
+ {
+ int index;
+ int len = prefix.length();
+
+ for (index = 0; index < len && index < s.length(); ++index)
+ {
+ if (prefix.charAt(index) != s.charAt(index))
+ return index;
+ }
+
+
+ return index;
+ }
+
+ /**
+ * Here we are merging two sets of sorting instructions: 'patch' into 'main'. This methods
+ * checks whether it is possible to find an anchor point for the rules to be merged and
+ * then insert them at that precise point.
+ *
+ * @param offset Offset in the string containing rules of the beginning of the rules
+ * being merged in.
+ * @param starter Text of the rules being merged.
+ * @param main Repository of all already parsed rules.
+ * @param patch Rules to be merged into the repository.
+ * @throws ParseException if it is impossible to find an anchor point for the new rules.
+ */
+ private void mergeRules(int offset, String starter, ArrayList main, ArrayList patch)
+ throws ParseException
+ {
+ int insertion_point = -1;
+ int max_length = 0;
+
+ /* We must check that no rules conflict with another already present. If it
+ * is the case delete the old rule.
+ */
+
+ /* For the moment good old O(N^2) algorithm.
+ */
+ for (int i = 0; i < patch.size(); i++)
+ {
+ int j = 0;
+
+ while (j < main.size())
+ {
+ CollationSorter rule1 = (CollationSorter) patch.get(i);
+ CollationSorter rule2 = (CollationSorter) main.get(j);
+
+ if (rule1.textElement.equals(rule2.textElement))
+ main.remove(j);
+ else
+ j++;
+ }
+ }
+
+ // Find the insertion point... O(N)
+ for (int i = 0; i < main.size(); i++)
+ {
+ CollationSorter sorter = (CollationSorter) main.get(i);
+ int length = findPrefixLength(starter, sorter.textElement);
+
+ if (length > max_length)
+ {
+ max_length = length;
+ insertion_point = i+1;
+ }
+ }
+
+ if (insertion_point < 0)
+ throw new ParseException("no insertion point found for " + starter, offset);
+
+ if (max_length < starter.length())
+ {
+ /*
+ * We need to expand the first entry. It must be sorted
+ * like if it was the reference key itself (like the spec
+ * said. So the first entry is special: the element is
+ * replaced by the specified text element for the sorting.
+ * This text replace the old one for comparisons. However
+ * to preserve the behaviour we replace the first key (corresponding
+ * to the found prefix) by a new code rightly ordered in the
+ * sequence. The rest of the subsequence must be appended
+ * to the end of the sequence.
+ */
+ CollationSorter sorter = (CollationSorter) patch.get(0);
+ CollationSorter expansionPrefix =
+ (CollationSorter) main.get(insertion_point-1);
+
+ sorter.expansionOrdering = starter.substring(max_length); // Skip the first good prefix element
+
+ main.add(insertion_point, sorter);
+
+ /*
+ * This is a new set of rules. Append to the list.
+ */
+ patch.remove(0);
+ insertion_point++;
+ }
+
+ // Now insert all elements of patch at the insertion point.
+ for (int i = 0; i < patch.size(); i++)
+ main.add(i+insertion_point, patch.get(i));
+ }
+
+ /**
+ * This method parses a string and build a set of sorting instructions. The parsing
+ * may only be partial on the case the rules are to be merged sometime later.
+ *
+ * @param stop_on_reset If this parameter is true then the parser stops when it
+ * encounters a reset instruction. In the other case, it tries to parse the subrules
+ * and merged it in the same repository.
+ * @param v Output vector for the set of instructions.
+ * @param base_offset Offset in the string to begin parsing.
+ * @param rules Rules to be parsed.
+ * @return -1 if the parser reached the end of the string, an integer representing the
+ * offset in the string at which it stopped parsing.
+ * @throws ParseException if something turned wrong during the parsing. To get details
+ * decode the message.
+ */
+ private int subParseString(boolean stop_on_reset, ArrayList v,
+ int base_offset, String rules)
+ throws ParseException
+ {
+ boolean ignoreChars = (base_offset == 0);
+ int operator = -1;
+ StringBuffer sb = new StringBuffer();
+ boolean doubleQuote = false;
+ boolean eatingChars = false;
+ boolean nextIsModifier = false;
+ boolean isModifier = false;
+ int i;
+
+main_parse_loop:
+ for (i = 0; i < rules.length(); i++)
+ {
+ char c = rules.charAt(i);
+ int type = -1;
+
+ if (!eatingChars &&
+ ((c >= 0x09 && c <= 0x0D) || (c == 0x20)))
+ continue;
+
+ isModifier = nextIsModifier;
+ nextIsModifier = false;
+
+ if (eatingChars && c != '\'')
+ {
+ doubleQuote = false;
+ sb.append(c);
+ continue;
+ }
+ if (doubleQuote && eatingChars)
+ {
+ sb.append(c);
+ doubleQuote = false;
+ continue;
+ }
+
+ switch (c)
+ {
+ case '!':
+ throw new ParseException
+ ("Modifier '!' is not yet supported by Classpath", i + base_offset);
+ case '<':
+ type = CollationSorter.GREATERP;
+ break;
+ case ';':
+ type = CollationSorter.GREATERS;
+ break;
+ case ',':
+ type = CollationSorter.GREATERT;
+ break;
+ case '=':
+ type = CollationSorter.EQUAL;
+ break;
+ case '\'':
+ eatingChars = !eatingChars;
+ doubleQuote = true;
+ break;
+ case '@':
+ if (ignoreChars)
+ throw new ParseException
+ ("comparison list has not yet been started. You may only use"
+ + "(<,;=&)", i + base_offset);
+ // Inverse the order of secondaries from now on.
+ nextIsModifier = true;
+ type = CollationSorter.INVERSE_SECONDARY;
+ break;
+ case '&':
+ type = CollationSorter.RESET;
+ if (stop_on_reset)
+ break main_parse_loop;
+ break;
+ default:
+ if (operator < 0)
+ throw new ParseException
+ ("operator missing at " + (i + base_offset), i + base_offset);
+ if (! eatingChars
+ && ((c >= 0x21 && c <= 0x2F)
+ || (c >= 0x3A && c <= 0x40)
+ || (c >= 0x5B && c <= 0x60)
+ || (c >= 0x7B && c <= 0x7E)))
+ throw new ParseException
+ ("unquoted punctuation character '" + c + "'", i + base_offset);
+
+ //type = ignoreChars ? CollationSorter.IGNORE : -1;
+ sb.append(c);
+ break;
+ }
+
+ if (type < 0)
+ continue;
+
+ if (operator < 0)
+ {
+ operator = type;
+ continue;
+ }
+
+ if (sb.length() == 0 && !isModifier)
+ throw new ParseException
+ ("text element empty at " + (i+base_offset), i+base_offset);
+
+ if (operator == CollationSorter.RESET)
+ {
+ /* Reposition in the sorting list at the position
+ * indicated by the text element.
+ */
+ String subrules = rules.substring(i);
+ ArrayList sorted_rules = new ArrayList();
+ int idx;
+
+ // Parse the subrules but do not iterate through all
+ // sublist. This is the priviledge of the first call.
+ idx = subParseString(true, sorted_rules, base_offset+i, subrules);
+
+ // Merge new parsed rules into the list.
+ mergeRules(base_offset+i, sb.toString(), v, sorted_rules);
+ sb.setLength(0);
+
+ // Reset state to none.
+ operator = -1;
+ type = -1;
+ // We have found a new subrule at 'idx' but it has not been parsed.
+ if (idx >= 0)
+ {
+ i += idx-1;
+ continue main_parse_loop;
+ }
+ else
+ // No more rules.
+ break main_parse_loop;
+ }
+
+ CollationSorter sorter = new CollationSorter();
+
+ if (operator == CollationSorter.GREATERP)
+ ignoreChars = false;
+
+ sorter.comparisonType = operator;
+ sorter.textElement = sb.toString();
+ sorter.hashText = sorter.textElement.hashCode();
+ sorter.offset = base_offset+rules.length();
+ sorter.ignore = ignoreChars;
+ sb.setLength(0);
+
+ v.add(sorter);
+ operator = type;
+ }
+
+ if (operator >= 0)
+ {
+ CollationSorter sorter = new CollationSorter();
+ int pos = rules.length() + base_offset;
+
+ if ((sb.length() != 0 && nextIsModifier)
+ || (sb.length() == 0 && !nextIsModifier && !eatingChars))
+ throw new ParseException("text element empty at " + pos, pos);
+
+ if (operator == CollationSorter.GREATERP)
+ ignoreChars = false;
+
+ sorter.comparisonType = operator;
+ sorter.textElement = sb.toString();
+ sorter.hashText = sorter.textElement.hashCode();
+ sorter.offset = base_offset+pos;
+ sorter.ignore = ignoreChars;
+ v.add(sorter);
+ }
+
+ if (i == rules.length())
+ return -1;
+ else
+ return i;
+ }
+
+ /**
+ * This method creates a copy of this object.
+ *
+ * @return A copy of this object.
+ */
+ public Object clone()
+ {
+ return super.clone();
+ }
+
+ /**
+ * This method completely parses a string 'rules' containing sorting rules.
+ *
+ * @param rules String containing the rules to be parsed.
+ * @return A set of sorting instructions stored in a Vector.
+ * @throws ParseException if something turned wrong during the parsing. To get details
+ * decode the message.
+ */
+ private ArrayList parseString(String rules)
+ throws ParseException
+ {
+ ArrayList v = new ArrayList();
+
+ // result of the first subParseString is not absolute (may be -1 or a
+ // positive integer). But we do not care.
+ subParseString(false, v, 0, rules);
+
+ return v;
+ }
+
+ /**
+ * This method uses the sorting instructions built by {@link #parseString}
+ * to build collation elements which can be directly used to sort strings.
+ *
+ * @param parsedElements Parsed instructions stored in a ArrayList.
+ * @throws ParseException if the order of the instructions are not valid.
+ */
+ private void buildCollationVector(ArrayList parsedElements)
+ throws ParseException
+ {
+ int primary_seq = 0;
+ int last_tertiary_seq = 0;
+ short secondary_seq = 0;
+ short tertiary_seq = 0;
+ short equality_seq = 0;
+ boolean inverseComparisons = false;
+ final boolean DECREASING = false;
+ final boolean INCREASING = true;
+ boolean secondaryType = INCREASING;
+ ArrayList v = new ArrayList();
+
+ // elts is completely sorted.
+element_loop:
+ for (int i = 0; i < parsedElements.size(); i++)
+ {
+ CollationSorter elt = (CollationSorter) parsedElements.get(i);
+ boolean ignoreChar = false;
+
+ switch (elt.comparisonType)
+ {
+ case CollationSorter.GREATERP:
+ primary_seq++;
+ if (inverseComparisons)
+ {
+ secondary_seq = Short.MAX_VALUE;
+ secondaryType = DECREASING;
+ }
+ else
+ {
+ secondary_seq = 0;
+ secondaryType = INCREASING;
+ }
+ tertiary_seq = 0;
+ equality_seq = 0;
+ inverseComparisons = false;
+ break;
+ case CollationSorter.GREATERS:
+ if (secondaryType == DECREASING)
+ secondary_seq--;
+ else
+ secondary_seq++;
+ tertiary_seq = 0;
+ equality_seq = 0;
+ break;
+ case CollationSorter.INVERSE_SECONDARY:
+ inverseComparisons = true;
+ continue element_loop;
+ case CollationSorter.GREATERT:
+ tertiary_seq++;
+ if (primary_seq == 0)
+ last_tertiary_seq = tertiary_seq;
+ equality_seq = 0;
+ break;
+ case CollationSorter.EQUAL:
+ equality_seq++;
+ break;
+ case CollationSorter.RESET:
+ throw new ParseException
+ ("Invalid reached state 'RESET'. Internal error", elt.offset);
+ default:
+ throw new ParseException
+ ("Invalid unknown state '" + elt.comparisonType + "'", elt.offset);
+ }
+
+ v.add(new CollationElement(elt.textElement, primary_seq,
+ secondary_seq, tertiary_seq,
+ equality_seq, elt.expansionOrdering, elt.ignore));
+ }
+
+ this.inverseAccentComparison = inverseComparisons;
+
+ ce_table = v.toArray();
+
+ last_primary_value = primary_seq+1;
+ last_tertiary_value = last_tertiary_seq+1;
+ }
+
+ /**
+ * Build a tree where all keys are the texts of collation elements and data is
+ * the collation element itself. The tree is used when extracting all prefix
+ * for a given text.
+ */
+ private void buildPrefixAccess()
+ {
+ prefix_tree = new HashMap();
+
+ for (int i = 0; i < ce_table.length; i++)
+ {
+ CollationElement e = (CollationElement) ce_table[i];
+
+ prefix_tree.put(e.key, e);
+ }
+ }
+
+ /**
+ * This method returns an integer which indicates whether the first
+ * specified <code>String</code> is less than, greater than, or equal to
+ * the second. The value depends not only on the collation rules in
+ * effect, but also the strength and decomposition settings of this object.
+ *
+ * @param source The first <code>String</code> to compare.
+ * @param target A second <code>String</code> to compare to the first.
+ *
+ * @return A negative integer if source &lt; target, a positive integer
+ * if source &gt; target, or 0 if source == target.
+ */
+ public int compare(String source, String target)
+ {
+ CollationElementIterator cs, ct;
+ CollationElement ord1block = null;
+ CollationElement ord2block = null;
+ boolean advance_block_1 = true;
+ boolean advance_block_2 = true;
+
+ cs = getCollationElementIterator(source);
+ ct = getCollationElementIterator(target);
+
+ for(;;)
+ {
+ int ord1;
+ int ord2;
+
+ /*
+ * We have to check whether the characters are ignorable.
+ * If it is the case then forget them.
+ */
+ if (advance_block_1)
+ {
+ ord1block = cs.nextBlock();
+ if (ord1block != null && ord1block.ignore)
+ continue;
+ }
+
+ if (advance_block_2)
+ {
+ ord2block = ct.nextBlock();
+ if (ord2block != null && ord2block.ignore)
+ {
+ advance_block_1 = false;
+ continue;
+ }
+ }
+ else
+ advance_block_2 = true;
+
+ if (!advance_block_1)
+ advance_block_1 = true;
+
+ if (ord1block != null)
+ ord1 = ord1block.getValue();
+ else
+ {
+ if (ord2block == null)
+ return 0;
+ return -1;
+ }
+
+ if (ord2block == null)
+ return 1;
+
+ ord2 = ord2block.getValue();
+
+ // We know chars are totally equal, so skip
+ if (ord1 == ord2)
+ {
+ if (getStrength() == IDENTICAL)
+ if (!ord1block.key.equals(ord2block.key))
+ return ord1block.key.compareTo(ord2block.key);
+ continue;
+ }
+
+ // Check for primary strength differences
+ int prim1 = CollationElementIterator.primaryOrder(ord1);
+ int prim2 = CollationElementIterator.primaryOrder(ord2);
+
+ if (prim1 == 0 && getStrength() < TERTIARY)
+ {
+ advance_block_2 = false;
+ continue;
+ }
+ else if (prim2 == 0 && getStrength() < TERTIARY)
+ {
+ advance_block_1 = false;
+ continue;
+ }
+
+ if (prim1 < prim2)
+ return -1;
+ else if (prim1 > prim2)
+ return 1;
+ else if (getStrength() == PRIMARY)
+ continue;
+
+ // Check for secondary strength differences
+ int sec1 = CollationElementIterator.secondaryOrder(ord1);
+ int sec2 = CollationElementIterator.secondaryOrder(ord2);
+
+ if (sec1 < sec2)
+ return -1;
+ else if (sec1 > sec2)
+ return 1;
+ else if (getStrength() == SECONDARY)
+ continue;
+
+ // Check for tertiary differences
+ int tert1 = CollationElementIterator.tertiaryOrder(ord1);
+ int tert2 = CollationElementIterator.tertiaryOrder(ord2);
+
+ if (tert1 < tert2)
+ return -1;
+ else if (tert1 > tert2)
+ return 1;
+ else if (getStrength() == TERTIARY)
+ continue;
+
+ // Apparently JDK does this (at least for my test case).
+ return ord1block.key.compareTo(ord2block.key);
+ }
+ }
+
+ /**
+ * This method tests this object for equality against the specified
+ * object. This will be true if and only if the specified object is
+ * another reference to this object.
+ *
+ * @param obj The <code>Object</code> to compare against this object.
+ *
+ * @return <code>true</code> if the specified object is equal to this object,
+ * <code>false</code> otherwise.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * This method builds a default collation element without invoking
+ * the database created from the rules passed to the constructor.
+ *
+ * @param c Character which needs a collation element.
+ * @return A valid brand new CollationElement instance.
+ */
+ CollationElement getDefaultElement(char c)
+ {
+ int v;
+
+ // Preliminary support for generic accent sorting inversion (I don't know if all
+ // characters in the range should be sorted backward). This is the place
+ // to fix this if needed.
+ if (inverseAccentComparison && (c >= 0x02B9 && c <= 0x0361))
+ v = 0x0361 - ((int) c - 0x02B9);
+ else
+ v = (short) c;
+ return new CollationElement("" + c, last_primary_value + v,
+ (short) 0, (short) 0, (short) 0, null, false);
+ }
+
+ /**
+ * This method builds a default collation element for an accented character
+ * without invoking the database created from the rules passed to the constructor.
+ *
+ * @param c Character which needs a collation element.
+ * @return A valid brand new CollationElement instance.
+ */
+ CollationElement getDefaultAccentedElement(char c)
+ {
+ int v;
+
+ // Preliminary support for generic accent sorting inversion (I don't know if all
+ // characters in the range should be sorted backward). This is the place
+ // to fix this if needed.
+ if (inverseAccentComparison && (c >= 0x02B9 && c <= 0x0361))
+ v = 0x0361 - ((int) c - 0x02B9);
+ else
+ v = (short) c;
+ return new CollationElement("" + c, (short) 0,
+ (short) 0, (short) (last_tertiary_value + v), (short) 0, null, false);
+ }
+
+ /**
+ * This method returns an instance for <code>CollationElementIterator</code>
+ * for the specified <code>String</code> under the collation rules for this
+ * object.
+ *
+ * @param source The <code>String</code> to return the
+ * <code>CollationElementIterator</code> instance for.
+ *
+ * @return A <code>CollationElementIterator</code> for the specified
+ * <code>String</code>.
+ */
+ public CollationElementIterator getCollationElementIterator(String source)
+ {
+ return new CollationElementIterator(this, source);
+ }
+
+ /**
+ * This method returns an instance of <code>CollationElementIterator</code>
+ * for the <code>String</code> represented by the specified
+ * <code>CharacterIterator</code>.
+ *
+ * @param source The <code>CharacterIterator</code> with the desired <code>String</code>.
+ *
+ * @return A <code>CollationElementIterator</code> for the specified <code>String</code>.
+ */
+ public CollationElementIterator getCollationElementIterator(CharacterIterator source)
+ {
+ StringBuffer expand = new StringBuffer("");
+
+ // Right now we assume that we will read from the beginning of the string.
+ for (char c = source.first();
+ c != CharacterIterator.DONE;
+ c = source.next())
+ decomposeCharacter(c, expand);
+
+ return getCollationElementIterator(expand.toString());
+ }
+
+ /**
+ * This method returns an instance of <code>CollationKey</code> for the
+ * specified <code>String</code>. The object returned will have a
+ * more efficient mechanism for its comparison function that could
+ * provide speed benefits if multiple comparisons are performed, such
+ * as during a sort.
+ *
+ * @param source The <code>String</code> to create a <code>CollationKey</code> for.
+ *
+ * @return A <code>CollationKey</code> for the specified <code>String</code>.
+ */
+ public CollationKey getCollationKey(String source)
+ {
+ CollationElementIterator cei = getCollationElementIterator(source);
+ ArrayList vect = new ArrayList();
+
+ int ord = cei.next();
+ cei.reset(); //set to start of string
+
+ while (ord != CollationElementIterator.NULLORDER)
+ {
+ // If the primary order is null, it means this is an ignorable
+ // character.
+ if (CollationElementIterator.primaryOrder(ord) == 0)
+ {
+ ord = cei.next();
+ continue;
+ }
+ switch (getStrength())
+ {
+ case PRIMARY:
+ ord = CollationElementIterator.primaryOrder(ord);
+ break;
+
+ case SECONDARY:
+ ord = CollationElementIterator.primaryOrder(ord) << 8;
+ ord |= CollationElementIterator.secondaryOrder(ord);
+
+ default:
+ break;
+ }
+
+ vect.add(new Integer(ord));
+ ord = cei.next(); //increment to next key
+ }
+
+ Object[] objarr = vect.toArray();
+ byte[] key = new byte[objarr.length * 4];
+
+ for (int i = 0; i < objarr.length; i++)
+ {
+ int j = ((Integer) objarr[i]).intValue();
+ key [i * 4] = (byte) ((j & 0xFF000000) >> 24);
+ key [i * 4 + 1] = (byte) ((j & 0x00FF0000) >> 16);
+ key [i * 4 + 2] = (byte) ((j & 0x0000FF00) >> 8);
+ key [i * 4 + 3] = (byte) (j & 0x000000FF);
+ }
+
+ return new CollationKey(this, source, key);
+ }
+
+ /**
+ * This method returns a <code>String</code> containing the collation rules
+ * for this object.
+ *
+ * @return The collation rules for this object.
+ */
+ public String getRules()
+ {
+ return rules;
+ }
+
+ /**
+ * This method returns a hash value for this object.
+ *
+ * @return A hash value for this object.
+ */
+ public int hashCode()
+ {
+ return System.identityHashCode(this);
+ }
+}
diff --git a/libjava/classpath/java/text/SimpleDateFormat.java b/libjava/classpath/java/text/SimpleDateFormat.java
new file mode 100644
index 00000000000..789cb83d86d
--- /dev/null
+++ b/libjava/classpath/java/text/SimpleDateFormat.java
@@ -0,0 +1,1257 @@
+/* SimpleDateFormat.java -- A class for parsing/formating simple
+ date constructs
+ Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005
+ Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+import gnu.java.text.AttributedFormatBuffer;
+import gnu.java.text.FormatBuffer;
+import gnu.java.text.FormatCharacterIterator;
+import gnu.java.text.StringFormatBuffer;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * SimpleDateFormat provides convenient methods for parsing and formatting
+ * dates using Gregorian calendars (see java.util.GregorianCalendar).
+ */
+public class SimpleDateFormat extends DateFormat
+{
+ /**
+ * This class is used by <code>SimpleDateFormat</code> as a
+ * compiled representation of a format string. The field
+ * ID, size, and character used are stored for each sequence
+ * of pattern characters.
+ */
+ private class CompiledField
+ {
+ /**
+ * The ID of the field within the local pattern characters.
+ * Package private for use in out class.
+ */
+ int field;
+
+ /**
+ * The size of the character sequence.
+ * Package private for use in out class.
+ */
+ int size;
+
+ /**
+ * The character used.
+ */
+ private char character;
+
+ /**
+ * Constructs a compiled field using the
+ * the given field ID, size and character
+ * values.
+ *
+ * @param f the field ID.
+ * @param s the size of the field.
+ * @param c the character used.
+ */
+ public CompiledField(int f, int s, char c)
+ {
+ field = f;
+ size = s;
+ character = c;
+ }
+
+ /**
+ * Retrieves the ID of the field relative to
+ * the local pattern characters.
+ */
+ public int getField()
+ {
+ return field;
+ }
+
+ /**
+ * Retrieves the size of the character sequence.
+ */
+ public int getSize()
+ {
+ return size;
+ }
+
+ /**
+ * Retrieves the character used in the sequence.
+ */
+ public char getCharacter()
+ {
+ return character;
+ }
+
+ /**
+ * Returns a <code>String</code> representation
+ * of the compiled field, primarily for debugging
+ * purposes.
+ *
+ * @return a <code>String</code> representation.
+ */
+ public String toString()
+ {
+ StringBuffer builder;
+
+ builder = new StringBuffer(getClass().getName());
+ builder.append("[field=");
+ builder.append(field);
+ builder.append(", size=");
+ builder.append(size);
+ builder.append(", character=");
+ builder.append(character);
+ builder.append("]");
+
+ return builder.toString();
+ }
+ }
+
+ /**
+ * A list of <code>CompiledField</code>s,
+ * representing the compiled version of the pattern.
+ *
+ * @see CompiledField
+ * @serial Ignored.
+ */
+ private transient ArrayList tokens;
+
+ /**
+ * The localised data used in formatting,
+ * such as the day and month names in the local
+ * language, and the localized pattern characters.
+ *
+ * @see DateFormatSymbols
+ * @serial The localisation data. May not be null.
+ */
+ private DateFormatSymbols formatData;
+
+ /**
+ * The date representing the start of the century
+ * used for interpreting two digit years. For
+ * example, 24/10/2004 would cause two digit
+ * years to be interpreted as representing
+ * the years between 2004 and 2104.
+ *
+ * @see get2DigitYearStart()
+ * @see set2DigitYearStart(java.util.Date)
+ * @see Date
+ * @serial The start date of the century for parsing two digit years.
+ * May not be null.
+ */
+ private Date defaultCenturyStart;
+
+ /**
+ * The year at which interpretation of two
+ * digit years starts.
+ *
+ * @see get2DigitYearStart()
+ * @see set2DigitYearStart(java.util.Date)
+ * @serial Ignored.
+ */
+ private transient int defaultCentury;
+
+ /**
+ * The non-localized pattern string. This
+ * only ever contains the pattern characters
+ * stored in standardChars. Localized patterns
+ * are translated to this form.
+ *
+ * @see applyPattern(String)
+ * @see applyLocalizedPattern(String)
+ * @see toPattern()
+ * @see toLocalizedPattern()
+ * @serial The non-localized pattern string. May not be null.
+ */
+ private String pattern;
+
+ /**
+ * The version of serialized data used by this class.
+ * Version 0 only includes the pattern and formatting
+ * data. Version 1 adds the start date for interpreting
+ * two digit years.
+ *
+ * @serial This specifies the version of the data being serialized.
+ * Version 0 (or no version) specifies just <code>pattern</code>
+ * and <code>formatData</code>. Version 1 adds
+ * the <code>defaultCenturyStart</code>. This implementation
+ * always writes out version 1 data.
+ */
+ private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier
+
+ /**
+ * For compatability.
+ */
+ private static final long serialVersionUID = 4774881970558875024L;
+
+ // This string is specified in the root of the CLDR. We set it here
+ // rather than doing a DateFormatSymbols(Locale.US).getLocalPatternChars()
+ // since someone could theoretically change those values (though unlikely).
+ private static final String standardChars = "GyMdkHmsSEDFwWahKzYeugAZ";
+
+ /**
+ * Reads the serialized version of this object.
+ * If the serialized data is only version 0,
+ * then the date for the start of the century
+ * for interpreting two digit years is computed.
+ * The pattern is parsed and compiled following the process
+ * of reading in the serialized data.
+ *
+ * @param stream the object stream to read the data from.
+ * @throws IOException if an I/O error occurs.
+ * @throws ClassNotFoundException if the class of the serialized data
+ * could not be found.
+ * @throws InvalidObjectException if the pattern is invalid.
+ */
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException
+ {
+ stream.defaultReadObject();
+ if (serialVersionOnStream < 1)
+ {
+ computeCenturyStart ();
+ serialVersionOnStream = 1;
+ }
+ else
+ // Ensure that defaultCentury gets set.
+ set2DigitYearStart(defaultCenturyStart);
+
+ // Set up items normally taken care of by the constructor.
+ tokens = new ArrayList();
+ try
+ {
+ compileFormat(pattern);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new InvalidObjectException("The stream pattern was invalid.");
+ }
+ }
+
+ /**
+ * Compiles the supplied non-localized pattern into a form
+ * from which formatting and parsing can be performed.
+ * This also detects errors in the pattern, which will
+ * be raised on later use of the compiled data.
+ *
+ * @param pattern the non-localized pattern to compile.
+ * @throws IllegalArgumentException if the pattern is invalid.
+ */
+ private void compileFormat(String pattern)
+ {
+ // Any alphabetical characters are treated as pattern characters
+ // unless enclosed in single quotes.
+
+ char thisChar;
+ int pos;
+ int field;
+ CompiledField current = null;
+
+ for (int i=0; i<pattern.length(); i++) {
+ thisChar = pattern.charAt(i);
+ field = standardChars.indexOf(thisChar);
+ if (field == -1) {
+ current = null;
+ if ((thisChar >= 'A' && thisChar <= 'Z')
+ || (thisChar >= 'a' && thisChar <= 'z')) {
+ // Not a valid letter
+ throw new IllegalArgumentException("Invalid letter " + thisChar +
+ "encountered at character " + i
+ + ".");
+ } else if (thisChar == '\'') {
+ // Quoted text section; skip to next single quote
+ pos = pattern.indexOf('\'',i+1);
+ if (pos == -1) {
+ throw new IllegalArgumentException("Quotes starting at character "
+ + i + " not closed.");
+ }
+ if ((pos+1 < pattern.length()) && (pattern.charAt(pos+1) == '\'')) {
+ tokens.add(pattern.substring(i+1,pos+1));
+ } else {
+ tokens.add(pattern.substring(i+1,pos));
+ }
+ i = pos;
+ } else {
+ // A special character
+ tokens.add(new Character(thisChar));
+ }
+ } else {
+ // A valid field
+ if ((current != null) && (field == current.field)) {
+ current.size++;
+ } else {
+ current = new CompiledField(field,1,thisChar);
+ tokens.add(current);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a string representation of this
+ * class.
+ *
+ * @return a string representation of the <code>SimpleDateFormat</code>
+ * instance.
+ */
+ public String toString()
+ {
+ StringBuffer output = new StringBuffer(getClass().getName());
+ output.append("[tokens=");
+ output.append(tokens);
+ output.append(", formatData=");
+ output.append(formatData);
+ output.append(", defaultCenturyStart=");
+ output.append(defaultCenturyStart);
+ output.append(", defaultCentury=");
+ output.append(defaultCentury);
+ output.append(", pattern=");
+ output.append(pattern);
+ output.append(", serialVersionOnStream=");
+ output.append(serialVersionOnStream);
+ output.append(", standardChars=");
+ output.append(standardChars);
+ output.append("]");
+ return output.toString();
+ }
+
+ /**
+ * Constructs a SimpleDateFormat using the default pattern for
+ * the default locale.
+ */
+ public SimpleDateFormat()
+ {
+ /*
+ * There does not appear to be a standard API for determining
+ * what the default pattern for a locale is, so use package-scope
+ * variables in DateFormatSymbols to encapsulate this.
+ */
+ super();
+ Locale locale = Locale.getDefault();
+ calendar = new GregorianCalendar(locale);
+ computeCenturyStart();
+ tokens = new ArrayList();
+ formatData = new DateFormatSymbols(locale);
+ pattern = (formatData.dateFormats[DEFAULT] + ' '
+ + formatData.timeFormats[DEFAULT]);
+ compileFormat(pattern);
+ numberFormat = NumberFormat.getInstance(locale);
+ numberFormat.setGroupingUsed (false);
+ numberFormat.setParseIntegerOnly (true);
+ numberFormat.setMaximumFractionDigits (0);
+ }
+
+ /**
+ * Creates a date formatter using the specified non-localized pattern,
+ * with the default DateFormatSymbols for the default locale.
+ *
+ * @param pattern the pattern to use.
+ * @throws NullPointerException if the pattern is null.
+ * @throws IllegalArgumentException if the pattern is invalid.
+ */
+ public SimpleDateFormat(String pattern)
+ {
+ this(pattern, Locale.getDefault());
+ }
+
+ /**
+ * Creates a date formatter using the specified non-localized pattern,
+ * with the default DateFormatSymbols for the given locale.
+ *
+ * @param pattern the non-localized pattern to use.
+ * @param locale the locale to use for the formatting symbols.
+ * @throws NullPointerException if the pattern is null.
+ * @throws IllegalArgumentException if the pattern is invalid.
+ */
+ public SimpleDateFormat(String pattern, Locale locale)
+ {
+ super();
+ calendar = new GregorianCalendar(locale);
+ computeCenturyStart();
+ tokens = new ArrayList();
+ formatData = new DateFormatSymbols(locale);
+ compileFormat(pattern);
+ this.pattern = pattern;
+ numberFormat = NumberFormat.getInstance(locale);
+ numberFormat.setGroupingUsed (false);
+ numberFormat.setParseIntegerOnly (true);
+ numberFormat.setMaximumFractionDigits (0);
+ }
+
+ /**
+ * Creates a date formatter using the specified non-localized
+ * pattern. The specified DateFormatSymbols will be used when
+ * formatting.
+ *
+ * @param pattern the non-localized pattern to use.
+ * @param formatData the formatting symbols to use.
+ * @throws NullPointerException if the pattern or formatData is null.
+ * @throws IllegalArgumentException if the pattern is invalid.
+ */
+ public SimpleDateFormat(String pattern, DateFormatSymbols formatData)
+ {
+ super();
+ calendar = new GregorianCalendar();
+ computeCenturyStart ();
+ tokens = new ArrayList();
+ if (formatData == null)
+ throw new NullPointerException("formatData");
+ this.formatData = formatData;
+ compileFormat(pattern);
+ this.pattern = pattern;
+ numberFormat = NumberFormat.getInstance();
+ numberFormat.setGroupingUsed (false);
+ numberFormat.setParseIntegerOnly (true);
+ numberFormat.setMaximumFractionDigits (0);
+ }
+
+ /**
+ * This method returns a string with the formatting pattern being used
+ * by this object. This string is unlocalized.
+ *
+ * @return The format string.
+ */
+ public String toPattern()
+ {
+ return pattern;
+ }
+
+ /**
+ * This method returns a string with the formatting pattern being used
+ * by this object. This string is localized.
+ *
+ * @return The format string.
+ */
+ public String toLocalizedPattern()
+ {
+ String localChars = formatData.getLocalPatternChars();
+ return translateLocalizedPattern(pattern, standardChars, localChars);
+ }
+
+ /**
+ * This method sets the formatting pattern that should be used by this
+ * object. This string is not localized.
+ *
+ * @param pattern The new format pattern.
+ * @throws NullPointerException if the pattern is null.
+ * @throws IllegalArgumentException if the pattern is invalid.
+ */
+ public void applyPattern(String pattern)
+ {
+ tokens = new ArrayList();
+ compileFormat(pattern);
+ this.pattern = pattern;
+ }
+
+ /**
+ * This method sets the formatting pattern that should be used by this
+ * object. This string is localized.
+ *
+ * @param pattern The new format pattern.
+ * @throws NullPointerException if the pattern is null.
+ * @throws IllegalArgumentException if the pattern is invalid.
+ */
+ public void applyLocalizedPattern(String pattern)
+ {
+ String localChars = formatData.getLocalPatternChars();
+ pattern = translateLocalizedPattern(pattern, localChars, standardChars);
+ applyPattern(pattern);
+ }
+
+ /**
+ * Translates either from or to a localized variant of the pattern
+ * string. For example, in the German locale, 't' (for 'tag') is
+ * used instead of 'd' (for 'date'). This method translates
+ * a localized pattern (such as 'ttt') to a non-localized pattern
+ * (such as 'ddd'), or vice versa. Non-localized patterns use
+ * a standard set of characters, which match those of the U.S. English
+ * locale.
+ *
+ * @param pattern the pattern to translate.
+ * @param oldChars the old set of characters (used in the pattern).
+ * @param newChars the new set of characters (which will be used in the
+ * pattern).
+ * @return a version of the pattern using the characters in
+ * <code>newChars</code>.
+ */
+ private String translateLocalizedPattern(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();
+ }
+
+ /**
+ * Returns the start of the century used for two digit years.
+ *
+ * @return A <code>Date</code> representing the start of the century
+ * for two digit years.
+ */
+ public Date get2DigitYearStart()
+ {
+ return defaultCenturyStart;
+ }
+
+ /**
+ * Sets the start of the century used for two digit years.
+ *
+ * @param date A <code>Date</code> representing the start of the century for
+ * two digit years.
+ */
+ public void set2DigitYearStart(Date date)
+ {
+ defaultCenturyStart = date;
+ calendar.clear();
+ calendar.setTime(date);
+ int year = calendar.get(Calendar.YEAR);
+ defaultCentury = year - (year % 100);
+ }
+
+ /**
+ * This method returns a copy of the format symbol information used
+ * for parsing and formatting dates.
+ *
+ * @return a copy of the date format symbols.
+ */
+ public DateFormatSymbols getDateFormatSymbols()
+ {
+ return (DateFormatSymbols) formatData.clone();
+ }
+
+ /**
+ * This method sets the format symbols information used for parsing
+ * and formatting dates.
+ *
+ * @param formatData The date format symbols.
+ * @throws NullPointerException if <code>formatData</code> is null.
+ */
+ public void setDateFormatSymbols(DateFormatSymbols formatData)
+ {
+ if (formatData == null)
+ {
+ throw new
+ NullPointerException("The supplied format data was null.");
+ }
+ this.formatData = formatData;
+ }
+
+ /**
+ * This methods tests whether the specified object is equal to this
+ * object. This will be true if and only if the specified object:
+ * <p>
+ * <ul>
+ * <li>Is not <code>null</code>.</li>
+ * <li>Is an instance of <code>SimpleDateFormat</code>.</li>
+ * <li>Is equal to this object at the superclass (i.e., <code>DateFormat</code>)
+ * level.</li>
+ * <li>Has the same formatting pattern.</li>
+ * <li>Is using the same formatting symbols.</li>
+ * <li>Is using the same century for two digit years.</li>
+ * </ul>
+ *
+ * @param obj The object to compare for equality against.
+ *
+ * @return <code>true</code> if the specified object is equal to this object,
+ * <code>false</code> otherwise.
+ */
+ public boolean equals(Object o)
+ {
+ if (!super.equals(o))
+ return false;
+
+ if (!(o instanceof SimpleDateFormat))
+ return false;
+
+ SimpleDateFormat sdf = (SimpleDateFormat)o;
+
+ if (defaultCentury != sdf.defaultCentury)
+ return false;
+
+ if (!toPattern().equals(sdf.toPattern()))
+ return false;
+
+ if (!getDateFormatSymbols().equals(sdf.getDateFormatSymbols()))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * This method returns a hash value for this object.
+ *
+ * @return A hash value for this object.
+ */
+ public int hashCode()
+ {
+ return super.hashCode() ^ toPattern().hashCode() ^ defaultCentury ^
+ getDateFormatSymbols().hashCode();
+ }
+
+
+ /**
+ * Formats the date input according to the format string in use,
+ * appending to the specified StringBuffer. The input StringBuffer
+ * is returned as output for convenience.
+ */
+ private void formatWithAttribute(Date date, FormatBuffer buffer, FieldPosition pos)
+ {
+ String temp;
+ AttributedCharacterIterator.Attribute attribute;
+ calendar.setTime(date);
+
+ // go through vector, filling in fields where applicable, else toString
+ Iterator iter = tokens.iterator();
+ while (iter.hasNext())
+ {
+ Object o = iter.next();
+ if (o instanceof CompiledField)
+ {
+ CompiledField cf = (CompiledField) o;
+ int beginIndex = buffer.length();
+
+ switch (cf.getField())
+ {
+ case ERA_FIELD:
+ buffer.append (formatData.eras[calendar.get (Calendar.ERA)], DateFormat.Field.ERA);
+ break;
+ case YEAR_FIELD:
+ // If we have two digits, then we truncate. Otherwise, we
+ // use the size of the pattern, and zero pad.
+ buffer.setDefaultAttribute (DateFormat.Field.YEAR);
+ if (cf.getSize() == 2)
+ {
+ temp = "00"+String.valueOf (calendar.get (Calendar.YEAR));
+ buffer.append (temp.substring (temp.length() - 2));
+ }
+ else
+ withLeadingZeros (calendar.get (Calendar.YEAR), cf.getSize(), buffer);
+ break;
+ case MONTH_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.MONTH);
+ if (cf.getSize() < 3)
+ withLeadingZeros (calendar.get (Calendar.MONTH) + 1, cf.getSize(), buffer);
+ else if (cf.getSize() < 4)
+ buffer.append (formatData.shortMonths[calendar.get (Calendar.MONTH)]);
+ else
+ buffer.append (formatData.months[calendar.get (Calendar.MONTH)]);
+ break;
+ case DATE_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_MONTH);
+ withLeadingZeros (calendar.get (Calendar.DATE), cf.getSize(), buffer);
+ break;
+ case HOUR_OF_DAY1_FIELD: // 1-24
+ buffer.setDefaultAttribute(DateFormat.Field.HOUR_OF_DAY1);
+ withLeadingZeros ( ((calendar.get (Calendar.HOUR_OF_DAY) + 23) % 24) + 1,
+ cf.getSize(), buffer);
+ break;
+ case HOUR_OF_DAY0_FIELD: // 0-23
+ buffer.setDefaultAttribute (DateFormat.Field.HOUR_OF_DAY0);
+ withLeadingZeros (calendar.get (Calendar.HOUR_OF_DAY), cf.getSize(), buffer);
+ break;
+ case MINUTE_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.MINUTE);
+ withLeadingZeros (calendar.get (Calendar.MINUTE),
+ cf.getSize(), buffer);
+ break;
+ case SECOND_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.SECOND);
+ withLeadingZeros(calendar.get (Calendar.SECOND),
+ cf.getSize(), buffer);
+ break;
+ case MILLISECOND_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.MILLISECOND);
+ withLeadingZeros (calendar.get (Calendar.MILLISECOND), cf.getSize(), buffer);
+ break;
+ case DAY_OF_WEEK_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK);
+ if (cf.getSize() < 4)
+ buffer.append (formatData.shortWeekdays[calendar.get (Calendar.DAY_OF_WEEK)]);
+ else
+ buffer.append (formatData.weekdays[calendar.get (Calendar.DAY_OF_WEEK)]);
+ break;
+ case DAY_OF_YEAR_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_YEAR);
+ withLeadingZeros (calendar.get (Calendar.DAY_OF_YEAR), cf.getSize(), buffer);
+ break;
+ case DAY_OF_WEEK_IN_MONTH_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK_IN_MONTH);
+ withLeadingZeros (calendar.get (Calendar.DAY_OF_WEEK_IN_MONTH),
+ cf.getSize(), buffer);
+ break;
+ case WEEK_OF_YEAR_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_YEAR);
+ withLeadingZeros (calendar.get (Calendar.WEEK_OF_YEAR),
+ cf.getSize(), buffer);
+ break;
+ case WEEK_OF_MONTH_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_MONTH);
+ withLeadingZeros (calendar.get (Calendar.WEEK_OF_MONTH),
+ cf.getSize(), buffer);
+ break;
+ case AM_PM_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.AM_PM);
+ buffer.append (formatData.ampms[calendar.get (Calendar.AM_PM)]);
+ break;
+ case HOUR1_FIELD: // 1-12
+ buffer.setDefaultAttribute (DateFormat.Field.HOUR1);
+ withLeadingZeros (((calendar.get (Calendar.HOUR) + 11) % 12) + 1,
+ cf.getSize(), buffer);
+ break;
+ case HOUR0_FIELD: // 0-11
+ buffer.setDefaultAttribute (DateFormat.Field.HOUR0);
+ withLeadingZeros (calendar.get (Calendar.HOUR), cf.getSize(), buffer);
+ break;
+ case TIMEZONE_FIELD:
+ buffer.setDefaultAttribute (DateFormat.Field.TIME_ZONE);
+ TimeZone zone = calendar.getTimeZone();
+ boolean isDST = calendar.get (Calendar.DST_OFFSET) != 0;
+ // FIXME: XXX: This should be a localized time zone.
+ String zoneID = zone.getDisplayName
+ (isDST, cf.getSize() > 3 ? TimeZone.LONG : TimeZone.SHORT);
+ buffer.append (zoneID);
+ break;
+ case RFC822_TIMEZONE_FIELD:
+ buffer.setDefaultAttribute(DateFormat.Field.RFC822_TIME_ZONE);
+ int pureMinutes = (calendar.get(Calendar.ZONE_OFFSET) +
+ calendar.get(Calendar.DST_OFFSET)) / (1000 * 60);
+ String sign = (pureMinutes < 0) ? "-" : "+";
+ int hours = pureMinutes / 60;
+ int minutes = pureMinutes % 60;
+ buffer.append(sign);
+ withLeadingZeros(hours, 2, buffer);
+ withLeadingZeros(minutes, 2, buffer);
+ break;
+ default:
+ throw new IllegalArgumentException ("Illegal pattern character " +
+ cf.getCharacter());
+ }
+ if (pos != null && (buffer.getDefaultAttribute() == pos.getFieldAttribute()
+ || cf.getField() == pos.getField()))
+ {
+ pos.setBeginIndex(beginIndex);
+ pos.setEndIndex(buffer.length());
+ }
+ }
+ else
+ {
+ buffer.append(o.toString(), null);
+ }
+ }
+ }
+
+ public StringBuffer format(Date date, StringBuffer buffer, FieldPosition pos)
+ {
+ formatWithAttribute(date, new StringFormatBuffer (buffer), pos);
+
+ return buffer;
+ }
+
+ public AttributedCharacterIterator formatToCharacterIterator(Object date)
+ throws IllegalArgumentException
+ {
+ if (date == null)
+ throw new NullPointerException("null argument");
+ if (!(date instanceof Date))
+ throw new IllegalArgumentException("argument should be an instance of java.util.Date");
+
+ AttributedFormatBuffer buf = new AttributedFormatBuffer();
+ formatWithAttribute((Date)date, buf,
+ null);
+ buf.sync();
+
+ return new FormatCharacterIterator(buf.getBuffer().toString(),
+ buf.getRanges(),
+ buf.getAttributes());
+ }
+
+ private void withLeadingZeros(int value, int length, FormatBuffer buffer)
+ {
+ String valStr = String.valueOf(value);
+ for (length -= valStr.length(); length > 0; length--)
+ buffer.append('0');
+ buffer.append(valStr);
+ }
+
+ private 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;
+ }
+
+ /**
+ * This method parses the specified string into a date.
+ *
+ * @param dateStr The date string to parse.
+ * @param pos The input and output parse position
+ *
+ * @return The parsed date, or <code>null</code> if the string cannot be
+ * parsed.
+ */
+ public Date parse (String dateStr, ParsePosition pos)
+ {
+ int fmt_index = 0;
+ int fmt_max = pattern.length();
+
+ calendar.clear();
+ boolean saw_timezone = false;
+ int quote_start = -1;
+ boolean is2DigitYear = false;
+ try
+ {
+ 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 (dateStr, 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 (dateStr, pos, ch))
+ return null;
+ continue;
+ }
+
+ // We've arrived at a potential pattern character in the
+ // pattern.
+ int fmt_count = 1;
+ while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch)
+ {
+ ++fmt_count;
+ }
+
+ // We might need to limit the number of digits to parse in
+ // some cases. We look to the next pattern character to
+ // decide.
+ boolean limit_digits = false;
+ if (fmt_index < fmt_max
+ && standardChars.indexOf(pattern.charAt(fmt_index)) >= 0)
+ limit_digits = true;
+ --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;
+ int offset = 0;
+ boolean maybe2DigitYear = false;
+ boolean oneBasedHour = false;
+ boolean oneBasedHourOfDay = false;
+ Integer simpleOffset;
+ String[] set1 = null;
+ String[] set2 = null;
+ 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;
+ set1 = formatData.getWeekdays();
+ set2 = formatData.getShortWeekdays();
+ 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 (fmt_count <= 2)
+ offset = -1;
+ else
+ {
+ is_numeric = false;
+ set1 = formatData.getMonths();
+ set2 = formatData.getShortMonths();
+ }
+ break;
+ case 'y':
+ calendar_field = Calendar.YEAR;
+ if (fmt_count <= 2)
+ maybe2DigitYear = true;
+ break;
+ case 'K':
+ calendar_field = Calendar.HOUR;
+ break;
+ case 'h':
+ calendar_field = Calendar.HOUR;
+ oneBasedHour = true;
+ break;
+ case 'H':
+ calendar_field = Calendar.HOUR_OF_DAY;
+ break;
+ case 'k':
+ calendar_field = Calendar.HOUR_OF_DAY;
+ oneBasedHourOfDay = true;
+ 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;
+ set1 = formatData.getAmPmStrings();
+ break;
+ case 'z':
+ 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.ZONE_OFFSET;
+ String[][] zoneStrings = formatData.getZoneStrings();
+ int zoneCount = zoneStrings.length;
+ int index = pos.getIndex();
+ boolean found_zone = false;
+ simpleOffset = computeOffset(dateStr.substring(index), pos);
+ if (simpleOffset != null)
+ {
+ found_zone = true;
+ saw_timezone = true;
+ calendar.set(Calendar.DST_OFFSET, 0);
+ offset = simpleOffset.intValue();
+ }
+ else
+ {
+ for (int j = 0; j < zoneCount; j++)
+ {
+ String[] strings = zoneStrings[j];
+ int k;
+ for (k = 0; k < strings.length; ++k)
+ {
+ if (dateStr.startsWith(strings[k], index))
+ break;
+ }
+ if (k != strings.length)
+ {
+ found_zone = true;
+ saw_timezone = true;
+ TimeZone tz = TimeZone.getTimeZone (strings[0]);
+ // Check if it's a DST zone or ordinary
+ if(k == 3 || k == 4)
+ calendar.set (Calendar.DST_OFFSET, tz.getDSTSavings());
+ else
+ calendar.set (Calendar.DST_OFFSET, 0);
+ offset = tz.getRawOffset ();
+ 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;
+ int index = -1;
+ if (is_numeric)
+ {
+ numberFormat.setMinimumIntegerDigits(fmt_count);
+ if (limit_digits)
+ numberFormat.setMaximumIntegerDigits(fmt_count);
+ if (maybe2DigitYear)
+ index = pos.getIndex();
+ Number n = numberFormat.parse(dateStr, pos);
+ if (pos == null || ! (n instanceof Long))
+ return null;
+ value = n.intValue() + offset;
+ }
+ else if (set1 != null)
+ {
+ index = pos.getIndex();
+ int i;
+ boolean found = false;
+ for (i = offset; i < set1.length; ++i)
+ {
+ if (set1[i] != null)
+ if (dateStr.toUpperCase().startsWith(set1[i].toUpperCase(),
+ index))
+ {
+ found = true;
+ pos.setIndex(index + set1[i].length());
+ break;
+ }
+ }
+ if (!found && set2 != null)
+ {
+ for (i = offset; i < set2.length; ++i)
+ {
+ if (set2[i] != null)
+ if (dateStr.toUpperCase().startsWith(set2[i].toUpperCase(),
+ index))
+ {
+ found = true;
+ pos.setIndex(index + set2[i].length());
+ break;
+ }
+ }
+ }
+ if (!found)
+ {
+ pos.setErrorIndex(index);
+ return null;
+ }
+ value = i;
+ }
+ else
+ value = offset;
+
+ if (maybe2DigitYear)
+ {
+ // Parse into default century if the numeric year string has
+ // exactly 2 digits.
+ int digit_count = pos.getIndex() - index;
+ if (digit_count == 2)
+ {
+ is2DigitYear = true;
+ value += defaultCentury;
+ }
+ }
+
+ // Calendar uses 0-based hours.
+ // I.e. 00:00 AM is midnight, not 12 AM or 24:00
+ if (oneBasedHour && value == 12)
+ value = 0;
+
+ if (oneBasedHourOfDay && value == 24)
+ value = 0;
+
+ // Assign the value and move on.
+ calendar.set(calendar_field, value);
+ }
+
+ if (is2DigitYear)
+ {
+ // Apply the 80-20 heuristic to dermine the full year based on
+ // defaultCenturyStart.
+ int year = calendar.get(Calendar.YEAR);
+ if (calendar.getTime().compareTo(defaultCenturyStart) < 0)
+ calendar.set(Calendar.YEAR, year + 100);
+ }
+ if (! saw_timezone)
+ {
+ // Use the real rules to determine whether or not this
+ // particular time is in daylight savings.
+ calendar.clear (Calendar.DST_OFFSET);
+ calendar.clear (Calendar.ZONE_OFFSET);
+ }
+ return calendar.getTime();
+ }
+ catch (IllegalArgumentException x)
+ {
+ pos.setErrorIndex(pos.getIndex());
+ return null;
+ }
+ }
+
+ /**
+ * <p>
+ * Computes the time zone offset in milliseconds
+ * relative to GMT, based on the supplied
+ * <code>String</code> representation.
+ * </p>
+ * <p>
+ * The supplied <code>String</code> must be a three
+ * or four digit signed number, with an optional 'GMT'
+ * prefix. The first one or two digits represents the hours,
+ * while the last two represent the minutes. The
+ * two sets of digits can optionally be separated by
+ * ':'. The mandatory sign prefix (either '+' or '-')
+ * indicates the direction of the offset from GMT.
+ * </p>
+ * <p>
+ * For example, 'GMT+0200' specifies 2 hours after
+ * GMT, while '-05:00' specifies 5 hours prior to
+ * GMT. The special case of 'GMT' alone can be used
+ * to represent the offset, 0.
+ * </p>
+ * <p>
+ * If the <code>String</code> can not be parsed,
+ * the result will be null. The resulting offset
+ * is wrapped in an <code>Integer</code> object, in
+ * order to allow such failure to be represented.
+ * </p>
+ *
+ * @param zoneString a string in the form
+ * (GMT)? sign hours : minutes
+ * where sign = '+' or '-', hours
+ * is a one or two digits representing
+ * a number between 0 and 23, and
+ * minutes is two digits representing
+ * a number between 0 and 59.
+ * @return the parsed offset, or null if parsing
+ * failed.
+ */
+ private Integer computeOffset(String zoneString, ParsePosition pos)
+ {
+ Pattern pattern =
+ Pattern.compile("(GMT)?([+-])([012])?([0-9]):?([0-9]{2})");
+ Matcher matcher = pattern.matcher(zoneString);
+
+ // Match from start, but ignore trailing parts
+ boolean hasAll = matcher.lookingAt();
+ try
+ {
+ // Do we have at least the sign, hour and minute?
+ matcher.group(2);
+ matcher.group(4);
+ matcher.group(5);
+ }
+ catch (IllegalStateException ise)
+ {
+ hasAll = false;
+ }
+ if (hasAll)
+ {
+ int sign = matcher.group(2).equals("+") ? 1 : -1;
+ int hour = Integer.parseInt(matcher.group(4));
+ if (!matcher.group(3).equals(""))
+ hour += (Integer.parseInt(matcher.group(3)) * 10);
+ int minutes = Integer.parseInt(matcher.group(5));
+
+ if (hour > 23)
+ return null;
+ int offset = sign * ((hour * 60) + minutes) * 60000;
+
+ // advance the index
+ pos.setIndex(pos.getIndex() + matcher.end());
+ return new Integer(offset);
+ }
+ else if (zoneString.startsWith("GMT"))
+ {
+ pos.setIndex(pos.getIndex() + 3);
+ return new Integer(0);
+ }
+ return null;
+ }
+
+ // Compute the start of the current century as defined by
+ // get2DigitYearStart.
+ private void computeCenturyStart()
+ {
+ int year = calendar.get(Calendar.YEAR);
+ calendar.set(Calendar.YEAR, year - 80);
+ set2DigitYearStart(calendar.getTime());
+ }
+
+ /**
+ * Returns a copy of this instance of
+ * <code>SimpleDateFormat</code>. The copy contains
+ * clones of the formatting symbols and the 2-digit
+ * year century start date.
+ */
+ public Object clone()
+ {
+ SimpleDateFormat clone = (SimpleDateFormat) super.clone();
+ clone.setDateFormatSymbols((DateFormatSymbols) formatData.clone());
+ clone.set2DigitYearStart((Date) defaultCenturyStart.clone());
+ return clone;
+ }
+
+}
diff --git a/libjava/classpath/java/text/StringCharacterIterator.java b/libjava/classpath/java/text/StringCharacterIterator.java
new file mode 100644
index 00000000000..e3adc857e51
--- /dev/null
+++ b/libjava/classpath/java/text/StringCharacterIterator.java
@@ -0,0 +1,356 @@
+/* StringCharacterIterator.java -- Iterate over a character range in a string
+ Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.text;
+
+/**
+ * This class iterates over a range of characters in a <code>String</code>.
+ * For a given range of text, a beginning and ending index,
+ * as well as a current index are defined. These values can be queried
+ * by the methods in this interface. Additionally, various methods allow
+ * the index to be set.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Tom Tromey (tromey@cygnus.com)
+ */
+public final class StringCharacterIterator implements CharacterIterator
+{
+ /**
+ * This is the string to iterate over
+ */
+ private String text;
+
+ /**
+ * This is the value of the start position of the text range.
+ */
+ private int begin;
+
+ /**
+ * This is the value of the ending position of the text range.
+ */
+ private int end;
+
+ /**
+ * This is the current value of the scan index.
+ */
+ private int index;
+
+ /**
+ * This method initializes a new instance of
+ * <code>StringCharacterIterator</code> to iterate over the entire
+ * text of the specified <code>String</code>. The initial index
+ * value will be set to the first character in the string.
+ *
+ * @param text The <code>String</code> to iterate through.
+ */
+ public StringCharacterIterator (String text)
+ {
+ this (text, 0, text.length (), 0);
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method initializes a new instance of
+ * <code>StringCharacterIterator</code> to iterate over the entire
+ * text of the specified <code>String</code>. The initial index
+ * value will be set to the specified value.
+ *
+ * @param text The <code>String</code> to iterate through.
+ * @param index The initial index position.
+ */
+ public StringCharacterIterator (String text, int index)
+ {
+ this (text, 0, text.length (), index);
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method initializes a new instance of
+ * <code>StringCharacterIterator</code> that iterates over the text
+ * in a subrange of the specified <code>String</code>. The
+ * beginning and end of the range are specified by the caller, as is
+ * the initial index position.
+ *
+ * @param text The <code>String</code> to iterate through.
+ * @param begin The beginning position in the character range.
+ * @param end The ending position in the character range.
+ * @param index The initial index position.
+ *
+ * @param IllegalArgumentException If any of the range values are
+ * invalid.
+ */
+ public StringCharacterIterator (String text, int begin, int end, int index)
+ {
+ int len = text.length ();
+
+ if ((begin < 0) || (begin > len))
+ throw new IllegalArgumentException ("Bad begin position");
+
+ if ((end < begin) || (end > len))
+ throw new IllegalArgumentException ("Bad end position");
+
+ if ((index < begin) || (index > end))
+ throw new IllegalArgumentException ("Bad initial index position");
+
+ this.text = text;
+ this.begin = begin;
+ this.end = end;
+ this.index = index;
+ }
+
+ /**
+ * This is a package level constructor that copies the text out of
+ * an existing StringCharacterIterator and resets the beginning and
+ * ending index.
+ *
+ * @param scci The StringCharacterIterator to copy the info from
+ * @param begin The beginning index of the range we are interested in.
+ * @param end The ending index of the range we are interested in.
+ */
+ StringCharacterIterator (StringCharacterIterator sci, int begin, int end)
+ {
+ this (sci.text, begin, end, begin);
+ }
+
+ /**
+ * This method returns the character at the current index position
+ *
+ * @return The character at the current index position.
+ */
+ public char current ()
+ {
+ return (index < end) ? text.charAt (index) : DONE;
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method increments the current index and then returns the
+ * character at the new index value. If the index is already at
+ * <code>getEndIndex () - 1</code>, it will not be incremented.
+ *
+ * @return The character at the position of the incremented index
+ * value, or <code>DONE</code> if the index has reached
+ * getEndIndex () - 1.
+ */
+ public char next ()
+ {
+ if (index == end)
+ return DONE;
+
+ ++index;
+ return current ();
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method decrements the current index and then returns the
+ * character at the new index value. If the index value is already
+ * at the beginning index, it will not be decremented.
+ *
+ * @return The character at the position of the decremented index
+ * value, or <code>DONE</code> if index was already equal to the
+ * beginning index value.
+ */
+ public char previous ()
+ {
+ if (index == begin)
+ return DONE;
+
+ --index;
+ return current ();
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method sets the index value to the beginning of the range and returns
+ * the character there.
+ *
+ * @return The character at the beginning of the range, or
+ * <code>DONE</code> if the range is empty.
+ */
+ public char first ()
+ {
+ index = begin;
+ return current ();
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method sets the index value to <code>getEndIndex () - 1</code> and
+ * returns the character there. If the range is empty, then the index value
+ * will be set equal to the beginning index.
+ *
+ * @return The character at the end of the range, or
+ * <code>DONE</code> if the range is empty.
+ */
+ public char last ()
+ {
+ if (end == begin)
+ return DONE;
+
+ index = end - 1;
+ return current ();
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method returns the current value of the index.
+ *
+ * @return The current index value
+ */
+ public int getIndex ()
+ {
+ return index;
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method sets the value of the index to the specified value, then
+ * returns the character at that position.
+ *
+ * @param index The new index value.
+ *
+ * @return The character at the new index value or <code>DONE</code>
+ * if the index value is equal to <code>getEndIndex</code>.
+ *
+ * @exception IllegalArgumentException If the specified index is not valid
+ */
+ public char setIndex (int index)
+ {
+ if ((index < begin) || (index > end))
+ throw new IllegalArgumentException ("Bad index specified");
+
+ this.index = index;
+ return current ();
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method returns the character position of the first character in the
+ * range.
+ *
+ * @return The index of the first character in the range.
+ */
+ public int getBeginIndex ()
+ {
+ return begin;
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method returns the character position of the end of the text range.
+ * This will actually be the index of the first character following the
+ * end of the range. In the event the text range is empty, this will be
+ * equal to the first character in the range.
+ *
+ * @return The index of the end of the range.
+ */
+ public int getEndIndex ()
+ {
+ return end;
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method creates a copy of this <code>CharacterIterator</code>.
+ *
+ * @return A copy of this <code>CharacterIterator</code>.
+ */
+ public Object clone ()
+ {
+ return new StringCharacterIterator (text, begin, end, index);
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method tests this object for equality againt the specified
+ * object. This will be true if and only if the specified object:
+ * <p>
+ * <ul>
+ * <li>is not <code>null</code>.</li>
+ * <li>is an instance of <code>StringCharacterIterator</code></li>
+ * <li>has the same text as this object</li>
+ * <li>has the same beginning, ending, and current index as this object.</li>
+ * </ul>
+ *
+ * @param obj The object to test for equality against.
+ *
+ * @return <code>true</code> if the specified object is equal to this
+ * object, <code>false</code> otherwise.
+ */
+ public boolean equals (Object obj)
+ {
+ if (! (obj instanceof StringCharacterIterator))
+ return false;
+
+ StringCharacterIterator sci = (StringCharacterIterator) obj;
+
+ return (begin == sci.begin
+ && end == sci.end
+ && index == sci.index
+ && text.equals (sci.text));
+ }
+
+ /*************************************************************************/
+
+ /**
+ * This method allows other classes in java.text to change the value
+ * of the underlying text being iterated through.
+ *
+ * @param text The new <code>String</code> to iterate through.
+ */
+ public void setText (String text)
+ {
+ this.text = text;
+ this.begin = 0;
+ this.end = text.length ();
+ this.index = 0;
+ }
+}
diff --git a/libjava/classpath/java/text/class-dependencies.conf b/libjava/classpath/java/text/class-dependencies.conf
new file mode 100644
index 00000000000..011b146ce10
--- /dev/null
+++ b/libjava/classpath/java/text/class-dependencies.conf
@@ -0,0 +1,220 @@
+# This property file contains dependencies of classes, methods, and
+# field on other methods or classes.
+#
+# Syntax:
+#
+# <used>: <needed 1> [... <needed N>]
+#
+# means that when <used> is included, <needed 1> (... <needed N>) must
+# be included as well.
+#
+# <needed X> and <used> are of the form
+#
+# <class.methodOrField(signature)>
+#
+# or just
+#
+# <class>
+#
+# Within dependencies, variables can be used. A variable is defined as
+# follows:
+#
+# {variable}: value1 value2 ... value<n>
+#
+# variables can be used on the right side of dependencies as follows:
+#
+# <used>: com.bla.blu.{variable}.Class.m()V
+#
+# The use of the variable will expand to <n> dependencies of the form
+#
+# <used>: com.bla.blu.value1.Class.m()V
+# <used>: com.bla.blu.value2.Class.m()V
+# ...
+# <used>: com.bla.blu.value<n>.Class.m()V
+#
+# Variables can be redefined when building a system to select the
+# required support for features like encodings, protocols, etc.
+#
+# Hints:
+#
+# - For methods and fields, the signature is mandatory. For
+# specification, please see the Java Virtual Machine Specification by
+# SUN. Unlike in the spec, field signatures (types) are in brackets.
+#
+# - Package names must be separated by '/' (and not '.'). E.g.,
+# java/lang/Class (this is necessary, because the '.' is used to
+# separate method or field names from classes)
+#
+# - In case <needed> refers to a class, only the class itself will be
+# included in the resulting binary, NOT necessarily all its methods
+# and fields. If you want to refer to all methods and fields, you can
+# write class.* as an abbreviation.
+#
+# - Abbreviations for packages are also possible: my/package/* means all
+# methods and fields of all classes in my/package.
+#
+# - A line with a trailing '\' continues in the next line.
+
+# end of file
+
+# All locales supported are loaded via classes from java.text (see below)
+# from class gnu/java/locale/LocaleInformation_<locale_id>
+#
+# This introduces a dependency for all locales. To allow an easy selection
+# and addition of locales, the library variable {text_locales} can be set to
+# the set of supported locales.
+#
+
+{text_locales}: \
+ af_ZA \
+ ar_AE \
+ ar_BH \
+ ar_DZ \
+ ar_EG \
+ ar_IN \
+ ar_IQ \
+ ar_JO \
+ ar_KW \
+ ar_LB \
+ ar_LY \
+ ar_MA \
+ ar_OM \
+ ar_QA \
+ ar_SD \
+ ar_SY \
+ ar_TN \
+ ar_YE \
+ be_BY \
+ bn_IN \
+ br_FR \
+ bs_BA \
+ ca_ES \
+ cs_CZ \
+ cy_GB \
+ da_DK \
+ de \
+ de_AT \
+ de_BE \
+ de_CH \
+ de_DE \
+ de_LU \
+ el_GR \
+ en \
+ en_AU \
+ en_BW \
+ en_CA \
+ en_DK \
+ en_GB \
+ en_HK \
+ en_IE \
+ en_IN \
+ en_NZ \
+ en_PH \
+ en_SG \
+ en_US \
+ en_ZA \
+ en_ZW \
+ es_AR \
+ es_BO \
+ es_CL \
+ es_CO \
+ es_CR \
+ es_DO \
+ es_EC \
+ es_ES \
+ es_GT \
+ es_HN \
+ es_MX \
+ es_NI \
+ es_PA \
+ es_PE \
+ es_PR \
+ es_PY \
+ es_SV \
+ es_US \
+ es_UY \
+ es_VE \
+ et_EE \
+ eu_ES \
+ fa_IR \
+ fi_FI \
+ fo_FO \
+ fr_BE \
+ fr_CA \
+ fr_CH \
+ fr_FR \
+ fr_LU \
+ ga_IE \
+ gd_GB \
+ gl_ES \
+ gv_GB \
+ he_IL \
+ hi_IN \
+ hr_HR \
+ hu_HU \
+ id_ID \
+ it_CH \
+ it_IT \
+ iw_IL \
+ ja_JP \
+ ka_GE \
+ kl_GL \
+ ko_KR \
+ kw_GB \
+ lt_LT \
+ lv_LV \
+ mi_NZ \
+ mk_MK \
+ mr_IN \
+ mt_MT \
+ nl \
+ nl_BE \
+ nl_NL \
+ nn_NO \
+ no_NO \
+ oc_FR \
+ pl_PL \
+ pt_BR \
+ pt_PT \
+ ro_RO \
+ ru_RU \
+ ru_UA \
+ se_NO \
+ sk_SK \
+ sl_SI \
+ sq_AL \
+ sr_YU \
+ sv_FI \
+ sv_SE \
+ ta_IN \
+ te_IN \
+ tg_TJ \
+ tl_PH \
+ tr_TR \
+ uk_UA \
+ ur_PK \
+ uz_UZ \
+ vi_VN \
+ yi_US \
+ zh_CN \
+ zh_HK \
+ zh_SG \
+ zh_TW
+
+java/text/Collator.getInstance(Ljava/util/Locale;)Ljava/text/Collator;: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
+
+java/text/DateFormatSymbols.<init>(Ljava/util/Locale;)V: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
+
+java/text/DecimalFormatSymbols.<init>(Ljava/util/Locale;)V: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
+
+java/text/BreakIterator.getInstance(Ljava/lang/String;Ljava/util/Locale;)Ljava/text/BreakIterator;: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
+
+java/text/NumberFormat.computeInstance(Ljava/util/Locale;Ljava/lang/String;Ljava/lang/String;)Ljava/text/NumberFormat;: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
+
+java/text/DateFormat.computeInstance(IILjava/util/Locale;ZZ)Ljava/text/DateFormat;: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
diff --git a/libjava/classpath/java/text/package.html b/libjava/classpath/java/text/package.html
new file mode 100644
index 00000000000..3c2e22ba5e4
--- /dev/null
+++ b/libjava/classpath/java/text/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in java.text package.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. -->
+
+<html>
+<head><title>GNU Classpath - java.text</title></head>
+
+<body>
+<p>Classes to iterate over strings and to format texts according to a
+specific locale.</p>
+
+</body>
+</html>
OpenPOWER on IntegriCloud