blob: 3e9de1554fbcf59a1e1cfd9f326ae5b65ab7232d [file] [log] [blame]
/*
* 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.cassandra.tools.nodetool;
import java.io.PrintWriter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.apache.cassandra.tools.nodetool.layout.CassandraCliHelpLayout;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.IHelpCommandInitializable2;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST_HEADING;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_EXIT_CODE_LIST;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_EXIT_CODE_LIST_HEADING;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_FOOTER;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_FOOTER_HEADING;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_HEADER;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_HEADER_HEADING;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_SYNOPSIS;
@Command(name = "help",
helpCommand = true,
description = "Display help information")
public class Help implements IHelpCommandInitializable2, Runnable
{
@Option(names = { "--help" }, hidden = true, usageHelp = true, descriptionKey = "helpCommand.help",
description = "Show usage help for the help command and exit.")
private boolean helpRequested;
@Parameters(paramLabel = "command", arity = "1..*", descriptionKey = "helpCommand.command",
description = "The COMMAND to display the usage help message for.")
private List<String> commands;
private CommandLine self;
private PrintWriter out;
private CommandLine.Help.ColorScheme colorScheme;
/**
* Invokes {@code #usage(PrintStream, CommandLine.Help.ColorScheme) usage} for the specified command,
* or for the parent command.
*/
@Override
public void run()
{
CommandLine parent = self == null ? null : self.getParent();
if (parent == null)
return;
CommandLine.Help.ColorScheme colors = colorScheme == null ?
CommandLine.Help.defaultColorScheme(CommandLine.Help.Ansi.AUTO) :
colorScheme;
if (commands == null)
{
// If the parent command is the top-level command, print help for the top-level command.
printTopCommandUsage(parent, colors, out);
return;
}
if (parent.isAbbreviatedSubcommandsAllowed())
throw new CommandLine.ParameterException(parent, "Abbreviated subcommands are not allowed.");
// Print help for the last command in the list of commands.
CommandLine subcommand = parent;
for (String command : commands)
{
subcommand = subcommand.getSubcommands().get(command);
if (subcommand == null)
throw new CommandLine.ParameterException(parent, "Unknown subcommand '" + command + "'.", null, command);
}
subcommand.usage(out, colors);
}
public static void printTopCommandUsage(CommandLine command, CommandLine.Help.ColorScheme colors, PrintWriter writer)
{
if (command == null)
return;
StringBuilder sb = new StringBuilder();
CommandLine.Help help = command.getHelpFactory().create(command.getCommandSpec(), colors);
if (!(help instanceof CassandraCliHelpLayout))
{
command.usage(writer, colors);
return;
}
Map<String, CommandLine.IHelpSectionRenderer> helpSectionMap = cassandraTopLevelHelpSectionKeys((CassandraCliHelpLayout) help);
for (String key : command.getHelpSectionKeys())
{
CommandLine.IHelpSectionRenderer renderer = helpSectionMap.get(key);
if (renderer == null)
continue;
String rendered = renderer.render(help);
if (!Strings.isNullOrEmpty(rendered))
sb.append(rendered);
}
writer.println(sb);
writer.flush();
}
/**
* Top-level help command (includes all the available nodetool commands) has a different layout, so we need to
* provide a different set of keys for the help sections.
* @param layout The help class layout.
* @return Map of supported keys for the help sections.
*/
public static Map<String, CommandLine.IHelpSectionRenderer> cassandraTopLevelHelpSectionKeys(CassandraCliHelpLayout layout)
{
Map<String, CommandLine.IHelpSectionRenderer> sectionMap = new LinkedHashMap<>();
sectionMap.put(SECTION_KEY_HEADER_HEADING, CommandLine.Help::headerHeading);
sectionMap.put(SECTION_KEY_HEADER, CommandLine.Help::header);
sectionMap.put(SECTION_KEY_SYNOPSIS, layout::topLevelSynopsis);
sectionMap.put(SECTION_KEY_COMMAND_LIST_HEADING, layout::topLevelCommandListHeading);
sectionMap.put(SECTION_KEY_COMMAND_LIST, layout::topLevelCommandList);
sectionMap.put(SECTION_KEY_EXIT_CODE_LIST_HEADING, CommandLine.Help::exitCodeListHeading);
sectionMap.put(SECTION_KEY_EXIT_CODE_LIST, CommandLine.Help::exitCodeList);
sectionMap.put(SECTION_KEY_FOOTER_HEADING, CommandLine.Help::footerHeading);
sectionMap.put(SECTION_KEY_FOOTER, CommandLine.Help::footer);
return sectionMap;
}
/**
* The printHelpIfRequested method calls the init method on commands marked
* as helpCommand before the help command's run or call method is called.
*/
public void init(CommandLine helpCommandLine,
CommandLine.Help.ColorScheme colorScheme,
PrintWriter out,
PrintWriter err)
{
this.self = Preconditions.checkNotNull(helpCommandLine, "helpCommandLine");
this.colorScheme = Preconditions.checkNotNull(colorScheme, "colorScheme");
this.out = Preconditions.checkNotNull(out, "outWriter");
}
}