Help.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.anttasks;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
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.rat.commandline.Arg;
import org.apache.rat.config.exclusion.StandardCollection;
import org.apache.rat.help.AbstractHelp;
import org.apache.rat.documentation.options.AbstractOption;
import org.apache.rat.documentation.options.AntOption;
import org.apache.rat.utils.DefaultLog;
import org.apache.rat.utils.Log;
import static java.lang.String.format;
/**
* A basic Ant task that generates a report on all files specified by the nested
* resource collection(s).
*
* <p>
* IHeaderMatcher(s) can be specified as nested elements as well.
* </p>
*
* <p>
* The attribute <code>format</code> defines the output format and can take the
* values
* <ul>
* <li>xml - RAT's native XML output.</li>
* <li>styled - transforms the XML output using the given stylesheet. The
* stylesheet attribute must be set as well if this attribute is used.</li>
* <li>plain - plain text using RAT's built-in stylesheet. This is the
* default.</li>
* </ul>
*/
public class Help extends BaseAntTask {
/**
* Constructor.
*/
public Help() {
// replace the logger only if it has not already been set.
Log oldLog = DefaultLog.getInstance();
if (oldLog instanceof DefaultLog) {
DefaultLog.setInstance(new Logger());
DefaultLog.getInstance().setLevel(oldLog.getLevel());
}
}
/**
* Generates the help.
*/
@Override
public void execute() {
org.apache.rat.help.Help helpObj = new org.apache.rat.help.Help(System.out) {
/**
* Print the usage to the specific PrintWriter.
* @param opts The defined options.
*/
public void printUsage(final Options opts) {
String syntax = "ant {target executing task <rat:help/>}";
AntHelpFormatter helpFormatter = new AntHelpFormatter();
helpFormatter.setOptPrefix("<");
helpFormatter.printHelp(writer, helpFormatter.getWidth(), syntax, AbstractHelp.header("Available options"), opts,
helpFormatter.getLeftPadding(), helpFormatter.getDescPadding(), header("Argument Types"));
String argumentPadding = printArgumentTypes();
writer.println(header("Standard Collections"));
for (StandardCollection sc : StandardCollection.values()) {
writer.format("%n<%s>%n", sc.name());
helpFormatter.printWrapped(writer, helpFormatter.getWidth(), helpFormatter.getLeftPadding() + HELP_PADDING + HELP_PADDING,
argumentPadding + sc.desc());
helpFormatter.printWrapped(writer, helpFormatter.getWidth(), helpFormatter.getLeftPadding() + HELP_PADDING + HELP_PADDING,
argumentPadding + "File patterns: " + (sc.patterns().isEmpty() ? "<none>" : String.join(", ", sc.patterns())));
helpFormatter.printWrapped(writer, helpFormatter.getWidth(), helpFormatter.getLeftPadding() + HELP_PADDING + HELP_PADDING,
argumentPadding + "Provides a path matcher: " + sc.hasStaticDocumentNameMatcher());
helpFormatter.printWrapped(writer, helpFormatter.getWidth(), helpFormatter.getLeftPadding() + HELP_PADDING + HELP_PADDING,
argumentPadding + "Provides a file processor: " + sc.fileProcessorBuilder().hasNext());
}
writer.println("\nA path matcher will match specific information about the file.");
writer.println("\nA file processor will process the associated \"ignore\" file for include and exclude directives");
writer.println(header("Notes"));
int idx = 1;
for (String note : NOTES) {
writer.format("%d. %s%n", idx++, note);
}
writer.flush();
}
};
helpObj.printUsage(Arg.getOptions());
}
@Override
public void log(final String msg, final int msgLevel) {
if (getProject() != null) {
getProject().log(msg, msgLevel);
} else {
DefaultLog.createDefault().log(Report.fromProjectLevel(msgLevel), msg);
}
}
@Override
public void log(final String msg, final Throwable t, final int msgLevel) {
if (getProject() == null) {
log(Log.formatLogEntry(msg, t), msgLevel);
} else {
getProject().log(this, msg, t, msgLevel);
}
}
/**
* A facade for the Logger provided by Ant.
*/
private final class Logger implements Log {
@Override
public Level getLevel() {
return Level.DEBUG;
}
@Override
public void log(final Level level, final String message, final Throwable throwable) {
log(level, Log.formatLogEntry(message, throwable));
}
@Override
public void log(final Level level, final String msg) {
Help.this.log(msg, Report.toProjectLevel(level));
}
}
public static class AntHelpFormatter extends HelpFormatter {
public AntHelpFormatter() {
super();
setWidth(180);
}
@Override
public Comparator<Option> getOptionComparator() {
return Comparator.comparing(Option::getLongOpt);
}
@Override
protected StringBuffer renderOptions(final StringBuffer sb, final int width, final Options options, final int leftPad, final int descPad) {
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
String optionTitle = " -- Option --";
String exampleTitle = " -- Example --";
String descriptionTitle = " -- Description --";
int max = optionTitle.length();
int maxExample = exampleTitle.length();
final List<AntOption> optList = options.getOptions().stream().filter(Option::hasLongOpt)
.map(AntOption::new).collect(Collectors.toList());
if (getOptionComparator() != null) {
optList.sort(Comparator.comparing(AbstractOption::getName));
}
List<String> exampleList = new ArrayList<>();
for (final AntOption option : optList) {
String argName = StringUtils.defaultIfEmpty(option.getArgName(), "value");
String fmt = option.isAttribute() ? "<rat:report %s='%s'>" : "<%1$s>%2$s</%1$s>";
String example = format(fmt, option.getName(), argName);
exampleList.add(example);
max = Math.max(option.cleanupName().length(), max);
maxExample = Math.max(example.length(), maxExample);
}
sb.append(optionTitle).append(createPadding(max - optionTitle.length()))
.append(dpad)
.append(exampleTitle).append(createPadding(maxExample - exampleTitle.length()))
.append(dpad)
.append(descriptionTitle)
.append(getNewLine());
int x = 0;
for (final Iterator<AntOption> it = optList.iterator(); it.hasNext();) {
final AntOption option = it.next();
String name = option.cleanupName();
String example = exampleList.get(x++);
final StringBuilder optBuf = new StringBuilder(name);
if (name.length() < max) {
optBuf.append(createPadding(max - name.length()));
}
optBuf.append(dpad).append(example);
if (example.length() < maxExample) {
optBuf.append(createPadding(maxExample - example.length()));
}
optBuf.append(dpad);
final int nextLineTabStop = max + maxExample + 2 * descPad;
if (option.isDeprecated()) {
optBuf.append(option.getDeprecated());
} else if (option.getDescription() != null) {
optBuf.append(option.getDescription());
}
renderWrappedText(sb, width, nextLineTabStop, optBuf.toString());
if (it.hasNext()) {
sb.append(getNewLine());
}
}
return sb;
}
}
}