Skip to content

Commit

Permalink
Merge branch 'develop' into bugfix/RepairingTaskPropagation
Browse files Browse the repository at this point in the history
  • Loading branch information
Andreas Schultz committed Aug 14, 2018
2 parents af0e7cd + 563e3b9 commit 150997a
Show file tree
Hide file tree
Showing 45 changed files with 1,981 additions and 84 deletions.
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ lazy val core = (project in file("silk-core"))
libraryDependencies += "com.thoughtworks.paranamer" % "paranamer" % "2.7",
// Additional scala standard libraries
libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "1.0.5",
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value,
libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.4",
libraryDependencies += "commons-io" % "commons-io" % "2.4"
)
Expand Down Expand Up @@ -241,7 +242,7 @@ lazy val workbenchCore = (project in file("silk-workbench/silk-workbench-core"))
.enablePlugins(PlayScala)
.enablePlugins(BuildInfoPlugin)
.dependsOn(workspace, workspace % "test -> test", core % "test->test", serializationJson, reactComponents)
.aggregate(workspace)
.aggregate(workspace, reactComponents)
.settings(commonSettings: _*)
.settings(
name := "Silk Workbench Core",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.silkframework.util.Uri
/**
* An empty data set.
*/
object EmptyDataset extends Dataset {
object EmptyDataset extends Dataset with Serializable {

/**
* Clears the contents of this dataset.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ trait EntitySink extends DataSink {
* Writes a new entity.
* @param entity - the entity to write
*/
def writeEntity(entity: Entity): Unit = if(! entity.hasFailed) writeEntity(entity.uri, entity.values)
def writeEntity(entity: Entity): Unit = if(! entity.hasFailed)
writeEntity(entity.uri, entity.values)
}

/**
Expand Down
92 changes: 63 additions & 29 deletions silk-core/src/main/scala/org/silkframework/entity/Entity.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,65 +15,90 @@
package org.silkframework.entity

import java.io.{DataInput, DataOutput}

import org.silkframework.entity.metadata.{EntityMetadata, EntityMetadataXml}
import org.silkframework.failures.FailureClass
import org.silkframework.util.Uri

import scala.xml.Node
import scala.language.existentials

/**
* A single entity.
*/
* An Entity can represent an instance of any given concept
* @param uri - an URI as identifier
* @param vals - A list of values of the properties defined in the provided EntitySchema
* @param schema - The EntitySchema defining the nature of this entity
* @param subEntities - optional, each entity can be composed of multiple sub-entities if defined with a suitable MultiEntitiySchema
* @param metadata - metadata object containing all available metadata information about this object
* an Entity is marked as 'failed' if [[org.silkframework.entity.metadata.EntityMetadata.failure]] is set. It becomes sealed.
*/
case class Entity private(
uri: Uri,
private val vals: IndexedSeq[Seq[String]],
schema: EntitySchema,
subEntities: IndexedSeq[Option[Entity]] = IndexedSeq.empty,
private val failureOpt: Option[Throwable] = None,
private val validateSchema: Boolean = true
metadata: EntityMetadata[_] = EntityMetadataXml()
) extends Serializable {

def copy(
uri: Uri = this.uri,
values: IndexedSeq[Seq[String]] = this.values,
schema: EntitySchema = this.schema,
subEntities: IndexedSeq[Option[Entity]] = this.subEntities,
failureOpt: Option[Throwable] = None,
validateSchema: Boolean = this.validateSchema,
metadata: EntityMetadata[_] = this.metadata,
failureOpt: Option[FailureClass] = None,
projectValuesIfNewSchema: Boolean = true
): Entity = this.failure match{
case Some(_) => this // if origin entity has already failed, we forward it so the failure is not overwritten
case None => failureOpt match{ // else we decided based on the provided failure option
case Some(f) => new Entity(uri, values, schema, subEntities, Some(f), validateSchema = false)
case None =>
val actualVals = if(schema != this.schema && projectValuesIfNewSchema) applyNewSchema(schema) else values //here we remap value indices for possible shifts of typed paths
val actualSubs = if(schema != this.schema && projectValuesIfNewSchema) subEntities.map(o => o.map(e => e.copy(schema = schema))) else subEntities
new Entity(uri, actualVals, schema, actualSubs, None, validateSchema)
}
case None =>
val actualVals = if(schema != this.schema && projectValuesIfNewSchema) shiftProperties(schema) else values //here we remap value indices for possible shifts of typed paths
val actualSubs = if(schema != this.schema && projectValuesIfNewSchema) subEntities.map(o => o.map(e => e.copy(schema = schema))) else subEntities
val actualMetadata = failureOpt match{
case Some(f) if metadata.failure.metadata.isEmpty => metadata.addFailure(f)
case _ => metadata
}
new Entity(uri, actualVals, schema, actualSubs, actualMetadata)
}

/**
* Will remap the index positions of values in case the typed paths of the EntitySchema were changed
* @param es - the new schema
* @return - the new value array
*/
private def applyNewSchema(es: EntitySchema): IndexedSeq[Seq[String]] ={
private def shiftProperties(es: EntitySchema): IndexedSeq[Seq[String]] ={
es.typedPaths.map(tp => this.schema.typedPaths.find(t => t == tp) match{
case Some(fp) if fp != TypedPath.empty => this.evaluate(fp)
case Some(fp) => this.evaluate(fp)
case None => Seq()
})
}

/**
* Convenience function for applying a new schema without validating (e.g. when renaaming properties)
* @param es - the schema
* @return
*/
def applyNewSchema(es: EntitySchema): Entity = copy(schema = es, projectValuesIfNewSchema = false)

/**
*
*/
val values: IndexedSeq[Seq[String]] = vals.map(Entity.handleNullsInValueSeq)

val failure: Option[Throwable] = if(failureOpt.isEmpty && validateSchema) { //if no failure has occurred yet and this entity shall be validated
if(schema.isInstanceOf[MultiEntitySchema] && schema.asInstanceOf[MultiEntitySchema].subSchemata.size < subEntities.size)
Some(new IllegalArgumentException("Number of sub-entities is not equal to the number of sub-schemata for: " + uri))
else if (! this.validate)
val failure: Option[Throwable] = if(metadata.failure.metadata.isEmpty) { // if no failure has occurred yet
if(schema.isInstanceOf[MultiEntitySchema] && schema.asInstanceOf[MultiEntitySchema].subSchemata.size < subEntities.size){
Some(new IllegalArgumentException("Number of sub-entities is not equal to the number of sub-schemata for: " + uri)) // if sub entities size is not equal to sub schemata size
}
else if(uri.uri.trim.isEmpty){
Some(new IllegalArgumentException("Entity with an empty URI is not allowed."))
}
else if (! this.validate) { // if entity is not valid
Some(new IllegalArgumentException("Provided schema does not fit entity values or sub-entities."))
else
}
else{
None
}
}}
else {
failureOpt
metadata.failure.metadata.map(_.rootCause) //propagate former failure
}

def hasFailed: Boolean = failure.isDefined
Expand Down Expand Up @@ -191,7 +216,11 @@ case class Entity private(
}
}

override def toString: String = uri + "\n{\n " + values.mkString("\n ") + "\n}"
override def toString: String = failure match{
case Some(f) => uri + " failed with: " + f.getMessage
case None => uri + "{\n " + (values ++ subEntities.flatMap(oe => oe.map(_.values)).flatten).mkString("\n ") + "\n}"
}


override def equals(other: Any): Boolean = other match {
case o: Entity => this.uri.toString == o.uri.toString && this.values == o.values && this.schema == o.schema
Expand Down Expand Up @@ -226,29 +255,34 @@ object Entity {
new Entity(uri, values, schema)
}

def apply(uri: String, values: IndexedSeq[Seq[String]], schema: EntitySchema, subEntities: IndexedSeq[Option[Entity]], failureOpt: Option[FailureClass]): Entity = {
new Entity(uri, values, schema, subEntities, failureOpt match{
case Some(t) => EntityMetadataXml(t)
case None => EntityMetadataXml()
})
}

def handleNullsInValueSeq(valueSeq: Seq[String]): Seq[String] = if(valueSeq == null) Seq() else valueSeq.flatMap(x => Option(x))

/**
* Instantiates a new Entity and fails it with the given Throwable
* NOTE: values are not recorded
* @param uri - uri of the entity
* @param schema - the EntitySchema pertaining to the Entity
* @param t - the Throwable which failed this Enity
* @param failure - the Throwable which failed this Enity as [[FailureClass]]
* @return - the failed Entity
*/
//FIXME add property option CMEM-719
def apply(uri: Uri, schema: EntitySchema, t: Throwable): Entity = Entity(uri, IndexedSeq(), schema, IndexedSeq(), Some(t), validateSchema = false)
def apply(uri: Uri, schema: EntitySchema, failure: FailureClass): Entity = Entity(uri, IndexedSeq(), schema, IndexedSeq(), Some(failure))

/**
* Instantiates a new Entity and fails it with the given Throwable
* @param uri - uri of the entity
* @param values - the values applied for the failed Entity
* @param schema - the EntitySchema pertaining to the Entity
* @param t - the Throwable which failed this Enity
* @param failure - the Throwable which failed this Enity as [[FailureClass]]
* @return - the failed Entity
*/
//FIXME add property option CMEM-719 maybe only allow SparkInstanceException
def apply(uri: Uri, values: IndexedSeq[Seq[String]], schema: EntitySchema, t: Throwable): Entity = Entity(uri, values, schema, IndexedSeq(), Some(t), validateSchema = false)
def apply(uri: Uri, values: IndexedSeq[Seq[String]], schema: EntitySchema, failure: FailureClass): Entity = Entity(uri, values, schema, IndexedSeq(), Some(failure))


def fromXML(node: Node, desc: EntitySchema): Entity = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,5 +229,4 @@ object EntitySchema {
</Paths>
</EntityDescription>
}

}
38 changes: 20 additions & 18 deletions silk-core/src/main/scala/org/silkframework/entity/TypedPath.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,24 +44,28 @@ case class TypedPath(

object TypedPath {

val META_FIELD_XML_ATTRIBUTE: String = "IS_XML_ATTRIBUTE"
val META_FIELD_ORIGIN_NAME: String = "ORIGINAL_NAME"
val META_FIELD_XML_ATTRIBUTE: String = "isXmlAttribute"
val META_FIELD_ORIGIN_NAME: String = "originalName"

/**
*
* @param path
* @param valueType
* @param isAttribute
* @return
* @param path - the untyped path
* @param valueType - the ValueType
* @param isAttribute - indicates whether this is an XML attribute
*/
def apply(path: Path, valueType: ValueType, isAttribute: Boolean): TypedPath = apply(path.operators, valueType, isAttribute)


/**
*
* @param path
* @param valueType
* @param isAttribute
* @return
* @param path - the untyped path
* @param valueType - the ValueType
* @param metadata - an immutable map that stores metadata objects
*/
def apply(path: Path, valueType: ValueType, metadata: Map[String, Any]): TypedPath = apply(path.operators, valueType, metadata)

/**
* @param path - the unparsed, untyped path
* @param valueType - the ValueType
* @param isAttribute - indicates whether this is an XML attribute
*/
def apply(path: String, valueType: ValueType, isAttribute: Boolean = false)(implicit prefixes: Prefixes = Prefixes.empty): TypedPath = {
val metadata = Map(
Expand All @@ -72,11 +76,9 @@ object TypedPath {
}

/**
*
* @param ops
* @param valueType
* @param isAttribute
* @return
* @param ops - the path operators
* @param valueType - the ValueType
* @param isAttribute - indicates whether this is an XML attribute
*/
def apply(ops: List[PathOperator], valueType: ValueType, isAttribute: Boolean): TypedPath =
apply(ops, valueType, if(isAttribute) Map(META_FIELD_XML_ATTRIBUTE -> true) else Map.empty[String, Any]) //if not an attribute, we can leave map empty, false is assumed
Expand Down Expand Up @@ -108,7 +110,7 @@ object TypedPath {
* Serializes a value.
*/
override def write(typedPath: TypedPath)(implicit writeContext: WriteContext[Node]): Node = {
implicit val p = writeContext.prefixes
implicit val p: Prefixes = writeContext.prefixes
<TypedPath isAttribute={typedPath.isAttribute.toString} >
<Path>
{typedPath.normalizedSerialization}
Expand Down
Loading

0 comments on commit 150997a

Please sign in to comment.