]> granicus.if.org Git - icu/commitdiff
ICU-10635 Preserve DisplayContext when serializing RelativeDateFormat, adjust seriali...
authorPeter Edberg <pedberg@unicode.org>
Tue, 4 Mar 2014 00:41:23 +0000 (00:41 +0000)
committerPeter Edberg <pedberg@unicode.org>
Tue, 4 Mar 2014 00:41:23 +0000 (00:41 +0000)
X-SVN-Rev: 35308

icu4j/main/classes/core/src/com/ibm/icu/impl/LocaleDisplayNamesImpl.java
icu4j/main/classes/core/src/com/ibm/icu/impl/RelativeDateFormat.java
icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java
icu4j/main/classes/core/src/com/ibm/icu/text/RuleBasedNumberFormat.java
icu4j/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatTest.java

index b2c082052fedca9752b07f6bcc5c5fc7e29244fa..785bde3612c7fb5480f53f62a8be98bb8f5ce3e1 100644 (file)
@@ -70,7 +70,7 @@ public class LocaleDisplayNamesImpl extends LocaleDisplayNames {
     /**
      * BreakIterator to use for capitalization
      */
-    private BreakIterator capitalizationBrkIter = null;
+    private transient BreakIterator capitalizationBrkIter = null;
 
 
     public static LocaleDisplayNames getInstance(ULocale locale, DialectHandling dialectHandling) {
@@ -216,11 +216,14 @@ public class LocaleDisplayNamesImpl extends LocaleDisplayNames {
 
     private String adjustForUsageAndContext(CapitalizationContextUsage usage, String name) {
         if (name != null && name.length() > 0 && UCharacter.isLowerCase(name.codePointAt(0)) &&
-              capitalizationBrkIter != null &&
               (capitalization==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
                 (capitalizationUsage != null && capitalizationUsage[usage.ordinal()]) )) {
             // Note, won't have capitalizationUsage != null && capitalizationUsage[usage.ordinal()]
             // unless capitalization is CAPITALIZATION_FOR_UI_LIST_OR_MENU or CAPITALIZATION_FOR_STANDALONE
+            if (capitalizationBrkIter == null) {
+                // should only happen when deserializing, etc.
+                capitalizationBrkIter = BreakIterator.getSentenceInstance(locale);
+            }
             return UCharacter.toTitleCase(locale, name, capitalizationBrkIter,
                             UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
         }
index bb7a730c730fbe910c2329fd314f1b9823adaaef..af5b5eb27ef486c147f0246e132ee62370be3c4d 100644 (file)
@@ -123,10 +123,13 @@ public class RelativeDateFormat extends DateFormat {
                 (fTimePattern == null || fCombinedFormat == null || combinedFormatHasDateAtStart) ) {
             // capitalize relativeDayString according to context for relative, set formatter no context
             if ( relativeDayString.length() > 0 && UCharacter.isLowerCase(relativeDayString.codePointAt(0)) &&
-                 capitalizationBrkIter != null &&
                  (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
                     (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationOfRelativeUnitsForListOrMenu) ||
                     (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationOfRelativeUnitsForStandAlone) )) {
+                if (capitalizationBrkIter == null) {
+                    // should only happen when deserializing, etc.
+                    capitalizationBrkIter = BreakIterator.getSentenceInstance(fLocale);
+                }
                 relativeDayString = UCharacter.toTitleCase(fLocale, relativeDayString, capitalizationBrkIter,
                                 UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
             }
@@ -218,7 +221,7 @@ public class RelativeDateFormat extends DateFormat {
     private boolean capitalizationInfoIsSet = false;
     private boolean capitalizationOfRelativeUnitsForListOrMenu = false;
     private boolean capitalizationOfRelativeUnitsForStandAlone = false;
-    private BreakIterator capitalizationBrkIter = null;
+    private transient BreakIterator capitalizationBrkIter = null;
    
     
     /**
index 19f79048475ef8bd51ef50e73f09653f775309d3..cf555985e6fb1283883e5a26997a1eb25c0554ca 100644 (file)
@@ -5,7 +5,9 @@
 
 package com.ibm.icu.text;
 
+import java.io.IOException;
 import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
 import java.text.FieldPosition;
 import java.text.Format;
 import java.text.ParseException;
@@ -475,11 +477,30 @@ public abstract class DateFormat extends UFormat {
     private EnumSet<BooleanAttribute> booleanAttributes = EnumSet.allOf(BooleanAttribute.class); 
 
     /*
-     *  Capitalization setting, hoisted to DateFormat ICU 53
-     *  Currently no serialization in DateFormat, but SimpleDateFormat serialization
-     *  may call getContext/setContext to read/write this for compatibility
+     * Capitalization setting, hoisted to DateFormat ICU 53
+     * Note that SimpleDateFormat serialization may call getContext/setContext to read/write
+     * this for compatibility with serialization for its old copy of capitalizationSetting.
+     * @serial
+     */
+    private DisplayContext capitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
+
+    static final int currentSerialVersion = 1;
+
+    /**
+     * Describes the version of <code>DateFormat</code> present on the stream.
+     * Possible values are:
+     * <ul>
+     * <li><b>0</b> (or uninitialized): the pre-ICU-53 version
+     *
+     * <li><b>1</b>: ICU 53, adds serialVersionOnStream and capitalizationSetting
+     * </ul>
+     * When streaming out a <code>DateFormat</code>, the most recent format
+     * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
+     * is always written.
+     *
+     * @serial
      */
-    private transient DisplayContext capitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
+    private int serialVersionOnStream = currentSerialVersion;
 
     // Proclaim serial compatibility with 1.1 FCS
     private static final long serialVersionUID = 7218322306649953788L;
@@ -1607,8 +1628,11 @@ public abstract class DateFormat extends UFormat {
         if (this == obj) return true;
         if (obj == null || getClass() != obj.getClass()) return false;
         DateFormat other = (DateFormat) obj;
-        return (calendar.isEquivalentTo(other.calendar) &&
-                numberFormat.equals(other.numberFormat));
+        return (((calendar==null && other.calendar==null) ||
+                    (calendar!=null && other.calendar!=null && calendar.isEquivalentTo(other.calendar))) &&
+                ((numberFormat==null && other.numberFormat==null) ||
+                    (numberFormat!=null && other.numberFormat!=null && numberFormat.equals(other.numberFormat))) &&
+                capitalizationSetting == other.capitalizationSetting);
     }
 
     /**
@@ -1619,7 +1643,9 @@ public abstract class DateFormat extends UFormat {
     {
         DateFormat other = (DateFormat) super.clone();
         other.calendar = (Calendar) calendar.clone();
-        other.numberFormat = (NumberFormat) numberFormat.clone();
+        if (numberFormat != null) {
+            other.numberFormat = (NumberFormat) numberFormat.clone();
+        }
         return other;
     }
 
@@ -1664,6 +1690,26 @@ public abstract class DateFormat extends UFormat {
         }
     }
 
+    /**
+     * First, read in the default serializable data.
+     *
+     * Then, if <code>serialVersionOnStream</code> is less than 1, indicating that
+     * the stream was written by a pre-ICU-53 version,
+     * set capitalizationSetting to a default value.
+     * Finally, set serialVersionOnStream back to the maximum allowed value so that
+     * default serialization will work properly if this object is streamed out again.
+     */
+    private void readObject(ObjectInputStream stream)
+         throws IOException, ClassNotFoundException
+    {
+        stream.defaultReadObject();
+        if (serialVersionOnStream < 1) {
+            // Didn't have capitalizationSetting, set it to default
+            capitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
+        }
+        serialVersionOnStream = currentSerialVersion;
+    }
+
     /**
      * Creates a new date format.
      * @stable ICU 2.0
index 5932fcbe47b48edfaff84cca08a086369684dc1a..a8b20059529b8956ec3da6a26395edfcc8986a20 100644 (file)
@@ -609,7 +609,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
     private boolean capitalizationInfoIsSet = false;
     private boolean capitalizationForListOrMenu = false;
     private boolean capitalizationForStandAlone = false;
-    private BreakIterator capitalizationBrkIter = null;
+    private transient BreakIterator capitalizationBrkIter = null;
 
 
     private static final boolean DEBUG  =  ICUDebug.enabled("rbnf");
@@ -1877,12 +1877,15 @@ public class RuleBasedNumberFormat extends NumberFormat {
      * Adjust capitalization of formatted result for display context
      */
     private String adjustForContext(String result) {
-        if (result != null && result.length() > 0 && UCharacter.isLowerCase(result.codePointAt(0)) &&
-              capitalizationBrkIter != null) {
+        if (result != null && result.length() > 0 && UCharacter.isLowerCase(result.codePointAt(0))) {
             DisplayContext capitalization = getContext(DisplayContext.Type.CAPITALIZATION);
             if (  capitalization==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
                   (capitalization == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForListOrMenu) ||
                   (capitalization == DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone) ) {
+                if (capitalizationBrkIter == null) {
+                    // should only happen when deserializing, etc.
+                    capitalizationBrkIter = BreakIterator.getSentenceInstance(locale);
+                }
                 return UCharacter.toTitleCase(locale, result, capitalizationBrkIter,
                                 UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
             }
index 72e2b9b44cfb8e9036ad1904f19b1bcd6e5fcb55..0597a4321baf61a9ceaa327b174916c2473092df 100644 (file)
@@ -858,7 +858,7 @@ public class SimpleDateFormat extends DateFormat {
     /**
      * BreakIterator to use for capitalization
      */
-    private BreakIterator capitalizationBrkIter = null;
+    private transient BreakIterator capitalizationBrkIter = null;
 
     /*
      *  Capitalization setting, introduced in ICU 50
@@ -1784,8 +1784,7 @@ public class SimpleDateFormat extends DateFormat {
             break;
         } // switch (patternCharIndex)
 
-        if (fieldNum == 0 && capitalizationContext != null && UCharacter.isLowerCase(buf.codePointAt(bufstart)) &&
-                capitalizationBrkIter != null) {
+        if (fieldNum == 0 && capitalizationContext != null && UCharacter.isLowerCase(buf.codePointAt(bufstart))) {
             boolean titlecase = false;
             switch (capitalizationContext) {
                 case CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
@@ -1803,6 +1802,10 @@ public class SimpleDateFormat extends DateFormat {
                    break;
             }
             if (titlecase) {
+                if (capitalizationBrkIter == null) {
+                    // should only happen when deserializing, etc.
+                    capitalizationBrkIter = BreakIterator.getSentenceInstance(locale);
+                }
                 String firstField = buf.substring(bufstart); // bufstart or beginOffset, should be the same
                 String firstFieldTitleCase = UCharacter.toTitleCase(locale, firstField, capitalizationBrkIter,
                                                      UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
index 00d664b4e898706127e250598891877519dccd91..8753902d12c20d7ed3327e14979c5a68558c9c52 100644 (file)
 
 package com.ibm.icu.dev.test.format;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.text.AttributedCharacterIterator;
 import java.text.CharacterIterator;
 import java.text.FieldPosition;
@@ -4233,6 +4238,12 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
 
             // now try context & standard format call
             sdfmt.setContext(item.capitalizationContext);
+            SimpleDateFormat sdfmtClone = (SimpleDateFormat)sdfmt.clone();
+            if (!sdfmtClone.equals(sdfmt)) {
+                errln("FAIL: for locale " + item.locale +  ", capitalizationContext " + item.capitalizationContext +
+                        ", sdfmt.clone() != sdfmt (for SimpleDateFormat)");
+            }
+            
             StringBuffer result2 = new StringBuffer();
             FieldPosition fpos2 = new FieldPosition(0);
             sdfmt.format(cal, result2, fpos2);
@@ -4255,6 +4266,49 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
 
             // now try context & standard format call
             dfmt.setContext(relItem.capitalizationContext);
+
+            // write to stream, then read a copy from stream & compare
+            boolean serializeTestFail = false;
+            ByteArrayOutputStream baos = null;
+            DateFormat dfmtFromStream = null;
+            try {
+                baos = new ByteArrayOutputStream();
+                ObjectOutputStream oos = new ObjectOutputStream(baos);
+                oos.writeObject(dfmt);
+                oos.close();
+            } catch (IOException i) {
+                errln("FAIL: for locale " + relItem.locale +  ", capitalizationContext " + relItem.capitalizationContext +
+                        ", serialization of RELATIVE_LONG DateFormat fails with IOException");
+                serializeTestFail = true;
+            }
+            if (!serializeTestFail) {
+                byte[] buf = baos.toByteArray();
+                try {
+                    ByteArrayInputStream bais = new ByteArrayInputStream(buf);
+                    ObjectInputStream ois = new ObjectInputStream(bais);
+                    dfmtFromStream = (DateFormat)ois.readObject();
+                    ois.close();
+                } catch (IOException i) {
+                    errln("FAIL: for locale " + relItem.locale +  ", capitalizationContext " + relItem.capitalizationContext +
+                            ", deserialization of RELATIVE_LONG DateFormat fails with IOException");
+                    serializeTestFail = true;
+                } catch (ClassNotFoundException c) {
+                    errln("FAIL: for locale " + relItem.locale +  ", capitalizationContext " + relItem.capitalizationContext +
+                            ", deserialization of RELATIVE_LONG DateFormat fails with ClassNotFoundException");
+                    serializeTestFail = true;
+                }
+            }
+            if (!serializeTestFail && dfmtFromStream==null) {
+                errln("FAIL: for locale " + relItem.locale +  ", capitalizationContext " + relItem.capitalizationContext +
+                        ", dfmtFromStream is null (for RELATIVE_LONG)");
+                serializeTestFail = true;
+            }
+            if (!serializeTestFail && !dfmtFromStream.equals(dfmt)) {
+                errln("FAIL: for locale " + relItem.locale +  ", capitalizationContext " + relItem.capitalizationContext +
+                        ", dfmtFromStream != dfmt (for RELATIVE_LONG)");
+                serializeTestFail = true;
+            }
+
             cal.setTime(today);
             StringBuffer result2 = new StringBuffer();
             FieldPosition fpos2 = new FieldPosition(0);
@@ -4263,6 +4317,15 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
                 errln("FAIL: format today for locale " + relItem.locale +  ", capitalizationContext " + relItem.capitalizationContext +
                         ", expected \"" + relItem.expectedFormatToday + "\", got \"" + result2 + "\"");
             }
+            if (!serializeTestFail) {
+                result2.setLength(0);
+                dfmtFromStream.format(cal, result2, fpos2);
+                if (result2.toString().compareTo(relItem.expectedFormatToday) != 0) {
+                    errln("FAIL: use dfmtFromStream to format today for locale " + relItem.locale +  ", capitalizationContext " +
+                            relItem.capitalizationContext + ", expected \"" + relItem.expectedFormatToday + "\", got \"" + result2 + "\"");
+                }
+            }
+
             cal.add(Calendar.DATE, -1);
             result2.setLength(0);
             dfmt.format(cal, result2, fpos2);