Skip to content

Commit 1954456

Browse files
committed
Implement DataTableType and DocStringType in the DSL
1 parent 8741fb1 commit 1954456

21 files changed

+1121
-12
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package io.cucumber.scala
2+
3+
import io.cucumber.datatable.DataTable
4+
5+
import scala.util.{Failure, Success, Try}
6+
import scala.collection.JavaConverters._
7+
8+
trait AbstractDatatableElementTransformerDefinition extends AbstractGlueDefinition {
9+
10+
val emptyPatterns: Seq[String]
11+
12+
protected def replaceEmptyPatternsWithEmptyString(row: Seq[String]): Seq[String] = {
13+
row.map(replaceEmptyPatternsWithEmptyString)
14+
}
15+
16+
protected def replaceEmptyPatternsWithEmptyString(table: DataTable): DataTable = {
17+
val rawWithEmptyStrings = table.cells().asScala
18+
.map(_.asScala.toSeq)
19+
.map(replaceEmptyPatternsWithEmptyString)
20+
.map(_.asJava)
21+
.toSeq
22+
.asJava
23+
24+
DataTable.create(rawWithEmptyStrings, table.getTableConverter)
25+
}
26+
27+
protected def replaceEmptyPatternsWithEmptyString(fromValue: Map[String, String]): Try[Map[String, String]] = {
28+
val replacement = fromValue.toSeq.map { case (key, value) =>
29+
val potentiallyEmptyKey = replaceEmptyPatternsWithEmptyString(key)
30+
val potentiallyEmptyValue = replaceEmptyPatternsWithEmptyString(value)
31+
32+
(potentiallyEmptyKey, potentiallyEmptyValue)
33+
}
34+
35+
if (containsDuplicateKey(replacement)) {
36+
Failure(createDuplicateKeyAfterReplacement(fromValue))
37+
} else {
38+
Success(replacement.toMap)
39+
}
40+
}
41+
42+
protected def replaceEmptyPatternsWithEmptyString(t: String): String = {
43+
if (emptyPatterns.contains(t)) {
44+
""
45+
} else {
46+
t
47+
}
48+
}
49+
50+
private def containsDuplicateKey(seq: Seq[(String, Any)]): Boolean = {
51+
seq.map { case (key, _) => key }.toSet.size != seq.size
52+
}
53+
54+
private def createDuplicateKeyAfterReplacement(fromValue: Map[String, String]): IllegalArgumentException = {
55+
val conflict = emptyPatterns.filter(emptyPattern => fromValue.contains(emptyPattern))
56+
val msg = s"After replacing ${conflict.headOption.getOrElse("")} and ${conflict.drop(1).headOption.getOrElse("")} with empty strings the datatable entry contains duplicate keys: $fromValue"
57+
new IllegalArgumentException(msg)
58+
}
59+
60+
}

