2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2017 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 * StringJustifier contains methods to convert one or more long lines of
36 * strings into justified text paragraphs.
38 public final class StringJustifier
{
41 * Left-justify a string into a list of lines.
43 * @param str the string
44 * @param n the maximum number of characters in a line
45 * @return the list of lines
47 public static List
<String
> left(final String str
, final int n
) {
48 List
<String
> result
= new LinkedList
<String
>();
53 * 1. Split on '\n' into paragraphs.
55 * 2. Scan each line, noting the position of the last
56 * beginning-of-a-word.
58 * 3. Chop at the last #2 if the next beginning-of-a-word exceeds
61 * 4. Return the lines.
64 String
[] rawLines
= str
.split("\n");
65 for (int i
= 0; i
< rawLines
.length
; i
++) {
66 StringBuilder line
= new StringBuilder();
67 StringBuilder word
= new StringBuilder();
68 boolean inWord
= false;
69 for (int j
= 0; j
< rawLines
[i
].length(); j
++) {
70 char ch
= rawLines
[i
].charAt(j
);
71 if ((ch
== ' ') || (ch
== '\t')) {
73 // We have just transitioned from a word to
74 // whitespace. See if we have enough space to add
75 // the word to the line.
76 if (word
.length() + line
.length() > n
) {
77 // This word will exceed the line length. Wrap
79 result
.add(line
.toString());
80 line
= new StringBuilder();
82 if ((word
.toString().startsWith(" "))
83 && (line
.length() == 0)
85 line
.append(word
.substring(1));
89 word
= new StringBuilder();
93 // We are in the whitespace before another word. Do
98 // We are appending to a word.
101 // We have transitioned from whitespace to a word.
106 } // for (int j = 0; j < rawLines[i].length(); j++)
108 if (word
.length() + line
.length() > n
) {
109 // This word will exceed the line length. Wrap at it
111 result
.add(line
.toString());
112 line
= new StringBuilder();
114 if ((word
.toString().startsWith(" "))
115 && (line
.length() == 0)
117 line
.append(word
.substring(1));
121 result
.add(line
.toString());
122 } // for (int i = 0; i < rawLines.length; i++) {
128 * Right-justify a string into a list of lines.
130 * @param str the string
131 * @param n the maximum number of characters in a line
132 * @return the list of lines
134 public static List
<String
> right(final String str
, final int n
) {
135 List
<String
> result
= new LinkedList
<String
>();
138 * Same as left(), but preceed each line with spaces to make it n
141 List
<String
> lines
= left(str
, n
);
142 for (String line
: lines
) {
143 StringBuilder sb
= new StringBuilder();
144 for (int i
= 0; i
< n
- line
.length(); i
++) {
148 result
.add(sb
.toString());
155 * Center a string into a list of lines.
157 * @param str the string
158 * @param n the maximum number of characters in a line
159 * @return the list of lines
161 public static List
<String
> center(final String str
, final int n
) {
162 List
<String
> result
= new LinkedList
<String
>();
165 * Same as left(), but preceed/succeed each line with spaces to make
168 List
<String
> lines
= left(str
, n
);
169 for (String line
: lines
) {
170 StringBuilder sb
= new StringBuilder();
171 int l
= (n
- line
.length()) / 2;
172 int r
= n
- line
.length() - l
;
173 for (int i
= 0; i
< l
; i
++) {
177 for (int i
= 0; i
< r
; i
++) {
180 result
.add(sb
.toString());
187 * Fully-justify a string into a list of lines.
189 * @param str the string
190 * @param n the maximum number of characters in a line
191 * @return the list of lines
193 public static List
<String
> full(final String str
, final int n
) {
194 List
<String
> result
= new LinkedList
<String
>();
197 * Same as left(), but insert spaces between words to make each line
198 * n chars long. The "algorithm" here is pretty dumb: it performs a
199 * split on space and then re-inserts multiples of n between words.
201 List
<String
> lines
= left(str
, n
);
202 for (int lineI
= 0; lineI
< lines
.size() - 1; lineI
++) {
203 String line
= lines
.get(lineI
);
204 String
[] words
= line
.split(" ");
205 if (words
.length
> 1) {
207 for (int i
= 0; i
< words
.length
; i
++) {
208 charCount
+= words
[i
].length();
210 int spaceCount
= n
- charCount
;
211 int q
= spaceCount
/ (words
.length
- 1);
212 int r
= spaceCount
% (words
.length
- 1);
213 StringBuilder sb
= new StringBuilder();
214 for (int i
= 0; i
< words
.length
- 1; i
++) {
216 for (int j
= 0; j
< q
; j
++) {
224 for (int j
= 0; j
< r
; j
++) {
227 sb
.append(words
[words
.length
- 1]);
228 result
.add(sb
.toString());
233 if (lines
.size() > 0) {
234 result
.add(lines
.get(lines
.size() - 1));