diff --git a/.gitignore b/.gitignore index ee44a96..dc7df28 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea +.java-version target diff --git a/src/main/scala/com/fulcrumgenomics/sopt/cmdline/ClpArgumentDefinitionPrinting.scala b/src/main/scala/com/fulcrumgenomics/sopt/cmdline/ClpArgumentDefinitionPrinting.scala index a4c9a00..7e39e19 100644 --- a/src/main/scala/com/fulcrumgenomics/sopt/cmdline/ClpArgumentDefinitionPrinting.scala +++ b/src/main/scala/com/fulcrumgenomics/sopt/cmdline/ClpArgumentDefinitionPrinting.scala @@ -42,6 +42,15 @@ object ClpArgumentDefinitionPrinting { private[cmdline] val ArgumentDefaultValuePrefix: String = "Default:" private[cmdline] val ArgumentOptionalValue: String = "Optional" + /** A collection of escaped non-printing ASCII characters and their string literals. */ + private[cmdline] val EscapedNonPrintingCharacters: Map[String, String] = Map( + "\t" -> """\t""", + "\b" -> """\b""", + "\n" -> """\n""", + "\r" -> """\r""", + "\f" -> """\f""", + ) + /** For formatting argument section of usage message. */ private val ArgumentColumnWidth: Int = 30 private val DescriptionColumnWidth: Int = Sopt.TerminalWidth - ArgumentColumnWidth @@ -115,17 +124,27 @@ object ClpArgumentDefinitionPrinting { KGRN(if (vs.isEmpty) s"[[$ArgumentOptionalValue]]." else s"[[$ArgumentDefaultValuePrefix ${vs.mkString(", ")}]].") } + /** Converts all non-printing characters in a string into their string literal form. */ + private[sopt] def nonPrintingToStringLiteral(string: String): String = { + EscapedNonPrintingCharacters.foldLeft(string) { case (toModify, (char, literal)) => + toModify.replaceAllLiterally(char, literal) + } + } + /** Returns the set of default values as a Seq of Strings, one per default value. */ private [sopt] def defaultValuesAsSeq(value: Option[_]): Seq[String] = value match { case None | Some(None) | Some(Nil) => Seq.empty case Some(s) if Set.empty == s => Seq.empty - case Some(c) if c.isInstanceOf[util.Collection[_]] => c.asInstanceOf[util.Collection[_]].iterator.map(_.toString).toSeq - case Some(t) if t.isInstanceOf[Iterable[_]] => t.asInstanceOf[Iterable[_]].toSeq.map(_.toString) - case Some(Some(x)) => Seq(x.toString) - case Some(x) => Seq(x.toString) + case Some(c) if c.isInstanceOf[util.Collection[_]] => { + c.asInstanceOf[util.Collection[_]].map(_.toString).map(nonPrintingToStringLiteral).toSeq + } + case Some(t) if t.isInstanceOf[Iterable[_]] => { + t.asInstanceOf[Iterable[_]].map(_.toString).map(nonPrintingToStringLiteral).toSeq + } + case Some(Some(x)) => Seq(nonPrintingToStringLiteral(x.toString)) + case Some(x) => Seq(nonPrintingToStringLiteral(x.toString)) } - /** Prints the usage for a given argument given its various elements */ private[cmdline] def printArgumentUsage(stringBuilder: StringBuilder, name: String, shortName: Option[Char], theType: String, collectionArityString: Option[String], argumentDescription: String, diff --git a/src/test/scala/com/fulcrumgenomics/sopt/cmdline/ClpArgumentDefinitionPrintingTest.scala b/src/test/scala/com/fulcrumgenomics/sopt/cmdline/ClpArgumentDefinitionPrintingTest.scala index db26d10..361e571 100644 --- a/src/test/scala/com/fulcrumgenomics/sopt/cmdline/ClpArgumentDefinitionPrintingTest.scala +++ b/src/test/scala/com/fulcrumgenomics/sopt/cmdline/ClpArgumentDefinitionPrintingTest.scala @@ -83,6 +83,47 @@ class ClpArgumentDefinitionPrintingTest extends UnitSpec with BeforeAndAfterAll makeDefaultValueString(Some(List("A", "B", "C"))) shouldBe "[[Default: A, B, C]]." } + it should "print non-printing characters as human readable defaults" in { + makeDefaultValueString(Some('\t')) shouldBe """[[Default: \t]].""" + makeDefaultValueString(Some('\b')) shouldBe """[[Default: \b]].""" + makeDefaultValueString(Some('\n')) shouldBe """[[Default: \n]].""" + makeDefaultValueString(Some('\r')) shouldBe """[[Default: \r]].""" + makeDefaultValueString(Some('\f')) shouldBe """[[Default: \f]].""" + } + + it should "print non-printing strings as human readable defaults" in { + makeDefaultValueString(Some("\t\t")) shouldBe """[[Default: \t\t]].""" + makeDefaultValueString(Some("\r\n")) shouldBe """[[Default: \r\n]].""" + } + + it should "print optional non-printing characters as human readable defaults" in { + makeDefaultValueString(Some(Some('\t'))) shouldBe """[[Default: \t]].""" + makeDefaultValueString(Some(Some('\n'))) shouldBe """[[Default: \n]].""" + } + + it should "print optional non-printing strings as human readable defaults" in { + makeDefaultValueString(Some(Some("\t"))) shouldBe """[[Default: \t]].""" + makeDefaultValueString(Some(Some("\r\n"))) shouldBe """[[Default: \r\n]].""" + } + + it should "print optional non-printing characters in options as human readable defaults" in { + makeDefaultValueString(Some(Some(Some('\t')))) shouldBe """[[Default: Some(\t)]].""" + makeDefaultValueString(Some(Some(Some('\n')))) shouldBe """[[Default: Some(\n)]].""" + } + + it should "print optional non-printing strings in options as human readable defaults" in { + makeDefaultValueString(Some(Some(Some("\t")))) shouldBe """[[Default: Some(\t)]].""" + makeDefaultValueString(Some(Some(Some("\r\n")))) shouldBe """[[Default: Some(\r\n)]].""" + } + + it should "print a collection of non-printing characters as human readable defaults" in { + makeDefaultValueString(Some(Seq('\t', '\r', '\b'))) shouldBe """[[Default: \t, \r, \b]].""" + } + + it should "print a collection of non-printing strings as human readable defaults" in { + makeDefaultValueString(Some(Seq("\t\t", "\r\n", "\b\b"))) shouldBe """[[Default: \t\t, \r\n, \b\b]].""" + } + private def printArgumentUsage(name: String, shortName: Option[Char], theType: String, collectionDescription: Option[String], argumentDescription: String, optional: Boolean = false): String = {