AbstractHelp.java
/*
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
*/
package org.apache.rat.help;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.WordUtils;
import org.apache.rat.OptionCollection;
import org.apache.rat.VersionInfo;
import org.apache.rat.commandline.Arg;
import static java.lang.String.format;
/**
* The base class to perform Help processing for programs.
*/
public abstract class AbstractHelp {
/** Text to display when multiple options are supported */
private static final String END_OF_OPTION_MSG = " Multiple values may be specified. " +
"Note that '--' or a following option is required when using this parameter.";
/** The width of the help report in chars. */
public static final int HELP_WIDTH = 120;
/** The number of chars to indent output with. */
public static final int HELP_PADDING = 4;
/** The help formatter for this instance */
protected final RatHelpFormatter helpFormatter;
/** The version info for this instance */
protected final VersionInfo versionInfo;
/**
* Base class to perform help output.
*/
protected AbstractHelp() {
helpFormatter = new RatHelpFormatter();
versionInfo = new VersionInfo();
}
/** Function to format deprecated display */
public static final Function<Option, String> DEPRECATED_MSG = o -> {
StringBuilder sb = new StringBuilder("[").append(o.getDeprecated().toString()).append("]");
if (o.getDescription() != null) {
sb.append(" ").append(o.getDescription());
}
return sb.toString();
};
/**
* Create a padding.
* @param len The length of the padding in characters.
* @return a string with len blanks.
*/
public static String createPadding(final int len) {
char[] padding = new char[len];
Arrays.fill(padding, ' ');
return new String(padding);
}
/**
* Create a section header for the output.
* @param txt the text to put in the header.
* @return the Header string.
*/
public static String header(final String txt) {
return String.format("%n====== %s ======%n", WordUtils.capitalizeFully(txt));
}
/**
* Provides help for formatting text.
*/
public class RatHelpFormatter extends HelpFormatter {
/**
* Constructor
*/
RatHelpFormatter() {
super();
this.optionComparator = OptionCollection.OPTION_COMPARATOR;
this.setWidth(HELP_WIDTH);
}
/**
* Prints the help text.
* @param writer the writer to write to.
* @param cmdLineSyntax The command line syntax for the program.
* @param header Additional information to proceed the Options descriptions.
* @param options the Options to output help for.
* @param footer Additional information to follow the Options descriptions.
*/
public void printHelp(final PrintWriter writer, final String cmdLineSyntax, final String header, final Options options, final String footer) {
if (StringUtils.isEmpty(cmdLineSyntax)) {
throw new IllegalArgumentException("cmdLineSyntax not provided");
}
helpFormatter.printUsage(writer, HELP_WIDTH, cmdLineSyntax);
if (header != null && !header.isEmpty()) {
helpFormatter.printWrapped(writer, HELP_WIDTH, header);
}
printOptions(writer, HELP_WIDTH, options, helpFormatter.getLeftPadding(), helpFormatter.getDescPadding());
if (footer != null && !footer.isEmpty()) {
helpFormatter.printWrapped(writer, helpFormatter.getWidth(), footer);
}
}
@Override
protected StringBuffer renderOptions(final StringBuffer sb, final int width, final Options options, final int leftPad, final int descPad) {
final String lpad = createPadding(leftPad);
final String dpad = createPadding(descPad);
// first create list containing only <lpad>-a,--aaa where
// -a is opt and --aaa is long opt; in parallel look for
// the longest opt string; this list will then be used to
// sort options ascending
int max = 0;
final List<StringBuffer> prefixList = new ArrayList<>();
final List<Option> optList = new ArrayList<>(options.getOptions());
optList.sort(helpFormatter.getOptionComparator());
for (final Option option : optList) {
final StringBuffer optBuf = new StringBuffer();
if (option.getOpt() == null) {
optBuf.append(lpad).append(" ").append(getLongOptPrefix()).append(option.getLongOpt());
} else {
optBuf.append(lpad).append(getOptPrefix()).append(option.getOpt());
if (option.hasLongOpt()) {
optBuf.append(',').append(getLongOptPrefix()).append(option.getLongOpt());
}
}
if (option.hasArg()) {
final String argName = option.getArgName();
if (argName != null && argName.isEmpty()) {
// if the option has a blank argname
optBuf.append(' ');
} else {
optBuf.append(option.hasLongOpt() ? helpFormatter.getLongOptSeparator() : " ");
optBuf.append("<").append(argName != null ? option.getArgName() : getArgName()).append(">");
}
}
prefixList.add(optBuf);
max = Math.max(optBuf.length(), max);
}
int x = 0;
for (final Iterator<Option> it = optList.iterator(); it.hasNext();) {
final Option option = it.next();
final StringBuilder optBuf = new StringBuilder(prefixList.get(x++).toString());
if (optBuf.length() < max) {
optBuf.append(createPadding(max - optBuf.length()));
}
optBuf.append(dpad);
final int nextLineTabStop = max + descPad;
// check for deprecation
if (option.isDeprecated()) {
optBuf.append(DEPRECATED_MSG.apply(option).trim());
} else if (option.getDescription() != null) {
optBuf.append(option.getDescription());
}
// check for multiple values
if (option.hasArgs()) {
optBuf.append(END_OF_OPTION_MSG);
}
// check for default value
Arg arg = Arg.findArg(option);
String defaultValue = arg == null ? null : arg.defaultValue();
if (defaultValue != null) {
optBuf.append(format(" (Default value = %s)", defaultValue));
}
renderWrappedText(sb, width, nextLineTabStop, optBuf.toString());
if (it.hasNext()) {
sb.append(getNewLine());
}
}
return sb;
}
}
}