<commit_before>//
// CmdLineParser.scala -- Scala trait CmdLineParser
// Project OrcScala
//
// Created by jthywiss on Jul 19, 2010.
//
// Copyright (c) 2017 The University of Texas at Austin. All rights reserved.
//
// Use and redistribution of this file is governed by the license terms in
// the LICENSE file found in the project's top-level directory and also found at
// URL: http://orc.csres.utexas.edu/license.shtml .
//

package orc.util

import java.io.File
import java.net.InetSocketAddress
import java.util.NoSuchElementException

/** Parses command line arguments per POSIX and GNU command line syntax guidelines.
  * Mix this trait in and add XxxOprd and XxxOpt statements to your class holding
  * the resulting parsed values.  Call parseCmdLine after all the XxxOprd and XxxOpt
  * statements.
  *
  * This CmdLineParser understands POSIX single-letter option names (-a -b -c) and GNU
  * long option names (--option-name=value).  It also handles the various option argument
  * syntax: -avalue -a value --long-name=value and -long-name value, it tries to
  * "do the right thing" in the ambiguous situation -abc (could mean -a -b -c or
  * -a bc or --abc), and it understands the -- option list terminator.
  * See POSIX XBD §12.1 and §12.2 and GNU libc §25.1.1.
  *
  * @author jthywiss
  */
trait CmdLineParser {

  var recognizedOprds = scala.collection.mutable.Map.empty[Int, CmdLineOprd]
  var recognizedOpts = scala.collection.mutable.Set.empty[CmdLineOpt]
  var recognizedShortOpts = scala.collection.mutable.Map.empty[Char, CmdLineOpt]
  var recognizedLongOpts = scala.collection.mutable.Map.empty[String, CmdLineOpt]

  ////////
  // Constructor
  ////////

  UnitOpt(() => false, () => printHelp, '?', "help", usage = "Give this help list")
  protected def printHelp {
    def shortOptHelp(opt: CmdLineOpt) =
      if (opt.shortName != 0 && opt.shortName != ' ')
        "-" + opt.shortName + (if (opt.argName != null && opt.argName.length > 0) (" " + opt.argName) else "")
      else
        ""
    def longOptHelp(opt: CmdLineOpt) =
      if (opt.longName != null && opt.longName.length > 0)
        "--" + opt.longName + (if (opt.argName != null && opt.argName.length > 0) ("=" + opt.argName) else "")
      else
        ""
    val helpString = usageString +
      (if (recognizedOpts.size == 0) ""
      else "\nOptions:\n" +
        (recognizedOpts.toList.sortBy(o => (o.longName, o.shortName)).map({ opt: CmdLineOpt =>
          //TODO: Aligned columns?
          "  " + shortOptHelp(opt) +
            "  " + longOptHelp(opt) +
            "  " + opt.usage
        })).mkString("\n"))
    throw new PrintVersionAndMessageException(helpString)
  }

  UnitOpt(() => false, () => printUsage, ' ', "usage", usage = "Give a short usage message")
  protected def printUsage { throw new PrintVersionAndMessageException(usageString) }

  UnitOpt(() => false, () => printVersion, 'V', "version", usage = "Print program version")
  protected def printVersion { throw new PrintVersionAndMessageException("") }

  def usageString =
    "usage: " +
      (if (recognizedOpts.size > 0) "[options...]" else "") +
      (for { i <- 0 until recognizedOprds.size } yield " " + recognizedOprds(i).argName).mkString(" ")
  //"Try the --help option for more information." //TODO: In simple cases, show the usage here

  ////////
  // Command line operand and option definitions
  ////////

  abstract class CmdLineOprdOpt(val argName: String, val usage: String, val required: Boolean, val hidden: Boolean) extends Serializable {
    def getValue: String
    def setValue(s: String): Unit
  }

  abstract class CmdLineOprd(val position: Int, override val argName: String, override val usage: String, override val required: Boolean, override val hidden: Boolean) extends CmdLineOprdOpt(argName, usage, required, hidden) {
    if (recognizedOprds.contains(position)) throw new MultiplyDefinedCmdLineOprndError(position)
    recognizedOprds += ((position, this))
  }

