All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sumologic.shellbase.ShellCommandSet.scala Maven / Gradle / Ivy

There is a newer version: 1.5.1
Show newest version
/**
 * 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 com.sumologic.shellbase

import jline.console.completer.AggregateCompleter
import org.apache.commons.cli.CommandLine

import scala.collection.JavaConversions._
import scala.collection.mutable.ListBuffer

object ShellCommandSet {
  type ExecuteHook = (ShellCommand, Seq[String]) => Unit
}

class DuplicateCommandException(message: String) extends RuntimeException(message)

/**
  * A group of shell commands.
  */
class ShellCommandSet(name: String, helpText: String, aliases: List[String] = List[String]())
  extends ShellCommand(name, helpText, aliases) {

  val commands = ListBuffer[ShellCommand]()
  val preExecuteHooks = ListBuffer[ShellCommandSet.ExecuteHook]()
  val postExecuteHooks = ListBuffer[ShellCommandSet.ExecuteHook]()

  private val emptyCommandLine = parseOptions(Seq())

  private[this] def preExecute(shellCommand: ShellCommand, arguments: Seq[String]) {
    preExecuteHooks.foreach(_(shellCommand, arguments))
  }

  private[this] def postExecute(shellCommand: ShellCommand, arguments: Seq[String]) {
    postExecuteHooks.foreach(_(shellCommand, arguments))
  }

  final override def executeLine(args: List[String], commandPath: List[String] = List()): Boolean = {

    if (args.length < 1) {
      println("Please run 'help' to learn more about this command set.")
      return true
    }

    val command = args(0)
    val arguments = args.splitAt(1)._2
    findCommand(command) match {

      case Some(shellCommand) =>
        validate(emptyCommandLine).foreach {
          error =>
            println(error)
            return false
        }

        try {
          preExecute(shellCommand, arguments)
        } catch {
          case e: Exception =>
            println(e.getMessage)
            return false
        }

        val ret = shellCommand.executeLine(arguments, commandPath ++ List(command.trim))

        try {
          postExecute(shellCommand, arguments)
        } catch {
          case e: Exception =>
            println(e.getMessage)
            return false
        }

        ret

      case None =>
        println(s"$name: command $command not found")
        false
    }
  }

  // NOTE(stefan, 2013-12-31): Disable the validation done in ShellCommand, executeLine does
  // something comparable that makes more sense for this use case.
  override def validate(cmdLine: CommandLine): Option[String] = None

  def validateCommands(): Unit = {

    var seen = Map[String, String]()

    def checkCommandName(name: String, className: String) {
      if (seen.containsKey(name)) {
        val otherClass = seen(name)
        throw new DuplicateCommandException(s"Command '$name' is defined in $otherClass and $className!")
      } else {
        seen = seen + (name -> className)
      }
    }

    def checkShellCommand(command: ShellCommand) {
      val className = command.getClass.getName
      checkCommandName(command.name, className)
      command.aliases.foreach(checkCommandName(_, className))
      command match {
        case set: ShellCommandSet => set.validateCommands()
        case _ =>
      }
    }

    commands.foreach(checkShellCommand)
  }

  final def execute(cmdLine: CommandLine) =
    throw new IllegalAccessException("Call the other signature!")

  override def argCompleter = new AggregateCompleter(commands.map(_.completer))

  def findCommand(command: String) = {
    val normalizedCommand = command.toLowerCase.trim
    commands.find(cmd => {
      cmd.name == normalizedCommand || cmd.aliases.contains(normalizedCommand)
    })
  }

  // -----------------------------------------------------------------------------------------------
  // Commands available with all command sets.
  // -----------------------------------------------------------------------------------------------

  commands += new ShellCommand("help", "Print online help.", List("?")) {

    override def maxNumberOfArguments = 1

    def execute(cmdLine: CommandLine) = {

      if (cmdLine.getArgList.size < 1 || cmdLine.getArgs()(0).trim.isEmpty) {
        printf("Available commands: %n")
        for (cmd <- commands.filterNot(_.deprecated).sortBy(_.name)) {
          printf("  %-15s %s%n", cmd.name, cmd.helpText)
        }
        true
      } else {
        val command = cmdLine.getArgs()(0)
        findCommand(command) match {
          case Some(shellCommand) =>
            shellCommand.help
            true
          case None =>
            printf("Command '%s' unknown.", command)
            false
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy