diff --git a/silk-core/src/main/resources/org/silkframework/LinkSpecificationLanguage.xsd b/silk-core/src/main/resources/org/silkframework/LinkSpecificationLanguage.xsd
index 90f6c25d5d..04844ea63a 100644
--- a/silk-core/src/main/resources/org/silkframework/LinkSpecificationLanguage.xsd
+++ b/silk-core/src/main/resources/org/silkframework/LinkSpecificationLanguage.xsd
@@ -308,6 +308,8 @@
+
+
diff --git a/silk-core/src/main/scala/org/silkframework/runtime/plugin/ParameterType.scala b/silk-core/src/main/scala/org/silkframework/runtime/plugin/ParameterType.scala
index 10ed89885b..a199c40868 100644
--- a/silk-core/src/main/scala/org/silkframework/runtime/plugin/ParameterType.scala
+++ b/silk-core/src/main/scala/org/silkframework/runtime/plugin/ParameterType.scala
@@ -183,7 +183,7 @@ object StringParameterType {
private val allStaticTypes: Seq[StringParameterType[_]] = {
Seq(StringType, CharType, IntType, DoubleType, BooleanType, IntOptionType, StringMapType, UriType, ResourceType,
WritableResourceType, ResourceOptionType, DurationType, ProjectReferenceType, TaskReferenceType, MultilineStringParameterType, SparqlEndpointDatasetParameterType, LongType,
- PasswordParameterType, IdentifierType, IdentifierOptionType, StringTraversableParameterType, RestrictionType)
+ PasswordParameterType, IdentifierType, IdentifierOptionType, StringTraversableParameterType, RestrictionType, StringOptionType)
}
/**
@@ -325,6 +325,25 @@ object StringParameterType {
}
}
+ object StringOptionType extends StringParameterType[StringOptionParameter] {
+
+ override def name: String = "option[string]"
+
+ override def description: String = "An optional non-empty string"
+
+ override def fromString(str: String)(implicit prefixes: Prefixes, resourceLoader: ResourceManager): StringOptionParameter = {
+ if(str.isEmpty) {
+ StringOptionParameter(None)
+ } else {
+ StringOptionParameter(Some(str))
+ }
+ }
+
+ override def toString(value: StringOptionParameter)(implicit prefixes: Prefixes): String = {
+ value.getOrElse("")
+ }
+ }
+
object IdentifierOptionType extends StringParameterType[IdentifierOptionParameter] {
override def name: String = "option[identifier]"
diff --git a/silk-core/src/main/scala/org/silkframework/runtime/plugin/StringOptionParameter.scala b/silk-core/src/main/scala/org/silkframework/runtime/plugin/StringOptionParameter.scala
new file mode 100644
index 0000000000..591304bf54
--- /dev/null
+++ b/silk-core/src/main/scala/org/silkframework/runtime/plugin/StringOptionParameter.scala
@@ -0,0 +1,11 @@
+package org.silkframework.runtime.plugin
+
+/**
+ * An optional string. Empty strings are interpreted as missing value.
+ */
+case class StringOptionParameter(value: Option[String])
+
+object StringOptionParameter {
+ implicit def toStringOptionParameter(v: Option[String]): StringOptionParameter = StringOptionParameter(v)
+ implicit def fromStringOptionParameter(v: StringOptionParameter): Option[String] = v.value
+}
diff --git a/silk-plugins/silk-serialization-json/src/main/scala/org/silkframework/serialization/json/JsonSerializers.scala b/silk-plugins/silk-serialization-json/src/main/scala/org/silkframework/serialization/json/JsonSerializers.scala
index ebc8cf4b87..b436528599 100644
--- a/silk-plugins/silk-serialization-json/src/main/scala/org/silkframework/serialization/json/JsonSerializers.scala
+++ b/silk-plugins/silk-serialization-json/src/main/scala/org/silkframework/serialization/json/JsonSerializers.scala
@@ -931,6 +931,7 @@ object JsonSerializers {
final val REFERENCE_LINKS = "referenceLinks"
final val LINK_LIMIT = "linkLimit"
final val MATCHING_EXECUTION_TIMEOUT = "matchingExecutionTimeout"
+ final val APPLICATION_DATA = "applicationData"
/** Deprecated properties */
final val DEPRECATED_OUTPUTS = "outputs"
@@ -952,7 +953,8 @@ object JsonSerializers {
output = stringValueOption(parametersObj, OUTPUT).filter(_.trim.nonEmpty).map(o => Identifier(o.trim)),
referenceLinks = optionalValue(parametersObj, REFERENCE_LINKS).map(fromJson[ReferenceLinks]).getOrElse(ReferenceLinks.empty),
linkLimit = numberValueOption(parametersObj, LINK_LIMIT).map(_.intValue).getOrElse(LinkSpec.DEFAULT_LINK_LIMIT),
- matchingExecutionTimeout = numberValueOption(parametersObj, MATCHING_EXECUTION_TIMEOUT).map(_.intValue).getOrElse(LinkSpec.DEFAULT_EXECUTION_TIMEOUT_SECONDS)
+ matchingExecutionTimeout = numberValueOption(parametersObj, MATCHING_EXECUTION_TIMEOUT).map(_.intValue).getOrElse(LinkSpec.DEFAULT_EXECUTION_TIMEOUT_SECONDS),
+ applicationData = stringValueOption(parametersObj, APPLICATION_DATA)
)
}
}
@@ -967,23 +969,30 @@ object JsonSerializers {
output = mustBeJsArray(mustBeDefined(value, DEPRECATED_OUTPUTS))(_.value.map(v => Identifier(v.as[JsString].value))).headOption,
referenceLinks = optionalValue(value, REFERENCE_LINKS).map(fromJson[ReferenceLinks]).getOrElse(ReferenceLinks.empty),
linkLimit = numberValueOption(value, LINK_LIMIT).map(_.intValue()).getOrElse(LinkSpec.DEFAULT_LINK_LIMIT),
- matchingExecutionTimeout = numberValueOption(value, MATCHING_EXECUTION_TIMEOUT).map(_.intValue()).getOrElse(LinkSpec.DEFAULT_EXECUTION_TIMEOUT_SECONDS)
+ matchingExecutionTimeout = numberValueOption(value, MATCHING_EXECUTION_TIMEOUT).map(_.intValue()).getOrElse(LinkSpec.DEFAULT_EXECUTION_TIMEOUT_SECONDS),
+ applicationData = stringValueOption(value, APPLICATION_DATA)
)
}
override def write(value: LinkSpec)(implicit writeContext: WriteContext[JsValue]): JsValue = {
+ var parameters = Json.obj(
+ SOURCE -> toJson(value.dataSelections.source),
+ TARGET -> toJson(value.dataSelections.target),
+ RULE -> toJson(value.rule),
+ OUTPUT -> JsString(value.output.map(_.toString).getOrElse("")),
+ REFERENCE_LINKS -> toJson(value.referenceLinks),
+ LINK_LIMIT -> JsString(value.linkLimit.toString),
+ MATCHING_EXECUTION_TIMEOUT -> JsString(value.matchingExecutionTimeout.toString)
+ )
+
+ for(data <- value.applicationData) {
+ parameters += (APPLICATION_DATA -> JsString(data))
+ }
+
Json.obj(
TASKTYPE -> TASK_TYPE_LINKING,
TYPE -> "linking",
- PARAMETERS -> Json.obj(
- SOURCE -> toJson(value.dataSelections.source),
- TARGET -> toJson(value.dataSelections.target),
- RULE -> toJson(value.rule),
- OUTPUT -> JsString(value.output.map(_.toString).getOrElse("")),
- REFERENCE_LINKS -> toJson(value.referenceLinks),
- LINK_LIMIT -> JsString(value.linkLimit.toString),
- MATCHING_EXECUTION_TIMEOUT -> JsString(value.matchingExecutionTimeout.toString)
- )
+ PARAMETERS -> parameters
)
}
}
diff --git a/silk-rules/src/main/scala/org/silkframework/rule/LinkSpec.scala b/silk-rules/src/main/scala/org/silkframework/rule/LinkSpec.scala
index 78162b928d..b2dc1f6268 100644
--- a/silk-rules/src/main/scala/org/silkframework/rule/LinkSpec.scala
+++ b/silk-rules/src/main/scala/org/silkframework/rule/LinkSpec.scala
@@ -25,7 +25,7 @@ import org.silkframework.rule.evaluation.ReferenceLinks
import org.silkframework.rule.input.{Input, PathInput, TransformInput}
import org.silkframework.rule.similarity.{Aggregation, Comparison, SimilarityOperator}
import org.silkframework.runtime.activity.UserContext
-import org.silkframework.runtime.plugin.IdentifierOptionParameter
+import org.silkframework.runtime.plugin.{IdentifierOptionParameter, StringOptionParameter}
import org.silkframework.runtime.plugin.annotations.{Param, Plugin}
import org.silkframework.runtime.resource.Resource
import org.silkframework.runtime.serialization.XmlSerialization._
@@ -35,7 +35,7 @@ import org.silkframework.util._
import org.silkframework.workspace.project.task.DatasetTaskReferenceAutoCompletionProvider
import scala.collection.mutable
-import scala.xml.Node
+import scala.xml.{Node, NodeSeq}
/**
* Represents a Silk Link Specification.
@@ -63,7 +63,10 @@ case class LinkSpec(@Param(label = "Source input", value = "The source input to
linkLimit: Int = LinkSpec.DEFAULT_LINK_LIMIT,
@Param(label = "Matching timeout", value = "The timeout for the matching phase. If the matching takes longer the execution will be stopped.",
advanced = true)
- matchingExecutionTimeout: Int = LinkSpec.DEFAULT_EXECUTION_TIMEOUT_SECONDS) extends TaskSpec {
+ matchingExecutionTimeout: Int = LinkSpec.DEFAULT_EXECUTION_TIMEOUT_SECONDS,
+ @Param(value = "Additional data that is set by the application managing the linking task.",
+ visibleInDialog = false)
+ applicationData: StringOptionParameter = None) extends TaskSpec {
assert(linkLimit >= 0, "The link limit must be greater equal 0!")
assert(matchingExecutionTimeout >= 0, "The matching execution timeout must be greater equal 0!")
@@ -238,7 +241,8 @@ object LinkSpec {
Identifier(id)
},
linkLimit = (node \ "@linkLimit").headOption.map(_.text.toInt).getOrElse(LinkSpec.DEFAULT_LINK_LIMIT),
- matchingExecutionTimeout = (node \ "@matchingExecutionTimeout").headOption.map(_.text.toInt).getOrElse(LinkSpec.DEFAULT_EXECUTION_TIMEOUT_SECONDS)
+ matchingExecutionTimeout = (node \ "@matchingExecutionTimeout").headOption.map(_.text.toInt).getOrElse(LinkSpec.DEFAULT_EXECUTION_TIMEOUT_SECONDS),
+ applicationData = (node \ "ApplicationData").headOption.map(_.text)
)
}
@@ -253,6 +257,7 @@ object LinkSpec {
{spec.output.value.toSeq.map(o => )}
+ { spec.applicationData.map(data => {data}).getOrElse(NodeSeq.Empty) }
}
}
diff --git a/silk-workbench/silk-workbench-workspace/test/controllers/workspace/TaskApiTest.scala b/silk-workbench/silk-workbench-workspace/test/controllers/workspace/TaskApiTest.scala
index 6be4d8382d..ebd3000d5e 100644
--- a/silk-workbench/silk-workbench-workspace/test/controllers/workspace/TaskApiTest.scala
+++ b/silk-workbench/silk-workbench-workspace/test/controllers/workspace/TaskApiTest.scala
@@ -260,6 +260,7 @@ class TaskApiTest extends PlaySpec with IntegrationTestTrait with MustMatchers {
(response.json \ DATA \ PARAMETERS \ "source" \ "typeUri").get mustBe JsString("")
(response.json \ DATA \ PARAMETERS \ "target" \ "typeUri").get mustBe JsString("")
(response.json \ DATA \ PARAMETERS \ "rule" \ "linkType").get mustBe JsString("owl:sameAs")
+ (response.json \ DATA \ PARAMETERS \ "applicationData").isEmpty mustBe true
}
"patch linking task" in {
@@ -278,7 +279,8 @@ class TaskApiTest extends PlaySpec with IntegrationTestTrait with MustMatchers {
| "inputId": "$datasetId",
| "typeUri": "",
| "restriction": ""
- | }
+ | },
+ | "applicationData": "Some Data"
| }
| }
| }
@@ -297,6 +299,7 @@ class TaskApiTest extends PlaySpec with IntegrationTestTrait with MustMatchers {
(response.json \ DATA \ PARAMETERS \ "source" \ "typeUri").get mustBe JsString("owl:Class")
(response.json \ DATA \ PARAMETERS \ "target" \ "typeUri").get mustBe JsString("")
(response.json \ DATA \ PARAMETERS \ "rule" \ "linkType").get mustBe JsString("owl:sameAs")
+ (response.json \ DATA \ PARAMETERS \ "applicationData").get mustBe JsString("Some Data")
}
"post workflow task" in {
diff --git a/silk-workspace/src/test/scala/org/silkframework/workspace/WorkspaceProviderTestTrait.scala b/silk-workspace/src/test/scala/org/silkframework/workspace/WorkspaceProviderTestTrait.scala
index ab06ea6da9..45aceba146 100644
--- a/silk-workspace/src/test/scala/org/silkframework/workspace/WorkspaceProviderTestTrait.scala
+++ b/silk-workspace/src/test/scala/org/silkframework/workspace/WorkspaceProviderTestTrait.scala
@@ -80,6 +80,10 @@ trait WorkspaceProviderTestTrait extends FlatSpec with Matchers with MockitoSuga
description = Some("Updated Task Description")
)
+ val applicationData = "Some Data"
+
+ val applicationDataUpdated = "Updated Data"
+
val dataset = PlainTask(DATASET_ID, DatasetSpec(MockDataset("default")), metaData = MetaData(DATASET_ID, Some(DATASET_ID + " description")))
val datasetUpdated = PlainTask(DATASET_ID, DatasetSpec(MockDataset("updated"), uriAttribute = Some("uri")), metaData = MetaData(DATASET_ID))
@@ -88,11 +92,11 @@ trait WorkspaceProviderTestTrait extends FlatSpec with Matchers with MockitoSuga
private val dummyRestriction = Restriction.custom(" ?a 1 .\n\n ?a true .\n")(Prefixes.default)
val linkSpec = LinkSpec(rule = rule, source = DatasetSelection(DUMMY_DATASET, dummyType, dummyRestriction), target = DatasetSelection(DUMMY_DATASET, dummyType, dummyRestriction),
- linkLimit = LinkSpec.DEFAULT_LINK_LIMIT + 1, matchingExecutionTimeout = LinkSpec.DEFAULT_EXECUTION_TIMEOUT_SECONDS + 1)
+ linkLimit = LinkSpec.DEFAULT_LINK_LIMIT + 1, matchingExecutionTimeout = LinkSpec.DEFAULT_EXECUTION_TIMEOUT_SECONDS + 1, applicationData = Some(applicationData))
val linkTask = PlainTask(LINKING_TASK_ID, linkSpec, metaData)
- val linkTaskUpdated = PlainTask(LINKING_TASK_ID, LinkSpec(rule = rule.copy(operator = None)), metaDataUpdated)
+ val linkTaskUpdated = PlainTask(LINKING_TASK_ID, LinkSpec(rule = rule.copy(operator = None), applicationData = Some(applicationDataUpdated)), metaDataUpdated)
val transformTask =
PlainTask(