scala/sources/src/main/scala/io/cucumber/scala/Aliases.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ object Aliases {
99

1010
type StepDefinitionBody = () => Unit
1111

12+
type DocStringDefinitionBody[T] = String => T
13+
1214
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package io.cucumber.scala
2+
3+
import io.cucumber.datatable.DataTable
4+
5+
trait DataTableEntryDefinitionBody[T] {
6+
7+
def transform(entry: Map[String, String]): T
8+
9+
}
10+
11+
trait DataTableRowDefinitionBody[T] {
12+
13+
def transform(row: Seq[String]): T
14+
15+
}
16+
17+
trait DataTableCellDefinitionBody[T] {
18+
19+
def transform(cell: String): T
20+
21+
}
22+
23+
trait DataTableDefinitionBody[T] {
24+
25+
def transform(dataTable: DataTable): T
26+
27+
}
28+
29+
object DataTableEntryDefinitionBody {
30+
31+
implicit def function1AsDataTableEntryDefinitionBody[T](f: (Map[String, String]) => T): DataTableEntryDefinitionBody[T] = new DataTableEntryDefinitionBody[T] {
32+
override def transform(entry: Map[String, String]): T = f.apply(entry)
33+
}
34+
35+
}
36+
37+
object DataTableRowDefinitionBody {
38+
39+
implicit def function1AsDataTableRowDefinitionBody[T](f: (Seq[String]) => T): DataTableRowDefinitionBody[T] = new DataTableRowDefinitionBody[T] {
40+
override def transform(row: Seq[String]): T = f.apply(row)
41+
}
42+
43+
}
44+
45+
object DataTableCellDefinitionBody {
46+
47+
implicit def function1AsDataTableCellDefinitionBody[T](f: (String) => T): DataTableCellDefinitionBody[T] = new DataTableCellDefinitionBody[T] {
48+
override def transform(cell: String): T = f.apply(cell)
49+
}
50+
51+
}
52+
53+
object DataTableDefinitionBody {
54+
55+
implicit def function1AsDataTableDefinitionBody[T](f: (DataTable) => T): DataTableDefinitionBody[T] = new DataTableDefinitionBody[T] {
56+
override def transform(dataTable: DataTable): T = f.apply(dataTable)
57+
}
58+
59+
}

scala/sources/src/main/scala/io/cucumber/scala/GlueAdaptor.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class GlueAdaptor(glue: Glue) {
1616
registry.afterHooks.map(ScalaHookDefinition(_, scenarioScoped)).foreach(glue.addAfterHook)
1717
registry.afterStepHooks.map(ScalaHookDefinition(_, scenarioScoped)).foreach(glue.addAfterStepHook)
1818
registry.beforeStepHooks.map(ScalaHookDefinition(_, scenarioScoped)).foreach(glue.addBeforeStepHook)
19+
registry.docStringTypes.map(ScalaDocStringTypeDefinition(_, scenarioScoped)).foreach(glue.addDocStringType)
20+
registry.dataTableTypes.map(ScalaDataTableTypeDefinition(_, scenarioScoped)).foreach(glue.addDataTableType)
1921
}
2022

2123
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.cucumber.scala
2+
3+
import io.cucumber.core.backend.ScenarioScoped
4+
import io.cucumber.datatable.{DataTableType, TableCellTransformer}
5+
6+
trait ScalaDataTableCellDefinition[T] extends ScalaDataTableTypeDefinition {
7+
8+
val details: ScalaDataTableCellTypeDetails[T]
9+
10+
override val emptyPatterns: Seq[String] = details.emptyPatterns
11+
12+
override val location: StackTraceElement = new Exception().getStackTrace()(3)
13+
14+
private val transformer: TableCellTransformer[T] = new TableCellTransformer[T] {
15+
override def transform(cell: String): T = {
16+
details.body.transform(replaceEmptyPatternsWithEmptyString(cell))
17+
}
18+
}
19+
20+
override val dataTableType = new DataTableType(details.tag.runtimeClass, transformer)
21+
22+
}
23+
24+
class ScalaScenarioScopedDataTableCellDefinition[T](override val details: ScalaDataTableCellTypeDetails[T]) extends ScalaDataTableCellDefinition[T] with ScenarioScoped {
25+
}
26+
27+
class ScalaGlobalDataTableCellDefinition[T](override val details: ScalaDataTableCellTypeDetails[T]) extends ScalaDataTableCellDefinition[T] {
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.cucumber.scala
2+
3+
import io.cucumber.core.backend.ScenarioScoped
4+
import io.cucumber.datatable.{DataTable, DataTableType, TableTransformer}
5+
6+
trait ScalaDataTableDefinition[T] extends ScalaDataTableTypeDefinition {
7+
8+
val details: ScalaDataTableTableTypeDetails[T]
9+
10+
override val emptyPatterns: Seq[String] = details.emptyPatterns
11+
12+
override val location: StackTraceElement = new Exception().getStackTrace()(3)
13+
14+
private val transformer: TableTransformer[T] = new TableTransformer[T] {
15+
override def transform(table: DataTable): T = {
16+
details.body.transform(replaceEmptyPatternsWithEmptyString(table))
17+
}
18+
}
19+
20+
override val dataTableType = new DataTableType(details.tag.runtimeClass, transformer)
21+
22+
}
23+
24+
class ScalaScenarioScopedDataTableDefinition[T](override val details: ScalaDataTableTableTypeDetails[T]) extends ScalaDataTableDefinition[T] with ScenarioScoped {
25+
}
26+
27+
class ScalaGlobalDataTableDefinition[T](override val details: ScalaDataTableTableTypeDetails[T]) extends ScalaDataTableDefinition[T] {
28+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.cucumber.scala
2+
3+
import java.util.{Map => JavaMap}
4+
5+
import io.cucumber.core.backend.ScenarioScoped
6+
import io.cucumber.datatable.{DataTableType, TableEntryTransformer}
7+
8+
import scala.collection.JavaConverters._
9+
10+
trait ScalaDataTableEntryDefinition[T] extends ScalaDataTableTypeDefinition {
11+
12+
val details: ScalaDataTableEntryTypeDetails[T]
13+
14+
override val emptyPatterns: Seq[String] = details.emptyPatterns
15+
16+
override val location: StackTraceElement = new Exception().getStackTrace()(3)
17+
18+
private val transformer: TableEntryTransformer[T] = new TableEntryTransformer[T] {
19+
override def transform(entry: JavaMap[String, String]): T = {
20+
replaceEmptyPatternsWithEmptyString(entry.asScala.toMap)
21+
.map(details.body.transform)
22+
.get
23+
}
24+
}
25+
26+
override val dataTableType = new DataTableType(details.tag.runtimeClass, transformer)
27+
28+
}
29+
30+
class ScalaScenarioScopedDataTableEntryDefinition[T](override val details: ScalaDataTableEntryTypeDetails[T]) extends ScalaDataTableEntryDefinition[T] with ScenarioScoped {
31+
}
32+
33+
class ScalaGlobalDataTableEntryDefinition[T](override val details: ScalaDataTableEntryTypeDetails[T]) extends ScalaDataTableEntryDefinition[T] {
34+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.cucumber.scala
2+
3+
import java.util.{List => JavaList}
4+
5+
import io.cucumber.core.backend.ScenarioScoped
6+
import io.cucumber.datatable.{DataTableType, TableRowTransformer}
7+
8+
import scala.collection.JavaConverters._
9+
10+
trait ScalaDataTableRowDefinition[T] extends ScalaDataTableTypeDefinition {
11+
12+
val details: ScalaDataTableRowTypeDetails[T]
13+
14+
override val emptyPatterns: Seq[String] = details.emptyPatterns
15+
16+
override val location: StackTraceElement = new Exception().getStackTrace()(3)
17+
18+
private val transformer: TableRowTransformer[T] = new TableRowTransformer[T] {
19+
override def transform(row: JavaList[String]): T = {
20+
details.body.transform(replaceEmptyPatternsWithEmptyString(row.asScala.toSeq))
21+
}
22+
}
23+
24+
override val dataTableType = new DataTableType(details.tag.runtimeClass, transformer)
25+
26+
}
27+
28+
class ScalaScenarioScopedDataTableRowDefinition[T](override val details: ScalaDataTableRowTypeDetails[T]) extends ScalaDataTableRowDefinition[T] with ScenarioScoped {
29+
}
30+
31+
class ScalaGlobalDataTableRowDefinition[T](override val details: ScalaDataTableRowTypeDetails[T]) extends ScalaDataTableRowDefinition[T] {
32+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.cucumber.scala
2+
3+
import io.cucumber.core.backend.DataTableTypeDefinition
4+
5+
trait ScalaDataTableTypeDefinition
6+
extends DataTableTypeDefinition
7+
with AbstractDatatableElementTransformerDefinition {
8+
9+
}
10+
11+
object ScalaDataTableTypeDefinition {
12+
13+
def apply[T](details: ScalaDataTableTypeDetails[T], scenarioScoped: Boolean): ScalaDataTableTypeDefinition = {
14+
details match {
15+
case entryDetails@ScalaDataTableEntryTypeDetails(_, _, _) =>
16+
if (scenarioScoped) {
17+
new ScalaScenarioScopedDataTableEntryDefinition[T](entryDetails)
18+
} else {
19+
new ScalaGlobalDataTableEntryDefinition[T](entryDetails)
20+
}
21+
case rowDetails@ScalaDataTableRowTypeDetails(_, _, _) =>
22+
if (scenarioScoped) {
23+
new ScalaScenarioScopedDataTableRowDefinition[T](rowDetails)
24+
} else {
25+
new ScalaGlobalDataTableRowDefinition[T](rowDetails)
26+
}
27+
case rowDetails@ScalaDataTableCellTypeDetails(_, _, _) =>
28+
if (scenarioScoped) {
29+
new ScalaScenarioScopedDataTableCellDefinition[T](rowDetails)
30+
} else {
31+
new ScalaGlobalDataTableCellDefinition[T](rowDetails)
32+
}
33+
case rowDetails@ScalaDataTableTableTypeDetails(_, _, _) =>
34+
if (scenarioScoped) {
35+
new ScalaScenarioScopedDataTableDefinition[T](rowDetails)
36+
} else {
37+
new ScalaGlobalDataTableDefinition[T](rowDetails)
38+
}
39+
}
40+
}
41+
42+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.cucumber.scala
2+
3+
import scala.reflect.ClassTag
4+
5+
sealed trait ScalaDataTableTypeDetails[T]
6+
7+
case class ScalaDataTableEntryTypeDetails[T](emptyPatterns: Seq[String], body: DataTableEntryDefinitionBody[T], tag: ClassTag[T]) extends ScalaDataTableTypeDetails[T]
8+
9+
case class ScalaDataTableRowTypeDetails[T](emptyPatterns: Seq[String], body: DataTableRowDefinitionBody[T], tag: ClassTag[T]) extends ScalaDataTableTypeDetails[T]
10+
11+
case class ScalaDataTableCellTypeDetails[T](emptyPatterns: Seq[String], body: DataTableCellDefinitionBody[T], tag: ClassTag[T]) extends ScalaDataTableTypeDetails[T]
12+
13+
case class ScalaDataTableTableTypeDetails[T](emptyPatterns: Seq[String], body: DataTableDefinitionBody[T], tag: ClassTag[T]) extends ScalaDataTableTypeDetails[T]
14+

0 commit comments

Comments
 (0)