  abstract class CmdLineOpt(val shortName: Char, val longName: String, override val argName: String, override val usage: String, override val required: Boolean, override val hidden: Boolean) extends CmdLineOprdOpt(argName, usage, required, hidden) {
    if (shortName != ' ') {
      if (recognizedShortOpts.contains(shortName)) throw new MultiplyDefinedCmdLineOptError(shortName.toString)
      recognizedShortOpts += ((shortName, this))
    }
    if (longName != null) {
      if (recognizedLongOpts.contains(longName)) throw new MultiplyDefinedCmdLineOptError(longName)
      recognizedLongOpts += ((longName, this))
    }
    recognizedOpts += this
  }

  case class BooleanOprd(val getter: Function0[Boolean], val setter: (Boolean => Unit), override val position: Int, override val argName: String = "BOOL", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) { setter(value.toBoolean) }
  }

  case class DoubleOprd(val getter: Function0[Double], val setter: (Double => Unit), override val position: Int, override val argName: String = "DOUBLE", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) { setter(value.toDouble) }
  }

  case class IntOprd(val getter: Function0[Int], val setter: (Int => Unit), override val position: Int, override val argName: String = "INT", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) { setter(value.toInt) }
  }

  case class CharOprd(val getter: Function0[Char], val setter: (Char => Unit), override val position: Int, override val argName: String = "CHAR", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) { setter(value(0)) }
  }

  case class StringOprd(val getter: Function0[String], val setter: (String => Unit), override val position: Int, override val argName: String = "STRING", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter() }
    def setValue(value: String) { setter(value) }
  }

  case class StringListOprd(val getter: Function0[Seq[String]], val setter: (Seq[String] => Unit), override val position: Int, override val argName: String = "STRING", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().mkString(File.pathSeparator) }
    def setValue(value: String) { setter(value.split(File.pathSeparator)) }
  }

  case class FileOprd(val getter: Function0[File], val setter: (File => Unit), override val position: Int, override val argName: String = "FILE", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = {
      getter() match {
        case null => ""
        case f => f.toString
      }
    }
    def setValue(value: String) { if (value != null && !value.isEmpty) setter(new File(value)) }
  }

  case class PathListOprd(val getter: Function0[Seq[File]], val setter: (Seq[File] => Unit), override val position: Int, override val argName: String = "PATH", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().map(_.toString).mkString(File.pathSeparator) }
    def setValue(value: String) { setter(value.split(File.pathSeparator).map(new File(_))) }
  }

  private def oprdString2socket(s: String, argName: String) = {
    val lastColon = s.lastIndexOf(":")
    if (lastColon < 0) {
      throw new UnrecognizedCmdLineOprdException("expecting host:port", argName, s, CmdLineParser.this)
    }
    try {
      new InetSocketAddress(s.substring(0, lastColon), s.substring(lastColon + 1).toInt)
    } catch {
      case _: NumberFormatException => throw new UnrecognizedCmdLineOprdException("expecting a decimal integer for port number", argName, s, CmdLineParser.this)
    }
  }

  case class SocketOprd(val getter: Function0[InetSocketAddress], val setter: (InetSocketAddress => Unit), override val position: Int, override val argName: String = "SOCKET", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().getHostString + ":" + getter().getPort }
    def setValue(value: String) { setter(oprdString2socket(value, argName)) }
  }

  case class SocketListOprd(val getter: Function0[Seq[InetSocketAddress]], val setter: (Seq[InetSocketAddress] => Unit), override val position: Int, override val argName: String = "SOCKET-LIST", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().map( { isa => isa.getHostString + ":" + isa.getPort} ).mkString(",") }
    def setValue(value: String) { setter(value.split(",").map(oprdString2socket(_, argName))) }
  }

  case class UnitOpt(val getter: Function0[Boolean], val setter: (() => Unit), override val shortName: Char, override val longName: String, override val argName: String = "", override val usage: String = "", override val required: Boolean = false, override val hidden: Boolean = false)
    extends CmdLineOpt(shortName, longName, argName, usage, required, hidden) {
    def getValue: String = ""
    def setValue(value: String) { setter() }
  }

  case class BooleanOpt(val getter: Function0[Boolean], val setter: (Boolean => Unit), override val shortName: Char, override val longName: String, override val argName: String = "BOOL", override val usage: String = "", override val required: Boolean = false, override val hidden: Boolean = false)
    extends CmdLineOpt(shortName, longName, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) {
      try {
        setter(value.toBoolean)
      } catch {
        case _: IllegalArgumentException => throw new UnrecognizedCmdLineOptArgException("expecting true or false", longName, value, CmdLineParser.this)
      }
    }
  }

  case class DoubleOpt(val getter: Function0[Double], val setter: (Double => Unit), override val shortName: Char, override val longName: String, override val argName: String = "DOUBLE", override val usage: String = "", override val required: Boolean = false, override val hidden: Boolean = false)
    extends CmdLineOpt(shortName, longName, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) {
      try {
        setter(value.toDouble)
      } catch {
        case _: NumberFormatException => throw new UnrecognizedCmdLineOptArgException("expecting a floating-point number", longName, value, CmdLineParser.this)
      }
    }
  }

  case class IntOpt(val getter: Function0[Int], val setter: (Int => Unit), override val shortName: Char, override val longName: String, override val argName: String = "INT", override val usage: String = "", override val required: Boolean = false, override val hidden: Boolean = false)
    extends CmdLineOpt(shortName, longName, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) {
      try {
        setter(value.toInt)
      } catch {
        case _: NumberFormatException => throw new UnrecognizedCmdLineOptArgException("expecting a decimal integer", longName, value, CmdLineParser.this)
      }
    }
  }<commit_msg>Allow radix prefixes on integer command-line parameters.<commit_after>//
