2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.util
.List
;
32 import java
.util
.LinkedList
;
35 * StringUtils contains methods to:
37 * - Convert one or more long lines of strings into justified text
40 * - Unescape C0 control codes.
43 public class StringUtils
{
46 * Left-justify a string into a list of lines.
48 * @param str the string
49 * @param n the maximum number of characters in a line
50 * @return the list of lines
52 public static List
<String
> left(final String str
, final int n
) {
53 List
<String
> result
= new LinkedList
<String
>();
58 * 1. Split on '\n' into paragraphs.
60 * 2. Scan each line, noting the position of the last
61 * beginning-of-a-word.
63 * 3. Chop at the last #2 if the next beginning-of-a-word exceeds
66 * 4. Return the lines.
69 String
[] rawLines
= str
.split("\n");
70 for (int i
= 0; i
< rawLines
.length
; i
++) {
71 StringBuilder line
= new StringBuilder();
72 StringBuilder word
= new StringBuilder();
73 boolean inWord
= false;
74 for (int j
= 0; j
< rawLines
[i
].length(); j
++) {
75 char ch
= rawLines
[i
].charAt(j
);
76 if ((ch
== ' ') || (ch
== '\t')) {
78 // We have just transitioned from a word to
79 // whitespace. See if we have enough space to add
80 // the word to the line.
81 if (word
.length() + line
.length() > n
) {
82 // This word will exceed the line length. Wrap
84 result
.add(line
.toString());
85 line
= new StringBuilder();
87 if ((word
.toString().startsWith(" "))
88 && (line
.length() == 0)
90 line
.append(word
.substring(1));
94 word
= new StringBuilder();
98 // We are in the whitespace before another word. Do
102 if (inWord
== true) {
103 // We are appending to a word.
106 // We have transitioned from whitespace to a word.
111 } // for (int j = 0; j < rawLines[i].length(); j++)
113 if (word
.length() + line
.length() > n
) {
114 // This word will exceed the line length. Wrap at it
116 result
.add(line
.toString());
117 line
= new StringBuilder();
119 if ((word
.toString().startsWith(" "))
120 && (line
.length() == 0)
122 line
.append(word
.substring(1));
126 result
.add(line
.toString());
127 } // for (int i = 0; i < rawLines.length; i++) {
133 * Right-justify a string into a list of lines.
135 * @param str the string
136 * @param n the maximum number of characters in a line
137 * @return the list of lines
139 public static List
<String
> right(final String str
, final int n
) {
140 List
<String
> result
= new LinkedList
<String
>();
143 * Same as left(), but preceed each line with spaces to make it n
146 List
<String
> lines
= left(str
, n
);
147 for (String line
: lines
) {
148 StringBuilder sb
= new StringBuilder();
149 for (int i
= 0; i
< n
- line
.length(); i
++) {
153 result
.add(sb
.toString());
160 * Center a string into a list of lines.
162 * @param str the string
163 * @param n the maximum number of characters in a line
164 * @return the list of lines
166 public static List
<String
> center(final String str
, final int n
) {
167 List
<String
> result
= new LinkedList
<String
>();
170 * Same as left(), but preceed/succeed each line with spaces to make
173 List
<String
> lines
= left(str
, n
);
174 for (String line
: lines
) {
175 StringBuilder sb
= new StringBuilder();
176 int l
= (n
- line
.length()) / 2;
177 int r
= n
- line
.length() - l
;
178 for (int i
= 0; i
< l
; i
++) {
182 for (int i
= 0; i
< r
; i
++) {
185 result
.add(sb
.toString());
192 * Fully-justify a string into a list of lines.
194 * @param str the string
195 * @param n the maximum number of characters in a line
196 * @return the list of lines
198 public static List
<String
> full(final String str
, final int n
) {
199 List
<String
> result
= new LinkedList
<String
>();
202 * Same as left(), but insert spaces between words to make each line
203 * n chars long. The "algorithm" here is pretty dumb: it performs a
204 * split on space and then re-inserts multiples of n between words.
206 List
<String
> lines
= left(str
, n
);
207 for (int lineI
= 0; lineI
< lines
.size() - 1; lineI
++) {
208 String line
= lines
.get(lineI
);
209 String
[] words
= line
.split(" ");
210 if (words
.length
> 1) {
212 for (int i
= 0; i
< words
.length
; i
++) {
213 charCount
+= words
[i
].length();
215 int spaceCount
= n
- charCount
;
216 int q
= spaceCount
/ (words
.length
- 1);
217 int r
= spaceCount
% (words
.length
- 1);
218 StringBuilder sb
= new StringBuilder();
219 for (int i
= 0; i
< words
.length
- 1; i
++) {
221 for (int j
= 0; j
< q
; j
++) {
229 for (int j
= 0; j
< r
; j
++) {
232 sb
.append(words
[words
.length
- 1]);
233 result
.add(sb
.toString());
238 if (lines
.size() > 0) {
239 result
.add(lines
.get(lines
.size() - 1));
246 * Convert raw strings into escaped strings that be splatted on the
249 * @param str the string
250 * @return a string that can be passed into Screen.putStringXY()
252 public static String
unescape(final String str
) {
253 StringBuilder sb
= new StringBuilder();
254 for (int i
= 0; i
< str
.length(); i
++) {
255 char ch
= str
.charAt(i
);
256 if ((ch
< 0x20) || (ch
== 0x7F)) {
283 return sb
.toString();