1 /*
2 Licensed to the Apache Software Foundation (ASF) under one or more
3 contributor license agreements. See the NOTICE file distributed with
4 this work for additional information regarding copyright ownership.
5 The ASF licenses this file to You under the Apache License, Version 2.0
6 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
8
9 https://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 */
17 package org.apache.commons.cli.help;
18
19 import java.util.function.Supplier;
20
21 /**
22 * The definition for styling recommendations blocks of text. Most common usage is to style columns in a table, but may also be used to specify default styling
23 * for a {@link HelpAppendable}. HelpWriters are free to ignore the TextStyle recommendations particularly where they are not supported or contradict common
24 * usage.
25 *
26 * @since 1.10.0
27 */
28 public final class TextStyle {
29
30 /**
31 * The alignment possibilities.
32 */
33 public enum Alignment {
34
35 /**
36 * Left justifies the text.
37 */
38 LEFT,
39
40 /**
41 * Centers the text.
42 */
43 CENTER,
44
45 /**
46 * Right justifies the text.
47 */
48 RIGHT
49 }
50
51 /**
52 * The builder for the TextStyle. The default values are:
53 * <ul>
54 * <li>alignment = LEFT</li>
55 * <li>leftPad = 0</li>
56 * <li>scaling = VARIABLE</li>
57 * <li>minWidth = 0</li>
58 * <li>maxWidth = UNSET_MAX_WIDTH</li>
59 * </ul>
60 */
61 public static final class Builder implements Supplier<TextStyle> {
62
63 /** The alignment. */
64 private Alignment alignment = Alignment.LEFT;
65
66 /** The left padding. */
67 private int leftPad;
68
69 /** The subsequent line indentation. */
70 private int indent;
71
72 /** The scalable flag. Identifies text blocks that can be made narrower or wider as needed by the HelpAppendable. */
73 private boolean scalable = true;
74
75 /** The minimum width. */
76 private int minWidth;
77
78 /** The maximum width. */
79 private int maxWidth = UNSET_MAX_WIDTH;
80
81 /**
82 * Constructs a new instance. The default values are:
83 * <ul>
84 * <li>alignment = LEFT</li>
85 * <li>leftPad = 0</li>
86 * <li>scaling = VARIABLE</li>
87 * <li>minWidth = 0</li>
88 * <li>maxWidth = UNSET_MAX_WIDTH</li>
89 * </ul>
90 */
91 private Builder() {
92 }
93
94 @Override
95 public TextStyle get() {
96 return new TextStyle(this);
97 }
98
99 /**
100 * Gets the currently specified indent value.
101 *
102 * @return The currently specified indent value.
103 */
104 public int getIndent() {
105 return indent;
106 }
107
108 /**
109 * Gets the currently specified leftPad.
110 *
111 * @return The currently specified leftPad.
112 */
113 public int getLeftPad() {
114 return leftPad;
115 }
116
117 /**
118 * Gets the currently specified maximum width value.
119 *
120 * @return The currently specified maximum width value.
121 */
122 public int getMaxWidth() {
123 return maxWidth;
124 }
125
126 /**
127 * Gets the currently specified minimum width value.
128 *
129 * @return The currently specified minimum width value.
130 */
131 public int getMinWidth() {
132 return minWidth;
133 }
134
135 /**
136 * Specifies if the column can be made wider or to narrower width to fit constraints of the HelpAppendable and formatting.
137 *
138 * @return The currently specified scaling value.
139 */
140 public boolean isScalable() {
141 return scalable;
142 }
143
144 /**
145 * Sets the alignment.
146 *
147 * @param alignment the desired alignment.
148 * @return this
149 */
150 public Builder setAlignment(final Alignment alignment) {
151 this.alignment = alignment;
152 return this;
153 }
154
155 /**
156 * Sets the indent value.
157 *
158 * @param indent the new indent value.
159 * @return this
160 */
161 public Builder setIndent(final int indent) {
162 this.indent = indent;
163 return this;
164 }
165
166 /**
167 * Sets the left padding.
168 *
169 * @param leftPad the new left padding.
170 * @return this
171 */
172 public Builder setLeftPad(final int leftPad) {
173 this.leftPad = leftPad;
174 return this;
175 }
176
177 /**
178 * Sets the currently specified minimum width.
179 *
180 * @param maxWidth The currently specified maximum width.
181 * @return this
182 */
183 public Builder setMaxWidth(final int maxWidth) {
184 this.maxWidth = maxWidth;
185 return this;
186 }
187
188 /**
189 * Sets the currently specified minimum width.
190 *
191 * @param minWidth The currently specified minimum width.
192 * @return this
193 */
194 public Builder setMinWidth(final int minWidth) {
195 this.minWidth = minWidth;
196 return this;
197 }
198
199 /**
200 * Sets whether the column can be made wider or to narrower width to fit constraints of the HelpAppendable and formatting.
201 *
202 * @param scalable Whether the text width can be adjusted.
203 * @return {@code this} instance.
204 */
205 public Builder setScalable(final boolean scalable) {
206 this.scalable = scalable;
207 return this;
208 }
209
210 /**
211 * Sets all properties from the given text style.
212 *
213 * @param style the source text style.
214 * @return {@code this} instance.
215 */
216 public Builder setTextStyle(final TextStyle style) {
217 this.alignment = style.alignment;
218 this.leftPad = style.leftPad;
219 this.indent = style.indent;
220 this.scalable = style.scalable;
221 this.minWidth = style.minWidth;
222 this.maxWidth = style.maxWidth;
223 return this;
224 }
225
226 }
227
228 /**
229 * The unset value for maxWidth: {@value}.
230 */
231 public static final int UNSET_MAX_WIDTH = Integer.MAX_VALUE;
232
233 /**
234 * The default style as generated by the default Builder.
235 */
236 public static final TextStyle DEFAULT = builder().get();
237
238 /**
239 * Creates a new builder.
240 *
241 * @return a new builder.
242 */
243 public static Builder builder() {
244 return new Builder();
245 }
246
247 /** The alignment. */
248 private final Alignment alignment;
249
250 /** The size of the left pad. This is placed before each line of text. */
251 private final int leftPad;
252
253 /** The size of the indent on the second and any subsequent lines of text. */
254 private final int indent;
255
256 /** The scaling allowed for the block. */
257 private final boolean scalable;
258
259 /** The minimum size of the text. */
260 private final int minWidth;
261
262 /** The maximum size of the text. */
263 private final int maxWidth;
264
265 /**
266 * Constructs a new instance.
267 *
268 * @param builder the builder to build the text style from.
269 */
270 private TextStyle(final Builder builder) {
271 this.alignment = builder.alignment;
272 this.leftPad = builder.leftPad;
273 this.indent = builder.indent;
274 this.scalable = builder.scalable;
275 this.minWidth = builder.minWidth;
276 this.maxWidth = builder.maxWidth;
277 }
278
279 /**
280 * Gets the alignment.
281 *
282 * @return the alignment.
283 */
284 public Alignment getAlignment() {
285 return alignment;
286 }
287
288 /**
289 * Gets the indent value.
290 *
291 * @return the indent value.
292 */
293 public int getIndent() {
294 return indent;
295 }
296
297 /**
298 * Gets the left padding.
299 *
300 * @return the left padding.
301 */
302 public int getLeftPad() {
303 return leftPad;
304 }
305
306 /**
307 * gets the maximum width.
308 *
309 * @return The maximum width.
310 */
311 public int getMaxWidth() {
312 return maxWidth;
313 }
314
315 /**
316 * gets the minimum width.
317 *
318 * @return The minimum width.
319 */
320 public int getMinWidth() {
321 return minWidth;
322 }
323
324 /**
325 * Specifies if the column can be made wider or to narrower width to fit constraints of the HelpAppendable and formatting.
326 *
327 * @return the scaling value.
328 */
329 public boolean isScalable() {
330 return scalable;
331 }
332
333 /**
334 * Pads a string to the maximum width or optionally to the maximum width - indent.
335 * <ul>
336 * <li>Returns the string unchanged if it is longer than the specified length.</li>
337 * <li>Will add the padding based on the alignment.</li>
338 * </ul>
339 *
340 * @param addIndent if {@code true} account for the indent when padding the string.
341 * @param text the text to pad.
342 * @return the padded string.
343 */
344 public CharSequence pad(final boolean addIndent, final CharSequence text) {
345 if (text.length() >= maxWidth) {
346 return text;
347 }
348 String indentPad;
349 String rest;
350 final StringBuilder sb = new StringBuilder();
351 switch (alignment) {
352 case CENTER:
353 int padLen;
354 if (maxWidth == UNSET_MAX_WIDTH) {
355 padLen = addIndent ? indent : 0;
356 } else {
357 padLen = maxWidth - text.length();
358 }
359 final int left = padLen / 2;
360 indentPad = Util.repeatSpace(left);
361 rest = Util.repeatSpace(padLen - left);
362 sb.append(indentPad).append(text).append(rest);
363 break;
364 case LEFT:
365 case RIGHT:
366 default: // default should never happen. It is here to keep code coverage happy.
367 if (maxWidth == UNSET_MAX_WIDTH) {
368 indentPad = addIndent ? Util.repeatSpace(indent) : "";
369 rest = "";
370 } else {
371 int restLen = maxWidth - text.length();
372 if (addIndent && restLen > indent) {
373 indentPad = Util.repeatSpace(indent);
374 restLen -= indent;
375 } else {
376 indentPad = "";
377 }
378 rest = Util.repeatSpace(restLen);
379 }
380
381 if (alignment == Alignment.LEFT) {
382 sb.append(indentPad).append(text).append(rest);
383 } else {
384 sb.append(indentPad).append(rest).append(text);
385 }
386 break;
387 }
388 return sb.toString();
389 }
390
391 @Override
392 public String toString() {
393 return String.format("TextStyle{%s, l:%s, i:%s, %s, min:%s, max:%s}", alignment, leftPad, indent, scalable, minWidth,
394 maxWidth == UNSET_MAX_WIDTH ? "unset" : maxWidth);
395 }
396 }