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  
18  package org.apache.commons.cli.help;
19  
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.List;
23  
24  import org.apache.commons.cli.Option;
25  
26  /**
27   * A default formatter implementation for standard usage.
28   * <p>
29   * Example:
30   * </p>
31   *
32   * <pre>
33   * Options options = new Options();
34   * options.addOption(OptionBuilder.withLongOpt("file").withDescription("The file to be processed").hasArg().withArgName("FILE").isRequired().create('f'));
35   * options.addOption(OptionBuilder.withLongOpt("version").withDescription("Print the version of the application").create('v'));
36   * options.addOption(OptionBuilder.withLongOpt("help").create('h'));
37   *
38   * String header = "Do something useful with an input file";
39   * String footer = "Please report issues at https://example.com/issues";
40   *
41   * HelpFormatter formatter = new HelpFormatter();
42   * formatter.printHelp("myapp", header, options, footer, true);
43   * </pre>
44   * <p>
45   * This produces the following output:
46   * </p>
47   *
48   * <pre>
49   *     {@code
50   * usage: myapp -f <FILE> [-h] [-v]
51   * Do something useful with an input file
52   *
53   *  -f,--file <FILE>   The file to be processed
54   *  -h,--help
55   *  -v,--version       Print the version of the application
56   *
57   * Please report issues at https://example.com/issues
58   * }
59   * </pre>
60   *
61   * @since 1.10.0
62   */
63  public class HelpFormatter extends AbstractHelpFormatter {
64  
65      /**
66       * A builder for the HelpFormatter. Intended to make more complex uses of the HelpFormatter class easier. Default values are:
67       * <ul>
68       * <li>showSince = true</li>
69       * <li>helpAppendable = a {@link TextHelpAppendable} writing to {@code System.out}</li>
70       * <li>optionFormatter.Builder = the default {@link OptionFormatter.Builder}</li>
71       * </ul>
72       */
73      public static class Builder extends AbstractHelpFormatter.Builder<Builder, HelpFormatter> {
74  
75          /** If {@code true} show the "Since" column, otherwise ignore it. */
76          private boolean showSince = true;
77  
78          /**
79           * Constructs a new instace.
80           * <p>
81           * Sets {@code showSince} to {@code true}.
82           * </p>
83           */
84          protected Builder() {
85              // empty
86          }
87  
88          @Override
89          public HelpFormatter get() {
90              return new HelpFormatter(this);
91          }
92  
93          /**
94           * Sets the showSince flag.
95           *
96           * @param showSince the desired value of the showSince flag.
97           * @return {@code this} instance.
98           */
99          public Builder setShowSince(final boolean showSince) {
100             this.showSince = showSince;
101             return this;
102         }
103     }
104 
105     /**
106      * Default number of characters per line: {@value}.
107      */
108     public static final int DEFAULT_WIDTH = 74;
109 
110     /**
111      * Default padding to the left of each line: {@value}.
112      */
113     public static final int DEFAULT_LEFT_PAD = 1;
114 
115     /**
116      * The default number of spaces between columns in the options table: {@value}.
117      */
118     public static final int DEFAULT_COLUMN_SPACING = 5;
119 
120     /**
121      * Constructs a new builder.
122      *
123      * @return a new builder.
124      */
125     public static Builder builder() {
126         return new Builder();
127     }
128 
129     /** If {@code true} show the "Since" column, otherwise ignore it. */
130     private final boolean showSince;
131 
132     /**
133      * Constructs the Help formatter.
134      *
135      * @param builder the Builder to build from.
136      */
137     protected HelpFormatter(final Builder builder) {
138         super(builder);
139         this.showSince = builder.showSince;
140     }
141 
142     /**
143      * Gets the table definition for the options.
144      *
145      * @param options the collection of {@link Option} instances to create the table from.
146      * @return A {@link TableDefinition} to display the options.
147      */
148     @Override
149     public TableDefinition getTableDefinition(final Iterable<Option> options) {
150         // set up the base TextStyle for the columns configured for the Option opt and arg values.
151         final TextStyle.Builder builder = TextStyle.builder().setAlignment(TextStyle.Alignment.LEFT).setIndent(DEFAULT_LEFT_PAD).setScalable(false);
152         final List<TextStyle> styles = new ArrayList<>();
153         styles.add(builder.get());
154         // set up showSince column
155         builder.setScalable(true).setLeftPad(DEFAULT_COLUMN_SPACING);
156         if (showSince) {
157             builder.setAlignment(TextStyle.Alignment.CENTER);
158             styles.add(builder.get());
159         }
160         // set up the description column.
161         builder.setAlignment(TextStyle.Alignment.LEFT);
162         styles.add(builder.get());
163         // setup the rows for the table.
164         final List<List<String>> rows = new ArrayList<>();
165         final StringBuilder sb = new StringBuilder();
166         options.forEach(option -> {
167             final List<String> row = new ArrayList<>();
168             // create an option formatter to correctly format the parts of the option
169             final OptionFormatter formatter = getOptionFormatBuilder().build(option);
170             sb.setLength(0);
171             // append the opt values.
172             sb.append(formatter.getBothOpt());
173             // append the arg name if it exists.
174             if (option.hasArg()) {
175                 sb.append(" ").append(formatter.getArgName());
176             }
177             row.add(sb.toString());
178             // append the "since" value if desired.
179             if (showSince) {
180                 row.add(formatter.getSince());
181             }
182             // add the option description
183             row.add(formatter.getDescription());
184             rows.add(row);
185         });
186         // return the TableDefinition with the proper column headers.
187         return TableDefinition.from("", styles, showSince ? Arrays.asList("Options", "Since", "Description") : Arrays.asList("Options", "Description"), rows);
188     }
189 }