< prev index next >
src/java.base/share/classes/java/lang/String.java
Print this page
rev 51519 : 8200434: String::align, String::indent
Reviewed-by: smarks
@@ -38,16 +38,19 @@
import java.util.Spliterator;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.vm.annotation.Stable;
+import static java.util.function.Predicate.not;
+
/**
* The {@code String} class represents character strings. All
* string literals in Java programs, such as {@code "abc"}, are
* implemented as instances of this class.
* <p>
@@ -2753,16 +2756,13 @@
*/
public boolean isBlank() {
return indexOfNonWhitespace() == length();
}
- private int indexOfNonWhitespace() {
- if (isLatin1()) {
- return StringLatin1.indexOfNonWhitespace(value);
- } else {
- return StringUTF16.indexOfNonWhitespace(value);
- }
+ private Stream<String> lines(int maxLeading, int maxTrailing) {
+ return isLatin1() ? StringLatin1.lines(value, maxLeading, maxTrailing)
+ : StringUTF16.lines(value, maxLeading, maxTrailing);
}
/**
* Returns a stream of lines extracted from this string,
* separated by line terminators.
@@ -2792,12 +2792,185 @@
* @return the stream of lines extracted from this string
*
* @since 11
*/
public Stream<String> lines() {
- return isLatin1() ? StringLatin1.lines(value)
- : StringUTF16.lines(value);
+ return lines(0, 0);
+ }
+
+ /**
+ * Adjusts the indentation of each line of this string based on the value of
+ * {@code n}, and normalizes line termination characters.
+ * <p>
+ * This string is conceptually separated into lines using
+ * {@link String#lines()}. Each line is then adjusted as described below
+ * and then suffixed with a line feed {@code "\n"} (U+000A). The resulting
+ * lines are then concatenated and returned.
+ * <p>
+ * If {@code n > 0} then {@code n} spaces (U+0020) are inserted at the
+ * beginning of each line. {@link String#isBlank() Blank lines} are
+ * unaffected.
+ * <p>
+ * If {@code n < 0} then up to {@code n}
+ * {@link Character#isWhitespace(int) white space characters} are removed
+ * from the beginning of each line. If a given line does not contain
+ * sufficient white space then all leading
+ * {@link Character#isWhitespace(int) white space characters} are removed.
+ * Each white space character is treated as a single character. In
+ * particular, the tab character {@code "\t"} (U+0009) is considered a
+ * single character; it is not expanded.
+ * <p>
+ * If {@code n == 0} then the line remains unchanged. However, line
+ * terminators are still normalized.
+ * <p>
+ *
+ * @param n number of leading
+ * {@link Character#isWhitespace(int) white space characters}
+ * to add or remove
+ *
+ * @return string with indentation adjusted and line endings normalized
+ *
+ * @see String#lines()
+ * @see String#isBlank()
+ * @see Character#isWhitespace(int)
+ *
+ * @since 12
+ */
+ public String indent(int n) {
+ return isEmpty() ? "" : indent(n, false);
+ }
+
+ private String indent(int n, boolean removeBlanks) {
+ Stream<String> stream = removeBlanks ? lines(Integer.MAX_VALUE, Integer.MAX_VALUE)
+ : lines();
+ if (n > 0) {
+ final String spaces = " ".repeat(n);
+ stream = stream.map(s -> s.isBlank() ? s : spaces + s);
+ } else if (n == Integer.MIN_VALUE) {
+ stream = stream.map(s -> s.stripLeading());
+ } else if (n < 0) {
+ stream = stream.map(s -> s.substring(Math.min(-n, s.indexOfNonWhitespace())));
+ }
+ return stream.collect(Collectors.joining("\n", "", "\n"));
+ }
+
+ private int indexOfNonWhitespace() {
+ return isLatin1() ? StringLatin1.indexOfNonWhitespace(value)
+ : StringUTF16.indexOfNonWhitespace(value);
+ }
+
+ private int lastIndexOfNonWhitespace() {
+ return isLatin1() ? StringLatin1.lastIndexOfNonWhitespace(value)
+ : StringUTF16.lastIndexOfNonWhitespace(value);
+ }
+
+ /**
+ * Removes vertical and horizontal white space margins from around the
+ * essential body of a multi-line string, while preserving relative
+ * indentation.
+ * <p>
+ * This string is first conceptually separated into lines as if by
+ * {@link String#lines()}.
+ * <p>
+ * Then, the <i>minimum indentation</i> (min) is determined as follows. For
+ * each non-blank line (as defined by {@link String#isBlank()}), the
+ * leading {@link Character#isWhitespace(int) white space} characters are
+ * counted. The <i>min</i> value is the smallest of these counts.
+ * <p>
+ * For each non-blank line, <i>min</i> leading white space characters are
+ * removed. Each white space character is treated as a single character. In
+ * particular, the tab character {@code "\t"} (U+0009) is considered a
+ * single character; it is not expanded.
+ * <p>
+ * Leading and trailing blank lines, if any, are removed. Trailing spaces are
+ * preserved.
+ * <p>
+ * Each line is suffixed with a line feed character {@code "\n"} (U+000A).
+ * <p>
+ * Finally, the lines are concatenated into a single string and returned.
+ *
+ * @apiNote
+ * This method's primary purpose is to shift a block of lines as far as
+ * possible to the left, while preserving relative indentation. Lines
+ * that were indented the least will thus have no leading white space.
+ *
+ * Example:
+ * <blockquote><pre>
+ * `
+ * This is the first line
+ * This is the second line
+ * `.align();
+ *
+ * returns
+ * This is the first line
+ * This is the second line
+ * </pre></blockquote>
+ *
+ * @return string with margins removed and line terminators normalized
+ *
+ * @see String#lines()
+ * @see String#isBlank()
+ * @see String#indent(int)
+ * @see Character#isWhitespace(int)
+ *
+ * @since 12
+ */
+ public String align() {
+ return align(0);
+ }
+
+ /**
+ * Removes vertical and horizontal white space margins from around the
+ * essential body of a multi-line string, while preserving relative
+ * indentation and with optional indentation adjustment.
+ * <p>
+ * Invoking this method is equivalent to:
+ * <blockquote>
+ * {@code this.align().indent(n)}
+ * </blockquote>
+ *
+ * @apiNote
+ * Examples:
+ * <blockquote><pre>
+ * `
+ * This is the first line
+ * This is the second line
+ * `.align(0);
+ *
+ * returns
+ * This is the first line
+ * This is the second line
+ *
+ *
+ * `
+ * This is the first line
+ * This is the second line
+ * `.align(4);
+ * returns
+ * This is the first line
+ * This is the second line
+ * </pre></blockquote>
+ *
+ * @param n number of leading white space characters
+ * to add or remove
+ *
+ * @return string with margins removed, indentation adjusted and
+ * line terminators normalized
+ *
+ * @see String#align()
+ *
+ * @since 12
+ */
+ public String align(int n) {
+ if (isEmpty()) {
+ return "";
+ }
+ int outdent = lines().filter(not(String::isBlank))
+ .mapToInt(String::indexOfNonWhitespace)
+ .min()
+ .orElse(0);
+ return indent(n - outdent, true);
}
/**
* This object (which is already a string!) is itself returned.
*
< prev index next >