la.getopt_2.11.1.2.4.source-code.getopt.scala Maven / Gradle / Ivy
The newest version!
/**
* Copyright (c) Radim Kolar 2013, 2018
*
* Licensed under MIT license:
* http://www.opensource.org/licenses/mit-license.php
**/
package com.filez.scala.getopt
import scala.collection.immutable.ListMap
import scala.collection.{Seq => GSeq}
/**
POSIX getopt(3) parser with GNU extension '--', but without long options.
@constructor Create immutable getopt instance
@param arg Sequence of command line arguments. Sequence can be empty but can not contain
zero sized strings.
@param optstring list of legitimate option characters. If character is
followed by colon, the option requires argument. Must not
be empty.
@param strict If argument is missing or option is unknown exception is raised.
@throws java.lang.IllegalArgumentException if input parameters are invalid
@throws java.lang.NoSuchElementException if option is unknown or parameter is missing.
Strict mode only.
@author Radim Kolar
@since 1.0
@version 1.1
@example
{{{
import com.filez.scala.getopt.getopt
object Example extends App {
protected val g = new getopt(args, "ab:c")
g.options.get('b').foreach{ arg => println(s"-b arg value is $arg") }
}
}}}
*/
class getopt @throws[IllegalArgumentException]("Input parameters are incorrect") @throws[NoSuchElementException]("Argument validation failed. Strict mode only") (arg:GSeq[String], optstring:String, strict:Boolean = false) {
// check if optstring is valid
if(! optstring.matches("""^([a-zA-Z0-9]:?)+$"""))
throw new IllegalArgumentException("invalid optstring")
// parse optstring into options map
/** options argument flag map. Value true indicates that option
has required argument. This map is created from optstring.
@since 1.0
*/
val options_map = """[a-zA-Z0-9]:?""".r.findAllIn(optstring)
.foldLeft(new ListMap[Char, Boolean]())( (m,s) => m + ((s(0),s.last == ':')) )
private var opts = Map[Char,String]()
private var args = Seq[String]()
private var nextOpt:Character = null
private var stopParsing:Boolean = false
for(element <- arg) {
if ( stopParsing == true ) {
args = args :+ (element)
} else
if ( nextOpt != null ) {
opts = opts + ((nextOpt, element))
nextOpt = null
} else {
try {
val ( omap, el, nopt ) = parseElement(element)
opts = opts ++ omap
if ( el != null ) {
args = args :+ (el)
}
nextOpt = nopt
} catch {
case nse: NoSuchElementException => stopParsing = true
}
}
}
// HANDLE MISSING ARGUMENT FOR LAST OPTION
if ( nextOpt != null ) {
opts = opts.updated(nextOpt, null)
}
// STORE RESULTS INTO PUBLIC IMMUTABLE FIELDS
/** Command line arguments with options and their arguments removed.
* @since 1.0
*/
val arguments = args
/** Command line options and their values. If option does not have argument map
contains empty string as value. If command line option was found but
not declared in optstring, null is stored.
@since 1.0
*/
val options = opts
if (strict) validate()
/** parse Command line Element
@param element command line element (one word on command line)
@returns map(Option->value). If option is unknown value is null, otherwise
value is option or "" if option do not have argument.
String - command line argument or null if element is option type.
Character - what option will be next element or null for none.
*/
private[scala] def parseElement(element:String):(Map[Char,String], String, Character) = {
if( element == "--" )
throw new NoSuchElementException
if( element.length == 0 )
throw new IllegalArgumentException("element must not be empty")
if( element(0) != '-' || element == "-" )
( Map[Char,String](), element, null )
else {
var opts = Map[Char,String]()
var argfollows = false
var optName:Character = null
var optArgument:String = ""
for(opt <- element.tail) {
if ( argfollows == true ) {
// ARGUMENT FOLLOWS TO END OF ELEMENT
optArgument += opt
} else {
val optarg = options_map.get(opt)
if ( optarg.isEmpty ) {
// UNKNOWN OPTION
opts = opts.updated(opt, null)
} else {
// KNOWN OPTION
if( optarg.get == true ) {
// OPTION WITH ARGUMENT
argfollows = true
optName = opt
} else {
// OPTION WITHOUT ARGUMENT
opts = opts.updated(opt, "")
}
}
}
}
// RETURN RESULT
if ( argfollows )
if ( optArgument == "" )
// ARGUMENT FOLLOWS IN NEXT ELEMENT
( opts, null, optName )
else
// SAVE ARGUMENT INTO MAP
( opts.updated(optName, optArgument), null, null )
else
// ONLY SHORT OPTIONS
( opts, null, null )
}
}
/**
* Validate parsed options. Throws an Exception if unknown option is encountered or
* required argument is missing. If getopt is created in strict mode this function is
* automatically called after options are parsed.
* @since 1.1
* @throws NoSuchElementException if option is unknown or argument is missing
*/
@throws[NoSuchElementException]("If option is unknown or required argument is missing")
def validate() : Unit = {
/* validate unknown options */
options.keys.foreach{ opt => if (! options_map.contains(opt)) throw new NoSuchElementException(s"Unknown option -$opt") }
/* validate missing arguments */
options_map.filter{ case (opt, arg) => arg == true }.keys
.foreach{ opt => if ( options.get(opt) == Some(null) )
throw new NoSuchElementException(s"Option -$opt do not have required argument")
}
}
}