1 /*
   2  * Copyright (c) 2015, 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 8058779
  26  * @run testng LiteralReplace
  27  * @summary Basic tests of String.replace(CharSequence, CharSequence)
  28  * @key randomness
  29  */
  30 
  31 import java.util.ArrayList;
  32 import java.util.Arrays;
  33 import java.util.Iterator;
  34 import java.util.regex.Matcher;
  35 import java.util.regex.Pattern;
  36 import java.util.Random;
  37 import sun.misc.JavaLangAccess;
  38 import sun.misc.SharedSecrets;
  39 
  40 import org.testng.annotations.Test;
  41 import org.testng.annotations.DataProvider;
  42 import static org.testng.Assert.fail;
  43 
  44 public class LiteralReplace {
  45 
  46     @Test(dataProvider="sourceTargetReplacementExpected")
  47     public void testExpected(String source, String target,
  48              String replacement, String expected)
  49     {
  50         String canonical = canonicalReplace(source, target, replacement);
  51         if (!canonical.equals(expected)) {
  52             fail("Canonical: " + canonical + " != " + expected);
  53         }
  54         test0(source, target, replacement, expected);
  55     }
  56 
  57     @Test(dataProvider="sourceTargetReplacement")
  58     public void testCanonical(String source, String target,
  59             String replacement)
  60     {
  61         String canonical = canonicalReplace(source, target, replacement);
  62         test0(source, target, replacement, canonical);
  63     }
  64 
  65     private void test0(String source, String target, String replacement,
  66             String expected)
  67     {
  68         String result = source.replace(target, replacement);
  69         if (!result.equals(expected)) {
  70             fail(result + " != " + expected);
  71         }
  72     }
  73 
  74     @Test(dataProvider="sourceTargetReplacementWithNull")
  75     public void testNPE(String source, String target, String replacement) {
  76         try {
  77             source.replace(target, replacement);
  78             fail("Expected to throw NPE: " + source + ".replace(" + target +
  79                     "," + replacement + ")");
  80         } catch (NullPointerException npe) {
  81         }
  82     }
  83 
  84 
  85     @DataProvider
  86     public static Object[][] sourceTargetReplacementExpected() {
  87         return new Object[][] {
  88             {"aaa", "aa", "b", "ba"},
  89             {"abcdefgh", "def", "DEF", "abcDEFgh"},
  90             {"abcdefgh", "123", "DEF", "abcdefgh"},
  91             {"abcdefgh", "abcdefghi", "DEF", "abcdefgh"},
  92             {"abcdefghabc", "abc", "DEF", "DEFdefghDEF"},
  93             {"abcdefghdef", "def", "", "abcgh"},
  94             {"abcdefgh", "", "_", "_a_b_c_d_e_f_g_h_"},
  95             {"", "", "", ""},
  96             {"", "a", "b", ""},
  97             {"", "", "abc", "abc"},
  98             {"abcdefgh", "abcdefgh", "abcdefgh", "abcdefgh"},
  99             {"abcdefgh", "abcdefgh", "abcdefghi", "abcdefghi"},
 100             {"abcdefgh", "abcdefgh", "", ""},
 101             {"abcdabcd", "abcd", "", ""},
 102             {"aaaaaaaaa", "aa", "_X_", "_X__X__X__X_a"},
 103             {"aaaaaaaaa", "aa", "aaa", "aaaaaaaaaaaaa"},
 104             {"aaaaaaaaa", "aa", "aa", "aaaaaaaaa"},
 105             {"a.c.e.g.", ".", "-", "a-c-e-g-"},
 106             {"abcdefgh", "[a-h]", "X", "abcdefgh"},
 107             {"aa+", "a+", "", "a"},
 108             {"^abc$", "abc", "x", "^x$"},
 109         };
 110     }
 111 
 112     @DataProvider
 113     public static Object[][] sourceTargetReplacementWithNull() {
 114         return new Object[][] {
 115             {"a", "a", null},
 116             {"a", "b", null},
 117             {"a", null, "a"},
 118             {"a", null, "b"},
 119             {"a", null, null},
 120             {null, "a", "a"},
 121             {null, "a", "b"},
 122             {null, "a", null},
 123             {null, null, "a"},
 124             {null, null, null},
 125         };
 126     }
 127 
 128     @DataProvider
 129     public static Iterator<Object[]> sourceTargetReplacement() {
 130         ArrayList<Object[]> list = new ArrayList<>();
 131         for (int maxSrcLen = 1; maxSrcLen <= (1 << 10); maxSrcLen <<= 1) {
 132             for (int maxTrgLen = 1; maxTrgLen <= (1 << 10); maxTrgLen <<= 1) {
 133                 for (int maxPrlLen = 1; maxPrlLen <= (1 << 10); maxPrlLen <<= 1) {
 134                     list.add(makeArray(makeRandomString(maxSrcLen),
 135                                        makeRandomString(maxTrgLen),
 136                                        makeRandomString(maxPrlLen)));
 137 
 138                     String source = makeRandomString(maxSrcLen);
 139                     list.add(makeArray(source,
 140                                        mekeRandomSubstring(source, maxTrgLen),
 141                                        makeRandomString(maxPrlLen)));
 142                 }
 143             }
 144         }
 145         return list.iterator();
 146     }
 147 
 148     // utilities
 149 
 150     /**
 151      * How the String.replace(CharSequence, CharSequence) used to be implemented
 152      */
 153     private static String canonicalReplace(String source, String target, String replacement) {
 154         return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
 155                 source).replaceAll(Matcher.quoteReplacement(replacement.toString()));
 156     }
 157 
 158     private static final Random random = new Random();
 159     private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 160 
 161     private static final char[] CHARS = ("qwertyuiop[]12345678" +
 162         "90-=\\`asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+|QWERTYUIOP{" +
 163         "}ASDFGHJKL:\"ZXCVBNM<>?\n\r\t\u0444\u044B\u0432\u0430").toCharArray();
 164 
 165     private static String makeRandomString(int maxLen) {
 166         int len = random.nextInt(maxLen);
 167         char[] buf = new char[len];
 168         for (int i = 0; i < len; ++i) {
 169             buf[i] = CHARS[random.nextInt(CHARS.length)];
 170         }
 171         return jla.newStringUnsafe(buf);
 172     }
 173 
 174     private static String mekeRandomSubstring(String source, int maxLen) {
 175         if (source.isEmpty()) {
 176             return source;
 177         }
 178         int pos = random.nextInt(source.length());
 179         int len = Integer.min(source.length() - pos,
 180                               random.nextInt(maxLen));
 181         return source.substring(pos, pos + len);
 182     }
 183 
 184     private static Object[] makeArray(Object... array) {
 185          return array;
 186     }
 187 }