// CmdLineParser.scala -- Scala trait CmdLineParser
// Project OrcScala
//
// Created by jthywiss on Jul 19, 2010.
//
// Copyright (c) 2017 The University of Texas at Austin. All rights reserved.
//
// Use and redistribution of this file is governed by the license terms in
// the LICENSE file found in the project's top-level directory and also found at
// URL: http://orc.csres.utexas.edu/license.shtml .
//

package orc.util

import java.io.File
import java.net.InetSocketAddress
import java.util.NoSuchElementException
import java.lang.Integer

/** Parses command line arguments per POSIX and GNU command line syntax guidelines.
  * Mix this trait in and add XxxOprd and XxxOpt statements to your class holding
  * the resulting parsed values.  Call parseCmdLine after all the XxxOprd and XxxOpt
  * statements.
  *
  * This CmdLineParser understands POSIX single-letter option names (-a -b -c) and GNU
  * long option names (--option-name=value).  It also handles the various option argument
  * syntax: -avalue -a value --long-name=value and -long-name value, it tries to
  * "do the right thing" in the ambiguous situation -abc (could mean -a -b -c or
  * -a bc or --abc), and it understands the -- option list terminator.
  * See POSIX XBD §12.1 and §12.2 and GNU libc §25.1.1.
  *
  * @author jthywiss
  */
trait CmdLineParser {

  var recognizedOprds = scala.collection.mutable.Map.empty[Int, CmdLineOprd]
  var recognizedOpts = scala.collection.mutable.Set.empty[CmdLineOpt]
  var recognizedShortOpts = scala.collection.mutable.Map.empty[Char, CmdLineOpt]
  var recognizedLongOpts = scala.collection.mutable.Map.empty[String, CmdLineOpt]

  ////////
  // Constructor
  ////////

  UnitOpt(() => false, () => printHelp, '?', "help", usage = "Give this help list")
  protected def printHelp {
    def shortOptHelp(opt: CmdLineOpt) =
      if (opt.shortName != 0 && opt.shortName != ' ')
        "-" + opt.shortName + (if (opt.argName != null && opt.argName.length > 0) (" " + opt.argName) else "")
      else
        ""
    def longOptHelp(opt: CmdLineOpt) =
      if (opt.longName != null && opt.longName.length > 0)
        "--" + opt.longName + (if (opt.argName != null && opt.argName.length > 0) ("=" + opt.argName) else "")
      else
        ""
    val helpString = usageString +
      (if (recognizedOpts.size == 0) ""
      else "\nOptions:\n" +
        (recognizedOpts.toList.sortBy(o => (o.longName, o.shortName)).map({ opt: CmdLineOpt =>
          //TODO: Aligned columns?
          "  " + shortOptHelp(opt) +
            "  " + longOptHelp(opt) +
            "  " + opt.usage
        })).mkString("\n"))
    throw new PrintVersionAndMessageException(helpString)
  }

  UnitOpt(() => false, () => printUsage, ' ', "usage", usage = "Give a short usage message")
  protected def printUsage { throw new PrintVersionAndMessageException(usageString) }

  UnitOpt(() => false, () => printVersion, 'V', "version", usage = "Print program version")
  protected def printVersion { throw new PrintVersionAndMessageException("") }

  def usageString =
    "usage: " +
      (if (recognizedOpts.size > 0) "[options...]" else "") +
      (for { i <- 0 until recognizedOprds.size } yield " " + recognizedOprds(i).argName).mkString(" ")
  //"Try the --help option for more information." //TODO: In simple cases, show the usage here

  ////////
  // Command line operand and option definitions
  ////////
      
  val HexLiteral = raw"0x([0-f]+)".r
  val BinLiteral = raw"0b([0-f]+)".r
  val OctLiteral = raw"0([0-f]+)".r
      
  def parseIntWithPossibleRadix(value: String): Int = {
    value match {
      case HexLiteral(s) =>
        Integer.parseInt(s, 16)
      case BinLiteral(s) =>
        Integer.parseInt(s, 2)
      case OctLiteral(s) =>
        Integer.parseInt(s, 8)
      case _ =>
        value.toInt          
    }
  }
  
  abstract class CmdLineOprdOpt(val argName: String, val usage: String, val required: Boolean, val hidden: Boolean) extends Serializable {
    def getValue: String
    def setValue(s: String): Unit
  }

  abstract class CmdLineOprd(val position: Int, override val argName: String, override val usage: String, override val required: Boolean, override val hidden: Boolean) extends CmdLineOprdOpt(argName, usage, required, hidden) {
    if (recognizedOprds.contains(position)) throw new MultiplyDefinedCmdLineOprndError(position)
    recognizedOprds += ((position, this))
  }

  abstract class CmdLineOpt(val shortName: Char, val longName: String, override val argName: String, override val usage: String, override val required: Boolean, override val hidden: Boolean) extends CmdLineOprdOpt(argName, usage, required, hidden) {
    if (shortName != ' ') {
      if (recognizedShortOpts.contains(shortName)) throw new MultiplyDefinedCmdLineOptError(shortName.toString)
      recognizedShortOpts += ((shortName, this))
    }
    if (longName != null) {
      if (recognizedLongOpts.contains(longName)) throw new MultiplyDefinedCmdLineOptError(longName)
      recognizedLongOpts += ((longName, this))
    }
    recognizedOpts += this
  }

