8190904: Incorrect currency instance returned by java.util.Currency.getInstance()
authornishjain
Thu, 22 Feb 2018 11:52:01 +0530
changeset 48929 28d8fc8cd3cd
parent 48928 cc30928a834e
child 48930 b1a5b4ad7427
8190904: Incorrect currency instance returned by java.util.Currency.getInstance() Reviewed-by: naoto
src/java.base/share/classes/java/util/Currency.java
test/jdk/java/util/Currency/PropertiesTest.java
test/jdk/java/util/Currency/PropertiesTest.sh
test/jdk/java/util/Currency/currency.properties
--- a/src/java.base/share/classes/java/util/Currency.java	Wed Feb 21 15:09:40 2018 -0800
+++ b/src/java.base/share/classes/java/util/Currency.java	Thu Feb 22 11:52:01 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2018, 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
@@ -41,6 +41,7 @@
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 import java.util.spi.CurrencyNameProvider;
+import java.util.stream.Collectors;
 import sun.util.locale.provider.CalendarDataUtility;
 import sun.util.locale.provider.LocaleServiceProviderPool;
 import sun.util.logging.PlatformLogger;
@@ -77,7 +78,10 @@
  * JP=JPZ,999,0
  * </code>
  * <p>
- * will supersede the currency data for Japan.
+ * will supersede the currency data for Japan. If JPZ is one of the existing
+ * ISO 4217 currency code referred by other countries, the existing
+ * JPZ currency data is updated with the given numeric code and minor
+ * unit value.
  *
  * <p>
  * <code>
@@ -93,6 +97,11 @@
  * country code entries exist, the behavior of the Currency information for that
  * {@code Currency} is undefined and the remainder of entries in file are processed.
  * <p>
+ * If multiple property entries with same currency code but different numeric code
+ * and/or minor unit are encountered, those entries are ignored and the remainder
+ * of entries in file are processed.
+ *
+ * <p>
  * It is recommended to use {@link java.math.BigDecimal} class while dealing
  * with {@code Currency} or monetary values as it provides better handling of floating
  * point numbers and their operations.
@@ -237,19 +246,17 @@
                         try (FileReader fr = new FileReader(propFile)) {
                             props.load(fr);
                         }
-                        Set<String> keys = props.stringPropertyNames();
                         Pattern propertiesPattern =
-                            Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*" +
-                                "(\\d+)\\s*,?\\s*(\\d{4}-\\d{2}-\\d{2}T\\d{2}:" +
-                                "\\d{2}:\\d{2})?");
-                        for (String key : keys) {
-                           replaceCurrencyData(propertiesPattern,
-                               key.toUpperCase(Locale.ROOT),
-                               props.getProperty(key).toUpperCase(Locale.ROOT));
-                        }
+                                Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*" +
+                                        "(\\d+)\\s*,?\\s*(\\d{4}-\\d{2}-\\d{2}T\\d{2}:" +
+                                        "\\d{2}:\\d{2})?");
+                        List<CurrencyProperty> currencyEntries
+                                = getValidCurrencyData(props, propertiesPattern);
+                        currencyEntries.forEach(Currency::replaceCurrencyData);
                     }
                 } catch (IOException e) {
-                    info("currency.properties is ignored because of an IOException", e);
+                    CurrencyProperty.info("currency.properties is ignored"
+                            + " because of an IOException", e);
                 }
                 return null;
             }
@@ -769,71 +776,111 @@
     }
 
     /**
-     * Replaces currency data found in the currencydata.properties file
+     * Parse currency data found in the properties file (that
+     * java.util.currency.data designates) to a List of CurrencyProperty
+     * instances. Also, remove invalid entries and the multiple currency
+     * code inconsistencies.
      *
-     * @param pattern regex pattern for the properties
-     * @param ctry country code
-     * @param curdata currency data.  This is a comma separated string that
-     *    consists of "three-letter alphabet code", "three-digit numeric code",
-     *    and "one-digit (0-9) default fraction digit".
-     *    For example, "JPZ,392,0".
-     *    An optional UTC date can be appended to the string (comma separated)
-     *    to allow a currency change take effect after date specified.
-     *    For example, "JP=JPZ,999,0,2014-01-01T00:00:00" has no effect unless
-     *    UTC time is past 1st January 2014 00:00:00 GMT.
+     * @param props properties containing currency data
+     * @param pattern regex pattern for the properties entry
+     * @return list of parsed property entries
      */
