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", null, "a"},
 117             {"a", null, null},
 118             {null, "a", "a"},
 119             {null, "a", null},
 120             {null, null, "a"},
 121             {null, null, null},
 122         };
 123     }
 124 
 125     @DataProvider
 126     public static Iterator<Object[]> sourceTargetReplacement() {
 127         ArrayList<Object[]> list = new ArrayList<>();
 128         for (int maxSrcLen = 1; maxSrcLen <= (1 << 10); maxSrcLen <<= 1) {
 129             for (int maxTrgLen = 1; maxTrgLen <= (1 << 10); maxTrgLen <<= 1) {
 130                 for (int maxPrlLen = 1; maxPrlLen <= (1 << 10); maxPrlLen <<= 1) {
 131                     list.add(makeArray(makeRandomString(maxSrcLen),
 132                                        makeRandomString(maxTrgLen),
 133                                        makeRandomString(maxPrlLen)));
 134 
 135                     String source = makeRandomString(maxSrcLen);
 136                     list.add(makeArray(source,
 137                                        mekeRandomSubstring(source, maxTrgLen),
 138                                        makeRandomString(maxPrlLen)));
 139                 }
 140             }
 141         }
 142         return list.iterator();
 143     }
 144 
 145     // utilities
 146 
 147     /**
 148      * How the String.replace(CharSequence, CharSequence) used to be implemented
 149      */
 150     private static String canonicalReplace(String source, String target, String replacement) {
 151         return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
 152                 source).replaceAll(Matcher.quoteReplacement(replacement.toString()));
 153     }
 154 
 155     private static final Random random = new Random();
 156     private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
 157 
 158     private static final char[] CHARS = ("qwertyuiop[]12345678" +
 159         "90-=\\`asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+|QWERTYUIOP{" +
 160         "}ASDFGHJKL:\"ZXCVBNM<>?\n\r\t\u0444\u044B\u0432\u0430").toCharArray();
 161 
 162     private static String makeRandomString(int maxLen) {
 163         int len = random.nextInt(maxLen);
 164         char[] buf = new char[len];
 165         for (int i = 0; i < len; ++i) {
 166             buf[i] = CHARS[random.nextInt(CHARS.length)];
 167         }
 168         return jla.newStringUnsafe(buf);
 169     }
 170 
 171     private static String mekeRandomSubstring(String source, int maxLen) {
 172         if (source.isEmpty()) {
 173             return source;
 174         }
 175         int pos = random.nextInt(source.length());
 176         int len = Integer.min(source.length() - pos,
 177                               random.nextInt(maxLen));
 178         return source.substring(pos, pos + len);
 179     }
 180 
 181     private static Object[] makeArray(Object... array) {
 182          return array;
 183     }
 184 }