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 /*
  25  * @test
  26  * @summary Unit tests for String#align and String#indent
  27  * @run main AlignIndent
  28  */
  29 
  30 import java.util.Arrays;
  31 import java.util.List;
  32 import java.util.stream.Collectors;
  33 import java.util.stream.Stream;
  34 
  35 public class AlignIndent {
  36     static final List<String> ENDS = List.of("", "\n", "   \n", "\n\n", "\n\n\n");
  37     static final List<String> MIDDLES = List.of(
  38             "",
  39             "xyz",
  40             "   xyz",
  41             "      xyz",
  42             "xyz   ",
  43             "   xyz   ",
  44             "      xyz   ",
  45             "xyz\u2022",
  46             "   xyz\u2022",
  47             "xyz\u2022   ",
  48             "   xyz\u2022   ",
  49             "   // comment"
  50     );
  51 
  52     public static void main(String[] args) {
  53         test1();
  54         test2();
  55         test3();
  56         test4();
  57     }
  58 
  59     /*
  60      * Test String#align() functionality.
  61      */
  62     static void test1() {
  63         for (String prefix : ENDS) {
  64             for (String suffix : ENDS) {
  65                 for (String middle : MIDDLES) {
  66                     {
  67                         String input = prefix + "   abc   \n" + middle + "\n   def   \n" + suffix;
  68                         String output = input.align();
  69 
  70                         String[] inLines = input.split("\\R");
  71                         String[] outLines = output.split("\\R");
  72 
  73                         String[] inLinesBody = getBody(inLines);
  74 
  75                         if (inLinesBody.length < outLines.length) {
  76                             report("String::align()", "Result has more lines than expected", input, output);
  77                         } else if (inLinesBody.length > outLines.length) {
  78                             report("String::align()", "Result has fewer lines than expected", input, output);
  79                         }
  80 
  81                         int indent = -1;
  82                         for (int i = 0; i < inLinesBody.length; i++) {
  83                             String in = inLinesBody[i];
  84                             String out = outLines[i];
  85                             if (!out.isBlank()) {
  86                                 int offset = in.indexOf(out);
  87                                 if (offset == -1) {
  88                                     report("String::align()", "Portions of line are missing", input, output);
  89                                 }
  90                                 if (indent == -1) {
  91                                     indent = offset;
  92                                 } else if (offset != indent) {
  93                                     report("String::align()",
  94                                             "Inconsistent indentation in result", input, output);
  95                                 }
  96                             }
  97                         }
  98                     }
  99                 }
 100             }
 101         }
 102     }
 103 
 104     /*
 105      * Test String#align(int n) functionality.
 106      */
 107     static void test2() {
 108         for (int adjust : new int[] {-8, -7, -4, -3, -2, -1, 0, 1, 2, 3, 4, 7, 8}) {
 109             for (String prefix : ENDS) {
 110                 for (String suffix : ENDS) {
 111                     for (String middle : MIDDLES) {
 112                         {
 113                             String input = prefix + "   abc   \n" + middle + "\n   def   \n" + suffix;
 114                             String output = input.align(adjust);
 115                             String expected = input.align().indent(adjust);
 116 
 117                             if (!output.equals(expected)) {
 118                                 report("String::align(int n)",
 119                                         "Result inconsistent with align().indent(n)", expected, output);
 120                             }
 121                         }
 122                     }
 123                 }
 124             }
 125         }
 126     }
 127 
 128     /*
 129      * Test String#indent(int n) functionality.
 130      */
 131     static void test3() {
 132         for (int adjust : new int[] {-8, -7, -4, -3, -2, -1, 0, 1, 2, 3, 4, 7, 8}) {
 133             for (String prefix : ENDS) {
 134                 for (String suffix : ENDS) {
 135                     for (String middle : MIDDLES) {
 136                         String input = prefix + "   abc   \n" + middle + "\n   def   \n" + suffix;
 137                         String output = input.indent(adjust);
 138 
 139                         Stream<String> stream = input.lines();
 140                         if (adjust > 0) {
 141                             final String spaces = " ".repeat(adjust);
 142                             stream = stream.map(s -> s.isBlank() ? s : spaces + s);
 143                         } else if (adjust < 0) {
 144                             stream = stream.map(s -> s.substring(Math.min(-adjust, indexOfNonWhitespace(s))));
 145                         }
 146                         String expected = stream.collect(Collectors.joining("\n", "", "\n"));
 147 
 148                         if (!output.equals(expected)) {
 149                             report("String::indent(int n)",
 150                                     "Result indentation not as expected", expected, output);
 151                         }
 152                     }
 153                 }
 154             }
 155         }
 156     }
 157 
 158     /*
 159      * JDK-8212694: Using Raw String Literals with align() and Integer.MIN_VALUE causes out of memory error
 160      */
 161     static void test4() {
 162         try {
 163             String str = "\n    A\n".align(Integer.MIN_VALUE);
 164         } catch (OutOfMemoryError ex) {
 165             System.err.println("align(Integer.MIN_VALUE) not clipping indentation");
 166             throw new RuntimeException();
 167         }
 168     }
 169 
 170     public static int indexOfNonWhitespace(String s) {
 171         int left = 0;
 172         while (left < s.length()) {
 173             char ch = s.charAt(left);
 174             if (ch != ' ' && ch != '\t' && !Character.isWhitespace(ch)) {
 175                 break;
 176             }
 177             left++;
 178         }
 179         return left;
 180     }
 181 
 182 
 183     private static String[] getBody(String[] inLines) {
 184         int from = -1, to = -1;
 185         for (int i = 0; i < inLines.length; i++) {
 186             String line = inLines[i];
 187             if (!line.isBlank()) {
 188                 if (from == -1) {
 189                     from = i;
 190                 }
 191                 to = i + 1;
 192             }
 193         }
 194         return Arrays.copyOfRange(inLines, from, to);
 195     }
 196 
 197     /*
 198      * Report difference in result.
 199      */
 200     static void report(String test, String message, String input, String output) {
 201         System.err.println("Testing " + test + ": " + message);
 202         System.err.println();
 203         System.err.println("Input: length = " + input.length());
 204         System.err.println("_".repeat(40));
 205         System.err.print(input.replaceAll(" ", "."));
 206         System.err.println("_".repeat(40));
 207         System.err.println();
 208         System.err.println("Output: length = " + output.length());
 209         System.err.println("_".repeat(40));
 210         System.err.print(output.replaceAll(" ", "."));
 211         System.err.println("_".repeat(40));
 212         throw new RuntimeException();
 213     }
 214 }