content: /*
   * 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");
      }
  }
  