1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 /*
  24  * @test
  25  * @bug 8177552
  26  * @summary Checks the functioning of
  27  *          CompactNumberFormat.formatToCharacterIterator method
  28  * @modules jdk.localedata
  29  * @run testng/othervm TestFormatToCharacterIterator
  30  */
  31 import java.math.BigDecimal;
  32 import java.math.BigInteger;
  33 import java.text.AttributedCharacterIterator;
  34 import java.text.CharacterIterator;
  35 import java.text.Format;
  36 import java.text.NumberFormat;
  37 import java.util.Locale;
  38 import java.util.Set;
  39 import static org.testng.Assert.assertEquals;
  40 import org.testng.annotations.DataProvider;
  41 import org.testng.annotations.Test;
  42 
  43 public class TestFormatToCharacterIterator {
  44 
  45     private static final NumberFormat FORMAT_DZ = NumberFormat
  46             .getCompactNumberInstance(new Locale("dz"),
  47                     NumberFormat.Style.LONG);
  48 
  49     private static final NumberFormat FORMAT_EN_US = NumberFormat
  50             .getCompactNumberInstance(Locale.US,
  51                     NumberFormat.Style.SHORT);
  52 
  53     private static final NumberFormat FORMAT_EN_LONG = NumberFormat
  54             .getCompactNumberInstance(new Locale("en"),
  55                     NumberFormat.Style.LONG);
  56 
  57     @DataProvider(name = "fieldPositions")
  58     Object[][] compactFieldPositionData() {
  59         return new Object[][]{
  60             // compact format instance, number, resulted string, attributes/fields, attribute positions
  61             {FORMAT_DZ, 1000.09, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F21",
  62                 new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 9, 9, 10}},
  63             {FORMAT_DZ, -999.99, "-\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F21",
  64                 new Format.Field[]{NumberFormat.Field.SIGN, NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER},
  65                 new int[]{0, 1, 1, 10, 10, 11}},
  66             {FORMAT_DZ, -0.0, "-\u0F20", new Format.Field[]{NumberFormat.Field.SIGN, NumberFormat.Field.INTEGER}, new int[]{0, 1, 1, 2}},
  67             {FORMAT_DZ, 3000L, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F23",
  68                 new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 9, 9, 10}},
  69             {FORMAT_DZ, new BigInteger("12345678901234567890"),
  70                 "\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21\u0F22\u0F23\u0F24\u0F25\u0F27",
  71                 new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 14, 14, 20}},
  72             {FORMAT_DZ, new BigDecimal("12345678901234567890.89"),
  73                 "\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21\u0F22\u0F23\u0F24\u0F25\u0F27",
  74                 new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 14, 14, 20}},
  75             // Zeros
  76             {FORMAT_EN_US, 0, "0", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 1}},
  77             {FORMAT_EN_US, 0.0, "0", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 1}},
  78             {FORMAT_EN_US, -0.0, "-0", new Format.Field[]{NumberFormat.Field.SIGN, NumberFormat.Field.INTEGER}, new int[]{0, 1, 1, 2}},
  79             // Less than 1000 no suffix
  80             {FORMAT_EN_US, 499, "499", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 3}},
  81             // Boundary number
  82             {FORMAT_EN_US, 1000.0, "1K",
  83                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
  84             // Long
  85             {FORMAT_EN_US, 3000L, "3K",
  86                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
  87             {FORMAT_EN_US, 30000L, "30K",
  88                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
  89             {FORMAT_EN_US, 300000L, "300K",
  90                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
  91             {FORMAT_EN_US, 3000000L, "3M",
  92                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
  93             {FORMAT_EN_US, 30000000L, "30M",
  94                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
  95             {FORMAT_EN_US, 300000000L, "300M",
  96                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
  97             {FORMAT_EN_US, 3000000000L, "3B",
  98                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
  99             {FORMAT_EN_US, 30000000000L, "30B",
 100                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
 101             {FORMAT_EN_US, 300000000000L, "300B",
 102                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
 103             {FORMAT_EN_US, 3000000000000L, "3T",
 104                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
 105             {FORMAT_EN_US, 30000000000000L, "30T",
 106                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
 107             {FORMAT_EN_US, 300000000000000L, "300T",
 108                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
 109             {FORMAT_EN_US, 3000000000000000L, "3000T",
 110                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 4, 4, 5}},
 111             // Double
 112             {FORMAT_EN_US, 3000.0, "3K",
 113                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
 114             {FORMAT_EN_US, 30000.0, "30K",
 115                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
 116             {FORMAT_EN_US, 300000.0, "300K",
 117                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
 118             {FORMAT_EN_US, 3000000000000000.0, "3000T",
 119                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 4, 4, 5}},
 120             // BigInteger
 121             {FORMAT_EN_US, new BigInteger("12345678901234567890"), "12345679T",
 122                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 9}},
 123             // BigDecimal
 124             {FORMAT_EN_US, new BigDecimal("12345678901234567890.89"), "12345679T",
 125                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 9}},
 126             // Number as exponent
 127             {FORMAT_EN_US, 9.78313E+3, "10K",
 128                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
 129             // Less than 1000 no suffix
 130             {FORMAT_EN_LONG, 999, "999", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 3}},
 131             // Round the value and then format
 132             {FORMAT_EN_LONG, 999.99, "1 thousand",
 133                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 10}},
 134             // 10 thousand
 135             {FORMAT_EN_LONG, 99000, "99 thousand",
 136                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 11}},
 137             // Long path
 138             {FORMAT_EN_LONG, 330000, "330 thousand",
 139                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 12}},
 140             // Double path
 141             {FORMAT_EN_LONG, 3000.90, "3 thousand",
 142                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 10}},
 143             // BigInteger path
 144             {FORMAT_EN_LONG, new BigInteger("12345678901234567890"), "12345679 trillion",
 145                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 17}},
 146             // BigDecimal path
 147             {FORMAT_EN_LONG, new BigDecimal("12345678901234567890.89"), "12345679 trillion",
 148                 new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 17}}
 149         };
 150     }
 151 
 152     @Test(dataProvider = "fieldPositions")
 153     public void testFormatToCharacterIterator(NumberFormat fmt, Object number,
 154             String expected, Format.Field[] expectedFields, int[] positions) {
 155         AttributedCharacterIterator iterator = fmt.formatToCharacterIterator(number);
 156         assertEquals(getText(iterator), expected, "Incorrect formatting of the number '"
 157                 + number + "'");
 158 
 159         iterator.first();
 160         // Check start and end index of the formatted string
 161         assertEquals(iterator.getBeginIndex(), 0, "Incorrect start index: "
 162                 + iterator.getBeginIndex() + " of the formatted string: " + expected);
 163         assertEquals(iterator.getEndIndex(), expected.length(), "Incorrect end index: "
 164                 + iterator.getEndIndex() + " of the formatted string: " + expected);
 165 
 166         // Check the attributes returned by the formatToCharacterIterator
 167         assertEquals(iterator.getAllAttributeKeys(), Set.of(expectedFields),
 168                 "Attributes do not match while formatting number: " + number);
 169 
 170         // Check the begin and end index for attributes
 171         iterator.first();
 172         int currentPosition = 0;
 173         do {
 174             int start = iterator.getRunStart();
 175             int end = iterator.getRunLimit();
 176             assertEquals(start, positions[currentPosition],
 177                     "Incorrect start position for the attribute(s): "
 178                     + iterator.getAttributes().keySet());
 179             assertEquals(end, positions[currentPosition + 1],
 180                     "Incorrect end position for the attribute(s): "
 181                     + iterator.getAttributes().keySet());
 182             currentPosition = currentPosition + 2;
 183             iterator.setIndex(end);
 184         } while (iterator.current() != CharacterIterator.DONE);
 185     }
 186 
 187     // Create the formatted string from returned AttributedCharacterIterator
 188     private String getText(AttributedCharacterIterator iterator) {
 189         StringBuffer buffer = new StringBuffer();
 190         for (char c = iterator.first(); c != CharacterIterator.DONE;
 191                 c = iterator.next()) {
 192             buffer.append(c);
 193         }
 194         return buffer.toString();
 195     }
 196 
 197 }