View Javadoc
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 }