From: Warren Levy Date: Fri, 27 Oct 2000 10:33:46 +0000 (+0000) Subject: Makefile.am: Added locale files from Classpath. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=df98a50bb05465d9ad789c039281975230a27188;p=gcc.git Makefile.am: Added locale files from Classpath. * Makefile.am: Added locale files from Classpath. * Makefile.in: Rebuilt. * gnu/java/locale/Calendar.java: New file. * gnu/java/locale/Calendar_de.java: New file. * gnu/java/locale/Calendar_en.java: New file. * gnu/java/locale/Calendar_nl.java: New file. * java/lang/ClassNotFoundException.java: Replaced with Classpath file. * java/math/BigDecimal.java (intVal): Renamed from 'num' for serialization compatibility. (scale): Made private. (serialVersionUID): New field. * java/math/BigInteger.java (ival): Made transient. (words): Made transient. (bitCount): New serialization field. (bitLength): Ditto. (firstNonzeroByteNum): Ditto. (lowestSetBit): Ditto. (magnitude): Ditto. (signum): Ditto. (serialVersionUID): New field. (readObject): New method. (writeObject): New method. * java/util/BitSet.java (serialVersionUID): New field. * java/util/Calendar.java: Replaced with Classpath file. * java/util/GregorianCalendar.java (GregorianCalendar): Pass result of getDefault() for TimeZone or Locale instead of passing nulls. * java/util/Locale.java (serialVersionUID): New field. (writeObject): New method. (readObject): New method. * java/util/SimpleTimeZone.java: Replaced with Classpath file. Serialization mods. From-SVN: r37080 --- diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 207d957e68e..c8c398689c1 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,36 @@ +2000-10-27 Warren Levy + + * Makefile.am: Added locale files from Classpath. + * Makefile.in: Rebuilt. + * gnu/java/locale/Calendar.java: New file. + * gnu/java/locale/Calendar_de.java: New file. + * gnu/java/locale/Calendar_en.java: New file. + * gnu/java/locale/Calendar_nl.java: New file. + * java/lang/ClassNotFoundException.java: Replaced with Classpath file. + * java/math/BigDecimal.java (intVal): Renamed from 'num' for + serialization compatibility. + (scale): Made private. + (serialVersionUID): New field. + * java/math/BigInteger.java (ival): Made transient. + (words): Made transient. + (bitCount): New serialization field. + (bitLength): Ditto. + (firstNonzeroByteNum): Ditto. + (lowestSetBit): Ditto. + (magnitude): Ditto. + (signum): Ditto. + (serialVersionUID): New field. + (readObject): New method. + (writeObject): New method. + * java/util/BitSet.java (serialVersionUID): New field. + * java/util/Calendar.java: Replaced with Classpath file. + * java/util/GregorianCalendar.java (GregorianCalendar): Pass result + of getDefault() for TimeZone or Locale instead of passing nulls. + * java/util/Locale.java (serialVersionUID): New field. + (writeObject): New method. + (readObject): New method. + * java/util/SimpleTimeZone.java: Replaced with Classpath file. + 2000-10-25 Bryce McKinlay * Makefile.am (GCJCOMPILE): Pass --tag=GCJ to libtool. diff --git a/libjava/Makefile.am b/libjava/Makefile.am index f51bafe3ab5..998968c93f2 100644 --- a/libjava/Makefile.am +++ b/libjava/Makefile.am @@ -998,6 +998,10 @@ gnu/java/io/ObjectIdentityWrapper.java \ gnu/java/lang/ArrayHelper.java \ gnu/java/lang/ClassHelper.java \ gnu/java/lang/reflect/TypeSignature.java \ +gnu/java/locale/Calendar.java \ +gnu/java/locale/Calendar_de.java \ +gnu/java/locale/Calendar_en.java \ +gnu/java/locale/Calendar_nl.java \ gnu/java/security/provider/Gnu.java \ gnu/java/security/provider/SHA.java \ gnu/java/security/provider/SHA1PRNG.java \ diff --git a/libjava/Makefile.in b/libjava/Makefile.in index 192acf90668..0422a1371f4 100644 --- a/libjava/Makefile.in +++ b/libjava/Makefile.in @@ -742,6 +742,10 @@ gnu/java/io/ObjectIdentityWrapper.java \ gnu/java/lang/ArrayHelper.java \ gnu/java/lang/ClassHelper.java \ gnu/java/lang/reflect/TypeSignature.java \ +gnu/java/locale/Calendar.java \ +gnu/java/locale/Calendar_de.java \ +gnu/java/locale/Calendar_en.java \ +gnu/java/locale/Calendar_nl.java \ gnu/java/security/provider/Gnu.java \ gnu/java/security/provider/SHA.java \ gnu/java/security/provider/SHA1PRNG.java \ @@ -1256,6 +1260,8 @@ DEP_FILES = .deps/$(srcdir)/$(CONVERT_DIR)/gen-from-JIS.P \ .deps/gnu/java/io/ObjectIdentityWrapper.P \ .deps/gnu/java/lang/ArrayHelper.P .deps/gnu/java/lang/ClassHelper.P \ .deps/gnu/java/lang/reflect/TypeSignature.P \ +.deps/gnu/java/locale/Calendar.P .deps/gnu/java/locale/Calendar_de.P \ +.deps/gnu/java/locale/Calendar_en.P .deps/gnu/java/locale/Calendar_nl.P \ .deps/gnu/java/security/provider/Gnu.P \ .deps/gnu/java/security/provider/SHA.P \ .deps/gnu/java/security/provider/SHA1PRNG.P .deps/interpret.P \ diff --git a/libjava/gnu/java/locale/Calendar.java b/libjava/gnu/java/locale/Calendar.java new file mode 100644 index 00000000000..70da30bbe7b --- /dev/null +++ b/libjava/gnu/java/locale/Calendar.java @@ -0,0 +1,112 @@ +/* Calendar.java -- Default Calendar locale data + Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package gnu.java.locale; + +import java.util.ListResourceBundle; +import java.util.Locale; +import java.util.Date; + +/** + * This class contains locale data for java.util.Calendar.
+ * + * If you localize this to another language only set fields, that + * you change. See Calendar_de for an example. Also add your + * locale to this list of availableLocales below in this(!) file. + * + * @author Jochen Hoenicke + */ +public class Calendar extends ListResourceBundle +{ + /** + * The locales for which Calendar is localized. + */ + private static final Locale[] availableLocales = { + Locale.GERMAN, Locale.ENGLISH, new Locale("nl", "") + }; + + /** + * This is the default calendar class, that is returned on + * java.util.Calendar.getInstance(). + * @see java.util.Calendar#getInstance() + */ + private static final String calendarClass = "java.util.GregorianCalendar"; + + /** + * This is used by java.util.Calendar. + * @see java.util.Calendar#getFirstDayOfWeek() + */ + private static final Integer firstDayOfWeek + = new Integer(java.util.Calendar.SUNDAY); + /** + * This is used by java.util.Calendar. + * @see java.util.Calendar#getMinimalDaysInFirstWeek() + */ + private static final Integer minimalDaysInFirstWeek = new Integer(1); + + /** + * The point at which the Gregorian calendar rules were used. + * The default for most catholic + * countries is midnight (UTC) on October 5, 1582 (Julian), + * or October 15, 1582 (Gregorian). + * @see java.util.GregorianCalendar#getGregorianCutOver + */ + /* If you change this date be aware, that this formular does only + * work for months from MARCH to DECEMBER and doesn't work in + * leap years (look in java.util.GregorianCalendar.getDayOfYear for + * more info). + */ + private static final Date gregorianCutOver = new Date + ((24*60*60*1000L) * + (((1582*(365*4+1))/4 + + (java.util.Calendar.OCTOBER*(31+30+31+30+31) - 9) / 5 + 5) - + ((1970*(365*4+1))/4 + 1 - 13))); + + /** + * This is the object array used to hold the keys and values + * for this bundle + */ + private static final Object[][] contents = + { + { "availableLocales", availableLocales }, + { "calendarClass", calendarClass }, + { "firstDayOfWeek", firstDayOfWeek }, + { "minimalDaysInFirstWeek", minimalDaysInFirstWeek }, + { "gregorianCutOver", gregorianCutOver } + }; + + /** + * This method returns the object array of key, value pairs containing + * the data for this bundle. + * + * @return The key, value information. + */ + public Object[][] getContents() + { + return(contents); + } +} diff --git a/libjava/gnu/java/locale/Calendar_de.java b/libjava/gnu/java/locale/Calendar_de.java new file mode 100644 index 00000000000..45ed5b77d64 --- /dev/null +++ b/libjava/gnu/java/locale/Calendar_de.java @@ -0,0 +1,60 @@ +/* Calendar_de.java -- German calendar locale data + Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package gnu.java.locale; + +import java.util.ListResourceBundle; +import java.util.Calendar; + +/** + * This class contains locale data for java.util.Calendar specific for + * german language. + * @author Jochen Hoenicke + */ +public class Calendar_de extends ListResourceBundle +{ + /** + * This is the object array used to hold the keys and values + * for this bundle + */ + private static final Object[][] contents = + { + { "firstDayOfWeek", new Integer(Calendar.MONDAY) }, + { "minimalDaysInFirstWeek", new Integer(4) }, + }; + + /** + * This method returns the object array of key, value pairs containing + * the data for this bundle. + * + * @return The key, value information. + */ + public Object[][] getContents() + { + return contents; + } +} diff --git a/libjava/gnu/java/locale/Calendar_en.java b/libjava/gnu/java/locale/Calendar_en.java new file mode 100644 index 00000000000..7bd0512e980 --- /dev/null +++ b/libjava/gnu/java/locale/Calendar_en.java @@ -0,0 +1,59 @@ +/* Calendar_en.java -- English calendar locale data + Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package gnu.java.locale; + +import java.util.ListResourceBundle; +import java.util.Calendar; + +/** + * This class contains locale data for java.util.Calendar specific for + * english language. + * @author Jochen Hoenicke + */ +public class Calendar_en extends ListResourceBundle +{ + /** + * This is the object array used to hold the keys and values + * for this bundle + */ + private static final Object[][] contents = + { + /* Use default values. */ + }; + + /** + * This method returns the object array of key, value pairs containing + * the data for this bundle. + * + * @return The key, value information. + */ + public Object[][] getContents() + { + return contents; + } +} diff --git a/libjava/gnu/java/locale/Calendar_nl.java b/libjava/gnu/java/locale/Calendar_nl.java new file mode 100644 index 00000000000..1c46012a289 --- /dev/null +++ b/libjava/gnu/java/locale/Calendar_nl.java @@ -0,0 +1,63 @@ +/* Calendar_nl.java -- Dutch calendar locale data + Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package gnu.java.locale; + +import java.util.ListResourceBundle; +import java.util.Calendar; + +/** + * This class contains locale data for java.util.Calendar specific for + * dutch language. + * @author Mark Wielaard + */ +public class Calendar_nl extends ListResourceBundle +{ + /** + * This is the object array used to hold the keys and values + * for this bundle + */ + private static final Object[][] contents = + { + { "firstDayOfWeek", new Integer(Calendar.MONDAY) }, + + /* XXX - I guess the default for gregorianCutover + * is also true for the Netherlands. But is it? + */ + }; + + /** + * This method returns the object array of key, value pairs containing + * the data for this bundle. + * + * @return The key, value information. + */ + public Object[][] getContents() + { + return contents; + } +} diff --git a/libjava/java/lang/ClassNotFoundException.java b/libjava/java/lang/ClassNotFoundException.java index 7a187e83b9f..3ce89ea6596 100644 --- a/libjava/java/lang/ClassNotFoundException.java +++ b/libjava/java/lang/ClassNotFoundException.java @@ -1,61 +1,164 @@ -/* Copyright (C) 1998, 1999, 2000 Free Software Foundation +/* ClassNotFoundException.java -- exception thrown when attempting to load + a class when no definition for the class can be found. + Copyright (C) 1998 Free Software Foundation, Inc. - This file is part of libgcj. +This file is part of GNU Classpath. -This software is copyrighted work licensed under the terms of the -Libgcj License. Please consult the file "LIBGCJ_LICENSE" for -details. */ +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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + package java.lang; - + +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; + /** - * @author Warren Levy - * @date September 18, 1998. + * Exceptions may be thrown by one part of a Java program and caught + * by another in order to deal with exceptional conditions. This + * exception can by thrown by specific methods of ClassLoader + * and Class when attempting to load a class when no definition + * for the specified class can be found. + * + * @since JDK 1.0 + * + * @author Brian Jones */ -/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 - * "The Java Language Specification", ISBN 0-201-63451-1 - * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. - * Status: Believed complete and correct. - */ - public class ClassNotFoundException extends Exception { - // TODO12: - // Throwable ex; + static final long serialVersionUID = 9176873029745254542L; + private Throwable ex = null; + + /** + * Create an exception without a message. + */ public ClassNotFoundException() - { - super(); - } - - // TODO12: - // public ClassNotFoundException(String msg, Throwable ex) - // { - // FIXME: Set 'ex' here. - // } - - public ClassNotFoundException(String msg) - { - super(msg); - } - - // TODO12: - // public Throwable getException() - // { - // } - - // TBD: if this needs to be implemented - // public void printStackTrace() - // { - // } - - // TBD: if this needs to be implemented - // public void printStackTrace(PrintStream ps) - // { - // } - - // TBD: if this needs to be implemented - // public void printStackTrace(PrintWriter pw) - // { - // } + { + super(); + } + + /** + * Create an exception with a message. + */ + public ClassNotFoundException(String s) + { + super(s); + } + + /** + * Create an exception with a message and include the exception + * which occurred while loading the class. + * + * @param ex the exception which occurred while loading the class + * + * @since JDK 1.2 + */ + public ClassNotFoundException(String s, Throwable ex) + { + super(s); + this.ex = ex; + } + + /** + * Returns the exception which occurred while loading the class, + * otherwise returns null. + * + * @since JDK 1.2 + */ + public Throwable getException() + { + return ex; + } + + /** + * Print a stack trace of the exception that occurred. + */ + public void printStackTrace() + { + if (ex == null) + { + super.printStackTrace(); + } + else + { + ex.printStackTrace(); + } + } + + /** + * Print a stack trace of the exception that occurred to + * the specified PrintStream. + */ + public void printStackTrace(PrintStream ps) + { + if (ex == null) + { + super.printStackTrace(ps); + } + else + { + ex.printStackTrace(ps); + } + } + + /** + * Print a stack trace of the exception that occurred to + * the specified PrintWriter. + */ + public void printStackTrace(PrintWriter pw) + { + if (ex == null) + { + super.printStackTrace(pw); + } + else + { + ex.printStackTrace(pw); + } + } + + /** + * Serialize the object in a manner binary compatible with the JDK 1.2 + */ + private void writeObject(java.io.ObjectOutputStream s) + throws IOException + { + ObjectOutputStream.PutField oFields; + oFields = s.putFields(); + oFields.put("ex", this.ex); + s.writeFields(); + } + + /** + * Deserialize the object in a manner binary compatible with the JDK 1.2 + */ + private void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException + { + ObjectInputStream.GetField oFields; + oFields = s.readFields(); + ex = (Throwable)oFields.get("ex", (Throwable)null); + } } diff --git a/libjava/java/math/BigDecimal.java b/libjava/java/math/BigDecimal.java index 30384ec1719..6844b24961a 100644 --- a/libjava/java/math/BigDecimal.java +++ b/libjava/java/math/BigDecimal.java @@ -29,8 +29,9 @@ package java.math; import java.math.BigInteger; public class BigDecimal extends Number implements Comparable { - BigInteger num; - int scale; + private BigInteger intVal; + private int scale; + private static final long serialVersionUID = 6108874887143696463L; private final static BigDecimal ZERO = new BigDecimal (BigInteger.valueOf (0), 0); @@ -56,7 +57,7 @@ public class BigDecimal extends Number implements Comparable { { if (scale < 0) throw new NumberFormatException ("scale of " + scale + " is < 0"); - this.num = num; + this.intVal = num; this.scale = scale; } @@ -68,7 +69,7 @@ public class BigDecimal extends Number implements Comparable { public BigDecimal (String num) throws NumberFormatException { int point = num.indexOf('.'); - this.num = new BigInteger (point == -1 ? num : + this.intVal = new BigInteger (point == -1 ? num : num.substring (0, point) + num.substring (point + 1)); scale = num.length() - (point == -1 ? num.length () : point + 1); @@ -99,8 +100,8 @@ public class BigDecimal extends Number implements Comparable { // For addition, need to line up decimals. Note that the movePointRight // method cannot be used for this as it might return a BigDecimal with // scale == 0 instead of the scale we need. - BigInteger op1 = num; - BigInteger op2 = val.num; + BigInteger op1 = intVal; + BigInteger op2 = val.intVal; if (scale < val.scale) op1 = op1.multiply (BigInteger.valueOf (10).pow (val.scale - scale)); else if (scale > val.scale) @@ -116,7 +117,7 @@ public class BigDecimal extends Number implements Comparable { public BigDecimal multiply (BigDecimal val) { - return new BigDecimal (num.multiply (val.num), scale + val.scale); + return new BigDecimal (intVal.multiply (val.intVal), scale + val.scale); } public BigDecimal divide (BigDecimal val, int roundingMode) @@ -135,13 +136,13 @@ public class BigDecimal extends Number implements Comparable { if (scale < 0) throw new ArithmeticException ("scale is negative: " + scale); - if (num.signum () == 0) // handle special case of 0.0/0.0 + if (intVal.signum () == 0) // handle special case of 0.0/0.0 return ZERO; - BigInteger dividend = num.multiply (BigInteger.valueOf (10).pow + BigInteger dividend = intVal.multiply (BigInteger.valueOf (10).pow (newScale + 1 - (scale - val.scale))); - BigInteger parts[] = dividend.divideAndRemainder (val.num); + BigInteger parts[] = dividend.divideAndRemainder (val.intVal); // System.out.println("int: " + parts[0]); // System.out.println("rem: " + parts[1]); @@ -194,12 +195,12 @@ public class BigDecimal extends Number implements Comparable { public int compareTo (BigDecimal val) { if (scale == val.scale) - return num.compareTo (val.num); + return intVal.compareTo (val.intVal); BigInteger thisParts[] = - num.divideAndRemainder (BigInteger.valueOf (10).pow (scale)); + intVal.divideAndRemainder (BigInteger.valueOf (10).pow (scale)); BigInteger valParts[] = - val.num.divideAndRemainder (BigInteger.valueOf (10).pow (val.scale)); + val.intVal.divideAndRemainder (BigInteger.valueOf (10).pow (val.scale)); int compare; if ((compare = thisParts[0].compareTo (valParts[0])) != 0) @@ -263,7 +264,7 @@ public class BigDecimal extends Number implements Comparable { public BigDecimal movePointLeft (int n) { - return (n < 0) ? movePointRight (-n) : new BigDecimal (num, scale + n); + return (n < 0) ? movePointRight (-n) : new BigDecimal (intVal, scale + n); } public BigDecimal movePointRight (int n) @@ -272,15 +273,15 @@ public class BigDecimal extends Number implements Comparable { return movePointLeft (-n); if (scale >= n) - return new BigDecimal (num, scale - n); + return new BigDecimal (intVal, scale - n); - return new BigDecimal (num.multiply + return new BigDecimal (intVal.multiply (BigInteger.valueOf (10).pow (n - scale)), 0); } public int signum () { - return num.signum (); + return intVal.signum (); } public int scale () @@ -290,17 +291,17 @@ public class BigDecimal extends Number implements Comparable { public BigDecimal abs () { - return new BigDecimal (num.abs (), scale); + return new BigDecimal (intVal.abs (), scale); } public BigDecimal negate () { - return new BigDecimal (num.negate (), scale); + return new BigDecimal (intVal.negate (), scale); } public String toString () { - String bigStr = num.toString(); + String bigStr = intVal.toString(); if (scale == 0) return bigStr; @@ -322,7 +323,8 @@ public class BigDecimal extends Number implements Comparable { public BigInteger toBigInteger () { - return scale == 0 ? num : num.divide (BigInteger.valueOf (10).pow (scale)); + return scale == 0 ? intVal : + intVal.divide (BigInteger.valueOf (10).pow (scale)); } diff --git a/libjava/java/math/BigInteger.java b/libjava/java/math/BigInteger.java index 738680a42c4..ed1f4f044cb 100644 --- a/libjava/java/math/BigInteger.java +++ b/libjava/java/math/BigInteger.java @@ -11,6 +11,9 @@ details. */ package java.math; import gnu.gcj.math.*; import java.util.Random; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.IOException; /** * @author Warren Levy @@ -35,8 +38,17 @@ public class BigInteger extends Number implements Comparable * If words == null, the ival is the value of this BigInteger. * Otherwise, the first ival elements of words make the value * of this BigInteger, stored in little-endian order, 2's-complement form. */ - private int ival; - private int[] words; + transient private int ival; + transient private int[] words; + + // Serialization fields. + private int bitCount = -1; + private int bitLength = -1; + private int firstNonzeroByteNum = -2; + private int lowestSetBit = -2; + private byte[] magnitude; + private int signum; + private static final long serialVersionUID = -8287574255936472291L; /** We pre-allocate integers in the range minFixNum..maxFixNum. */ @@ -2201,4 +2213,22 @@ public class BigInteger extends Number implements Comparable } return isNegative() ? x_len * 32 - i : i; } + + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + words = byteArrayToIntArray(magnitude, signum < 0 ? -1 : 0); + BigInteger result = make(words, words.length); + this.ival = result.ival; + this.words = result.words; + } + + private void writeObject(ObjectOutputStream s) + throws IOException, ClassNotFoundException + { + signum = signum(); + magnitude = toByteArray(); + s.defaultWriteObject(); + } } diff --git a/libjava/java/util/BitSet.java b/libjava/java/util/BitSet.java index 56d89b116b6..b40da61f3d6 100644 --- a/libjava/java/util/BitSet.java +++ b/libjava/java/util/BitSet.java @@ -174,4 +174,5 @@ public final class BitSet implements Cloneable, Serializable // The actual bits. private long[] bits; + private static final long serialVersionUID = 7997698588986878753L; } diff --git a/libjava/java/util/Calendar.java b/libjava/java/util/Calendar.java index b5eaa4d39d6..f31577a9dcf 100644 --- a/libjava/java/util/Calendar.java +++ b/libjava/java/util/Calendar.java @@ -1,274 +1,978 @@ -/* Copyright (C) 1998, 1999, 2000 Free Software Foundation +/* java.util.Calendar + Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. - This file is part of libgcj. +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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ -This software is copyrighted work licensed under the terms of the -Libgcj License. Please consult the file "LIBGCJ_LICENSE" for -details. */ package java.util; +import java.lang.reflect.InvocationTargetException; +import java.io.*; /** - * @author Per Bothner - * @date October 24, 1998. + * This class is an abstract base class for Calendars, which can be + * used to convert between Date objects and a set of + * integer fields which represent YEAR, + * MONTH, DAY, etc. The Date + * object represents a time in milliseconds since the Epoch.
+ * + * This class is locale sensitive. To get the Object matching the + * current locale you can use getInstance. You can even provide + * a locale or a timezone. getInstance returns currently + * a GregorianCalendar for the current date.
+ * + * If you want to convert a date from the Year, Month, Day, DayOfWeek, + * etc. Representation to a Date-Object, you can create + * a new Calendar with getInstance(), + * clear() all fields, set(int,int) the + * fields you need and convert it with getTime().
+ * + * If you want to convert a Date-object to the Calendar + * representation, create a new Calendar, assign the + * Date-Object with setTime(), and read the + * fields with get(int).
+ * + * When computing the date from time fields, it may happen, that there + * are either two few fields set, or some fields are inconsistent. This + * cases will handled in a calender specific way. Missing fields are + * replaced by the fields of the epoch: 1970 January 1 00:00.
+ * + * To understand, how the day of year is computed out of the fields + * look at the following table. It is traversed from top to bottom, + * and for the first line all fields are set, that line is used to + * compute the day.
+ * + *
+ * month + day_of_month
+ * month + week_of_month + day_of_week
+ * month + day_of_week_of_month + day_of_week
+ * day_of_year
+ * day_of_week + week_of_year
+ * 
+ * + * The hour_of_day-field takes precedence over the ampm and + * hour_of_ampm fields.
+ * + * Note: This can differ for non-Gregorian calendar.
+ * + * To convert a calendar to a human readable form and vice versa, use + * the java.text.DateFormat class.
+ * + * Other useful things you can do with an calendar, is + * rolling fields (that means increase/decrease a + * specific field by one, propagating overflows), or + * adding/substracting a fixed amount to a field. + * + * @see Date + * @see GregorianCalendar + * @see TimeZone + * @see java.text.DateFormat */ - -/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3, - * and "The Java Language Specification", ISBN 0-201-63451-1. - * Status: Unimplemented: getAvailableLocales. - * No Locale knowledge. - */ - -public abstract class Calendar implements java.io.Serializable, Cloneable +public abstract class Calendar implements Serializable, Cloneable { - public final static int JANUARY = 0; - public final static int FEBRUARY = 1; - public final static int MARCH = 2; - public final static int APRIL = 3; - public final static int MAY = 4; - public final static int JUNE = 5; - public final static int JULY = 6; - public final static int AUGUST = 7; - public final static int SEPTEMBER = 8; - public final static int OCTOBER = 9; - public final static int NOVEMBER = 10; - public final static int DECEMBER = 11; - public final static int UNDECIMBER = 12; - - public final static int SUNDAY = 1; - public final static int MONDAY = 2; - public final static int TUESDAY = 3; - public final static int WEDNESDAY = 4; - public final static int THURSDAY = 5; - public final static int FRIDAY = 6; - public final static int SATURDAY = 7; - - public final static int AM = 0; - public final static int PM = 1; - - public final static int FIELD_COUNT = 17; - - // These constants are not docuemnted, but were determined using - // a simple test program. - public final static int ERA = 0; - public final static int YEAR = 1; - public final static int MONTH = 2; - public final static int WEEK_OF_YEAR = 3; - public final static int WEEK_OF_MONTH = 4; - public final static int DATE = 5; - public final static int DAY_OF_MONTH = 5; - public final static int DAY_OF_YEAR = 6; - public final static int DAY_OF_WEEK = 7; - public final static int DAY_OF_WEEK_IN_MONTH = 8; - public final static int AM_PM = 9; - public final static int HOUR = 10; - public final static int HOUR_OF_DAY = 11; - public final static int MINUTE = 12; - public final static int SECOND = 13; - public final static int MILLISECOND = 14; - public final static int ZONE_OFFSET = 15; - public final static int DST_OFFSET = 16; - - // The fields are as specified in Sun's "Serialized Form" - // in the JDK 1.2 beta 4 API specification. - protected boolean areFieldsSet; - protected int[] fields; - private int firstDayOfWeek; - protected boolean[] isSet; + /** + * Constant representing the era time field. + */ + public static final int ERA = 0; + /** + * Constant representing the year time field. + */ + public static final int YEAR = 1; + /** + * Constant representing the month time field. This field + * should contain one of the JANUARY,...,DECEMBER constants below. + */ + public static final int MONTH = 2; + /** + * Constant representing the week of the year field. + * @see #setFirstDayOfWeek(int) + */ + public static final int WEEK_OF_YEAR = 3; + /** + * Constant representing the week of the month time field. + * @see #setFirstDayOfWeek(int) + */ + public static final int WEEK_OF_MONTH = 4; + /** + * Constant representing the day time field, synonym for DAY_OF_MONTH. + */ + public static final int DATE = 5; + /** + * Constant representing the day time field. + */ + public static final int DAY_OF_MONTH = 5; + /** + * Constant representing the day of year time field. This is + * 1 for the first day in month. + */ + public static final int DAY_OF_YEAR = 6; + /** + * Constant representing the day of week time field. This field + * should contain one of the SUNDAY,...,SATURDAY constants below. + */ + public static final int DAY_OF_WEEK = 7; + /** + * Constant representing the day-of-week-in-month field. For + * instance this field contains 2 for the second thursday in a + * month. If you give a negative number here, the day will count + * from the end of the month. + */ + public static final int DAY_OF_WEEK_IN_MONTH = 8; + /** + * Constant representing the part of the day for 12-hour clock. This + * should be one of AM or PM. + */ + public static final int AM_PM = 9; + /** + * Constant representing the hour time field for 12-hour clock. + */ + public static final int HOUR = 10; + /** + * Constant representing the hour of day time field for 24-hour clock. + */ + public static final int HOUR_OF_DAY = 11; + /** + * Constant representing the minute of hour time field. + */ + public static final int MINUTE = 12; + /** + * Constant representing the second time field. + */ + public static final int SECOND = 13; + /** + * Constant representing the millisecond time field. + */ + public static final int MILLISECOND = 14; + /** + * Constant representing the time zone offset time field for the + * time given in the other fields. It is measured in + * milliseconds. The default is the offset of the time zone. + */ + public static final int ZONE_OFFSET = 15; + /** + * Constant representing the daylight saving time offset in + * milliseconds. The default is the value given by the time zone. + */ + public static final int DST_OFFSET = 16; + /** + * Number of time fields. + */ + public static final int FIELD_COUNT = 17; + + /** + * Constant representing Sunday. + */ + public static final int SUNDAY = 1; + /** + * Constant representing Monday. + */ + public static final int MONDAY = 2; + /** + * Constant representing Tuesday. + */ + public static final int TUESDAY = 3; + /** + * Constant representing Wednesday. + */ + public static final int WEDNESDAY = 4; + /** + * Constant representing Thursday. + */ + public static final int THURSDAY = 5; + /** + * Constant representing Friday. + */ + public static final int FRIDAY = 6; + /** + * Constant representing Saturday. + */ + public static final int SATURDAY = 7; + + /** + * Constant representing January. + */ + public static final int JANUARY = 0; + /** + * Constant representing February. + */ + public static final int FEBRUARY = 1; + /** + * Constant representing March. + */ + public static final int MARCH = 2; + /** + * Constant representing April. + */ + public static final int APRIL = 3; + /** + * Constant representing May. + */ + public static final int MAY = 4; + /** + * Constant representing June. + */ + public static final int JUNE = 5; + /** + * Constant representing July. + */ + public static final int JULY = 6; + /** + * Constant representing August. + */ + public static final int AUGUST = 7; + /** + * Constant representing September. + */ + public static final int SEPTEMBER = 8; + /** + * Constant representing October. + */ + public static final int OCTOBER = 9; + /** + * Constant representing November. + */ + public static final int NOVEMBER = 10; + /** + * Constant representing December. + */ + public static final int DECEMBER = 11; + /** + * Constant representing Undecimber. This is an artifical name useful + * for lunar calendars. + */ + public static final int UNDECIMBER = 12; + + /** + * Useful constant for 12-hour clock. + */ + public static final int AM = 0; + /** + * Useful constant for 12-hour clock. + */ + public static final int PM = 1; + + /** + * The time fields. The array is indexed by the constants YEAR to + * DST_OFFSET. + * @serial + */ + protected int[] fields = new int[FIELD_COUNT]; + /** + * The flags which tell if the fields above have a value. + * @serial + */ + protected boolean[] isSet = new boolean[FIELD_COUNT]; + /** + * The time in milliseconds since the epoch. + * @serial + */ + protected long time; + /** + * Tells if the above field has a valid value. + * @serial + */ protected boolean isTimeSet; + /** + * Tells if the fields have a valid value. This superseeds the isSet + * array. + * @serial + */ + protected boolean areFieldsSet; + + /** + * The time zone of this calendar. Used by sub classes to do UTC / local + * time conversion. Sub classes can access this field with getTimeZone(). + * @serial + */ + private TimeZone zone; + + /** + * Specifies if the date/time interpretation should be lenient. + * If the flag is set, a date such as "February 30, 1996" will be + * treated as the 29th day after the February 1. If this flag + * is false, such dates will cause an exception. + * @serial + */ private boolean lenient; + + /** + * Sets what the first day of week is. This is used for + * WEEK_OF_MONTH and WEEK_OF_YEAR fields. + * @serial + */ + private int firstDayOfWeek; + + /** + * Sets how many days are required in the first week of the year. + * If the first day of the year should be the first week you should + * set this value to 1. If the first week must be a full week, set + * it to 7. + * @serial + */ private int minimalDaysInFirstWeek; - private int nextStamp; - //private int serialVersionOnStream; - protected long time; - private TimeZone zone; - protected Calendar () + /** + * The version of the serialized data on the stream. + *
0 or not present
+ *
JDK 1.1.5 or later.
+ *
1
+ *
JDK 1.1.6 or later. This always writes a correct `time' value + * on the stream, as well as the other fields, to be compatible with + * earlier versions
+ * @since JDK1.1.6 + * @serial + */ + private int serialVersionOnStream = 1; + + /** + * XXX - I have not checked the compatibility. The documentation of + * the serialized-form is quite hairy... + */ + static final long serialVersionUID = -1807547505821590642L; + + /** + * The name of the resource bundle. + */ + private static final String bundleName = "gnu.java.locale.Calendar"; + + /** + * Constructs a new Calender with the default time zone and the default + * locale. + */ + protected Calendar() { - this (null, null); + this(TimeZone.getDefault(), Locale.getDefault()); } - protected Calendar (TimeZone zone, Locale loc) + /** + * Constructs a new Calender with the given time zone and the given + * locale. + * @param zone a time zone. + * @param locale a locale. + */ + protected Calendar(TimeZone zone, Locale locale) { - fields = new int[FIELD_COUNT]; - isSet = new boolean[FIELD_COUNT]; - firstDayOfWeek = SUNDAY; // Locale-dependent. FIXME. - this.zone = zone != null ? zone : TimeZone.getDefault(); - } + this.zone = zone; + lenient = true; - public Object clone () - { - try - { - return super.clone(); - } - catch (CloneNotSupportedException ex) - { - throw new RuntimeException("internal error - "+ex); - } + ResourceBundle rb = ResourceBundle.getBundle(bundleName, locale); + + firstDayOfWeek = ((Integer) rb.getObject("firstDayOfWeek")).intValue(); + minimalDaysInFirstWeek = + ((Integer) rb.getObject("minimalDaysInFirstWeek")).intValue(); } - public String toString () + /** + * Creates a calendar representing the actual time, using the default + * time zone and locale. + */ + public static synchronized Calendar getInstance() { - // We have much latitude in how we implement this. - return ("areFieldsSet " + areFieldsSet - + "; fields " + fields - + "; firstDayOfWeek " + firstDayOfWeek - + "; isSet " + isSet - + "; isTimeSet " + isTimeSet - + "; lenient " + lenient - + "; minimalDaysInFirstWeek " + minimalDaysInFirstWeek - + "; nextStamp " + nextStamp - + "; time " + time - + "; zone " + zone); + return getInstance(TimeZone.getDefault(), Locale.getDefault()); } - public static Calendar getInstance () + /** + * Creates a calendar representing the actual time, using the given + * time zone and the default locale. + * @param zone a time zone. + */ + public static synchronized Calendar getInstance(TimeZone zone) { - return new GregorianCalendar (); + return getInstance(zone, Locale.getDefault()); } - public static Calendar getInstance (TimeZone zone) + /** + * Creates a calendar representing the actual time, using the default + * time zone and the given locale. + * @param locale a locale. + */ + public static synchronized Calendar getInstance(Locale locale) { - return new GregorianCalendar (zone); + return getInstance(TimeZone.getDefault(), locale); } - public static Calendar getInstance (Locale locale) + /** + * Creates a calendar representing the actual time, using the given + * time zone and locale. + * @param zone a time zone. + * @param locale a locale. + */ + public static synchronized Calendar getInstance(TimeZone zone, Locale locale) { - return new GregorianCalendar (locale); + String calendarClassName = null; + ResourceBundle rb = ResourceBundle.getBundle(bundleName, locale); + calendarClassName = rb.getString("calendarClass"); + if (calendarClassName != null) + { + try + { + Class calendarClass = Class.forName(calendarClassName); + if (Calendar.class.isAssignableFrom(calendarClass)) + { + return (Calendar) calendarClass.getConstructor( + new Class[] { TimeZone.class, Locale.class} + ).newInstance(new Object[] {zone, locale} ); + } + } + catch (ClassNotFoundException ex) {} + catch (IllegalAccessException ex) {} + catch (NoSuchMethodException ex) {} + catch (InstantiationException ex) {} + catch (InvocationTargetException ex) {} + // XXX should we ignore these errors or throw an exception ? + } + return new GregorianCalendar(zone, locale); } - public static Calendar getInstance (TimeZone zone, Locale locale) + /** + * Gets the set of locales for which a Calendar is availiable. + * @exception MissingResourceException if locale data couldn't be found. + * @return the set of locales. + */ + public static synchronized Locale[] getAvailableLocales() { - return new GregorianCalendar (zone, locale); + ResourceBundle rb = ResourceBundle.getBundle(bundleName, + new Locale("", "")); + return (Locale[]) rb.getObject("availableLocales"); } - public boolean isLenient() { return lenient; } - public void setLenient (boolean lenient) { this.lenient = lenient; } + /** + * Converts the time field values (fields) to + * milliseconds since the epoch UTC (time). Override + * this method if you write your own Calendar. */ + protected abstract void computeTime(); - public int getFirstDayOfWeek () + /** + * Converts the milliseconds since the epoch UTC + * (time) to time fields + * (fields). Override this method if you write your + * own Calendar. + */ + protected abstract void computeFields(); + + /** + * Converts the time represented by this object to a + * Date-Object. + * @return the Date. + */ + public final Date getTime() { - return firstDayOfWeek; + if (!isTimeSet) + computeTime(); + return new Date(time); } - public void setFirstDayOfWeek (int value) + /** + * Sets this Calender's time to the given Date. All time fields + * are invalidated by this method. + */ + public final void setTime(Date date) { - firstDayOfWeek = value; + setTimeInMillis(date.getTime()); } - public int getMinimalDaysInFirstWeek () + /** + * Returns the time represented by this Calendar. + * @return the time in milliseconds since the epoch. + */ + protected long getTimeInMillis() { - return minimalDaysInFirstWeek; + if (!isTimeSet) + computeTime(); + return time; } - public void setMinimalDaysInFirstWeek (int value) + /** + * Sets this Calender's time to the given Time. All time fields + * are invalidated by this method. + * @param time the time in milliseconds since the epoch + */ + protected void setTimeInMillis(long time) { - minimalDaysInFirstWeek = value; + this.time = time; + isTimeSet = true; + computeFields(); } - public TimeZone getTimeZone () + /** + * Gets the value of the specified field. They are recomputed + * if they are invalid. + * @param field the time field. One of the time field constants. + * @return the value of the specified field + */ + public final int get(int field) { - return zone; + complete(); + return fields[field]; } - public void setTimeZone (TimeZone tz) + /** + * Gets the value of the specified field. This method doesn't + * recompute the fields, if they are invalid. + * @param field the time field. One of the time field constants. + * @return the value of the specified field, undefined if + * areFieldsSet or isSet[field] is false. + */ + protected final int internalGet(int field) { - zone = tz; + return fields[field]; } - abstract public void add(int fld, int amount); - abstract public void roll (int fld, boolean up); + /** + * Sets the time field with the given value. This does invalidate + * the time in milliseconds. + * @param field the time field. One of the time field constants + * @param value the value to be set. + */ + public final void set(int field, int value) + { + if (!areFieldsSet) + computeFields(); + isTimeSet = false; + fields[field] = value; + isSet[field] = true; + } - public final void set (int year, int month, int date) + /** + * Sets the fields for year, month, and date + * @param year the year. + * @param month the month, one of the constants JANUARY..UNDICEMBER. + * @param date the day of the month + */ + public final void set(int year, int month, int date) { - set(YEAR, year); - set(MONTH, month); - set(DATE, date); + if (!areFieldsSet) + computeFields(); + isTimeSet = false; + fields[YEAR] = year; + fields[MONTH] = month; + fields[DATE] = date; + isSet[YEAR] = isSet[MONTH] = isSet[DATE] = true; } - public final void set (int year, int month, int date, int hour, int minute) + /** + * Sets the fields for year, month, date, hour, and minute + * @param year the year. + * @param month the month, one of the constants JANUARY..UNDICEMBER. + * @param date the day of the month + * @param hour the hour of day. + * @param minute the minute. + */ + public final void set(int year, int month, int date, int hour, int minute) { set(year, month, date); - set(HOUR_OF_DAY, hour); - set(MINUTE, minute); + fields[HOUR] = hour; + fields[MINUTE] = minute; + isSet[HOUR] = isSet[MINUTE] = true; } - public final void set (int year, int month, int date, - int hour, int minute, int second) + /** + * Sets the fields for year, month, date, hour, and minute + * @param year the year. + * @param month the month, one of the constants JANUARY..UNDICEMBER. + * @param date the day of the month + * @param hour the hour of day. + * @param minute the minute. + * @param second the second. + */ + public final void set(int year, int month, int date, + int hour, int minute, int second) { set(year, month, date, hour, minute); - set(SECOND, second); + fields[SECOND] = second; + isSet[SECOND] = true; } - public final void set (int fld, int value) + /** + * Clears the values of all the time fields. + */ + public final void clear() { - if (! areFieldsSet) computeFields(); - fields[fld] = value; isTimeSet = false; + areFieldsSet = false; + for (int i = 0; i < FIELD_COUNT; i++) + isSet[i] = false; } - public final void clear (int fld) + /** + * Clears the values of the specified time field. + * @param field the time field. One of the time field constants. + */ + public final void clear(int field) { - fields[fld] = 0; - isSet[fld] = false; + isTimeSet = false; areFieldsSet = false; + isSet[field] = false; + } + + /** + * Determines if the specified field has a valid value. + * @return true if the specified field has a value. + */ + public final boolean isSet(int field) + { + return isSet[field]; + } + + /** + * Fills any unset fields in the time field list + * @return true if the specified field has a value. + */ + protected void complete() + { + if (!isTimeSet) + computeTime(); + if (!areFieldsSet) + computeFields(); } - public final void clear () + /** + * Compares the given calender with this. + * @param o the object to that we should compare. + * @return true, if the given object is a calendar, that represents + * the same time (but doesn't neccessary have the same fields). + */ + public boolean equals(Object o) { - for (int fld = FIELD_COUNT; --fld >= 0; ) + return (o instanceof Calendar) + && getTimeInMillis() == ((Calendar) o).getTimeInMillis(); + } + + /** + * Returns a hash code for this calendar. + * @return a hash code, which fullfits the general contract of + * hashCode() + */ + public int hashCode() + { + long time = getTimeInMillis(); + return (int) ((time & 0xffffffffL) ^ (time >> 32)); + } + + /** + * Compares the given calender with this. + * @param o the object to that we should compare. + * @return true, if the given object is a calendar, and this calendar + * represents a smaller time than the calender o. + * @exception ClassCastException if o is not an calendar. + * @since JDK1.2 you don't need to override this method + */ + public boolean before(Object o) + { + return getTimeInMillis() < ((Calendar) o).getTimeInMillis(); + } + + /** + * Compares the given calender with this. + * @param o the object to that we should compare. + * @return true, if the given object is a calendar, and this calendar + * represents a bigger time than the calender o. + * @exception ClassCastException if o is not an calendar. + * @since JDK1.2 you don't need to override this method + */ + public boolean after(Object o) + { + return getTimeInMillis() > ((Calendar) o).getTimeInMillis(); + } + + /** + * Adds the specified amount of time to the given time field. The + * amount may be negative to subtract the time. If the field overflows + * it does what you expect: Jan, 25 + 10 Days is Feb, 4. + * @param field the time field. One of the time field constants. + * @param amount the amount of time. + */ + public abstract void add(int field, int amount); + + /** + * Rolls the specified time field up or down. This means add one + * to the specified field, but don't change the other fields. If + * the maximum for this field is reached, start over with the + * minimum value.
+ * + * Note: There may be situation, where the other + * fields must be changed, e.g rolling the month on May, 31. + * The date June, 31 is automatically converted to July, 1. + * @param field the time field. One of the time field constants. + * @param up the direction, true for up, false for down. + */ + public abstract void roll(int field, boolean up); + + /** + * Rolls up or down the specified time field by the given amount. + * A negative amount rolls down. The default implementation is + * call roll(int, boolean) for the specified amount. + * + * Subclasses should override this method to do more intuitiv things. + * + * @param field the time field. One of the time field constants. + * @param amount the amount to roll by, positive for rolling up, + * negative for rolling down. + * @since JDK1.2 + */ + public void roll(int field, int amount) + { + while (amount > 0) { - fields[fld] = 0; - isSet[fld] = false; + roll(field, true); + amount--; + } + while (amount < 0) + { + roll(field, false); + amount++; } - areFieldsSet = false; } - protected void complete() + + /** + * Sets the time zone to the specified value. + * @param zone the new time zone + */ + public void setTimeZone(TimeZone zone) { - if (!isTimeSet) computeTime(); - if (!areFieldsSet) computeFields(); + this.zone = zone; } - protected abstract void computeFields(); - protected abstract void computeTime(); + /** + * Gets the time zone of this calendar + * @return the current time zone. + */ + public TimeZone getTimeZone() + { + return zone; + } - protected final int internalGet (int fld) { return fields[fld]; } + /** + * Specifies if the date/time interpretation should be lenient. + * If the flag is set, a date such as "February 30, 1996" will be + * treated as the 29th day after the February 1. If this flag + * is false, such dates will cause an exception. + * @param lenient true, if the date should be interpreted linient, + * false if it should be interpreted strict. + */ + public void setLenient(boolean lenient) + { + this.lenient = lenient; + } - public final int get(int fld) + /** + * Tells if the date/time interpretation is lenient. + * @return true, if the date should be interpreted linient, + * false if it should be interpreted strict. + */ + public boolean isLenient() { - complete(); - return fields[fld]; + return lenient; } - public abstract boolean after (Object cal); - public abstract boolean before (Object cal); - public abstract boolean equals (Object obj); + /** + * Sets what the first day of week is. This is used for + * WEEK_OF_MONTH and WEEK_OF_YEAR fields. + * @param value the first day of week. One of SUNDAY to SATURDAY. + */ + public void setFirstDayOfWeek(int value) + { + firstDayOfWeek = value; + } - protected long getTimeInMillis() + /** + * Gets what the first day of week is. This is used for + * WEEK_OF_MONTH and WEEK_OF_YEAR fields. + * @return the first day of week. One of SUNDAY to SATURDAY. + */ + public int getFirstDayOfWeek() { - if (!isTimeSet) computeTime(); - return time; + return firstDayOfWeek; } - public final Date getTime() { return new Date(getTimeInMillis()); } + /** + * Sets how many days are required in the first week of the year. + * If the first day of the year should be the first week you should + * set this value to 1. If the first week must be a full week, set + * it to 7. + * @param value the minimal days required in the first week. + */ + public void setMinimalDaysInFirstWeek(int value) + { + minimalDaysInFirstWeek = value; + } - public final void setTime (Date date) + /** + * Gets how many days are required in the first week of the year. + * @return the minimal days required in the first week. + * @see #setMinimalDaysInFirstWeek + */ + public int getMinimalDaysInFirstWeek() { - setTimeInMillis(date.getTime()); + return minimalDaysInFirstWeek; } - protected void setTimeInMillis (long millis) + /** + * Gets the smallest value that is allowed for the specified field. + * @param field the time field. One of the time field constants. + * @return the smallest value. + */ + public abstract int getMinimum(int field); + + /** + * Gets the biggest value that is allowed for the specified field. + * @param field the time field. One of the time field constants. + * @return the biggest value. + */ + public abstract int getMaximum(int field); + + + /** + * Gets the greatest minimum value that is allowed for the specified field. + * @param field the time field. One of the time field constants. + * @return the greatest minimum value. + */ + public abstract int getGreatestMinimum(int field); + + /** + * Gets the smallest maximum value that is allowed for the + * specified field. For example this is 28 for DAY_OF_MONTH. + * @param field the time field. One of the time field constants. + * @return the least maximum value. + */ + public abstract int getLeastMaximum(int field); + + /** + * Gets the actual minimum value that is allowed for the specified field. + * This value is dependant on the values of the other fields. + * @param field the time field. One of the time field constants. + * @return the actual minimum value. + * @since jdk1.2 + */ + // FIXME: XXX: Not abstract in JDK 1.2. + // public abstract int getActualMinimum(int field); + + /** + * Gets the actual maximum value that is allowed for the specified field. + * This value is dependant on the values of the other fields. + * @param field the time field. One of the time field constants. + * @return the actual maximum value. + * @since jdk1.2 + */ + // FIXME: XXX: Not abstract in JDK 1.2. + // public abstract int getActualMaximum(int field); + + /** + * Return a clone of this object. + */ + public Object clone() { - time = millis; - isTimeSet = true; - clear(); + try + { + Calendar cal = (Calendar) super.clone(); + cal.fields = (int[]) fields.clone(); + cal.isSet = (boolean[])isSet.clone(); + return cal; + } + catch (CloneNotSupportedException ex) + { + return null; + } } - abstract public int getMaximum(int fld); - abstract public int getMinimum(int fld); - abstract public int getGreatestMinimum(int fld); - abstract public int getLeastMaximum(int fld); + private final static String[] fieldNames = { + ",ERA=", ",YEAR=", ",MONTH=", + ",WEEK_OF_YEAR=", ",WEEK_OF_MONTH=", + ",DAY_OF_MONTH=", ",DAY_OF_YEAR=", ",DAY_OF_WEEK=", + ",DAY_OF_WEEK_IN_MONTH=", + ",AM_PM=", ",HOUR=", ",HOUR_OF_DAY=", + ",MINUTE=", ",SECOND=", ",MILLISECOND=", + ",ZONE_OFFSET=", ",DST_OFFSET=" + }; + + + /** + * Returns a string representation of this object. It is mainly + * for debugging purposes and its content is implementation + * specific. + */ + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append(getClass().getName()).append('['); + sb.append("time="); + if (isTimeSet) + sb.append(time); + else + sb.append("?"); + sb.append(",zone=" + zone); + sb.append(",areFieldsSet=" + areFieldsSet); + for (int i = 0; i < FIELD_COUNT; i++) + { + sb.append(fieldNames[i]); + if (isSet[i]) + sb.append(fields[i]); + else + sb.append("?"); + } + sb.append(",lenient=").append(lenient); + sb.append(",firstDayOfWeek=").append(firstDayOfWeek); + sb.append(",minimalDaysInFirstWeek=").append(minimalDaysInFirstWeek); + sb.append("]"); + return sb.toString(); + } - public final boolean isSet(int fld) { return isSet[fld]; } + /** + * Saves the state of the object to the stream. Ideally we would + * only write the time field, but we need to be compatible with + * earlier versions.
+ * + * This doesn't write the JDK1.1 field nextStamp to the stream, as + * I don't know what it is good for, and because the documentation + * says, that it could be omitted. */ + private void writeObject(ObjectOutputStream stream) throws IOException + { + if (!isTimeSet) + computeTime(); + stream.defaultWriteObject(); + } + + /** + * Reads the object back from stream (deserialization). + */ + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException + { + stream.defaultReadObject(); + if (!isTimeSet) + computeTime(); + + if (serialVersionOnStream > 1) + { + // This is my interpretation of the serial number: + // Sun wants to remove all fields from the stream someday + // and will then increase the serialVersion number again. + // We prepare to be compatible. + + fields = new int[FIELD_COUNT]; + isSet = new boolean[FIELD_COUNT]; + areFieldsSet = false; + } + } } diff --git a/libjava/java/util/GregorianCalendar.java b/libjava/java/util/GregorianCalendar.java index 9e73131c768..bef7828d56c 100644 --- a/libjava/java/util/GregorianCalendar.java +++ b/libjava/java/util/GregorianCalendar.java @@ -1,4 +1,4 @@ -/* Copyright (C) 1998, 1999 Free Software Foundation +/* Copyright (C) 1998, 1999, 2000 Free Software Foundation This file is part of libgcj. @@ -93,17 +93,17 @@ public class GregorianCalendar extends Calendar { public GregorianCalendar () { - this(null, null); + this(TimeZone.getDefault (), Locale.getDefault ()); } public GregorianCalendar (TimeZone zone) { - this (zone, null); + this (zone, Locale.getDefault ()); } public GregorianCalendar (Locale locale) { - this (null, locale); + this (TimeZone.getDefault (), locale); } public GregorianCalendar (TimeZone zone, Locale locale) diff --git a/libjava/java/util/Locale.java b/libjava/java/util/Locale.java index d2dc2f75917..c9e1e2cb0f5 100644 --- a/libjava/java/util/Locale.java +++ b/libjava/java/util/Locale.java @@ -26,6 +26,7 @@ public final class Locale implements java.io.Serializable, Cloneable private String language; private String variant; private static Locale defaultLocale; + private static final long serialVersionUID = 9149081749638150636L; // These are as specified in the JDK 1.2 AP documentation @@ -145,4 +146,28 @@ public final class Locale implements java.io.Serializable, Cloneable } return result.toString(); } + + /** + * @serialdata According to jdk1.2 the hashcode should always be + * written as -1; + */ + private void writeObject(java.io.ObjectOutputStream output) + throws java.io.IOException + { + int tmpHashcode = hashcode; + hashcode = -1; + output.defaultWriteObject(); + hashcode = tmpHashcode; + } + + /** + * @serialdata According to jdk1.2 the hashCode is always invalid + * and must be recomputed. + */ + private void readObject(java.io.ObjectInputStream input) + throws java.io.IOException, ClassNotFoundException + { + input.defaultReadObject(); + hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); + } } diff --git a/libjava/java/util/SimpleTimeZone.java b/libjava/java/util/SimpleTimeZone.java index 64f9c505a50..4fc05e4c19c 100644 --- a/libjava/java/util/SimpleTimeZone.java +++ b/libjava/java/util/SimpleTimeZone.java @@ -1,208 +1,830 @@ -/* Copyright (C) 1998, 1999, 2000 Free Software Foundation +/* java.util.SimpleTimeZone + Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. - This file is part of libgcj. +This file is part of GNU Classpath. -This software is copyrighted work licensed under the terms of the -Libgcj License. Please consult the file "LIBGCJ_LICENSE" for -details. */ +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. -package java.util; - -/** - * @author Per Bothner - * @date October 24, 1998. - */ +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. -/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3. - * Status: Does not know how to figure out if daylight savings time - * is in effect; hence only correct for zones without DST. - * No known spec for hashCode. - */ +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. -public class SimpleTimeZone extends TimeZone -{ - // The fields are as specified in Sun's "Serialized Form" - // in the JDK 1.2 beta 4 API specification. +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ - int dstSavings = 60 * 60 * 1000; - int rawOffset; +package java.util; - // int serialVersionOnStream; +import java.text.DateFormatSymbols; - int startDay; - int startDayOfWeek; - int startMode; /// Seems to be JDK 1.2 only. +/** + * This class represents a simple time zone offset and handles + * daylight savings. It can only handle one daylight savings rule, so + * it can't represent historical changes. + * + * This object is tightly bound to the Gregorian calendar. It assumes + * a regular seven days week, and the month lengths are that of the + * Gregorian Calendar. It can only handle daylight savings for years + * lying in the AD era. + * + * @see Calendar + * @see GregorianCalender + * @author Jochen Hoenicke */ +public class SimpleTimeZone extends TimeZone +{ + /** + * The raw time zone offset in milliseconds to GMT, ignoring + * daylight savings. + * @serial + */ + private int rawOffset; + + /** + * True, if this timezone uses daylight savings, false otherwise. + * @serial + */ + private boolean useDaylight; + + /** + * The daylight savings offset. This is a positive offset in + * milliseconds with respect to standard time. Typically this + * is one hour, but for some time zones this may be half an our. + * @serial + * @since JDK1.1.4 + */ + private int dstSavings = 60 * 60 * 1000; + + /** + * The first year, in which daylight savings rules applies. + * @serial + */ + private int startYear; + + private static final int DOM_MODE = 1; + private static final int DOW_IN_MONTH_MODE = 2; + private static final int DOW_GE_DOM_MODE = 3; + private static final int DOW_LE_DOM_MODE = 4; + /** + * The mode of the start rule. This takes one of the following values: + *
+ *
DOM_MODE (1)
+ *
startDay contains the day in month of the start date, + * startDayOfWeek is unused.
+ *
DOW_IN_MONTH_MODE (2)
+ *
The startDay gives the day of week in month, and + * startDayOfWeek the day of week. For example startDay=2 and + * startDayOfWeek=Calender.SUNDAY specifies that the change is on + * the second sunday in that month. You must make sure, that this + * day always exists (ie. don't specify the 5th sunday). + *
+ *
DOW_GE_DOM_MODE (3)
+ *
The start is on the first startDayOfWeek on or after + * startDay. For example startDay=13 and + * startDayOfWeek=Calendar.FRIDAY specifies that the daylight + * savings start on the first FRIDAY on or after the 13th of that + * Month. Make sure that the change is always in the given month, or + * the result is undefined. + *
+ *
DOW_LE_DOM_MONTH (4)
+ *
The start is on the first startDayOfWeek on or before the + * startDay. Make sure that the change is always in the given + * month, or the result is undefined. +
+ *
+ * @serial */ + private int startMode; + + /** + * The month in which daylight savings start. This is one of the + * constants Calendar.JANUARY, ..., Calendar.DECEMBER. + * @serial + */ + private int startMonth; + + /** + * This variable can have different meanings. See startMode for details + * @see #startMode; + * @serial + */ + private int startDay; + + /** + * This variable specifies the day of week the change takes place. If + * startMode == DOM_MODE, this is undefined. + * @serial + * @see #startMode; + */ + private int startDayOfWeek; + + /** + * This variable specifies the time of change to daylight savings. + * This time is given in milliseconds after midnight local + * standard time. + * @serial + */ + private int startTime; + + /** + * The month in which daylight savings ends. This is one of the + * constants Calendar.JANUARY, ..., Calendar.DECEMBER. + * @serial + */ + private int endMonth; + + /** + * This variable gives the mode for the end of daylight savings rule. + * It can take the same values as startMode. + * @serial + * @see #startMode + */ + private int endMode; + + /** + * This variable can have different meanings. See startMode for details + * @serial + * @see #startMode; + */ + private int endDay; + + /** + * This variable specifies the day of week the change takes place. If + * endMode == DOM_MODE, this is undefined. + * @serial + * @see #startMode; + */ + private int endDayOfWeek; + + /** + * This variable specifies the time of change back to standard time. + * This time is given in milliseconds after midnight local + * standard time. + * @serial + */ + private int endTime; + + /** + * This variable points to a deprecated array from JDK 1.1. It is + * ignored in JDK 1.2 but streamed out for compatibility with JDK 1.1. + * The array contains the lengths of the months in the year and is + * assigned from a private static final field to avoid allocating + * the array for every instance of the object. + * Note that static final fields are not serialized. + * @serial + */ + private byte[] monthLength = monthArr; + private static final byte[] monthArr = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + /** + * The version of the serialized data on the stream. + *
+ *
0 or not present on stream
+ *
JDK 1.1.3 or earlier, only provides this fields: + * rawOffset, startDay, startDayOfWeek, startMonth, startTime, + * startYear, endDay, endDayOfWeek, endMonth, endTime + *
+ *
JDK 1.1.4 or later. This includes three new fields, namely + * startMode, endMode and dstSavings. And there is a optional section + * as described in writeObject. + *
+ * + * XXX - JDK 1.2 Beta 4 docu states 1.1.4, but my 1.1.5 has the old + * version. + * + * When streaming out this class it is always written in the latest + * version. + * @serial + * @since JDK1.1.4 + */ + private int serialVersionOnStream = 1; + + private static final long serialVersionUID = -403250971215465050L; + + /** + * Create a SimpleTimeZone with the given time offset + * from GMT and without daylight savings. + * @param rawOffset the time offset from GMT in milliseconds. + * @param id The identifier of this time zone. + */ + public SimpleTimeZone(int rawOffset, String id) + { + this.rawOffset = rawOffset; + setID(id); + useDaylight = false; + startYear = 0; + } - int startMonth; + /** + * Create a SimpleTimeZone with the given time offset + * from GMT and with daylight savings. The start/end parameters + * can have different meaning (replace WEEKDAY with a real day of + * week). Only the first two meanings were supported by earlier + * versions of jdk. + * + *
+ *
day > 0, dayOfWeek = Calendar.WEEKDAY
+ *
The start/end of daylight savings is on the day-th + * WEEKDAY in the given month.
+ *
day < 0, dayOfWeek = Calendar.WEEKDAY
+ *
The start/end of daylight savings is on the -day-th + * WEEKDAY counted from the end of the month.
+ *
day > 0, dayOfWeek = 0
+ *
The start/end of daylight is on the day-th day of + * the month.
+ *
day > 0, dayOfWeek = -Calendar.WEEKDAY
+ *
The start/end of daylight is on the first WEEKDAY on or after + * the day-th day of the month. You must make sure that + * this day lies in the same month.
+ *
day < 0, dayOfWeek = -Calendar.WEEKDAY
+ *
The start/end of daylight is on the first WEEKDAY on or + * before the -day-th day of the month. You + * must make sure that this day lies in the same month.
+ *
+ * + * If you give a non existing month, a day that is zero, or too big, + * or a dayOfWeek that is too big, the result is undefined. + * + * The start rule must have a different month than the end rule. + * This restriction shouldn't hurt for all possible time zones. + * + * @param rawOffset The time offset from GMT in milliseconds. + * @param id The identifier of this time zone. + * @param startMonth The start month of daylight savings; use the + * constants in Calendar. + * @param startday A day in month or a day of week number, as + * described above. + * @param startDayOfWeek The start rule day of week; see above. + * @param startTime A time in millis in standard time. + * @param endMonth The end month of daylight savings; use the + * constants in Calendar. + * @param endday A day in month or a day of week number, as + * described above. + * @param endDayOfWeek The end rule day of week; see above. + * @param endTime A time in millis in standard time. */ + public SimpleTimeZone(int rawOffset, String id, + int startMonth, int startDayOfWeekInMonth, + int startDayOfWeek, int startTime, + int endMonth, int endDayOfWeekInMonth, + int endDayOfWeek, int endTime) + { + this.rawOffset = rawOffset; + setID(id); + useDaylight = true; + + setStartRule(startMonth, startDayOfWeekInMonth, + startDayOfWeek, startTime); + setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); + if (startMonth == endMonth) + throw new IllegalArgumentException + ("startMonth and endMonth must be different"); + this.startYear = 0; + } - int startTime; + /** + * This constructs a new SimpleTimeZone that supports a daylight savings + * rule. The parameter are the same as for the constructor above, except + * there is the additional dstSavaings parameter. + * + * @param dstSavings the amount of savings for daylight savings + * time in milliseconds. This must be positive. + */ + public SimpleTimeZone(int rawOffset, String id, + int startMonth, int startDayOfWeekInMonth, + int startDayOfWeek, int startTime, + int endMonth, int endDayOfWeekInMonth, + int endDayOfWeek, int endTime, int dstSavings) + { + this(rawOffset, id, + startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime, + endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); - int startYear; + this.dstSavings = dstSavings; + } - int endDay; + /** + * Sets the first year, where daylight savings applies. The daylight + * savings rule never apply for years in the BC era. Note that this + * is gregorian calendar specific. + * @param year the start year. + */ + public void setStartYear(int year) + { + startYear = year; + useDaylight = true; + } - int endDayOfWeek; + /** + * Checks if the month, day, dayOfWeek arguments are in range and + * returns the mode of the rule. + * @param month the month parameter as in the constructor + * @param day the day parameter as in the constructor + * @param dayOfWeek the day of week parameter as in the constructor + * @return the mode of this rule see startMode. + * @exception IllegalArgumentException if parameters are out of range. + * @see #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int) + * @see #startMode + */ + private int checkRule(int month, int day, int dayOfWeek) + { + int daysInMonth = getDaysInMonth(month, 1); + if (dayOfWeek == 0) + { + if (day <= 0 || day > daysInMonth) + throw new IllegalArgumentException("day out of range"); + return DOM_MODE; + } + else if (dayOfWeek > 0) + { + if (Math.abs(day) > (daysInMonth + 6) / 7) + throw new IllegalArgumentException("dayOfWeekInMonth out of range"); + if (dayOfWeek > Calendar.SATURDAY) + throw new IllegalArgumentException("dayOfWeek out of range"); + return DOW_IN_MONTH_MODE; + } + else + { + if (day == 0 || Math.abs(day) > daysInMonth) + throw new IllegalArgumentException("day out of range"); + if (dayOfWeek < -Calendar.SATURDAY) + throw new IllegalArgumentException("dayOfWeek out of range"); + if (day < 0) + return DOW_LE_DOM_MODE; + else + return DOW_GE_DOM_MODE; + } + } - int endMode; // Seems to be JDK 1.2 only. - int endMonth; + /** + * Sets the daylight savings start rule. You must also set the + * end rule with setEndRule or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * @param month The month where daylight savings start, zero + * based. You should use the constants in Calendar. + * @param day A day of month or day of week in month. + * @param dayOfWeek The day of week where daylight savings start. + * @param time The time in milliseconds standard time where daylight + * savings start. + * @see SimpleTimeZone */ + public void setStartRule(int month, int day, int dayOfWeek, int time) + { + this.startMode = checkRule(month, day, dayOfWeek); + this.startMonth = month; + // FIXME: XXX: JDK 1.2 allows negative values and has 2 new variations + // of this method. + this.startDay = Math.abs(day); + this.startDayOfWeek = Math.abs(dayOfWeek); + this.startTime = time; + useDaylight = true; + } - int endTime; + /** + * Sets the daylight savings end rule. You must also set the + * start rule with setStartRule or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * @param rawOffset The time offset from GMT. + * @param id The identifier of this time zone. + * @param Month The end month of daylight savings. + * @param day A day in month, or a day of week in month. + * @param DayOfWeek A day of week, when daylight savings ends. + * @param Time A time in millis in standard time. + * @see #setStartRule */ + public void setEndRule(int month, int day, int dayOfWeek, int time) + { + this.endMode = checkRule(month, day, dayOfWeek); + this.endMonth = month; + // FIXME: XXX: JDK 1.2 allows negative values and has 2 new variations + // of this method. + this.endDay = Math.abs(day); + this.endDayOfWeek = Math.abs(dayOfWeek); + this.endTime = time; + useDaylight = true; + } - // byte[] monthLength; + /** + * Gets the time zone offset, for current date, modified in case of + * daylight savings. This is the offset to add to UTC to get the local + * time. + * + * In the standard JDK the results given by this method may result in + * inaccurate results at the end of February or the beginning of March. + * To avoid this, you should use Calendar instead: + *
+   * offset = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET);
+   * 
+ * You could also use in + * + * This version doesn't suffer this inaccuracy. + * + * @param era the era of the given date + * @param year the year of the given date + * @param month the month of the given date, 0 for January. + * @param day the day of month + * @param dayOfWeek the day of week; this must be matching the + * other fields. + * @param millis the millis in the day (in local standard time) + * @return the time zone offset in milliseconds. */ + public int getOffset(int era, int year, int month, + int day, int dayOfWeek, int millis) + { + // This method is called by Calendar, so we mustn't use that class. + int daylightSavings = 0; + if (useDaylight && era == GregorianCalendar.AD && year >= startYear) + { + // This does only work for Gregorian calendars :-( + // This is mainly because setStartYear doesn't take an era. + + boolean afterStart = !isBefore(year, month, day, dayOfWeek, millis, + startMode, startMonth, + startDay, startDayOfWeek, startTime); + boolean beforeEnd = isBefore(year, month, day, dayOfWeek, millis, + endMode, endMonth, + endDay, endDayOfWeek, endTime); + + if (startMonth < endMonth) + { + // use daylight savings, if the date is after the start of + // savings, and before the end of savings. + daylightSavings = afterStart && beforeEnd ? dstSavings : 0; + } + else + { + // use daylight savings, if the date is before the end of + // savings, or after the start of savings. + daylightSavings = beforeEnd || afterStart ? dstSavings : 0; + } + } + return rawOffset + daylightSavings; + } - boolean useDaylight; + /** + * Returns the time zone offset to GMT in milliseconds, ignoring + * day light savings. + * @return the time zone offset. */ + public int getRawOffset() + { + return rawOffset; + } - public SimpleTimeZone (int rawOffset, String ID) + /** + * Sets the standard time zone offset to GMT. + * @param rawOffset The time offset from GMT in milliseconds. + */ + public void setRawOffset(int rawOffset) { - setID(ID); this.rawOffset = rawOffset; } - public SimpleTimeZone (int rawOffset, String ID, - int startMonth, int startDay, - int startDayOfWeek, int startTime, - int endMonth, int endDay, - int endDayOfWeek, int endTime) + /** + * Gets the daylight savings offset. This is a positive offset in + * milliseconds with respect to standard time. Typically this + * is one hour, but for some time zones this may be half an our. + * @return the daylight savings offset in milliseconds. + * @since JDK1.1.4? + */ + public int getDSTSavings() { - this(rawOffset, ID); - setStartRule (startMonth, startDay, startDayOfWeek, startTime); - setEndRule (endMonth, endDay, endDayOfWeek, endTime); + return dstSavings; } - public int getRawOffset() { return rawOffset; } - public void setRawOffset (int offsetMillis) { rawOffset = offsetMillis; } + /** + * Returns if this time zone uses daylight savings time. + * @return true, if we use daylight savings time, false otherwise. + */ + public boolean useDaylightTime() + { + return useDaylight; + } - public int getOffset (int era, int year, int month, int day, - int dayOfWeek, int millis) + /** + * Returns the number of days in the given month. It does always + * use the Gregorian leap year rule. + * @param month The month, zero based; use one of the Calendar constants. + * @param year The year. + */ + private int getDaysInMonth(int month, int year) { - int offset = getRawOffset(); - if (useDaylight) + // Most of this is copied from GregorianCalendar.getActualMaximum() + if (month == Calendar.FEBRUARY) { - if (startYear != 0 - && (year < startYear || era == GregorianCalendar.BC)) - return offset; - boolean midYearSummer = startMonth < endMonth; - if (midYearSummer ? (month < startMonth || month > endMonth) - : (month < startMonth && month > endMonth)) - return offset; // Definitely not DST. - if (midYearSummer ? (month > startMonth && month < endMonth) - : (month > startMonth || month < endMonth)) - return offset + dstSavings; // Definitely DST. - // Now it gets more complicated. Bail for now. - throw new Error("not implemented - SimpleTimeZone.getOffset"); + return ((year & 3) == 0 && (year % 100 != 0 || year % 400 == 0)) + ? 29 : 28; } - return offset; + else if (month < Calendar.AUGUST) + return 31 - (month & 1); + else + return 30 + (month & 1); } - public boolean useDaylightTime() { return useDaylight; } - - public boolean inDaylightTime(Date date) + /** + * Checks if the date given in calXXXX, is before the change between + * dst and standard time. + * @param calYear the year of the date to check (for leap day cheking). + * @param calMonth the month of the date to check. + * @param calDay the day of month of the date to check. + * @param calDayOfWeek the day of week of the date to check. + * @param calMillis the millis of day of the date to check (standard time). + * @param mode the change mode; same semantic as startMode. + * @param month the change month; same semantic as startMonth. + * @param day the change day; same semantic as startDay. + * @param dayOfWeek the change day of week; + * @param millis the change time in millis since midnight standard time. + * same semantic as startDayOfWeek. + * @return true, if cal is before the change, false if cal is on + * or after the change. + */ + private boolean isBefore(int calYear, + int calMonth, int calDayOfMonth, int calDayOfWeek, + int calMillis, int mode, int month, + int day, int dayOfWeek, int millis) { - if (! useDaylight) - return false; - throw new Error("not implemented - SimpleTimeZone.inDaylightTime"); - } - public int getDSTSavings () { return dstSavings; } + // This method is called by Calendar, so we mustn't use that class. + // We have to do all calculations by hand. + + // check the months: - public void setDSTSavings (int millisSavedDuringDST) - { dstSavings = millisSavedDuringDST; } + // XXX - this is not correct: + // for the DOW_GE_DOM and DOW_LE_DOM modes the change date may + // be in a different month. + if (calMonth != month) + return calMonth < month; - public void setStartRule (int month, int dayOfWeekInMonth, - int dayOfWeek, int time) + // check the day: + switch (mode) + { + case DOM_MODE: + if (calDayOfMonth != day) + return calDayOfMonth < day; + break; + case DOW_IN_MONTH_MODE: + { + // This computes the day of month of the day of type + // "dayOfWeek" that lies in the same (sunday based) week as cal. + calDayOfMonth += (dayOfWeek - calDayOfWeek); + + // Now we convert it to 7 based number (to get a one based offset + // after dividing by 7). If we count from the end of the + // month, we get want a -7 based number counting the days from + // the end: + + if (day < 0) + calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7; + else + calDayOfMonth += 6; + + // day > 0 day < 0 + // S M T W T F S S M T W T F S + // 7 8 9 10 11 12 -36-35-34-33-32-31 + // 13 14 15 16 17 18 19 -30-29-28-27-26-25-24 + // 20 21 22 23 24 25 26 -23-22-21-20-19-18-17 + // 27 28 29 30 31 32 33 -16-15-14-13-12-11-10 + // 34 35 36 -9 -8 -7 + + // Now we calculate the day of week in month: + int week = calDayOfMonth / 7; + // day > 0 day < 0 + // S M T W T F S S M T W T F S + // 1 1 1 1 1 1 -5 -5 -4 -4 -4 -4 + // 1 2 2 2 2 2 2 -4 -4 -4 -3 -3 -3 -3 + // 2 3 3 3 3 3 3 -3 -3 -3 -2 -2 -2 -2 + // 3 4 4 4 4 4 4 -2 -2 -2 -1 -1 -1 -1 + // 4 5 5 -1 -1 -1 + + if (week != day) + return week < day; + + if (calDayOfWeek != dayOfWeek) + return calDayOfWeek < dayOfWeek; + + // daylight savings starts/ends on the given day. + break; + } + + case DOW_LE_DOM_MODE: + // The greatest sunday before or equal December, 12 + // is the same as smallest sunday after or equal December, 6. + day -= 6; + + case DOW_GE_DOM_MODE: + + // Calculate the day of month of the day of type + // "dayOfWeek" that lies before (or on) the given date. + calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) + + calDayOfWeek - dayOfWeek; + if (calDayOfMonth < day) + return true; + if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7) + return false; + // now we have the same day + break; + } + // the millis decides: + return (calMillis < millis); + } + + /** + * Determines if the given date is in daylight savings time. + * @return true, if it is in daylight savings time, false otherwise. + */ + public boolean inDaylightTime(Date date) { - this.startMonth = month; - this.startDay = dayOfWeekInMonth; - this.startDayOfWeek = dayOfWeek; - this.startTime = time; - this.useDaylight = true; + Calendar cal = Calendar.getInstance(this); + cal.setTime(date); + return (cal.get(Calendar.DST_OFFSET) != 0); } - public void setEndRule (int month, int dayOfWeekInMonth, - int dayOfWeek, int time) + /** + * Generates the hashCode for the SimpleDateFormat object. It is + * the rawOffset, possibly, if useDaylightSavings is true, xored + * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime. + */ + public synchronized int hashCode() { - this.endMonth = month; - this.endDay = dayOfWeekInMonth; - this.endDayOfWeek = dayOfWeek; - this.endTime = time; - this.useDaylight = true; + return rawOffset ^ + (useDaylight ? + startMonth ^ startDay ^ startDayOfWeek ^ startTime + ^ endMonth ^ endDay ^ endDayOfWeek ^ endTime : 0); } - public void setStartYear (int year) + public synchronized boolean equals(Object o) { - this.startYear = startYear; + if (this == o) + return true; + if (!(o instanceof SimpleTimeZone)) + return false; + SimpleTimeZone zone = (SimpleTimeZone) o; + if (zone.hashCode() != hashCode() + || !getID().equals(zone.getID()) + || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) + return false; + if (!useDaylight) + return true; + return (startYear == zone.startYear + && startMonth == zone.startMonth + && startDay == zone.startDay + && startDayOfWeek == zone.startDayOfWeek + && startTime == zone.startTime + && endMonth == zone.endMonth + && endDay == zone.endDay + && endDayOfWeek == zone.endDayOfWeek + && endTime == zone.endTime); } - public boolean hasSameRules (TimeZone other) + /** + * Test if the other time zone uses the same rule and only + * possibly differs in ID. This implementation for this particular + * class will return true if the other object is a SimpleTimeZone, + * the raw offsets and useDaylight are identical and if useDaylight + * is true, also the start and end datas are identical. + * @return true if this zone uses the same rule. + */ + public boolean hasSameRules(TimeZone other) { if (this == other) return true; - if (! (other instanceof SimpleTimeZone)) - return false; - SimpleTimeZone o = (SimpleTimeZone) other; - if (rawOffset != o.rawOffset) + if (!(other instanceof SimpleTimeZone)) return false; - if (useDaylight != o.useDaylight) + SimpleTimeZone zone = (SimpleTimeZone) other; + if (zone.hashCode() != hashCode() + || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) return false; - if (! useDaylight) + if (!useDaylight) return true; - return startDay == o.startDay - && startDayOfWeek == o.startDayOfWeek - && startMonth == o.startMonth - && startTime == o.startTime - && endDay == o.endDay - && endDayOfWeek == o.endDayOfWeek - && endMonth == o.endMonth - && endTime == o.endTime - && startYear == o.startYear - && startMode == o.startMode - && endMode == o.endMode; - } - - public boolean equals (Object obj) - { - if (! (obj instanceof SimpleTimeZone)) - return false; - SimpleTimeZone other = (SimpleTimeZone) obj; - return getID() == other.getID() && hasSameRules(other); + return (startYear == zone.startYear + && startMonth == zone.startMonth + && startDay == zone.startDay + && startDayOfWeek == zone.startDayOfWeek + && startTime == zone.startTime + && endMonth == zone.endMonth + && endDay == zone.endDay + && endDayOfWeek == zone.endDayOfWeek && endTime == zone.endTime); } - public Object clone () + /** + * Returns a string representation of this SimpleTimeZone object. + * @return a string representation of this SimpleTimeZone object. + */ + public String toString() { - // We know the superclass just call's Object's generic cloner. - return super.clone (); + // the test for useDaylight is an incompatibility to jdk1.2, but + // I think this shouldn't hurt. + return getClass().getName() + "[" + + "id=" + getID() + + ",offset=" + rawOffset + + ",dstSavings=" + dstSavings + + ",useDaylight=" + useDaylight + + (useDaylight ? + ",startYear=" + startYear + + ",startMode=" + startMode + + ",startMonth=" + startMonth + + ",startDay=" + startDay + + ",startDayOfWeek=" + startDayOfWeek + + ",startTime=" + startTime + + ",endMode=" + endMode + + ",endMonth=" + endMonth + + ",endDay=" + endDay + + ",endDayOfWeek=" + endDayOfWeek + + ",endTime=" + endTime : "") + "]"; } - public String toString () + /** + * Reads a serialized simple time zone from stream. + * @see #writeObject + */ + private void readObject(java.io.ObjectInputStream input) + throws java.io.IOException, ClassNotFoundException { - // The docs don't say much about how we might implement this. - // We choose a debugging implementation. - return ("dstSavings " + dstSavings - + "; rawOffset " + rawOffset - + "; startDay " + startDay - + "; startDayOfWeek " + startDayOfWeek - + "; startMode " + startMode - + "; startMonth " + startMonth - + "; startTime " + startTime - + "; startYear " + startYear - + "; endDay " + endDay - + "; endDayOfWeek " + endDayOfWeek - + "; endMode " + endMode - + "; endMonth " + endMonth - + "; endTime " + endTime - + "; useDaylight " + useDaylight); + input.defaultReadObject(); + if (serialVersionOnStream == 0) + { + // initialize the new fields to default values. + dstSavings = 60 * 60 * 1000; + endMode = DOW_IN_MONTH_MODE; + startMode = DOW_IN_MONTH_MODE; + serialVersionOnStream = 1; + } + else + { + int length = input.readInt(); + byte[]byteArray = new byte[length]; + input.read(byteArray, 0, length); + if (length >= 4) + { + // Lets hope that Sun does extensions to the serialized + // form in a sane manner. + startDay = byteArray[0]; + startDayOfWeek = byteArray[1]; + endDay = byteArray[2]; + endDayOfWeek = byteArray[3]; + } + } } - public int hashCode () + /** + * Serializes this object to a stream. @serialdata The object is + * first written in the old JDK 1.1 format, so that it can be read + * by by the old classes. This means, that the + * start/endDay(OfWeek)-Fields are written in the + * DOW_IN_MONTH_MODE rule, since this was the only supported rule + * in 1.1. + * + * In the optional section, we write first the length of an byte + * array as int and afterwards the byte array itself. The byte + * array contains in this release four elements, namely the real + * startDay, startDayOfWeek endDay, endDayOfWeek in that Order. + * These fields are needed, because for compatibility reasons only + * approximative values are written to the required section, as + * described above. + */ + private void writeObject(java.io.ObjectOutputStream output) + throws java.io.IOException { - // FIXME - this does not folow any spec (since none is public)! - int hash = rawOffset; - if (useDaylight) - hash += dstSavings + startYear + startMode + endMode - + startDay + startDayOfWeek + startMonth + startTime - + endDay + endDayOfWeek + endMonth + endTime; - return hash; + byte[] byteArray = new byte[] + { + (byte) startDay, (byte) startDayOfWeek, + (byte) endDay, (byte) endDayOfWeek}; + + /* calculate the approximation for JDK 1.1 */ + switch (startMode) + { + case DOM_MODE: + startDayOfWeek = Calendar.SUNDAY; // random day of week + // fall through + case DOW_GE_DOM_MODE: + case DOW_LE_DOM_MODE: + startDay = (startDay + 6) / 7; + } + switch (endMode) + { + case DOM_MODE: + endDayOfWeek = Calendar.SUNDAY; + // fall through + case DOW_GE_DOM_MODE: + case DOW_LE_DOM_MODE: + endDay = (endDay + 6) / 7; + } + + // the required part: + output.defaultWriteObject(); + // the optional part: + output.writeInt(byteArray.length); + output.write(byteArray, 0, byteArray.length); } }