summaryrefslogtreecommitdiff
path: root/cli/source
diff options
context:
space:
mode:
authorRocky Madden <git@rockymadden.com>2012-10-06 23:19:20 -0600
committerRocky Madden <git@rockymadden.com>2012-10-06 23:19:20 -0600
commitdadd1221ec7c1301b3cc2dfc178dba2091e1f9b8 (patch)
treece698016721cdc2636d66742d705b232ad1c9fe9 /cli/source
downloadstringmetric-dadd1221ec7c1301b3cc2dfc178dba2091e1f9b8.tar.gz
stringmetric-dadd1221ec7c1301b3cc2dfc178dba2091e1f9b8.tar.bz2
stringmetric-dadd1221ec7c1301b3cc2dfc178dba2091e1f9b8.zip
Created repository.v0.0.0
Diffstat (limited to 'cli/source')
-rwxr-xr-xcli/source/core/scala/org/hashtree/stringmetric/cli/OptionMapUtility.scala55
-rwxr-xr-xcli/source/core/scala/org/hashtree/stringmetric/cli/command/Command.scala34
-rwxr-xr-xcli/source/core/scala/org/hashtree/stringmetric/cli/command/jaroWinklerMetric.scala52
-rwxr-xr-xcli/source/core/scala/org/hashtree/stringmetric/cli/command/package.scala6
-rwxr-xr-xcli/source/core/scala/org/hashtree/stringmetric/cli/package.scala6
-rwxr-xr-xcli/source/test/scala/org/hashtree/stringmetric/cli/OptionMapUtilitySpec.scala151
-rwxr-xr-xcli/source/test/scala/org/hashtree/stringmetric/cli/command/jaroWinklerMetricSpec.scala39
7 files changed, 343 insertions, 0 deletions
diff --git a/cli/source/core/scala/org/hashtree/stringmetric/cli/OptionMapUtility.scala b/cli/source/core/scala/org/hashtree/stringmetric/cli/OptionMapUtility.scala
new file mode 100755
index 0000000..926ba8b
--- /dev/null
+++ b/cli/source/core/scala/org/hashtree/stringmetric/cli/OptionMapUtility.scala
@@ -0,0 +1,55 @@
+package org.hashtree.stringmetric.cli
+
+import scala.collection.immutable.HashMap
+
+/** Utility standalone for OptionMap based operations. */
+object OptionMapUtility {
+ def toOptionMap(arguments: Array[String]): OptionMap = {
+ toOptionMap(arguments.toList)
+ }
+
+ def toOptionMap(arguments: List[String]): OptionMap = {
+ next(new HashMap[Symbol, String](), arguments)
+ }
+
+ private[this] def next(optionMap: OptionMap, arguments: List[String]): OptionMap = {
+ val double = """^(--[a-zA-Z0-9]+)(\=[a-zA-Z0-9\.\-\_]+)?""".r
+ val single = """^(-[a-zA-Z0-9]+)(\=[a-zA-Z0-9\.\-\_]+)?""".r
+ val less = """([a-zA-Z0-9\/\-\_\$\.]+)""".r
+
+ arguments match {
+ // Empty List, return OptionMap.
+ case Nil => optionMap
+ // Double dash options, without value.
+ case double(name, null) :: tail => {
+ next(optionMap + (Symbol(name.tail.tail) -> ""), tail)
+ }
+ // Double dash options, with value.
+ case double(name, value) :: tail => {
+ next(optionMap + (Symbol(name.tail.tail) -> value.tail), tail)
+ }
+ // Single dash options, without value.
+ case single(name, null) :: tail => {
+ next(optionMap + (Symbol(name.tail) -> ""), tail)
+ }
+ // Single dash options, with value. Value is discarded.
+ case single(name, value) :: tail => {
+ next(optionMap + (Symbol(name.tail) -> ""), tail)
+ }
+ // Dashless options.
+ case less(value) :: tail if value.head != '-' => {
+ if (optionMap.contains('dashless)) {
+ val dashless = optionMap('dashless) + " " + value.trim
+
+ next((optionMap - 'dashless) + ('dashless -> dashless), tail)
+ } else {
+ next(optionMap + ('dashless -> value.trim), tail)
+ }
+ }
+ // Invalid option, ignore.
+ case _ :: tail => {
+ next(optionMap, tail)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/cli/source/core/scala/org/hashtree/stringmetric/cli/command/Command.scala b/cli/source/core/scala/org/hashtree/stringmetric/cli/command/Command.scala
new file mode 100755
index 0000000..bac7786
--- /dev/null
+++ b/cli/source/core/scala/org/hashtree/stringmetric/cli/command/Command.scala
@@ -0,0 +1,34 @@
+package org.hashtree.stringmetric.cli.command
+
+import org.hashtree.stringmetric.cli.OptionMap
+
+/** Defines the traits and provides basic implementations of a command. Commands are always implemented as objects. */
+trait Command {
+ def main(args: Array[String]): Unit
+
+ def help(): Unit
+
+ final def error(error: Throwable)(implicit options: OptionMap): Unit = {
+ if (! isUnitTest(options)) {
+ println(error.getMessage)
+ sys.exit(1)
+ } else {
+ throw error
+ }
+ }
+
+ def execute(options: OptionMap): Unit
+
+ final def exit(implicit options: OptionMap): Unit = {
+ if (! isUnitTest(options)) sys.exit(0)
+ }
+
+ protected[this] def isUnitTest(options: OptionMap): Boolean = {
+ (options.contains('ut) || (options.contains('unitTest) && options.get('unitTest) != "false"))
+ }
+
+ protected[this] def isDebug(options: OptionMap): Boolean = {
+ (options.contains('d) || (options.contains('debug) && options.get('debug) != "false"))
+ }
+}
+
diff --git a/cli/source/core/scala/org/hashtree/stringmetric/cli/command/jaroWinklerMetric.scala b/cli/source/core/scala/org/hashtree/stringmetric/cli/command/jaroWinklerMetric.scala
new file mode 100755
index 0000000..63ab69c
--- /dev/null
+++ b/cli/source/core/scala/org/hashtree/stringmetric/cli/command/jaroWinklerMetric.scala
@@ -0,0 +1,52 @@
+package org.hashtree.stringmetric.cli.command
+
+import org.hashtree.stringmetric.JaroWinklerMetric
+import org.hashtree.stringmetric.cli._
+import org.hashtree.stringmetric.cli.command._
+
+/**
+ * The jaroWinklerMetric [[org.hashtree.stringmetric.cli.command.Command]]. Compares two strings to calculate the
+ * Jaro-Winkler distance.
+ */
+object jaroWinklerMetric extends Command {
+ override def main(args: Array[String]): Unit = {
+ val options = OptionMapUtility.toOptionMap(args)
+
+ try {
+ // Help.
+ if (options.contains('h) || options.contains('help)) {
+ help()
+ exit(options)
+ // Execute.
+ } else if (options.contains('dashless) && options('dashless).count(_ == ' ') == 1) {
+ execute(options)
+ exit(options)
+ // Invalid syntax.
+ } else {
+ throw new IllegalArgumentException("Expected valid syntax. See --help.")
+ }
+ } catch {
+ case e => error(e)(options)
+ }
+ }
+
+ override def help(): Unit = {
+ val ls = sys.props("line.separator")
+ val tab = " "
+
+ println(
+ "Compares two strings to calculate the Jaro-Winkler distance." + ls + ls +
+ "Syntax:" + ls +
+ tab + "jaroWinklerMetric [Options] string1 string2..." + ls + ls +
+ "Options:" + ls +
+ tab + "-h, --help" + ls +
+ tab + tab + "Outputs description, syntax, and options."
+ )
+ }
+
+ override def execute(options: OptionMap): Unit = {
+ val strings = options('dashless).split(" ")
+
+ println(JaroWinklerMetric.compare(strings(0), strings(1)).toString)
+ }
+} \ No newline at end of file
diff --git a/cli/source/core/scala/org/hashtree/stringmetric/cli/command/package.scala b/cli/source/core/scala/org/hashtree/stringmetric/cli/command/package.scala
new file mode 100755
index 0000000..b0610ba
--- /dev/null
+++ b/cli/source/core/scala/org/hashtree/stringmetric/cli/command/package.scala
@@ -0,0 +1,6 @@
+package org.hashtree.stringmetric.cli
+
+/** Provides core command functionality. */
+package object command {
+ implicit val optionMap: OptionMap = OptionMapUtility.toOptionMap(Array("--unitTest=false"))
+} \ No newline at end of file
diff --git a/cli/source/core/scala/org/hashtree/stringmetric/cli/package.scala b/cli/source/core/scala/org/hashtree/stringmetric/cli/package.scala
new file mode 100755
index 0000000..a8c1c01
--- /dev/null
+++ b/cli/source/core/scala/org/hashtree/stringmetric/cli/package.scala
@@ -0,0 +1,6 @@
+package org.hashtree.stringmetric
+
+/** Provides core CLI functionality. */
+package object cli {
+ type OptionMap = Map[Symbol, String]
+} \ No newline at end of file
diff --git a/cli/source/test/scala/org/hashtree/stringmetric/cli/OptionMapUtilitySpec.scala b/cli/source/test/scala/org/hashtree/stringmetric/cli/OptionMapUtilitySpec.scala
new file mode 100755
index 0000000..a5a8eb1
--- /dev/null
+++ b/cli/source/test/scala/org/hashtree/stringmetric/cli/OptionMapUtilitySpec.scala
@@ -0,0 +1,151 @@
+package org.hashtree.stringmetric.cli
+
+import org.hashtree.stringmetric.ScalaTest
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+@RunWith(classOf[JUnitRunner])
+final class OptionMapUtilitySpec extends ScalaTest {
+ "OptionMapUtility" should provide {
+ "overloaded toOptionMap method" when passed {
+ "Array with a single valid double dashed option" should returns {
+ "populated Map" in {
+ val options = OptionMapUtility.toOptionMap(Array("--help"))
+
+ options('help) should equal ("")
+ }
+ }
+ "List with a single valid double dashed option" should returns {
+ "populated Map" in {
+ val options = OptionMapUtility.toOptionMap(List("--help"))
+
+ options('help) should equal ("")
+ }
+ }
+ "Array with a multiple valid double dashed options" should returns {
+ "populated Map" in {
+ val options = OptionMapUtility.toOptionMap(Array("--help", "--test=test"))
+
+ options('help) should equal ("")
+ options('test) should equal ("test")
+ }
+ }
+ "List with a multiple valid double dashed options" should returns {
+ "populated Map" in {
+ val options = OptionMapUtility.toOptionMap(List("--help", "--test=test"))
+
+ options('help) should equal ("")
+ options('test) should equal ("test")
+ }
+ }
+ "Array with invalid double dashed options" should returns {
+ "empty Map" in {
+ val options = OptionMapUtility.toOptionMap(Array("--help#", "--test%=test"))
+
+ options.keysIterator.length should be (0)
+ }
+ }
+ "List with invalid double dashed options" should returns {
+ "empty Map" in {
+ val options = OptionMapUtility.toOptionMap(List("--help#", "--test%=test"))
+
+ options.keysIterator.length should be (0)
+ }
+ }
+ "Array with a single valid single dashed option" should returns {
+ "populated Map" in {
+ val options = OptionMapUtility.toOptionMap(Array("-h"))
+
+ options('h) should equal ("")
+ }
+ }
+ "List with a single valid single dashed option" should returns {
+ "populated Map" in {
+ val options = OptionMapUtility.toOptionMap(List("-h"))
+
+ options('h) should equal ("")
+ }
+ }
+ "Array with multiple valid single dashed options" should returns {
+ "populated Map" in {
+ val options = OptionMapUtility.toOptionMap(Array("-h", "-i"))
+
+ options('h) should equal ("")
+ options('i) should equal ("")
+ }
+ }
+ "List with multiple valid single dashed options" should returns {
+ "populated Map" in {
+ val options = OptionMapUtility.toOptionMap(List("-h", "-i"))
+
+ options('h) should equal ("")
+ options('i) should equal ("")
+ }
+ }
+ "Array with an invalid single dashed options" should returns {
+ "empty Map" in {
+ val options = OptionMapUtility.toOptionMap(Array("-h-i", "-i#gloo"))
+
+ options.keysIterator.length should be (0)
+ }
+ }
+ "List with an invalid single dashed options" should returns {
+ "empty Map" in {
+ val options = OptionMapUtility.toOptionMap(List("-h-i", "-i#gloo"))
+
+ options.keysIterator.length should be (0)
+ }
+ }
+ "Array with a single nameless option" should returns {
+ "single key populated Map" in {
+ val options = OptionMapUtility.toOptionMap(Array("filename0"))
+
+ options('dashless).count(_ == ' ') should be (0)
+ }
+ }
+ "List with a single nameless option" should returns {
+ "single key populated Map" in {
+ val options = OptionMapUtility.toOptionMap(List("filename0"))
+
+ options('dashless).count(_ == ' ') should be (0)
+ }
+ }
+ "Array with multiple single nameless options" should returns {
+ "single key populated Map" in {
+ val options = OptionMapUtility.toOptionMap(Array("filename0", "filename1", "filename2"))
+
+ options('dashless).count(_ == ' ') should be (2)
+ }
+ }
+ "List with multiple single nameless options" should returns {
+ "single key populated Map" in {
+ val options = OptionMapUtility.toOptionMap(List("filename0", "filename1", "filename2"))
+
+ options('dashless).count(_ == ' ') should be (2)
+ }
+ }
+ "Array with mixed options" should returns {
+ "populated Map" in {
+ val options = OptionMapUtility.toOptionMap(Array("-q", "--help", "--test=test", "-go", "filename0", "filename1", "filename2"))
+
+ options('q) should equal ("")
+ options('help) should equal ("")
+ options('test) should equal ("test")
+ options('go) should equal ("")
+ options('dashless).count(_ == ' ') should be (2)
+ }
+ }
+ "List with mixed options" should returns {
+ "populated Map" in {
+ val options = OptionMapUtility.toOptionMap(List("-q", "--help", "--test=test", "-go", "filename0", "filename1", "filename2"))
+
+ options('q) should equal ("")
+ options('help) should equal ("")
+ options('test) should equal ("test")
+ options('go) should equal ("")
+ options('dashless).count(_ == ' ') should be (2)
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/cli/source/test/scala/org/hashtree/stringmetric/cli/command/jaroWinklerMetricSpec.scala b/cli/source/test/scala/org/hashtree/stringmetric/cli/command/jaroWinklerMetricSpec.scala
new file mode 100755
index 0000000..071454a
--- /dev/null
+++ b/cli/source/test/scala/org/hashtree/stringmetric/cli/command/jaroWinklerMetricSpec.scala
@@ -0,0 +1,39 @@
+package org.hashtree.stringmetric.cli.command
+
+import org.hashtree.stringmetric.ScalaTest
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+@RunWith(classOf[JUnitRunner])
+final class jaroWinklerMetricSpec extends ScalaTest {
+ "jaroWinklerMetric" should provide {
+ "main method" when passed {
+ "valid dashless arguments" should executes {
+ "print the distance" in {
+ val out = new java.io.ByteArrayOutputStream()
+
+ Console.withOut(out)(
+ jaroWinklerMetric.main(Array("--unitTest", "--debug", "abc", "abc"))
+ )
+
+ out.toString should equal ("1.0\n")
+ out.reset()
+
+ Console.withOut(out)(
+ jaroWinklerMetric.main(Array("--unitTest", "--debug", "abc", "xyz"))
+ )
+
+ out.toString should equal ("0.0\n")
+ out.reset()
+ }
+ }
+ "no dashless arguments" should throws {
+ "IllegalArgumentException" in {
+ evaluating {
+ jaroWinklerMetric.main(Array("--unitTest", "--debug"))
+ } should produce [IllegalArgumentException]
+ }
+ }
+ }
+ }
+} \ No newline at end of file