38 import java.util.Spliterator; |
38 import java.util.Spliterator; |
39 import java.util.StringJoiner; |
39 import java.util.StringJoiner; |
40 import java.util.regex.Matcher; |
40 import java.util.regex.Matcher; |
41 import java.util.regex.Pattern; |
41 import java.util.regex.Pattern; |
42 import java.util.regex.PatternSyntaxException; |
42 import java.util.regex.PatternSyntaxException; |
|
43 import java.util.stream.Collectors; |
43 import java.util.stream.IntStream; |
44 import java.util.stream.IntStream; |
44 import java.util.stream.Stream; |
45 import java.util.stream.Stream; |
45 import java.util.stream.StreamSupport; |
46 import java.util.stream.StreamSupport; |
46 import jdk.internal.HotSpotIntrinsicCandidate; |
47 import jdk.internal.HotSpotIntrinsicCandidate; |
47 import jdk.internal.vm.annotation.Stable; |
48 import jdk.internal.vm.annotation.Stable; |
|
49 |
|
50 import static java.util.function.Predicate.not; |
48 |
51 |
49 /** |
52 /** |
50 * The {@code String} class represents character strings. All |
53 * The {@code String} class represents character strings. All |
51 * string literals in Java programs, such as {@code "abc"}, are |
54 * string literals in Java programs, such as {@code "abc"}, are |
52 * implemented as instances of this class. |
55 * implemented as instances of this class. |
2792 * @return the stream of lines extracted from this string |
2792 * @return the stream of lines extracted from this string |
2793 * |
2793 * |
2794 * @since 11 |
2794 * @since 11 |
2795 */ |
2795 */ |
2796 public Stream<String> lines() { |
2796 public Stream<String> lines() { |
2797 return isLatin1() ? StringLatin1.lines(value) |
2797 return lines(0, 0); |
2798 : StringUTF16.lines(value); |
2798 } |
|
2799 |
|
2800 /** |
|
2801 * Adjusts the indentation of each line of this string based on the value of |
|
2802 * {@code n}, and normalizes line termination characters. |
|
2803 * <p> |
|
2804 * This string is conceptually separated into lines using |
|
2805 * {@link String#lines()}. Each line is then adjusted as described below |
|
2806 * and then suffixed with a line feed {@code "\n"} (U+000A). The resulting |
|
2807 * lines are then concatenated and returned. |
|
2808 * <p> |
|
2809 * If {@code n > 0} then {@code n} spaces (U+0020) are inserted at the |
|
2810 * beginning of each line. {@link String#isBlank() Blank lines} are |
|
2811 * unaffected. |
|
2812 * <p> |
|
2813 * If {@code n < 0} then up to {@code n} |
|
2814 * {@link Character#isWhitespace(int) white space characters} are removed |
|
2815 * from the beginning of each line. If a given line does not contain |
|
2816 * sufficient white space then all leading |
|
2817 * {@link Character#isWhitespace(int) white space characters} are removed. |
|
2818 * Each white space character is treated as a single character. In |
|
2819 * particular, the tab character {@code "\t"} (U+0009) is considered a |
|
2820 * single character; it is not expanded. |
|
2821 * <p> |
|
2822 * If {@code n == 0} then the line remains unchanged. However, line |
|
2823 * terminators are still normalized. |
|
2824 * <p> |
|
2825 * |
|
2826 * @param n number of leading |
|
2827 * {@link Character#isWhitespace(int) white space characters} |
|
2828 * to add or remove |
|
2829 * |
|
2830 * @return string with indentation adjusted and line endings normalized |
|
2831 * |
|
2832 * @see String#lines() |
|
2833 * @see String#isBlank() |
|
2834 * @see Character#isWhitespace(int) |
|
2835 * |
|
2836 * @since 12 |
|
2837 */ |
|
2838 public String indent(int n) { |
|
2839 return isEmpty() ? "" : indent(n, false); |
|
2840 } |
|
2841 |
|
2842 private String indent(int n, boolean removeBlanks) { |
|
2843 Stream<String> stream = removeBlanks ? lines(Integer.MAX_VALUE, Integer.MAX_VALUE) |
|
2844 : lines(); |
|
2845 if (n > 0) { |
|
2846 final String spaces = " ".repeat(n); |
|
2847 stream = stream.map(s -> s.isBlank() ? s : spaces + s); |
|
2848 } else if (n == Integer.MIN_VALUE) { |
|
2849 stream = stream.map(s -> s.stripLeading()); |
|
2850 } else if (n < 0) { |
|
2851 stream = stream.map(s -> s.substring(Math.min(-n, s.indexOfNonWhitespace()))); |
|
2852 } |
|
2853 return stream.collect(Collectors.joining("\n", "", "\n")); |
|
2854 } |
|
2855 |
|
2856 private int indexOfNonWhitespace() { |
|
2857 return isLatin1() ? StringLatin1.indexOfNonWhitespace(value) |
|
2858 : StringUTF16.indexOfNonWhitespace(value); |
|
2859 } |
|
2860 |
|
2861 private int lastIndexOfNonWhitespace() { |
|
2862 return isLatin1() ? StringLatin1.lastIndexOfNonWhitespace(value) |
|
2863 : StringUTF16.lastIndexOfNonWhitespace(value); |
|
2864 } |
|
2865 |
|
2866 /** |
|
2867 * Removes vertical and horizontal white space margins from around the |
|
2868 * essential body of a multi-line string, while preserving relative |
|
2869 * indentation. |
|
2870 * <p> |
|
2871 * This string is first conceptually separated into lines as if by |
|
2872 * {@link String#lines()}. |
|
2873 * <p> |
|
2874 * Then, the <i>minimum indentation</i> (min) is determined as follows. For |
|
2875 * each non-blank line (as defined by {@link String#isBlank()}), the |
|
2876 * leading {@link Character#isWhitespace(int) white space} characters are |
|
2877 * counted. The <i>min</i> value is the smallest of these counts. |
|
2878 * <p> |
|
2879 * For each non-blank line, <i>min</i> leading white space characters are |
|
2880 * removed. Each white space character is treated as a single character. In |
|
2881 * particular, the tab character {@code "\t"} (U+0009) is considered a |
|
2882 * single character; it is not expanded. |
|
2883 * <p> |
|
2884 * Leading and trailing blank lines, if any, are removed. Trailing spaces are |
|
2885 * preserved. |
|
2886 * <p> |
|
2887 * Each line is suffixed with a line feed character {@code "\n"} (U+000A). |
|
2888 * <p> |
|
2889 * Finally, the lines are concatenated into a single string and returned. |
|
2890 * |
|
2891 * @apiNote |
|
2892 * This method's primary purpose is to shift a block of lines as far as |
|
2893 * possible to the left, while preserving relative indentation. Lines |
|
2894 * that were indented the least will thus have no leading white space. |
|
2895 * |
|
2896 * Example: |
|
2897 * <blockquote><pre> |
|
2898 * ` |
|
2899 * This is the first line |
|
2900 * This is the second line |
|
2901 * `.align(); |
|
2902 * |
|
2903 * returns |
|
2904 * This is the first line |
|
2905 * This is the second line |
|
2906 * </pre></blockquote> |
|
2907 * |
|
2908 * @return string with margins removed and line terminators normalized |
|
2909 * |
|
2910 * @see String#lines() |
|
2911 * @see String#isBlank() |
|
2912 * @see String#indent(int) |
|
2913 * @see Character#isWhitespace(int) |
|
2914 * |
|
2915 * @since 12 |
|
2916 */ |
|
2917 public String align() { |
|
2918 return align(0); |
|
2919 } |
|
2920 |
|
2921 /** |
|
2922 * Removes vertical and horizontal white space margins from around the |
|
2923 * essential body of a multi-line string, while preserving relative |
|
2924 * indentation and with optional indentation adjustment. |
|
2925 * <p> |
|
2926 * Invoking this method is equivalent to: |
|
2927 * <blockquote> |
|
2928 * {@code this.align().indent(n)} |
|
2929 * </blockquote> |
|
2930 * |
|
2931 * @apiNote |
|
2932 * Examples: |
|
2933 * <blockquote><pre> |
|
2934 * ` |
|
2935 * This is the first line |
|
2936 * This is the second line |
|
2937 * `.align(0); |
|
2938 * |
|
2939 * returns |
|
2940 * This is the first line |
|
2941 * This is the second line |
|
2942 * |
|
2943 * |
|
2944 * ` |
|
2945 * This is the first line |
|
2946 * This is the second line |
|
2947 * `.align(4); |
|
2948 * returns |
|
2949 * This is the first line |
|
2950 * This is the second line |
|
2951 * </pre></blockquote> |
|
2952 * |
|
2953 * @param n number of leading white space characters |
|
2954 * to add or remove |
|
2955 * |
|
2956 * @return string with margins removed, indentation adjusted and |
|
2957 * line terminators normalized |
|
2958 * |
|
2959 * @see String#align() |
|
2960 * |
|
2961 * @since 12 |
|
2962 */ |
|
2963 public String align(int n) { |
|
2964 if (isEmpty()) { |
|
2965 return ""; |
|
2966 } |
|
2967 int outdent = lines().filter(not(String::isBlank)) |
|
2968 .mapToInt(String::indexOfNonWhitespace) |
|
2969 .min() |
|
2970 .orElse(0); |
|
2971 return indent(n - outdent, true); |
2799 } |
2972 } |
2800 |
2973 |
2801 /** |
2974 /** |
2802 * This object (which is already a string!) is itself returned. |
2975 * This object (which is already a string!) is itself returned. |
2803 * |
2976 * |