PatchSet 5707 
Date: 2005/04/19 10:25:03
Author: robilad
Branch: HEAD
Tag: (none) 
Log:
Resynced with GNU Classpath: SimpleDateFormat fixlet

2005-04-19  Dalibor Topic  <[EMAIL PROTECTED]>

        Resynced with GNU Classpath.

        2005-04-10  Sven de Marothy  <[EMAIL PROTECTED]>

        * java/text/SimpleDateFormat.java:
        (formatWithAttribute): Pad year before truncating digits.

Members: 
        ChangeLog:1.3874->1.3875 
        libraries/javalib/java/text/SimpleDateFormat.java:INITIAL->1.51 

Index: kaffe/ChangeLog
diff -u kaffe/ChangeLog:1.3874 kaffe/ChangeLog:1.3875
--- kaffe/ChangeLog:1.3874      Tue Apr 19 10:12:19 2005
+++ kaffe/ChangeLog     Tue Apr 19 10:25:03 2005
@@ -2,6 +2,15 @@
 
         Resynced with GNU Classpath.
 
+       2005-04-10  Sven de Marothy  <[EMAIL PROTECTED]>
+
+        * java/text/SimpleDateFormat.java:
+        (formatWithAttribute): Pad year before truncating digits.
+
+2005-04-19  Dalibor Topic  <[EMAIL PROTECTED]>
+
+        Resynced with GNU Classpath.
+
        2005-04-15  Sven de Marothy  <[EMAIL PROTECTED]>
 
         * gnu/java/nio/charset/EncodingHelper.java: Added method
===================================================================
Checking out kaffe/libraries/javalib/java/text/SimpleDateFormat.java
RCS:  /home/cvs/kaffe/kaffe/libraries/javalib/java/text/SimpleDateFormat.java,v
VERS: 1.51
***************
--- /dev/null   Sun Aug  4 19:57:58 2002
+++ kaffe/libraries/javalib/java/text/SimpleDateFormat.java     Tue Apr 19 
10:32:02 2005
@@ -0,0 +1,1237 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 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,
+     */
+    private int field;
+
+    /**
+     * The size of the character sequence.
+     */
+    private 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;

*** Patch too long, truncated ***

_______________________________________________
kaffe mailing list
[email protected]
http://kaffe.org/cgi-bin/mailman/listinfo/kaffe

Reply via email to