001/* 002 Licensed to the Apache Software Foundation (ASF) under one or more 003 contributor license agreements. See the NOTICE file distributed with 004 this work for additional information regarding copyright ownership. 005 The ASF licenses this file to You under the Apache License, Version 2.0 006 (the "License"); you may not use this file except in compliance with 007 the License. You may obtain a copy of the License at 008 009 https://www.apache.org/licenses/LICENSE-2.0 010 011 Unless required by applicable law or agreed to in writing, software 012 distributed under the License is distributed on an "AS IS" BASIS, 013 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 See the License for the specific language governing permissions and 015 limitations under the License. 016 */ 017package org.apache.commons.cli.help; 018 019import java.util.function.Supplier; 020 021/** 022 * 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 023 * for a {@link HelpAppendable}. HelpWriters are free to ignore the TextStyle recommendations particularly where they are not supported or contradict common 024 * usage. 025 * 026 * @since 1.10.0 027 */ 028public final class TextStyle { 029 030 /** 031 * The alignment possibilities. 032 */ 033 public enum Alignment { 034 035 /** 036 * Left justifies the text. 037 */ 038 LEFT, 039 040 /** 041 * Centers the text. 042 */ 043 CENTER, 044 045 /** 046 * Right justifies the text. 047 */ 048 RIGHT 049 } 050 051 /** 052 * The builder for the TextStyle. The default values are: 053 * <ul> 054 * <li>alignment = LEFT</li> 055 * <li>leftPad = 0</li> 056 * <li>scaling = VARIABLE</li> 057 * <li>minWidth = 0</li> 058 * <li>maxWidth = UNSET_MAX_WIDTH</li> 059 * </ul> 060 */ 061 public static final class Builder implements Supplier<TextStyle> { 062 063 /** The alignment. */ 064 private Alignment alignment = Alignment.LEFT; 065 066 /** The left padding. */ 067 private int leftPad; 068 069 /** The subsequent line indentation. */ 070 private int indent; 071 072 /** The scalable flag. Identifies text blocks that can be made narrower or wider as needed by the HelpAppendable. */ 073 private boolean scalable = true; 074 075 /** The minimum width. */ 076 private int minWidth; 077 078 /** The maximum width. */ 079 private int maxWidth = UNSET_MAX_WIDTH; 080 081 /** 082 * Constructs a new instance. The default values are: 083 * <ul> 084 * <li>alignment = LEFT</li> 085 * <li>leftPad = 0</li> 086 * <li>scaling = VARIABLE</li> 087 * <li>minWidth = 0</li> 088 * <li>maxWidth = UNSET_MAX_WIDTH</li> 089 * </ul> 090 */ 091 private Builder() { 092 } 093 094 @Override 095 public TextStyle get() { 096 return new TextStyle(this); 097 } 098 099 /** 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 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 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}