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

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")
        }
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy