Skip to content

Commit 95903ee

Browse files
committed
Merge branch 'fix/optionalLabels-CMEM-1897' into release/3.6.0
2 parents 2a30b87 + f1b540b commit 95903ee

File tree

82 files changed

+273
-238
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+273
-238
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.silkframework.config
2+
3+
import org.silkframework.util.Identifier
4+
5+
/**
6+
* Inherited by classes that provide an identifier and metadata.
7+
*/
8+
trait HasMetaData {
9+
10+
/**
11+
* The unique identifier for this object.
12+
*/
13+
def id: Identifier
14+
15+
/**
16+
* The metadata for this object.
17+
*/
18+
def metaData: MetaData
19+
20+
/**
21+
* Returns a label for this object.
22+
* Per default, it will fall back to generating a label from the identifier, if no label is defined.
23+
* Subclasses may override this behaviour.
24+
* Truncates the label to maxLength characters.
25+
*
26+
* @param maxLength the max length in characters
27+
*/
28+
def label(maxLength: Int = MetaData.DEFAULT_LABEL_MAX_LENGTH)(implicit prefixes: Prefixes = Prefixes.empty): String = {
29+
metaData.formattedLabel(MetaData.labelFromId(id), maxLength)
30+
}
31+
32+
/**
33+
* Returns a label for this object with no length restriction.
34+
*/
35+
def fullLabel(implicit prefixes: Prefixes = Prefixes.empty): String = label(Int.MaxValue)
36+
37+
}

