# HG changeset patch # User rpatil # Date 1456941539 -19800 # Node ID 69169578a67f472395c47d7a44bb0f919f62136a # Parent e20e1167fb0855e70914a90a3bc3c6ea72f0c5b5 8087104: DateFormatSymbols triggers this.clone() in the constructor Summary: Instead of using its own instance for caching and calling clone in DateFormatSymbols, a nested class SymbolsCacheEntry is introduced. Reviewed-by: okutsu, peytoia diff -r e20e1167fb08 -r 69169578a67f jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java --- a/jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java Thu Mar 03 22:55:41 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java Wed Mar 02 23:28:59 2016 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -606,7 +606,7 @@ try { DateFormatSymbols other = (DateFormatSymbols)super.clone(); - copyMembers(this, other); + copyMembers(new SymbolsCacheEntry(locale), other); return other; } catch (CloneNotSupportedException e) { throw new InternalError(e); @@ -669,7 +669,7 @@ /** * Cache to hold DateFormatSymbols instances per Locale. */ - private static final ConcurrentMap> cachedInstances + private static final ConcurrentMap> cachedInstances = new ConcurrentHashMap<>(3); private transient int lastZoneIndex; @@ -683,10 +683,10 @@ locale = desiredLocale; // Copy values of a cached instance if any. - SoftReference ref = cachedInstances.get(locale); - DateFormatSymbols dfs; - if (ref != null && (dfs = ref.get()) != null) { - copyMembers(dfs, this); + SoftReference ref = cachedInstances.get(locale); + SymbolsCacheEntry sce; + if (ref != null && (sce = ref.get()) != null) { + copyMembers(sce, this); return; } @@ -717,11 +717,11 @@ weekdays = toOneBasedArray(resource.getStringArray("DayNames")); shortWeekdays = toOneBasedArray(resource.getStringArray("DayAbbreviations")); - // Put a clone in the cache - ref = new SoftReference<>((DateFormatSymbols)this.clone()); - SoftReference x = cachedInstances.putIfAbsent(locale, ref); + sce = new SymbolsCacheEntry(locale); + ref = new SoftReference<>(sce); + SoftReference x = cachedInstances.putIfAbsent(locale, ref); if (x != null) { - DateFormatSymbols y = x.get(); + SymbolsCacheEntry y = x.get(); if (y == null) { // Replace the empty SoftReference with ref. cachedInstances.put(locale, ref); @@ -812,7 +812,7 @@ * @param src the source DateFormatSymbols. * @param dst the target DateFormatSymbols. */ - private void copyMembers(DateFormatSymbols src, DateFormatSymbols dst) + private void copyMembers(SymbolsCacheEntry src, DateFormatSymbols dst) { dst.eras = Arrays.copyOf(src.eras, src.eras.length); dst.months = Arrays.copyOf(src.months, src.months.length); @@ -821,7 +821,7 @@ dst.shortWeekdays = Arrays.copyOf(src.shortWeekdays, src.shortWeekdays.length); dst.ampms = Arrays.copyOf(src.ampms, src.ampms.length); if (src.zoneStrings != null) { - dst.zoneStrings = src.getZoneStringsImpl(true); + dst.zoneStrings = getZoneStringsImpl(true); } else { dst.zoneStrings = null; } @@ -842,4 +842,43 @@ } stream.defaultWriteObject(); } + + private static class SymbolsCacheEntry { + + final String eras[]; + final String months[]; + final String shortMonths[]; + final String weekdays[]; + final String shortWeekdays[]; + final String ampms[]; + final String zoneStrings[][]; + final String localPatternChars; + + SymbolsCacheEntry(Locale locale) { + // Initialize the fields from the ResourceBundle for locale. + LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DateFormatSymbolsProvider.class, locale); + // Avoid any potential recursions + if (!(adapter instanceof ResourceBundleBasedAdapter)) { + adapter = LocaleProviderAdapter.getResourceBundleBased(); + } + ResourceBundle resource = ((ResourceBundleBasedAdapter) adapter).getLocaleData().getDateFormatData(locale); + if (resource.containsKey("Eras")) { + this.eras = resource.getStringArray("Eras"); + } else if (resource.containsKey("long.Eras")) { + this.eras = resource.getStringArray("long.Eras"); + } else if (resource.containsKey("short.Eras")) { + this.eras = resource.getStringArray("short.Eras"); + } else { + this.eras = null; + } + this.months = resource.getStringArray("MonthNames"); + this.shortMonths = resource.getStringArray("MonthAbbreviations"); + this.weekdays = toOneBasedArray(resource.getStringArray("DayNames")); + this.shortWeekdays = toOneBasedArray(resource.getStringArray("DayAbbreviations")); + this.ampms = resource.getStringArray("AmPmMarkers"); + this.zoneStrings = TimeZoneNameUtility.getZoneStrings(locale); + this.localPatternChars = resource.getString("DateTimePatternChars"); + + } + } } diff -r e20e1167fb08 -r 69169578a67f jdk/test/java/text/Format/DateFormat/DFSConstructorCloneTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/text/Format/DateFormat/DFSConstructorCloneTest.java Wed Mar 02 23:28:59 2016 +0530 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8087104 + * @summary Make sure that clone() method is not called from DateFormatSymbols constructor. + */ +import java.text.DateFormatSymbols; + +public class DFSymbolsCloneTest extends DateFormatSymbols { + + private Foo foo; + + public DFSymbolsCloneTest(Foo fooObj) { + if (fooObj == null) { + this.foo = new Foo(); + } else { + this.foo = fooObj; + } + } + + @Override + public Object clone() { + DFSymbolsCloneTest dfsclone = (DFSymbolsCloneTest) super.clone(); + if (this.foo == null) { + throw new RuntimeException("Clone method should not be called from " + + " Superclass(DateFormatSymbols) Constructor..."); + } else { + dfsclone.foo = (Foo) this.foo.clone(); + } + return dfsclone; + } + + public static void main(String[] args) { + DFSymbolsCloneTest dfsctest = new DFSymbolsCloneTest(new Foo()); + } +} + +class Foo { + + public Foo() { + } + + @Override + protected Object clone() { + return new Foo(); + } + +}