StringUtils: add a new (failed) test
[fanfix.git] / src / be / nikiroo / utils / StringJustifier.java
CommitLineData
cc3e7291
NR
1/*
2 * This file was taken from:
3 * Jexer - Java Text User Interface
4 *
5 * The MIT License (MIT)
6 *
7 * Copyright (C) 2017 Kevin Lamonte
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 *
27 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
28 * @version 1
29 */
30package be.nikiroo.utils;
31
32import java.util.LinkedList;
33import java.util.List;
34
35/**
36 * StringJustifier contains methods to convert one or more long lines of strings
37 * into justified text paragraphs.
38 */
39class StringJustifier {
6a493e05
NR
40 /**
41 * Process the given text into a list of left-justified lines of a given
42 * max-width.
43 *
44 * @param data
45 * the text to justify
46 * @param width
47 * the maximum width of a line
48 *
49 * @return the list of justified lines
50 */
771c4ba4 51 static List<String> left(final String data, final int width) {
620f7329 52 return left(data, width, false);
cc3e7291
NR
53 }
54
55 /**
56 * Right-justify a string into a list of lines.
57 *
58 * @param str
59 * the string
60 * @param n
61 * the maximum number of characters in a line
62 * @return the list of lines
63 */
64 static List<String> right(final String str, final int n) {
65 List<String> result = new LinkedList<String>();
66
67 /*
68 * Same as left(), but preceed each line with spaces to make it n chars
69 * long.
70 */
71 List<String> lines = left(str, n);
72 for (String line : lines) {
73 StringBuilder sb = new StringBuilder();
74 for (int i = 0; i < n - line.length(); i++) {
75 sb.append(' ');
76 }
77 sb.append(line);
78 result.add(sb.toString());
79 }
80
81 return result;
82 }
83
84 /**
85 * Center a string into a list of lines.
86 *
87 * @param str
88 * the string
89 * @param n
90 * the maximum number of characters in a line
91 * @return the list of lines
92 */
93 static List<String> center(final String str, final int n) {
94 List<String> result = new LinkedList<String>();
95
96 /*
97 * Same as left(), but preceed/succeed each line with spaces to make it
98 * n chars long.
99 */
100 List<String> lines = left(str, n);
101 for (String line : lines) {
102 StringBuilder sb = new StringBuilder();
103 int l = (n - line.length()) / 2;
104 int r = n - line.length() - l;
105 for (int i = 0; i < l; i++) {
106 sb.append(' ');
107 }
108 sb.append(line);
109 for (int i = 0; i < r; i++) {
110 sb.append(' ');
111 }
112 result.add(sb.toString());
113 }
114
115 return result;
116 }
117
118 /**
119 * Fully-justify a string into a list of lines.
120 *
121 * @param str
122 * the string
123 * @param n
124 * the maximum number of characters in a line
125 * @return the list of lines
126 */
127 static List<String> full(final String str, final int n) {
128 List<String> result = new LinkedList<String>();
129
130 /*
620f7329
NR
131 * Same as left(true), but insert spaces between words to make each line
132 * n chars long. The "algorithm" here is pretty dumb: it performs a
133 * split on space and then re-inserts multiples of n between words.
cc3e7291 134 */
620f7329 135 List<String> lines = left(str, n, true);
cc3e7291
NR
136 for (int lineI = 0; lineI < lines.size() - 1; lineI++) {
137 String line = lines.get(lineI);
138 String[] words = line.split(" ");
139 if (words.length > 1) {
140 int charCount = 0;
141 for (int i = 0; i < words.length; i++) {
142 charCount += words[i].length();
143 }
144 int spaceCount = n - charCount;
145 int q = spaceCount / (words.length - 1);
146 int r = spaceCount % (words.length - 1);
147 StringBuilder sb = new StringBuilder();
148 for (int i = 0; i < words.length - 1; i++) {
149 sb.append(words[i]);
150 for (int j = 0; j < q; j++) {
151 sb.append(' ');
152 }
153 if (r > 0) {
154 sb.append(' ');
155 r--;
156 }
157 }
158 for (int j = 0; j < r; j++) {
159 sb.append(' ');
160 }
161 sb.append(words[words.length - 1]);
162 result.add(sb.toString());
163 } else {
164 result.add(line);
165 }
166 }
167 if (lines.size() > 0) {
168 result.add(lines.get(lines.size() - 1));
169 }
170
171 return result;
172 }
771c4ba4 173
620f7329
NR
174 /**
175 * Process the given text into a list of left-justified lines of a given
176 * max-width.
177 *
178 * @param data
179 * the text to justify
180 * @param width
181 * the maximum width of a line
182 * @param minTwoWords
183 * use 2 words per line minimum if the text allows it
184 *
185 * @return the list of justified lines
186 */
187 static private List<String> left(final String data, final int width,
188 boolean minTwoWords) {
189 List<String> lines = new LinkedList<String>();
190
191 for (String dataLine : data.split("\n")) {
192 String line = rightTrim(dataLine.replace("\t", " "));
193
194 if (width > 0 && line.length() > width) {
195 while (line.length() > 0) {
196 int i = Math.min(line.length(), width - 1); // -1 for "-"
197
198 boolean needDash = true;
199 // find the best space if any and if needed
200 int prevSpace = 0;
201 if (i < line.length()) {
202 prevSpace = -1;
203 int space = line.indexOf(' ');
204 int numOfSpaces = 0;
205
206 while (space > -1 && space <= i) {
207 prevSpace = space;
208 space = line.indexOf(' ', space + 1);
209 numOfSpaces++;
210 }
211
212 if (prevSpace > 0 && (!minTwoWords || numOfSpaces >= 2)) {
213 i = prevSpace;
214 needDash = false;
215 }
216 }
217 //
218
219 // no dash before space/dash
220 if ((i + 1) < line.length()) {
221 char car = line.charAt(i);
222 char nextCar = line.charAt(i + 1);
223 if (nextCar == ' ' || car == '-' || nextCar == '-') {
224 needDash = false;
225 }
226 }
227
228 // if the space freed by the removed dash allows it, or if
229 // it is the last char, add the next char
230 if (!needDash || i >= line.length() - 1) {
231 int checkI = Math.min(i + 1, line.length());
232 if (checkI == i || checkI <= width) {
233 needDash = false;
234 i = checkI;
235 }
236 }
237
238 // no dash before parenthesis (but cannot add one more
239 // after)
240 if ((i + 1) < line.length()) {
241 char car = line.charAt(i + 1);
242 if (car == '(' || car == ')') {
243 needDash = false;
244 }
245 }
246
247 if (needDash) {
248 lines.add(rightTrim(line.substring(0, i)) + "-");
249 } else {
250 lines.add(rightTrim(line.substring(0, i)));
251 }
252
253 // full trim (remove spaces when cutting)
254 line = line.substring(i).trim();
255 }
256 } else {
257 lines.add(line);
258 }
259 }
260
261 return lines;
262 }
263
771c4ba4
NR
264 /**
265 * Trim the given {@link String} on the right only.
266 *
267 * @param data
268 * the source {@link String}
269 * @return the right-trimmed String or Empty if it was NULL
270 */
271 static private String rightTrim(String data) {
272 if (data == null)
273 return "";
274
275 return ("|" + data).trim().substring(1);
276 }
cc3e7291 277}