/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed 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.springframework.boot.cli.command.options;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import joptsimple.BuiltinHelpFormatter;
import joptsimple.HelpFormatter;
import joptsimple.OptionDescriptor;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpecBuilder;
import org.springframework.boot.cli.command.OptionParsingCommand;
import org.springframework.boot.cli.command.status.ExitStatus;
/**
* Delegate used by {@link OptionParsingCommand} to parse options and run the command.
*
* @author Dave Syer
* @see OptionParsingCommand
* @see #run(OptionSet)
*/
public class OptionHandler {
private OptionParser parser;
private String help;
private Collection<OptionHelp> optionHelp;
public OptionSpecBuilder option(String name, String description) {
return getParser().accepts(name, description);
}
public OptionSpecBuilder option(Collection<String> aliases, String description) {
return getParser().acceptsAll(aliases, description);
}
public OptionParser getParser() {
if (this.parser == null) {
this.parser = new OptionParser();
options();
}
return this.parser;
}
protected void options() {
}
public final ExitStatus run(String... args) throws Exception {
String[] argsToUse = args.clone();
for (int i = 0; i < argsToUse.length; i++) {
if ("-cp".equals(argsToUse[i])) {
argsToUse[i] = "--cp";
}
}
OptionSet options = getParser().parse(argsToUse);
return run(options);
}
/**
* Run the command using the specified parsed {@link OptionSet}.
* @param options the parsed option set
* @return an ExitStatus
* @throws Exception in case of errors
*/
protected ExitStatus run(OptionSet options) throws Exception {
return ExitStatus.OK;
}
public String getHelp() {
if (this.help == null) {
getParser().formatHelpWith(new BuiltinHelpFormatter(80, 2));
OutputStream out = new ByteArrayOutputStream();
try {
getParser().printHelpOn(out);
}
catch (IOException ex) {
return "Help not available";
}
this.help = out.toString().replace(" --cp ", " -cp ");
}
return this.help;
}
public Collection<OptionHelp> getOptionsHelp() {
if (this.optionHelp == null) {
OptionHelpFormatter formatter = new OptionHelpFormatter();
getParser().formatHelpWith(formatter);
try {
getParser().printHelpOn(new ByteArrayOutputStream());
}
catch (Exception ex) {
// Ignore and provide no hints
}
this.optionHelp = formatter.getOptionHelp();
}
return this.optionHelp;
}
private static class OptionHelpFormatter implements HelpFormatter {
private final List<OptionHelp> help = new ArrayList<>();
@Override
public String format(Map<String, ? extends OptionDescriptor> options) {
Comparator<OptionDescriptor> comparator = new Comparator<OptionDescriptor>() {
@Override
public int compare(OptionDescriptor first, OptionDescriptor second) {
return first.options().iterator().next()
.compareTo(second.options().iterator().next());
}
};
Set<OptionDescriptor> sorted = new TreeSet<>(comparator);
sorted.addAll(options.values());
for (OptionDescriptor descriptor : sorted) {
if (!descriptor.representsNonOptions()) {
this.help.add(new OptionHelpAdapter(descriptor));
}
}
return "";
}
public Collection<OptionHelp> getOptionHelp() {
return Collections.unmodifiableList(this.help);
}
}
private static class OptionHelpAdapter implements OptionHelp {
private final LinkedHashSet<String> options;
private final String description;
OptionHelpAdapter(OptionDescriptor descriptor) {
this.options = new LinkedHashSet<>();
for (String option : descriptor.options()) {
this.options.add((option.length() == 1 ? "-" : "--") + option);
}
if (this.options.contains("--cp")) {
this.options.remove("--cp");
this.options.add("-cp");
}
this.description = descriptor.description();
}
@Override
public Set<String> getOptions() {
return this.options;
}
@Override
public String getUsageHelp() {
return this.description;
}
}
}