silk-core/src/main/scala/org/silkframework/config/MetaData.scala

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import scala.xml._
1111
/**
1212
* Holds meta data about a task.
1313
*/
14-
case class MetaData(label: String,
14+
case class MetaData(label: Option[String],
1515
description: Option[String] = None,
1616
modified: Option[Instant] = None,
1717
created: Option[Instant] = None,
@@ -26,10 +26,11 @@ case class MetaData(label: String,
2626
*/
2727
def formattedLabel(defaultLabel: String, maxLength: Int = MetaData.DEFAULT_LABEL_MAX_LENGTH): String = {
2828
assert(maxLength > 5, "maxLength for task label must be at least 5 chars long")
29-
val trimmedLabel = if(label.trim != "") {
30-
label.trim
31-
} else {
32-
defaultLabel
29+
val trimmedLabel = label match {
30+
case Some(l) if l.trim != "" =>
31+
l.trim
32+
case _ =>
33+
defaultLabel
3334
}
3435
if(trimmedLabel.length > maxLength) {
3536
val sideLength = (maxLength - 2) / 2
@@ -72,7 +73,7 @@ object MetaData {
7273

7374
val DEFAULT_LABEL_MAX_LENGTH = 50
7475

75-
def empty: MetaData = MetaData("", None, None, None, None, None)
76+
def empty: MetaData = MetaData(None, None, None, None, None, None)
7677

7778
/**
7879
* Generates a nice label from an identifier.
@@ -114,7 +115,7 @@ object MetaData {
114115
*/
115116
def read(node: Node)(implicit readContext: ReadContext): MetaData = {
116117
MetaData(
117-
label = (node \ "Label").text,
118+
label = Some((node \ "Label").text).filter(_.nonEmpty),
118119
description = Some((node \ "Description").text).filter(_.nonEmpty),
119120
modified = (node \ "Modified").headOption.map(node => Instant.parse(node.text)),
120121
created = (node \ "Created").headOption.map(node => Instant.parse(node.text)),
@@ -129,7 +130,7 @@ object MetaData {
129130
def write(data: MetaData)(implicit writeContext: WriteContext[Node]): Node = {
130131
val descriptionPCData = PCData(data.description.getOrElse(""))
131132
<MetaData>
132-
<Label>{data.label}</Label>
133+
<Label>{data.label.getOrElse("")}</Label>
133134
<Description xml:space="preserve">{descriptionPCData}</Description>
134135
{ data.modified.map(instant => <Modified>{instant.toString}</Modified>).toSeq }
135136
{ data.created.map(instant => <Created>{instant.toString}</Created>).toSeq }

silk-core/src/main/scala/org/silkframework/config/Task.scala

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import scala.xml._
1313
*
1414
* @tparam TaskType The type of this task, e.g., TransformSpec.
1515
*/
16-
trait Task[+TaskType <: TaskSpec] {
16+
trait Task[+TaskType <: TaskSpec] extends HasMetaData {
1717
/** The id of this task. */
1818
def id: Identifier
1919

@@ -46,16 +46,6 @@ trait Task[+TaskType <: TaskSpec] {
4646
/** Find tasks that are either input or output to this task. */
4747
def findRelatedTasksInsideWorkflows()(implicit userContext: UserContext): Set[Identifier] = Set.empty
4848

49-
/**
50-
* Returns the label if defined or the task ID. Truncates the label to maxLength characters.
51-
* @param maxLength the max length in characters
52-
*/
53-
def taskLabel(maxLength: Int = MetaData.DEFAULT_LABEL_MAX_LENGTH): String = {
54-
metaData.formattedLabel(id, maxLength)
55-
}
56-
57-
def fullTaskLabel: String = taskLabel(Int.MaxValue)
58-
5949
override def equals(obj: scala.Any): Boolean = obj match {
6050
case task: Task[_] =>
6151
id == task.id &&

silk-core/src/main/scala/org/silkframework/execution/ExecutionReport.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ trait ExecutionReport {
1515
/**
1616
* A user-friendly label for this report, usually just the task label.
1717
*/
18-
def label: String = task.taskLabel()
18+
def label: String = task.label()
1919

2020
/**
2121
* Short label for the executed operation, e.g., read or write (optional).

silk-core/src/main/scala/org/silkframework/workspace/ProjectConfig.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
package org.silkframework.workspace
1616

1717
import java.util.logging.Logger
18-
1918
import com.typesafe.config.ConfigException
19+
2020
import javax.inject.Inject
21-
import org.silkframework.config.{Config, DefaultConfig, MetaData, Prefixes}
21+
import org.silkframework.config.{Config, DefaultConfig, HasMetaData, MetaData, Prefixes}
2222
import org.silkframework.util.Identifier
2323

2424
/**
@@ -32,7 +32,8 @@ import org.silkframework.util.Identifier
3232
case class ProjectConfig(id: Identifier = Identifier.random,
3333
prefixes: Prefixes = Prefixes.default,
3434
projectResourceUriOpt: Option[String] = None,
35-
metaData: MetaData = MetaData.empty) {
35+
metaData: MetaData = MetaData.empty) extends HasMetaData {
36+
3637
def withMetaData(metaData: MetaData): ProjectConfig = this.copy(metaData = metaData)
3738

3839
def generateDefaultUri: String = {
@@ -43,6 +44,10 @@ case class ProjectConfig(id: Identifier = Identifier.random,
4344
def resourceUriOrElseDefaultUri: String = {
4445
projectResourceUriOpt.getOrElse(generateDefaultUri)
4546
}
47+
48+
override def label(maxLength: Int)(implicit prefixes: Prefixes): String = {
49+
metaData.formattedLabel(id, maxLength)
50+
}
4651
}
4752

4853
object ProjectConfig {

silk-core/src/main/scala/org/silkframework/workspace/project/task/DatasetTaskReferenceAutoCompletionProvider.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ case class DatasetTaskReferenceAutoCompletionProvider() extends PluginParameterA
1717
workspace: WorkspaceReadTrait)
1818
(implicit userContext: UserContext): Traversable[AutoCompletionResult] = {
1919
val taskProject = dependOnParameterValues.headOption.getOrElse(projectId)
20-
val allDatasets = workspace.project(taskProject).tasks[GenericDatasetSpec].map(spec => AutoCompletionResult(spec.id, Some(spec.metaData.label)))
20+
val allDatasets = workspace.project(taskProject).tasks[GenericDatasetSpec].map(spec => AutoCompletionResult(spec.id, spec.metaData.label))
2121
filterResults(searchQuery, allDatasets)
2222
}
2323

@@ -29,6 +29,6 @@ case class DatasetTaskReferenceAutoCompletionProvider() extends PluginParameterA
2929
workspace: WorkspaceReadTrait)
3030
(implicit userContext: UserContext): Option[String] = {
3131
val taskProject = dependOnParameterValues.headOption.getOrElse(projectId)
32-
workspace.project(taskProject).taskOption[GenericDatasetSpec](value).map(_.metaData.label)
32+
workspace.project(taskProject).taskOption[GenericDatasetSpec](value).flatMap(_.metaData.label)
3333
}
3434
}

silk-plugins/silk-plugins-rdf/src/main/scala/org/silkframework/plugins/dataset/rdf/endpoint/JenaDatasetEndpoint.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ package org.silkframework.plugins.dataset.rdf.endpoint
22

33
import java.io._
44
import java.util.logging.Logger
5-
65
import org.apache.jena.query.{Dataset, Query, QueryExecution, QueryExecutionFactory}
76
import org.apache.jena.rdf.model.{Model, ModelFactory}
87
import org.apache.jena.riot.{Lang, RDFLanguages}
98
import org.apache.jena.update.{UpdateExecutionFactory, UpdateFactory, UpdateProcessor}
109
import org.silkframework.dataset.rdf.{GraphStoreTrait, SparqlEndpoint, SparqlParams}
1110
import org.silkframework.runtime.activity.UserContext
1211

12+
import scala.util.control.NonFatal
13+
1314
/**
1415
* A SPARQL endpoint which executes all queries on a Jena Dataset.
1516
*/
@@ -20,7 +21,13 @@ class JenaDatasetEndpoint(dataset: Dataset, val sparqlParams: SparqlParams = Spa
2021
}
2122

2223
override def createUpdateExecution(query: String): UpdateProcessor = {
23-
UpdateExecutionFactory.create(UpdateFactory.create(query), dataset)
24+
try {
25+
UpdateExecutionFactory.create(UpdateFactory.create(query), dataset)
26+
}
27+
catch {
28+
case NonFatal(ex) =>
29+
throw ex
30+
}
2431
}
2532

2633
override def graphStoreEndpoint(graph: String): String = {

silk-plugins/silk-plugins-rdf/src/main/scala/org/silkframework/plugins/dataset/rdf/tasks/SparqlEndpointDatasetAutoCompletionProvider.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ case class SparqlEndpointDatasetAutoCompletionProvider() extends PluginParameter
1414
(implicit userContext: UserContext): Traversable[AutoCompletionResult] = {
1515
val allResults = workspace.project(projectId).tasks[GenericDatasetSpec]
1616
.filter(datasetSpec => datasetSpec.data.plugin.isInstanceOf[RdfDataset])
17-
.map(datasetSpec => AutoCompletionResult(datasetSpec.id, Some(datasetSpec.metaData.label)))
17+
.map(datasetSpec => AutoCompletionResult(datasetSpec.id, datasetSpec.metaData.label))
1818
filterResults(searchQuery, allResults)
1919
}
2020

2121
override def valueToLabel(projectId: String, value: String, dependOnParameterValues: Seq[String], workspace: WorkspaceReadTrait)
2222
(implicit userContext: UserContext): Option[String] = {
23-
workspace.project(projectId).taskOption[GenericDatasetSpec](value).map(_.metaData.label)
23+
workspace.project(projectId).taskOption[GenericDatasetSpec](value).flatMap(_.metaData.label)
2424
}
2525
}

silk-plugins/silk-serialization-json/src/main/scala/org/silkframework/serialization/json/ExecutionReportSerializers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ object ExecutionReportSerializers {
5353

5454
def serializeBasicValues(value: ExecutionReport)(implicit writeContext: WriteContext[JsValue]): JsObject = {
5555
Json.obj(
56-
LABEL -> value.task.taskLabel(),
56+
LABEL -> value.task.label(),
5757
OPERATION -> value.operation,
5858
OPERATION_DESC -> value.operationDesc,
5959
TASK -> GenericTaskJsonFormat.write(value.task),

silk-plugins/silk-serialization-json/src/main/scala/org/silkframework/serialization/json/JsonSerializers.scala

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -90,19 +90,14 @@ object JsonSerializers {
9090
final val CREATED_BY = "createdBy"
9191
final val LAST_MODIFIED_BY = "lastModifiedBy"
9292

93-
override def read(value: JsValue)(implicit readContext: ReadContext): MetaData = {
94-
read(value, "")
95-
}
96-
9793
/**
9894
* Reads meta data. Generates a label if no label is provided in the json.
9995
*
10096
* @param value The json to read the meta data from.
101-
* @param identifier If no label is provided in the json, use this identifier to generate a label.
10297
*/
103-
def read(value: JsValue, identifier: String)(implicit readContext: ReadContext): MetaData = {
98+
override def read(value: JsValue)(implicit readContext: ReadContext): MetaData = {
10499
MetaData(
105-
label = stringValueOption(value, LABEL).getOrElse(MetaData.labelFromId(identifier)),
100+
label = stringValueOption(value, LABEL).filter(_.nonEmpty),
106101
description = stringValueOption(value, DESCRIPTION),
107102
modified = stringValueOption(value, MODIFIED).map(Instant.parse),
108103
created = stringValueOption(value, CREATED).map(Instant.parse),
@@ -112,10 +107,10 @@ object JsonSerializers {
112107
}
113108

114109
override def write(value: MetaData)(implicit writeContext: WriteContext[JsValue]): JsValue = {
115-
var json =
116-
Json.obj(
117-
LABEL -> JsString(value.label)
118-
)
110+
var json = Json.obj()
111+
for(label <- value.label if label.nonEmpty) {
112+
json += LABEL -> JsString(label)
113+
}
119114
for(description <- value.description if description.nonEmpty) {
120115
json += DESCRIPTION -> JsString(description)
121116
}
@@ -387,10 +382,9 @@ object JsonSerializers {
387382
*/
388383
override def read(value: JsValue)(implicit readContext: ReadContext): RootMappingRule = {
389384
val mappingRules = fromJson[MappingRules](mustBeDefined(value, RULES_PROPERTY))
390-
val typeName = mappingRules.typeRules.flatMap(_.typeUri.localName).headOption
391385
val id = identifier(value, RootMappingRule.defaultId)
392386
val mappingTarget = optionalValue(value, MAPPING_TARGET).map(fromJson[MappingTarget]).getOrElse(RootMappingRule.defaultMappingTarget)
393-
RootMappingRule(id = id, rules = mappingRules, mappingTarget = mappingTarget, metaData = metaData(value, typeName.getOrElse("RootMapping")))
387+
RootMappingRule(id = id, rules = mappingRules, mappingTarget = mappingTarget, metaData = metaData(value))
394388
}
395389

396390
/**
@@ -422,7 +416,7 @@ object JsonSerializers {
422416
val typeUri = Uri.parse(stringValue(value, TYPE_PROPERTY), readContext.prefixes)
423417
val typeName = typeUri.localName.getOrElse("type")
424418
val name = identifier(value, typeName)
425-
TypeMapping(name,typeUri, metaData(value, typeName))
419+
TypeMapping(name,typeUri, metaData(value))
426420
}
427421

428422
/**
@@ -456,7 +450,7 @@ object JsonSerializers {
456450
if(readContext.validationEnabled) {
457451
UriPatternParser.parseIntoSegments(pattern, allowIncompletePattern = false).validateAndThrow()
458452
}
459-
PatternUriMapping(name, pattern.trim(), metaData(value, "URI"), readContext.prefixes)
453+
PatternUriMapping(name, pattern.trim(), metaData(value), readContext.prefixes)
460454
}
461455

462456
/**
@@ -486,7 +480,7 @@ object JsonSerializers {
486480
ComplexUriMapping(
487481
id = identifier(value, "uri"),
488482
operator = fromJson[Input]((value \ OPERATOR).get),
489-
metaData(value, "URI")
483+
metaData(value)
490484
)
491485
}
492486

@@ -547,7 +541,7 @@ object JsonSerializers {
547541
val mappingName = mappingTarget.propertyUri.localName.getOrElse("ValueMapping")
548542
val id = identifier(value, mappingName)
549543
val sourcePath = silkPath(id, stringValue(value, SOURCE_PATH_PROPERTY))
550-
DirectMapping(id, sourcePath, mappingTarget, metaData(value, mappingName))
544+
DirectMapping(id, sourcePath, mappingTarget, metaData(value))
551545
}
552546

553547
/**
@@ -583,7 +577,7 @@ object JsonSerializers {
583577
val mappingName = mappingTarget.flatMap(_.propertyUri.localName).getOrElse("ObjectMapping")
584578
val id = identifier(value, mappingName)
585579
val sourcePath = silkPath(id, stringValue(value, SOURCE_PATH))
586-
ObjectMapping(id, sourcePath, mappingTarget, children, metaData(value, mappingName), readContext.prefixes)
580+
ObjectMapping(id, sourcePath, mappingTarget, children, metaData(value), readContext.prefixes)
587581
}
588582

589583
/**
@@ -638,7 +632,7 @@ object JsonSerializers {
638632
id = id,
639633
operator = fromJson[Input]((jsValue \ OPERATOR).get),
640634
target = mappingTarget,
641-
metaData(jsValue, mappingName)
635+
metaData(jsValue)
642636
)
643637
TransformRule.simplify(complex)(readContext.prefixes)
644638
}
@@ -1061,19 +1055,20 @@ object JsonSerializers {
10611055
}
10621056
val id: Identifier = stringValueOption(value, ID).map(_.trim).filter(_.nonEmpty).map(Identifier.apply).getOrElse {
10631057
// Generate unique ID from label if no ID was supplied
1064-
val md = metaData(value, "id")
1065-
val label = md.label.trim
1066-
if(label.isEmpty) {
1067-
throw BadUserInputException("The label must not be empty if no ID is provided!")
1058+
val md = metaData(value)
1059+
md.label match {
1060+
case Some(label) if label.trim.nonEmpty =>
1061+
generateTaskId(label)
1062+
case _ =>
1063+
throw BadUserInputException("The label must not be empty if no ID is provided!")
10681064
}
1069-
generateTaskId(label)
10701065
}
10711066
// In older serializations the task data has been directly attached to this JSON object
10721067
val dataJson = optionalValue(value, DATA).getOrElse(value)
10731068
PlainTask(
10741069
id = id,
10751070
data = fromJson[T](dataJson),
1076-
metaData = metaData(value, id)
1071+
metaData = metaData(value)
10771072
)
10781073
}
10791074

@@ -1348,12 +1343,12 @@ object JsonSerializers {
13481343
* @param json The json to read the meta data from.
13491344
* @param identifier If no label is provided in the json, use this identifier to generate a label.
13501345
*/
1351-
private def metaData(json: JsValue, identifier: String)(implicit readContext: ReadContext): MetaData = {
1346+
private def metaData(json: JsValue)(implicit readContext: ReadContext): MetaData = {
13521347
optionalValue(json, METADATA) match {
13531348
case Some(metaDataJson) =>
1354-
MetaDataJsonFormat.read(metaDataJson, identifier)
1349+
MetaDataJsonFormat.read(metaDataJson)
13551350
case None =>
1356-
MetaData(MetaData.labelFromId(identifier))
1351+
MetaData.empty
13571352
}
13581353
}
13591354

0 commit comments

Comments
 (0)