  case class BooleanOprd(val getter: Function0[Boolean], val setter: (Boolean => Unit), override val position: Int, override val argName: String = "BOOL", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) { setter(value.toBoolean) }
  }

  case class DoubleOprd(val getter: Function0[Double], val setter: (Double => Unit), override val position: Int, override val argName: String = "DOUBLE", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) { setter(value.toDouble) }
  }

  case class IntOprd(val getter: Function0[Int], val setter: (Int => Unit), override val position: Int, override val argName: String = "INT", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) { setter(parseIntWithPossibleRadix(value)) }
  }

  case class CharOprd(val getter: Function0[Char], val setter: (Char => Unit), override val position: Int, override val argName: String = "CHAR", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) { setter(value(0)) }
  }

  case class StringOprd(val getter: Function0[String], val setter: (String => Unit), override val position: Int, override val argName: String = "STRING", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter() }
    def setValue(value: String) { setter(value) }
  }

  case class StringListOprd(val getter: Function0[Seq[String]], val setter: (Seq[String] => Unit), override val position: Int, override val argName: String = "STRING", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().mkString(File.pathSeparator) }
    def setValue(value: String) { setter(value.split(File.pathSeparator)) }
  }

  case class FileOprd(val getter: Function0[File], val setter: (File => Unit), override val position: Int, override val argName: String = "FILE", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = {
      getter() match {
        case null => ""
        case f => f.toString
      }
    }
    def setValue(value: String) { if (value != null && !value.isEmpty) setter(new File(value)) }
  }

  case class PathListOprd(val getter: Function0[Seq[File]], val setter: (Seq[File] => Unit), override val position: Int, override val argName: String = "PATH", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().map(_.toString).mkString(File.pathSeparator) }
    def setValue(value: String) { setter(value.split(File.pathSeparator).map(new File(_))) }
  }

  private def oprdString2socket(s: String, argName: String) = {
    val lastColon = s.lastIndexOf(":")
    if (lastColon < 0) {
      throw new UnrecognizedCmdLineOprdException("expecting host:port", argName, s, CmdLineParser.this)
    }
    try {
      new InetSocketAddress(s.substring(0, lastColon), s.substring(lastColon + 1).toInt)
    } catch {
      case _: NumberFormatException => throw new UnrecognizedCmdLineOprdException("expecting a decimal integer for port number", argName, s, CmdLineParser.this)
    }
  }

  case class SocketOprd(val getter: Function0[InetSocketAddress], val setter: (InetSocketAddress => Unit), override val position: Int, override val argName: String = "SOCKET", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().getHostString + ":" + getter().getPort }
    def setValue(value: String) { setter(oprdString2socket(value, argName)) }
  }

  case class SocketListOprd(val getter: Function0[Seq[InetSocketAddress]], val setter: (Seq[InetSocketAddress] => Unit), override val position: Int, override val argName: String = "SOCKET-LIST", override val usage: String = "", override val required: Boolean = true, override val hidden: Boolean = false)
    extends CmdLineOprd(position, argName, usage, required, hidden) {
    def getValue: String = { getter().map( { isa => isa.getHostString + ":" + isa.getPort} ).mkString(",") }
    def setValue(value: String) { setter(value.split(",").map(oprdString2socket(_, argName))) }
  }

  case class UnitOpt(val getter: Function0[Boolean], val setter: (() => Unit), override val shortName: Char, override val longName: String, override val argName: String = "", override val usage: String = "", override val required: Boolean = false, override val hidden: Boolean = false)
    extends CmdLineOpt(shortName, longName, argName, usage, required, hidden) {
    def getValue: String = ""
    def setValue(value: String) { setter() }
  }

  case class BooleanOpt(val getter: Function0[Boolean], val setter: (Boolean => Unit), override val shortName: Char, override val longName: String, override val argName: String = "BOOL", override val usage: String = "", override val required: Boolean = false, override val hidden: Boolean = false)
    extends CmdLineOpt(shortName, longName, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) {
      try {
        setter(value.toBoolean)
      } catch {
        case _: IllegalArgumentException => throw new UnrecognizedCmdLineOptArgException("expecting true or false", longName, value, CmdLineParser.this)
      }
    }
  }

  case class DoubleOpt(val getter: Function0[Double], val setter: (Double => Unit), override val shortName: Char, override val longName: String, override val argName: String = "DOUBLE", override val usage: String = "", override val required: Boolean = false, override val hidden: Boolean = false)
    extends CmdLineOpt(shortName, longName, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) {
      try {
        setter(value.toDouble)
      } catch {
        case _: NumberFormatException => throw new UnrecognizedCmdLineOptArgException("expecting a floating-point number", longName, value, CmdLineParser.this)
      }
    }
  }

  case class IntOpt(val getter: Function0[Int], val setter: (Int => Unit), override val shortName: Char, override val longName: String, override val argName: String = "INT", override val usage: String = "", override val required: Boolean = false, override val hidden: Boolean = false)
    extends CmdLineOpt(shortName, longName, argName, usage, required, hidden) {
    def getValue: String = { getter().toString }
    def setValue(value: String) {
      try {
        setter(parseIntWithPossibleRadix(value))
      } catch {
        case _: NumberFormatException => throw new UnrecognizedCmdLineOptArgException("expecting an integer (with optional hexadecimal, octal, or binary prefix to select radix)", longName, value, CmdLineParser.this)
      }
    }
  }
{'fraction_non_alphanumeric': 0.12074737251848969, 'fraction_numerical': 0.004126119112495134, 'mean_word_length': 3.8991228070175437, 'pattern_counts': {'":': 6, '<': 7, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '356175', 'n_tokens_mistral': 7629, 'n_tokens_neox': 7539, 'n_words': 2434}