• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.app.appsearch.util;
18 
19 import android.app.appsearch.annotation.CanIgnoreReturnValue;
20 
21 import org.jspecify.annotations.NonNull;
22 
23 /**
24  * Utility for building indented strings.
25  *
26  * <p>This is a wrapper for {@link StringBuilder} for appending strings with indentation. The
27  * indentation level can be increased by calling {@link #increaseIndentLevel()} and decreased by
28  * calling {@link #decreaseIndentLevel()}.
29  *
30  * <p>Indentation is applied after each newline character for the given indent level.
31  *
32  * @hide
33  */
34 public class IndentingStringBuilder {
35     private final StringBuilder mStringBuilder = new StringBuilder();
36 
37     // Indicates whether next non-newline character should have an indent applied before it.
38     private boolean mIndentNext = false;
39     private int mIndentLevel = 0;
40 
41     /** Increases the indent level by one for appended strings. */
42     @CanIgnoreReturnValue
increaseIndentLevel()43     public @NonNull IndentingStringBuilder increaseIndentLevel() {
44         mIndentLevel++;
45         return this;
46     }
47 
48     /** Decreases the indent level by one for appended strings. */
49     @CanIgnoreReturnValue
decreaseIndentLevel()50     public @NonNull IndentingStringBuilder decreaseIndentLevel() throws IllegalStateException {
51         if (mIndentLevel == 0) {
52             throw new IllegalStateException("Cannot set indent level below 0.");
53         }
54         mIndentLevel--;
55         return this;
56     }
57 
58     /**
59      * Appends provided {@code String} at the current indentation level.
60      *
61      * <p>Indentation is applied after each newline character.
62      */
63     @CanIgnoreReturnValue
append(@onNull String str)64     public @NonNull IndentingStringBuilder append(@NonNull String str) {
65         applyIndentToString(str);
66         return this;
67     }
68 
69     /**
70      * Appends provided {@code Object}, represented as a {@code String}, at the current indentation
71      * level.
72      *
73      * <p>Indentation is applied after each newline character.
74      */
75     @CanIgnoreReturnValue
append(@onNull Object obj)76     public @NonNull IndentingStringBuilder append(@NonNull Object obj) {
77         applyIndentToString(obj.toString());
78         return this;
79     }
80 
81     @Override
toString()82     public @NonNull String toString() {
83         return mStringBuilder.toString();
84     }
85 
86     /** Adds indent string to the {@link StringBuilder} instance for current indent level. */
applyIndent()87     private void applyIndent() {
88         for (int i = 0; i < mIndentLevel; i++) {
89             mStringBuilder.append("  ");
90         }
91     }
92 
93     /**
94      * Applies indent, for current indent level, after each newline character.
95      *
96      * <p>Consecutive newline characters are not indented.
97      */
applyIndentToString(@onNull String str)98     private void applyIndentToString(@NonNull String str) {
99         int index = str.indexOf("\n");
100         if (index == 0) {
101             // String begins with new line character: append newline and slide past newline.
102             mStringBuilder.append("\n");
103             mIndentNext = true;
104             if (str.length() > 1) {
105                 applyIndentToString(str.substring(index + 1));
106             }
107         } else if (index >= 1) {
108             // String contains new line character: divide string between newline, append new line,
109             // and recurse on each string.
110             String beforeIndentString = str.substring(0, index);
111             applyIndentToString(beforeIndentString);
112             mStringBuilder.append("\n");
113             mIndentNext = true;
114             if (str.length() > index + 1) {
115                 String afterIndentString = str.substring(index + 1);
116                 applyIndentToString(afterIndentString);
117             }
118         } else {
119             // String does not contain newline character: append string.
120             if (mIndentNext) {
121                 applyIndent();
122                 mIndentNext = false;
123             }
124             mStringBuilder.append(str);
125         }
126     }
127 }
128