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.Arrays; 020import java.util.function.BiFunction; 021import java.util.function.Function; 022import java.util.function.Supplier; 023 024import org.apache.commons.cli.DeprecatedAttributes; 025import org.apache.commons.cli.Option; 026 027/** 028 * The definition of how to display Option attributes. 029 * 030 * @since 1.10.0 031 */ 032public final class OptionFormatter { 033 034 /** 035 * Builds instances of {@link OptionFormatter}. 036 */ 037 public static final class Builder implements Supplier<OptionFormatter> { 038 039 /** The argument name delimiters */ 040 private final String[] argNameDelimiters; 041 042 /** The default argument name */ 043 private String defaultArgName; 044 045 /** The function to create the deprecated message for an option */ 046 private Function<Option, String> deprecatedFormatFunction; 047 048 /** The long option prefix */ 049 private String longOptPrefix; 050 051 /** The option prefix */ 052 private String optPrefix; 053 054 /** The separator between long and short options */ 055 private String optSeparator; 056 057 /** The separator between the opt and/or longOpt and the argument name */ 058 private String optArgSeparator; 059 060 /** The delimiters surrounding optional {@link Option} instances. */ 061 private final String[] optionalDelimiters; 062 063 /** A function to convert the {@link OptionFormatter} into an entry in the syntax description. */ 064 private BiFunction<OptionFormatter, Boolean, String> syntaxFormatFunction; 065 066 /** 067 * Default constructor. Uses the defaults specified in {@link OptionFormatter}. 068 */ 069 private Builder() { 070 argNameDelimiters = Arrays.copyOf(DEFAULT_ARG_NAME_DELIMITERS, 2); 071 defaultArgName = DEFAULT_ARG_NAME; 072 deprecatedFormatFunction = NO_DEPRECATED_FORMAT; 073 longOptPrefix = DEFAULT_LONG_OPT_PREFIX; 074 optPrefix = DEFAULT_OPT_PREFIX; 075 optSeparator = DEFAULT_OPT_SEPARATOR; 076 optArgSeparator = DEFAULT_OPT_ARG_SEPARATOR; 077 optionalDelimiters = Arrays.copyOf(DEFAULT_OPTIONAL_DELIMITERS, 2); 078 } 079 080 /** 081 * Constructor that takes the arguments from the supplied {@link OptionFormatter} 082 * 083 * @param optionFormatter The option formatter to provide values for the builder. 084 */ 085 public Builder(final OptionFormatter optionFormatter) { 086 optionalDelimiters = Arrays.copyOf(optionFormatter.optionalDelimiters, 2); 087 argNameDelimiters = Arrays.copyOf(optionFormatter.argNameDelimiters, 2); 088 defaultArgName = optionFormatter.defaultArgName; 089 optPrefix = optionFormatter.optPrefix; 090 longOptPrefix = optionFormatter.longOptPrefix; 091 optSeparator = optionFormatter.optSeparator; 092 deprecatedFormatFunction = optionFormatter.deprecatedFormatFunction; 093 syntaxFormatFunction = optionFormatter.syntaxFormatFunction; 094 } 095 096 /** 097 * Build an OptionFormatter to format the specified option. 098 * 099 * @param option The Option to format. 100 * @return An OptionFormatter to format the specified option. 101 */ 102 public OptionFormatter build(final Option option) { 103 return new OptionFormatter(option, this); 104 } 105 106 @Override 107 public OptionFormatter get() { 108 // TODO Auto-generated method stub 109 return null; 110 } 111 112 /** 113 * Specifies the starting and ending argument name delimiters for {@link Option} instances. 114 * 115 * @param begin the beginning delimiter. 116 * @param end the ending delimiter. 117 * @return this instance. 118 */ 119 public Builder setArgumentNameDelimiters(final String begin, final String end) { 120 this.argNameDelimiters[0] = Util.defaultValue(begin, ""); 121 this.argNameDelimiters[1] = Util.defaultValue(end, ""); 122 return this; 123 } 124 125 /** 126 * Sets the default argument name. 127 * 128 * @param name the new value of default argument name. 129 * @return this 130 */ 131 public Builder setDefaultArgName(final String name) { 132 this.defaultArgName = Util.defaultValue(name, DEFAULT_ARG_NAME); 133 return this; 134 } 135 136 /** 137 * Specifies the function to construct the deprecated massage for the Option. Should include the description text if desired. 138 * 139 * @param deprecatedFormatFunction the function to specify the deprecated message for the option. 140 * @return this instance. 141 */ 142 public Builder setDeprecatedFormatFunction(final Function<Option, String> deprecatedFormatFunction) { 143 this.deprecatedFormatFunction = deprecatedFormatFunction; 144 return this; 145 } 146 147 /** 148 * Sets the long option prefix. 149 * 150 * @param prefix prefix for long options. 151 * @return this 152 */ 153 public Builder setLongOptPrefix(final String prefix) { 154 this.longOptPrefix = Util.defaultValue(prefix, ""); 155 return this; 156 } 157 158 /** 159 * Sets the separator displayed between a options and the argument name. Typically ' ' or '='. 160 * 161 * @param optArgSeparator the separator. 162 * @return this 163 * @since 1.3 164 */ 165 public Builder setOptArgSeparator(final String optArgSeparator) { 166 this.optArgSeparator = Util.defaultValue(optArgSeparator, ""); 167 return this; 168 } 169 170 /** 171 * Specifies the starting and ending delimiters for optional {@link Option} instances. 172 * 173 * @param begin the beginning delimiter. 174 * @param end the ending delimiter. 175 * @return this instance. 176 */ 177 public Builder setOptionalDelimiters(final String begin, final String end) { 178 this.optionalDelimiters[0] = Util.defaultValue(begin, ""); 179 this.optionalDelimiters[1] = Util.defaultValue(end, ""); 180 return this; 181 } 182 183 /** 184 * Specifies the short option prefix. 185 * 186 * @param optPrefix the prefix for short options. 187 * @return this instance. 188 */ 189 public Builder setOptPrefix(final String optPrefix) { 190 this.optPrefix = Util.defaultValue(optPrefix, ""); 191 return this; 192 } 193 194 /** 195 * Sets the separator displayed between a long option and short options. Typically ',' or ' '. 196 * 197 * @param optSeparator the separator. 198 * @return this 199 * @since 1.3 200 */ 201 public Builder setOptSeparator(final String optSeparator) { 202 this.optSeparator = Util.defaultValue(optSeparator, ""); 203 return this; 204 } 205 206 /** 207 * Specifies the function to convert an {@link OptionFormatter} into the syntax format for the option. 208 * 209 * @param syntaxFormatFunction The function to convert an {@link OptionFormatter} into the syntax format for the option. 210 * @return this 211 */ 212 public Builder setSyntaxFormatFunction(final BiFunction<OptionFormatter, Boolean, String> syntaxFormatFunction) { 213 this.syntaxFormatFunction = syntaxFormatFunction; 214 return this; 215 } 216 217 /** 218 * A helper method to format any string as an argument name based on this builder. 219 * 220 * @param argName the name of the argument. 221 * @return the formatted argument. 222 */ 223 public String toArgName(final String argName) { 224 return argNameDelimiters[0] + Util.defaultValue(argName, "") + argNameDelimiters[1]; 225 } 226 } 227 228 /** The default delimiters for optional arguments */ 229 private static final String[] DEFAULT_OPTIONAL_DELIMITERS = { "[", "]" }; 230 231 /** The default delimiters for an argument name */ 232 private static final String[] DEFAULT_ARG_NAME_DELIMITERS = { "<", ">" }; 233 234 /** 235 * The default argument name: {@value}. 236 */ 237 public static final String DEFAULT_ARG_NAME = "arg"; 238 239 /** 240 * A function to display a deprecated option with the "[Deprecated]" prefix. 241 */ 242 public static final Function<Option, String> SIMPLE_DEPRECATED_FORMAT = o -> "[Deprecated] " + Util.defaultValue(o.getDescription(), ""); 243 244 /** 245 * A function to display a deprecated option with a "Deprecated" prefix that displays all deprecation information. 246 */ 247 public static final Function<Option, String> COMPLEX_DEPRECATED_FORMAT = o -> { 248 final StringBuilder sb = new StringBuilder("[Deprecated"); 249 final DeprecatedAttributes attr = o.getDeprecated(); 250 if (attr.isForRemoval()) { 251 sb.append(" for removal"); 252 } 253 if (!Util.isEmpty(attr.getSince())) { 254 sb.append(" since ").append(attr.getSince()); 255 } 256 if (!Util.isEmpty(attr.getDescription())) { 257 sb.append(". ").append(attr.getDescription()); 258 } 259 sb.append("]"); 260 if (!Util.isEmpty(o.getDescription())) { 261 sb.append(" ").append(o.getDescription()); 262 } 263 return sb.toString(); 264 }; 265 266 /** 267 * A function to display a deprecated option with the "[Deprecated]" prefix. 268 */ 269 public static final Function<Option, String> NO_DEPRECATED_FORMAT = o -> Util.defaultValue(o.getDescription(), ""); 270 271 /** 272 * The string to display at the beginning of the usage statement: {@value}. 273 */ 274 public static final String DEFAULT_SYNTAX_PREFIX = "usage: "; 275 276 /** 277 * Default prefix for short options: {@value}. 278 */ 279 public static final String DEFAULT_OPT_PREFIX = "-"; 280 281 /** 282 * Default prefix for long options: {@value}. 283 */ 284 public static final String DEFAULT_LONG_OPT_PREFIX = "--"; 285 286 /** 287 * The default separator between options: {@value}. 288 */ 289 public static final String DEFAULT_OPT_SEPARATOR = ", "; 290 291 /** 292 * The default separator between the opt and/or longOpt and the argument name: {@value}. 293 */ 294 public static final String DEFAULT_OPT_ARG_SEPARATOR = " "; 295 296 /** 297 * Creates a new builder. 298 * 299 * @return a new builder. 300 */ 301 public static Builder builder() { 302 return new Builder(); 303 } 304 305 /** 306 * Construct the {@link OptionFormatter} from an {@link Option} using the default {@link OptionFormatter.Builder}. 307 * 308 * @param option the option to format. 309 * @return an OptionFormatter for the specified @{code option}. 310 */ 311 public static OptionFormatter from(final Option option) { 312 return new Builder().build(option); 313 } 314 315 /** 316 * The delimiters around argument names. 317 */ 318 private final String[] argNameDelimiters; 319 320 /** The default argument name */ 321 private final String defaultArgName; 322 323 /** The function to display the deprecated option message */ 324 private final Function<Option, String> deprecatedFormatFunction; 325 326 /** The prefix for the long option text */ 327 private final String longOptPrefix; 328 329 /** The prefix for the short option text */ 330 private final String optPrefix; 331 332 /** The separator between the options */ 333 private final String optSeparator; 334 335 /** the separator between the opt and/or longOpt and the argument name */ 336 private final String optArgSeparator; 337 338 /** The delimiters for optional {@link Option}s. */ 339 private final String[] optionalDelimiters; 340 341 /** The method to convert an Option formatter into a syntax notation. */ 342 private final BiFunction<OptionFormatter, Boolean, String> syntaxFormatFunction; 343 344 /** The {@link Option} being formatted */ 345 private final Option option; 346 347 /** 348 * An OptionFormatter applies formatting options to various {@link Option} attributes for textual display. 349 * 350 * @param option the Option to apply formatting to. 351 * @param builder The Builder that specifies the various formatting options. 352 */ 353 private OptionFormatter(final Option option, final Builder builder) { 354 this.optionalDelimiters = builder.optionalDelimiters; 355 this.argNameDelimiters = builder.argNameDelimiters; 356 this.defaultArgName = builder.defaultArgName; 357 this.optPrefix = builder.optPrefix; 358 this.longOptPrefix = builder.longOptPrefix; 359 this.optSeparator = builder.optSeparator; 360 this.optArgSeparator = builder.optArgSeparator; 361 this.deprecatedFormatFunction = builder.deprecatedFormatFunction; 362 this.option = option; 363 this.syntaxFormatFunction = builder.syntaxFormatFunction != null ? builder.syntaxFormatFunction : (o, required) -> { 364 final StringBuilder buff = new StringBuilder(); 365 final String argName = o.getArgName(); 366 buff.append(Util.defaultValue(o.getOpt(), o.getLongOpt())); 367 if (!Util.isEmpty(argName)) { 368 buff.append(optArgSeparator).append(argName); 369 } 370 final boolean requiredFlg = required == null ? o.isRequired() : required; 371 return requiredFlg ? buff.toString() : o.toOptional(buff.toString()); 372 }; 373 } 374 375 /** 376 * Gets the argument name wrapped in the argument name delimiters. 377 * <ul> 378 * <li>If option has no arguments an empty string is returned</li> 379 * <li>If the argument name is not set the default argument name is used.</li> 380 * </ul> 381 * 382 * @return The argument name wrapped in the argument name delimiters or an empty string. 383 */ 384 public String getArgName() { 385 return option.hasArg() ? argNameDelimiters[0] + Util.defaultValue(option.getArgName(), defaultArgName) + argNameDelimiters[1] : ""; 386 } 387 388 /** 389 * Gets both options separated by the specified option separator. Correctly handles the case where one option is not specified. 390 * 391 * @return The one or both of the short and/or long Opt with the associate prefixes. 392 */ 393 public String getBothOpt() { 394 final String lOpt = getLongOpt(); 395 396 final StringBuilder sb = new StringBuilder(getOpt()); 397 if (sb.length() > 0 && !Util.isEmpty(lOpt)) { 398 sb.append(optSeparator); 399 } 400 // sb will not be empty as Option requries at least one of opt or longOpt. 401 return sb.append(getLongOpt()).toString(); 402 } 403 404 /** 405 * Gets the description for the option. This will include any deprecation notices if the deprecated format function has been set. 406 * 407 * @return The Description from the option or an empty string is no description was provided and the option is not deprecated. 408 */ 409 public String getDescription() { 410 return option.isDeprecated() ? deprecatedFormatFunction.apply(option) : Util.defaultValue(option.getDescription(), ""); 411 } 412 413 /** 414 * Gets the long Opt from the @{link Option} with the associate prefix. 415 * 416 * @return The long Opt from the @{link Option} with the associate prefix or an empty string. 417 */ 418 public String getLongOpt() { 419 return Util.isEmpty(option.getLongOpt()) ? "" : longOptPrefix + option.getLongOpt(); 420 } 421 422 /** 423 * Gets the Opt from the @{link Option} with the associate prefix. 424 * 425 * @return The Opt from the @{link Option} with the associate prefix or an empty string. 426 */ 427 public String getOpt() { 428 return Util.isEmpty(option.getOpt()) ? "" : optPrefix + option.getOpt(); 429 } 430 431 /** 432 * Gets the "since" value from the Option. 433 * 434 * @return The since valeu from the option or "--" if no since value was set. 435 */ 436 public String getSince() { 437 return Util.defaultValue(option.getSince(), "--"); 438 } 439 440 /** 441 * Gets the required flag from the enclosed {@link Option}. 442 * 443 * @return The required flag from the enclosed {@link Option}. 444 */ 445 public boolean isRequired() { 446 return option.isRequired(); 447 } 448 449 /** 450 * Wraps the provided text in the optional delimiters. 451 * 452 * @param text the text to wrap. 453 * @return The text wrapped in the optional delimiters or an eppty string of the text is null or an empty string. 454 */ 455 public String toOptional(final String text) { 456 if (Util.isEmpty(text)) { 457 return ""; 458 } 459 return optionalDelimiters[0] + text + optionalDelimiters[1]; 460 } 461 462 /** 463 * Gets the syntax format for this option. 464 * 465 * @return the syntax format for this option as specified by the syntaxFormatFunction. 466 */ 467 public String toSyntaxOption() { 468 return toSyntaxOption(isRequired()); 469 } 470 471 /** 472 * Gets the syntax format for this option. 473 * 474 * @param isRequired if {@code true} the options is printed as a required option, otherwise it is optional. 475 * @return the syntax format for this option as specified by the syntaxFormatFunction. 476 */ 477 public String toSyntaxOption(final boolean isRequired) { 478 return syntaxFormatFunction.apply(this, isRequired); 479 } 480}