-    private static void replaceCurrencyData(Pattern pattern, String ctry, String curdata) {
+    private static List<CurrencyProperty> getValidCurrencyData(Properties props,
+            Pattern pattern) {
+
+        Set<String> keys = props.stringPropertyNames();
+        List<CurrencyProperty> propertyEntries = new ArrayList<>();
 
-        if (ctry.length() != 2) {
-            // ignore invalid country code
-            info("currency.properties entry for " + ctry +
-                    " is ignored because of the invalid country code.", null);
-            return;
-        }
+        // remove all invalid entries and parse all valid currency properties
+        // entries to a group of CurrencyProperty, classified by currency code
+        Map<String, List<CurrencyProperty>> currencyCodeGroup = keys.stream()
+                .map(k -> CurrencyProperty
+                .getValidEntry(k.toUpperCase(Locale.ROOT),
+                        props.getProperty(k).toUpperCase(Locale.ROOT),
+                        pattern)).flatMap(o -> o.stream())
+                .collect(Collectors.groupingBy(entry -> entry.currencyCode));
 
-        Matcher m = pattern.matcher(curdata);
-        if (!m.find() || (m.group(4) == null && countOccurrences(curdata, ',') >= 3)) {
-            // format is not recognized.  ignore the data
-            // if group(4) date string is null and we've 4 values, bad date value
-            info("currency.properties entry for " + ctry +
-                    " ignored because the value format is not recognized.", null);
-            return;
-        }
-
-        try {
-            if (m.group(4) != null && !isPastCutoverDate(m.group(4))) {
-                info("currency.properties entry for " + ctry +
-                        " ignored since cutover date has not passed :" + curdata, null);
-                return;
+        // check each group for inconsistencies
+        currencyCodeGroup.forEach((curCode, list) -> {
+            boolean inconsistent = CurrencyProperty
+                    .containsInconsistentInstances(list);
+            if (inconsistent) {
+                list.forEach(prop -> CurrencyProperty.info("The property"
+                        + " entry for " + prop.country + " is inconsistent."
+                        + " Ignored.", null));
+            } else {
+                propertyEntries.addAll(list);
             }
-        } catch (ParseException ex) {
-            info("currency.properties entry for " + ctry +
-                        " ignored since exception encountered :" + ex.getMessage(), null);
-            return;
-        }
+        });
+
+        return propertyEntries;
+    }
 
-        String code = m.group(1);
-        int numeric = Integer.parseInt(m.group(2));
+    /**
+     * Replaces currency data found in the properties file that
+     * java.util.currency.data designates. This method is invoked for
+     * each valid currency entry.
+     *
+     * @param prop CurrencyProperty instance of the valid property entry
+     */
+    private static void replaceCurrencyData(CurrencyProperty prop) {
+
+
+        String ctry = prop.country;
+        String code = prop.currencyCode;
+        int numeric = prop.numericCode;
+        int fraction = prop.fraction;
         int entry = numeric << NUMERIC_CODE_SHIFT;
-        int fraction = Integer.parseInt(m.group(3));
-        if (fraction > SIMPLE_CASE_COUNTRY_MAX_DEFAULT_DIGITS) {
-            info("currency.properties entry for " + ctry +
-                " ignored since the fraction is more than " +
-                SIMPLE_CASE_COUNTRY_MAX_DEFAULT_DIGITS + ":" + curdata, null);
-            return;
-        }
 
         int index = SpecialCaseEntry.indexOf(code, fraction, numeric);
 
-        /* if a country switches from simple case to special case or
+
+        // If a new entry changes the numeric code/dfd of an existing
+        // currency code, update it in the sc list at the respective
+        // index and also change it in the other currencies list and
+        // main table (if that currency code is also used as a
+        // simple case).
+
+        // If all three components do not match with the new entry,
+        // but the currency code exists in the special case list
+        // update the sc entry with the new entry
+        int scCurrencyCodeIndex = -1;
+        if (index == -1) {
+            scCurrencyCodeIndex = SpecialCaseEntry.currencyCodeIndex(code);
+            if (scCurrencyCodeIndex != -1) {
+                //currency code exists in sc list, then update the old entry
+                specialCasesList.set(scCurrencyCodeIndex,
+                        new SpecialCaseEntry(code, fraction, numeric));
+
+                // also update the entry in other currencies list
+                OtherCurrencyEntry oe = OtherCurrencyEntry.findEntry(code);
+                if (oe != null) {
+                    int oIndex = otherCurrenciesList.indexOf(oe);
+                    otherCurrenciesList.set(oIndex, new OtherCurrencyEntry(
+                            code, fraction, numeric));
+                }
+            }
+        }
+
+        /* If a country switches from simple case to special case or
          * one special case to other special case which is not present
-         * in the sc arrays then insert the new entry in special case arrays
+         * in the sc arrays then insert the new entry in special case arrays.
+         * If an entry with given currency code exists, update with the new
+         * entry.
          */
         if (index == -1 && (ctry.charAt(0) != code.charAt(0)
                 || ctry.charAt(1) != code.charAt(1))) {
 
-            specialCasesList.add(new SpecialCaseEntry(code, fraction, numeric));
-            index = specialCasesList.size() - 1;
+            if(scCurrencyCodeIndex == -1) {
+                specialCasesList.add(new SpecialCaseEntry(code, fraction,
+                        numeric));
+                index = specialCasesList.size() - 1;
+            } else {
+                index = scCurrencyCodeIndex;
+            }
+
+            // update the entry in main table if it exists as a simple case
+            updateMainTableEntry(code, fraction, numeric);
         }
 
         if (index == -1) {
@@ -848,32 +895,29 @@
         setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry);
     }
 
-    private static boolean isPastCutoverDate(String s) throws ParseException {
-        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ROOT);
-        format.setTimeZone(TimeZone.getTimeZone("UTC"));
-        format.setLenient(false);
-        long time = format.parse(s.trim()).getTime();
-        return System.currentTimeMillis() > time;
-
-    }
+    // update the entry in maintable for any simple case found, if a new
+    // entry as a special case updates the entry in sc list with
+    // existing currency code
+    private static void updateMainTableEntry(String code, int fraction,
+            int numeric) {
+        // checking the existence of currency code in mainTable
+        int tableEntry = getMainTableEntry(code.charAt(0), code.charAt(1));
+        int entry = numeric << NUMERIC_CODE_SHIFT;
+        if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
+                && tableEntry != INVALID_COUNTRY_ENTRY
+                && code.charAt(2) - 'A' == (tableEntry
+                & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK)) {
 
-    private static int countOccurrences(String value, char match) {
-        int count = 0;
-        for (char c : value.toCharArray()) {
-            if (c == match) {
-               ++count;
-            }
-        }
-        return count;
-    }
-
-    private static void info(String message, Throwable t) {
-        PlatformLogger logger = PlatformLogger.getLogger("java.util.Currency");
-        if (logger.isLoggable(PlatformLogger.Level.INFO)) {
-            if (t != null) {
-                logger.info(message, t);
-            } else {
-                logger.info(message);
+            int numericCode = (tableEntry & NUMERIC_CODE_MASK)
+                    >> NUMERIC_CODE_SHIFT;
+            int defaultFractionDigits = (tableEntry
+                    & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK)
+                    >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
+            if (numeric != numericCode || fraction != defaultFractionDigits) {
+                // update the entry in main table
+                entry |= (fraction << SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT)
+                        | (code.charAt(2) - 'A');
+                setMainTableEntry(code.charAt(0), code.charAt(1), entry);
             }
         }
     }
@@ -959,6 +1003,25 @@
             return fractionAndNumericCode;
         }
 
+        // get the index based on currency code
+        private static int currencyCodeIndex(String code) {
+            int size = specialCasesList.size();
+            for (int index = 0; index < size; index++) {
+                SpecialCaseEntry scEntry = specialCasesList.get(index);
+                if (scEntry.oldCurrency.equals(code) && (scEntry.cutOverTime == Long.MAX_VALUE
+                        || System.currentTimeMillis() < scEntry.cutOverTime)) {
+                    //consider only when there is no new currency or cutover time is not passed
+                    return index;
+                } else if (scEntry.newCurrency.equals(code)
+                        && System.currentTimeMillis() >= scEntry.cutOverTime) {
+                    //consider only if the cutover time is passed
+                    return index;
+                }
+            }
+            return -1;
+        }
+
+
         // convert the special case entry to sc arrays index
         private static int toIndex(int tableEntry) {
             return (tableEntry & SPECIAL_CASE_COUNTRY_INDEX_MASK) - SPECIAL_CASE_COUNTRY_INDEX_DELTA;
@@ -999,6 +1062,136 @@
 
     }
 
+
+    /*
+     * Used to represent an entry of the properties file that
+     * java.util.currency.data designates
+     *
+     * - country: country representing the currency entry
+     * - currencyCode: currency code
+     * - fraction: default fraction digit
+     * - numericCode: numeric code
+     * - date: cutover date
+     */
+    private static class CurrencyProperty {
+        final private String country;
+        final private String currencyCode;
+        final private int fraction;
+        final private int numericCode;
+        final private String date;
+
+        private CurrencyProperty(String country, String currencyCode,
+                int fraction, int numericCode, String date) {
+            this.country = country;
+            this.currencyCode = currencyCode;
+            this.fraction = fraction;
+            this.numericCode = numericCode;
+            this.date = date;
+        }
+
+        /**
+         * Check the valid currency data and create/return an Optional instance
+         * of CurrencyProperty
+         *
+         * @param ctry    country representing the currency data
+         * @param curData currency data of the given {@code ctry}
+         * @param pattern regex pattern for the properties entry
+         * @return Optional containing CurrencyProperty instance, If valid;
+         *         empty otherwise
+         */
+        private static Optional<CurrencyProperty> getValidEntry(String ctry,
+                String curData,
+                Pattern pattern) {
+
+            CurrencyProperty prop = null;
+
+            if (ctry.length() != 2) {
+                // Invalid country code. Ignore the entry.
+            } else {
+
+                prop = parseProperty(ctry, curData, pattern);
+                // if the property entry failed any of the below checked
+                // criteria it is ignored
+                if (prop == null
+                        || (prop.date == null && curData.chars()
+                                .map(c -> c == ',' ? 1 : 0).sum() >= 3)) {
+                    // format is not recognized.  ignore the data if date
+                    // string is null and we've 4 values, bad date value
+                    prop = null;
+                } else if (prop.fraction
+                        > SIMPLE_CASE_COUNTRY_MAX_DEFAULT_DIGITS) {
+                    prop = null;
+                } else {
+                    try {
+                        if (prop.date != null
+                                && !isPastCutoverDate(prop.date)) {
+                            prop = null;
+                        }
+                    } catch (ParseException ex) {
+                        prop = null;
+                    }
+                }
+            }
+
+            if (prop == null) {
+                info("The property entry for " + ctry + " is invalid."
+                        + " Ignored.", null);
+            }
+
+            return Optional.ofNullable(prop);
+        }
+
+        /*
+         * Parse properties entry and return CurrencyProperty instance
+         */
+        private static CurrencyProperty parseProperty(String ctry,
+                String curData, Pattern pattern) {
+            Matcher m = pattern.matcher(curData);
+            if (!m.find()) {
+                return null;
+            } else {
+                return new CurrencyProperty(ctry, m.group(1),
+                        Integer.parseInt(m.group(3)),
+                        Integer.parseInt(m.group(2)), m.group(4));
+            }
+        }
+
+        /**
+         * Checks if the given list contains multiple inconsistent currency instances
+         */
+        private static boolean containsInconsistentInstances(
+                List<CurrencyProperty> list) {
+            int numCode = list.get(0).numericCode;
+            int fractionDigit = list.get(0).fraction;
+            return list.stream().anyMatch(prop -> prop.numericCode != numCode
+                    || prop.fraction != fractionDigit);
+        }
+
+        private static boolean isPastCutoverDate(String s)
+                throws ParseException {
+            SimpleDateFormat format = new SimpleDateFormat(
+                    "yyyy-MM-dd'T'HH:mm:ss", Locale.ROOT);
+            format.setTimeZone(TimeZone.getTimeZone("UTC"));
+            format.setLenient(false);
+            long time = format.parse(s.trim()).getTime();
+            return System.currentTimeMillis() > time;
+
+        }
+
+        private static void info(String message, Throwable t) {
+            PlatformLogger logger = PlatformLogger
+                    .getLogger("java.util.Currency");
+            if (logger.isLoggable(PlatformLogger.Level.INFO)) {
+                if (t != null) {
+                    logger.info(message, t);
+                } else {
+                    logger.info(message);
+                }
+            }
+        }
+
+    }
+
 }
 
 
--- a/test/jdk/java/util/Currency/PropertiesTest.java	Wed Feb 21 15:09:40 2018 -0800
+++ b/test/jdk/java/util/Currency/PropertiesTest.java	Thu Feb 22 11:52:01 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, 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
@@ -37,6 +37,8 @@
             bug7102969();
         } else if (args.length == 1 && args[0].equals("bug8157138")) {
             bug8157138();
+        } else if (args.length == 1 && args[0].equals("bug8190904")) {
+            bug8190904();
         } else {
             System.err.println("Usage:  java PropertiesTest -d <dumpfile>");
             System.err.println("        java PropertiesTest -c <beforedump> <afterdump> <propsfile>");
@@ -118,8 +120,9 @@
         for (String key: keys) {
             String val = p.getProperty(key);
             try {
-                if (countOccurrences(val, ',') == 3 && !isPastCutoverDate(val)) {
-                    System.out.println("Skipping since date is in future");
+                if (val.chars().map(c -> c == ',' ? 1 : 0).sum() >= 3
+                        && !isPastCutoverDate(val)) {
+                    System.out.println("Skipping " + key + " since date is in future");
                     continue; // skip since date in future (no effect)
                 }
             } catch (ParseException pe) {
@@ -130,6 +133,13 @@
             System.out.printf("Testing key: %s, val: %s... ", key, val);
             System.out.println("AfterVal is : " + afterVal);
 
+            if (afterVal == null) {
+                System.out.println("Testing key " + key + " is ignored"
+                        + " because of the inconsistent numeric code and/or"
+                        + " dfd for the given currency code: "+val);
+                continue;
+            }
+
             Matcher m = propertiesPattern.matcher(val.toUpperCase(Locale.ROOT));
             if (!m.find()) {
                 // format is not recognized.
@@ -166,22 +176,24 @@
             System.out.printf("Success!\n");
         }
         if (!after.isEmpty()) {
-            StringBuilder sb = new StringBuilder()
-                .append("Currency data replacement failed.  Unnecessary modification was(were) made for the following currencies:\n");
+
             keys = after.stringPropertyNames();
             for (String key : keys) {
-                sb.append("    country: ")
-                .append(key)
-                .append(" currency: ")
-                .append(after.getProperty(key))
-                .append("\n");
+                String modified = after.getProperty(key);
+                if(!p.containsValue(modified)) {
+                    throw new RuntimeException("Unnecessary modification was"
+                            + " made to county: "+ key + " with currency value:"
+                                    + " " + modified);
+                } else {
+                    System.out.println(key + " modified by an entry in"
+                            + " currency.properties with currency value "
+                            + modified);
+                }
             }
-            throw new RuntimeException(sb.toString());
         }
     }
 
     private static void bug7102969() {
-
         // check the correct overriding of special case entries
         Currency cur = Currency.getInstance(new Locale("", "JP"));
         if (!cur.getCurrencyCode().equals("ABC")) {
@@ -248,6 +260,41 @@
 
     }
 
+    private static void bug8190904() {
+        // should throw IllegalArgumentException as currency code
+        // does not exist as valid ISO 4217 code and failed to load
+        // from currency.properties file because of inconsistent numeric/dfd
+        try {
+            Currency.getInstance("MCC");
+            throw new RuntimeException("[FAILED: Should throw"
+                    + " IllegalArgumentException for invalid currency code]");
+        } catch (IllegalArgumentException ex) {
+            // expected to throw IllegalArgumentException
+        }
+
+        // should keep the XOF instance as XOF,952,0, as the XOF entries in
+        // currency.properties IT=XOF,952,1, XY=XOF,955,0 are ignored because
+        // of inconsistency in numeric code and/or dfd
+        checkCurrencyInstance("XOF", 952, 0);
+        // property entry "AS=USD,841,2" should change all occurences
+        // of USD with USD,841,2
+        checkCurrencyInstance("USD", 841, 2);
+    }
+
+    /**
+     * Test the numeric code and fraction of the Currency instance obtained
+     * by given currencyCode, with the expected numericCode and fraction
+     */
+    private static void checkCurrencyInstance(String currencyCode,
+            int numericCode, int fraction) {
+        Currency cur = Currency.getInstance(currencyCode);
+        if (cur.getNumericCode() != numericCode
+                || cur.getDefaultFractionDigits() != fraction) {
+            throw new RuntimeException("[FAILED: Incorrect numeric code or"
+                    + " dfd for currency code: " + currencyCode + "]");
+        }
+    }
+
     private static boolean isPastCutoverDate(String s)
             throws IndexOutOfBoundsException, NullPointerException, ParseException {
         String dateString = s.substring(s.lastIndexOf(',')+1, s.length()).trim();
@@ -263,13 +310,4 @@
         }
     }
 
-    private static int countOccurrences(String value, char match) {
-        int count = 0;
-        for (char c : value.toCharArray()) {
-            if (c == match) {
-               ++count;
-            }
-        }
-        return count;
-    }
 }
--- a/test/jdk/java/util/Currency/PropertiesTest.sh	Wed Feb 21 15:09:40 2018 -0800
+++ b/test/jdk/java/util/Currency/PropertiesTest.sh	Thu Feb 22 11:52:01 2018 +0530
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-# Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2018, 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
@@ -24,7 +24,7 @@
 
 # @test
 # @bug 6332666 6863624 7180362 8003846 8074350 8074351 8130246 8149735 7102969
-#      8157138
+#      8157138 8190904
 # @summary tests the capability of replacing the currency data with user
 #     specified currency properties file
 # @build PropertiesTest
@@ -124,6 +124,11 @@
 ${WRITABLEJDK}${FS}bin${FS}java ${TESTVMOPTS} -cp ${TESTCLASSES} PropertiesTest bug8157138
 if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
 
+# run bug8190904 test
+echo ''
+${WRITABLEJDK}${FS}bin${FS}java ${TESTVMOPTS} -cp ${TESTCLASSES} PropertiesTest bug8190904
+if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
+
 # Cleanup
 rm -rf $WRITABLEJDK
 
--- a/test/jdk/java/util/Currency/currency.properties	Wed Feb 21 15:09:40 2018 -0800
+++ b/test/jdk/java/util/Currency/currency.properties	Thu Feb 22 11:52:01 2018 +0530
@@ -26,5 +26,9 @@
 MG=MGG,990,10
 MX=SSS,493,2,2001-01-01-00-00-00
 PE=EUR   ,978  ,2,  20399-01-01T00:00:00
-MG=MGG,990,10
 =euR,111,2, 2099-01-01-00-00-00
+MR=MCC,556,7
+IT=XOF,952,1
+XY=XOF,955,0
+AS=USD,841,2
+CY=CYP,822,2