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 */ 017 018package org.apache.commons.cli.help; 019 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.List; 023 024import org.apache.commons.cli.Option; 025 026/** 027 * A default formatter implementation for standard usage. 028 * <p> 029 * Example: 030 * </p> 031 * 032 * <pre> 033 * Options options = new Options(); 034 * options.addOption(OptionBuilder.withLongOpt("file").withDescription("The file to be processed").hasArg().withArgName("FILE").isRequired().create('f')); 035 * options.addOption(OptionBuilder.withLongOpt("version").withDescription("Print the version of the application").create('v')); 036 * options.addOption(OptionBuilder.withLongOpt("help").create('h')); 037 * 038 * String header = "Do something useful with an input file"; 039 * String footer = "Please report issues at https://example.com/issues"; 040 * 041 * HelpFormatter formatter = new HelpFormatter(); 042 * formatter.printHelp("myapp", header, options, footer, true); 043 * </pre> 044 * <p> 045 * This produces the following output: 046 * </p> 047 * 048 * <pre> 049 * {@code 050 * usage: myapp -f <FILE> [-h] [-v] 051 * Do something useful with an input file 052 * 053 * -f,--file <FILE> The file to be processed 054 * -h,--help 055 * -v,--version Print the version of the application 056 * 057 * Please report issues at https://example.com/issues 058 * } 059 * </pre> 060 * 061 * @since 1.10.0 062 */ 063public class HelpFormatter extends AbstractHelpFormatter { 064 065 /** 066 * A builder for the HelpFormatter. Intended to make more complex uses of the HelpFormatter class easier. Default values are: 067 * <ul> 068 * <li>showSince = true</li> 069 * <li>helpAppendable = a {@link TextHelpAppendable} writing to {@code System.out}</li> 070 * <li>optionFormatter.Builder = the default {@link OptionFormatter.Builder}</li> 071 * </ul> 072 */ 073 public static class Builder extends AbstractHelpFormatter.Builder<Builder, HelpFormatter> { 074 075 /** If {@code true} show the "Since" column, otherwise ignore it. */ 076 private boolean showSince = true; 077 078 /** 079 * Constructs a new instace. 080 * <p> 081 * Sets {@code showSince} to {@code true}. 082 * </p> 083 */ 084 protected Builder() { 085 // empty 086 } 087 088 @Override 089 public HelpFormatter get() { 090 return new HelpFormatter(this); 091 } 092 093 /** 094 * Sets the showSince flag. 095 * 096 * @param showSince the desired value of the showSince flag. 097 * @return this instance. 098 */ 099 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 for (final Option option : options) { 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}