Commit ae5a2d94 authored by Jonas Waeber's avatar Jonas Waeber
Browse files

Refactor rico:Concept Mapper implementation

Is now more flexible and the accepted fields have been expanded to any.
parent 6f969111
......@@ -20,6 +20,7 @@ package org.memobase.builder
import org.apache.jena.rdf.model.Resource
import org.memobase.helpers.StringHelpers
import org.memobase.mapping.KEYS
import org.memobase.rdf.NS
import org.memobase.rdf.RICO
......@@ -28,6 +29,6 @@ class DigitalObject(id: String, institutionId: String, count: Int) : Instantiati
init {
addRdfType(RICO.Instantiation)
resource.addProperty(RICO.type, "digitalObject")
addIdentifier("main", resource.uri, model.createResource(uri(NS.memint, "Memoriav")))
addRicoConcept(KEYS.identifiers, "main", listOf(literal(resource.uri)))
}
}
\ No newline at end of file
......@@ -22,9 +22,7 @@ import org.apache.jena.rdf.model.Literal
interface IResource {
fun addLiteral(property: String, value: Literal)
fun addLanguage(type: String, text: String)
fun addTitle(type: String, text: String, language: String)
fun addIdentifier(type: String, identifier: String)
fun addRicoConcept(rdfType: String, ricoType: String, value: List<Literal>)
fun addSkosConcept(type: String, properties: List<Pair<String, Literal>>)
fun addPlace(type: String, properties: List<Pair<String, Literal>>)
fun addDate(property: String, value: String)
......
......@@ -20,6 +20,7 @@ package org.memobase.builder
import org.apache.jena.rdf.model.Resource
import org.memobase.helpers.StringHelpers
import org.memobase.mapping.KEYS
import org.memobase.rdf.NS
import org.memobase.rdf.RICO
......@@ -28,6 +29,6 @@ class PhysicalObject(id: String, institutionId: String, count: Int) : Instantiat
init {
addRdfType(RICO.Instantiation)
resource.addProperty(RICO.type, "physicalObject")
addIdentifier("main", resource.uri, model.createResource(uri(NS.memint, "Memoriav")))
addRicoConcept(KEYS.identifiers, "main", listOf(literal(resource.uri)))
}
}
\ No newline at end of file
......@@ -20,6 +20,7 @@ package org.memobase.builder
import org.apache.jena.rdf.model.Resource
import org.memobase.helpers.StringHelpers
import org.memobase.mapping.KEYS
import org.memobase.rdf.NS
import org.memobase.rdf.RICO
......@@ -32,7 +33,7 @@ class Record(id: String, type: String, recordSetId: String, institutionId: Strin
resource.addProperty(RICO.type, type)
resource.addProperty(RICO.isPartOf, recordSetUri(recordSetId))
resource.addProperty(RICO.heldBy, institutionUri)
addIdentifier("main", resource.uri, model.createResource(uri(NS.memint, "Memoriav")))
addRicoConcept(KEYS.identifiers, "main", listOf(literal(resource.uri)))
}
fun addInstantiation(instantiation: Instantiation) {
......
......@@ -21,6 +21,7 @@ package org.memobase.builder
import org.apache.jena.rdf.model.Literal
import org.apache.jena.rdf.model.Model
import org.apache.jena.rdf.model.ModelFactory
import org.apache.jena.rdf.model.Property
import org.apache.jena.rdf.model.Resource
import org.memobase.mapping.KEYS
import org.memobase.rdf.DC
......@@ -54,33 +55,26 @@ abstract class RecordResource(institutionId: String) : IResource {
resource.addLiteral(KEYS.keysToPropertyMap[property], value)
}
override fun addLanguage(type: String, text: String) {
val blank = model.createResource()
blank.addProperty(RICO.name, literal(text))
blank.addProperty(RICO.type, type)
blank.addProperty(RDF.type, RICO.Language)
resource.addProperty(RICO.hasLanguage, blank)
}
override fun addTitle(type: String, text: String, language: String) {
val blank = model.createResource()
blank.addProperty(RICO.title, langLiteral(text, language))
blank.addProperty(RICO.type, type)
blank.addProperty(RDF.type, RICO.Title)
blank.addProperty(RICO.isTitleOf, resource)
resource.addProperty(RICO.hasTitle, blank)
override fun addRicoConcept(rdfType: String, ricoType: String, value: List<Literal>) {
when (rdfType) {
KEYS.titles ->
addRicoConcept(RICO.Title, ricoType, RICO.title, value, RICO.hasTitle)
KEYS.identifiers ->
addRicoConcept(RICO.Identifier, ricoType, RICO.identifier, value, RICO.identifiedBy)
KEYS.languages ->
addRicoConcept(RICO.Language, ricoType, RICO.name, value, RICO.hasLanguage)
else -> throw Exception("Unknown rdf:type alias for rico:Concept $rdfType.")
}
}
protected fun addIdentifier(type: String, identifier: String, institution: Resource) {
private fun addRicoConcept(rdfType: Resource, ricoType: String, valueProperty: Property, value: List<Literal>, resourceProperty: Property) {
val blank = model.createResource()
blank.addProperty(RICO.identifier, literal(identifier))
blank.addProperty(RICO.type, type)
blank.addProperty(RDF.type, RICO.Identifier)
resource.addProperty(RICO.identifiedBy, blank)
}
override fun addIdentifier(type: String, identifier: String) {
addIdentifier(type, identifier, institutionUri)
blank.addProperty(RDF.type, rdfType)
blank.addProperty(RICO.type, ricoType)
value.forEach {
blank.addProperty(valueProperty, it)
}
resource.addProperty(resourceProperty, blank)
}
override fun addSkosConcept(type: String, properties: List<Pair<String, Literal>>) {
......
......@@ -96,17 +96,21 @@ object KEYS {
const val issuedDate = "issuedDate"
const val temporal = "temporal"
// Title
// rico:Concepts
const val titles = "titles"
// Identifiers
const val identifiers = "identifiers"
const val languages = "languages"
// Languages
private val identifierTypes = listOf("original", "callNumber", "main")
private val titleTypes = listOf("main", "serial", "broadcast")
private val languageTypes = listOf("content", "caption")
const val languages = "languages"
val ricoConceptCategoryToTypesMap = mapOf(
Pair(titles, titleTypes),
Pair(identifiers, identifierTypes),
Pair(languages, languageTypes)
)
// Physical Object Keys
......
......@@ -32,8 +32,6 @@ import org.memobase.mapping.mappers.AgentFieldMapper
import org.memobase.mapping.mappers.ConstantFieldMapper
import org.memobase.mapping.mappers.DateFieldMapper
import org.memobase.mapping.mappers.IFieldMapper
import org.memobase.mapping.mappers.IdentifierFieldMapper
import org.memobase.mapping.mappers.LanguageMapper
import org.memobase.mapping.mappers.LanguageFieldMapper
import org.memobase.mapping.mappers.ListFieldMapper
import org.memobase.mapping.mappers.PlaceFieldMapper
......@@ -41,7 +39,7 @@ import org.memobase.mapping.mappers.PrefixFieldMapper
import org.memobase.mapping.mappers.RuleFieldMapper
import org.memobase.mapping.mappers.SkosConceptFieldMapper
import org.memobase.mapping.mappers.DirectFieldMapper
import org.memobase.mapping.mappers.TitleFieldMapper
import org.memobase.mapping.mappers.RicoConceptMapper
import org.memobase.mapping.mappers.TypeFieldMapper
import org.memobase.rdf.SKOS
import kotlin.reflect.full.createInstance
......@@ -55,9 +53,6 @@ class MappingConfig(directory: String) {
val physicalObjectFieldMappers = mutableListOf<IFieldMapper>()
val digitalObjectFieldMappers = mutableListOf<IFieldMapper>()
private val identifierTypes = listOf("original", "callNumber", "main")
private val titleTypes = listOf("main", "serial", "broadcast")
private val languageTypes = listOf("content", "caption")
private val ruleTypes = listOf(KEYS.holder, KEYS.access, KEYS.usage)
private val recordTypes = listOf("Film", "Foto", "Radio", "Ton", "Tonbildschau", "TV", "Video")
......@@ -117,41 +112,8 @@ class MappingConfig(directory: String) {
recordFieldMappers.add(buildAnnotationMappers(entry))
KEYS.rights ->
recordFieldMappers.addAll(buildRuleMappers(entry.value))
KEYS.titles ->
when (val value = entry.value) {
is Map<*, *> -> {
for (titleEntry in value.entries) {
val configField =
parseFieldWithKeyValidation(
KEYS.titles,
titleEntry as Map.Entry<String, Any>, titleTypes
)
if (configField is ConfigField.AnnotationField.ComplexAnnotationField.LanguageField) {
recordFieldMappers.add(
TitleFieldMapper(
configField
)
)
} else
throw InvalidMappingException("Title mapping requires language tags!")
}
}
else -> throw InvalidMappingException("Expected key value map under title label.")
}
KEYS.identifiers ->
recordFieldMappers.addAll(buildIdentifierMapper(entry.value))
KEYS.languages ->
when (val value = entry.value) {
is Map<*, *> -> {
extractLanguageConfigFieldData(value as Map<String, Any>)
}
is List<*> -> {
for (item in value) {
extractLanguageConfigFieldData(item as Map<String, Any>)
}
}
else -> throw InvalidMappingException("Expected key value map under languages label.")
}
KEYS.titles, KEYS.identifiers, KEYS.languages ->
recordFieldMappers.addAll(buildRicoConceptMappers(key, entry.value))
KEYS.subject, KEYS.genre ->
when (val value = entry.value) {
is Map<*, *> -> {
......@@ -200,7 +162,7 @@ class MappingConfig(directory: String) {
KEYS.playbackSpeed, KEYS.hasStandard ->
physicalObjectFieldMappers.add(buildAnnotationMappers(entry))
KEYS.identifiers ->
physicalObjectFieldMappers.addAll(buildIdentifierMapper(entry.value))
physicalObjectFieldMappers.addAll(buildRicoConceptMappers(key, entry.value))
KEYS.rights ->
physicalObjectFieldMappers.addAll(buildRuleMappers(entry.value))
else -> throw InvalidMappingException("Unknown key '$key' in physical object mapping.")
......@@ -213,6 +175,8 @@ class MappingConfig(directory: String) {
when (val key = entry.key) {
KEYS.locator, KEYS.descriptiveNote, KEYS.duration ->
digitalObjectFieldMappers.add(buildAnnotationMappers(entry))
KEYS.identifiers ->
digitalObjectFieldMappers.addAll(buildRicoConceptMappers(key, entry.value))
KEYS.rights ->
digitalObjectFieldMappers.addAll(buildRuleMappers(entry.value))
else -> throw InvalidMappingException("Unknown key '$key' in digital object mapping.")
......@@ -230,18 +194,25 @@ class MappingConfig(directory: String) {
}
}
private fun buildIdentifierMapper(value: Any?): List<IFieldMapper> {
private fun buildRicoConceptMappers(key: String, value: Any?): List<IFieldMapper> {
return when (value) {
is Map<*, *> -> {
extractIdentifierConfigFieldData(value as Map<String, Any>)
}
is List<*> -> {
value.map {
return@map extractIdentifierConfigFieldData(it as Map<String, Any>)
}.flatten()
value.entries.map {
KEYS.ricoConceptCategoryToTypesMap[key].let { typeList ->
if (typeList != null)
parseFieldWithKeyValidation(key, it as Map.Entry<String, Any>, typeList)
else
throw InvalidMappingException("Could not find a types configuration for field $key.")
}
}.map {
RicoConceptMapper(key, it)
}
}
else -> throw InvalidMappingException("Expected key value map under identifiers label.")
else -> throw InvalidMappingException("Expected a key-value map in section $key.")
}
}
private fun buildAgentMapper(key: String, value: Any?): List<IFieldMapper> {
......@@ -349,37 +320,6 @@ class MappingConfig(directory: String) {
return instance
}
private fun extractLanguageConfigFieldData(value: Map<String, Any>) {
for (subEntry in value.entries) {
val configField =
parseFieldWithKeyValidation(
KEYS.languages,
subEntry, languageTypes
)
if (configField is DirectMapField) {
recordFieldMappers.add(LanguageMapper(configField))
} else
throw InvalidMappingException("Languages only allow simple direct mappings.")
}
}
private fun extractIdentifierConfigFieldData(value: Map<String, Any>): List<IFieldMapper> {
return value.entries.map {
val configField =
parseFieldWithKeyValidation(
KEYS.identifiers,
it, identifierTypes
)
if (configField is DirectMapField) {
return@map IdentifierFieldMapper(configField)
} else
throw InvalidMappingException("Identifiers only allow simple direct mappings.")
}
}
private fun parsePrefixField(key: String, values: Map<String, String>): PrefixField {
return values["field"].let { field ->
if (field != null) {
......@@ -474,7 +414,7 @@ class MappingConfig(directory: String) {
parent: String,
entry: Map.Entry<String, Any>,
Map: List<String>
): ConfigField {
): AnnotationField {
if (Map.contains(entry.key)) {
return parseAnnotationField(entry)
} else {
......
/*
* mapper-service
* Copyright (C) 2020 Memoriav
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.memobase.mapping.mappers
import org.memobase.builder.IResource
import org.memobase.mapping.fields.DirectMapField
class IdentifierFieldMapper(private val directMapField: DirectMapField) : IFieldMapper {
override fun apply(source: Map<String, String>, subject: IResource) {
if (source.containsKey(directMapField.field)) {
subject.addIdentifier(directMapField.key, source[directMapField.field] as String)
}
}
}
\ No newline at end of file
/*
* mapper-service
* Copyright (C) 2020 Memoriav
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.memobase.mapping.mappers
import org.memobase.builder.IResource
import org.memobase.mapping.fields.DirectMapField
class LanguageMapper(private val directMapField: DirectMapField) :
IFieldMapper {
override fun apply(source: Map<String, String>, subject: IResource) {
source[directMapField.field].let {
if (it != null) {
subject.addLanguage(directMapField.key, it)
}
}
}
}
\ No newline at end of file
......@@ -19,32 +19,27 @@
package org.memobase.mapping.mappers
import org.memobase.builder.IResource
import org.memobase.mapping.fields.AnnotationField
import org.memobase.mapping.fields.ConstantField
import org.memobase.mapping.fields.DirectMapField
import org.memobase.mapping.fields.LanguageField
import org.memobase.mapping.fields.PrefixField
import org.memobase.mapping.fields.ListField
import org.memobase.mapping.fields.MappedAnnotationField
class TitleFieldMapper(private val languageField: LanguageField) : IFieldMapper {
class RicoConceptMapper(private val rdfType: String, private val field: AnnotationField) : IFieldMapper {
override fun apply(source: Map<String, String>, subject: IResource) {
for (pair in languageField.fields) {
for (field in pair.second) {
when (field) {
is ConstantField ->
subject.addTitle(field.key, field.constant, pair.first)
is PrefixField ->
source[field.field].let {
if (it != null) {
subject.addTitle(field.key, field.prefix + it.trim(), pair.first)
}
}
is DirectMapField ->
source[field.field].let {
if (it != null) {
subject.addTitle(field.key, it, pair.first)
}
}
when (field) {
is MappedAnnotationField ->
source[field.field].let {
if (it != null) {
subject.addRicoConcept(rdfType, field.key, listOf(field.toLiteral(it)))
}
}
}
is ConstantField ->
subject.addRicoConcept(rdfType, field.key, listOf(field.toLiteral()))
is LanguageField ->
subject.addRicoConcept(rdfType, field.key, field.toLangLiterals(source))
is ListField ->
subject.addRicoConcept(rdfType, field.key, field.toLiterals(source))
}
}
}
\ No newline at end of file
......@@ -33,7 +33,6 @@ _:B <https://www.ica.org/standards/RiC/ontology#creationRelationHasTarget> _:B .
_:B <https://www.ica.org/standards/RiC/ontology#expressedDate> "19210914" .
_:B <https://www.ica.org/standards/RiC/ontology#identifier> "MEI_49884" .
_:B <https://www.ica.org/standards/RiC/ontology#identifier> "https://memobase.ch/record/BAZ-MEI_49884" .
_:B <https://www.ica.org/standards/RiC/ontology#isTitleOf> <https://memobase.ch/record/BAZ-MEI_49884> .
_:B <https://www.ica.org/standards/RiC/ontology#name> "Walz"@de .
_:B <https://www.ica.org/standards/RiC/ontology#name> "Zürich"@de .
_:B <https://www.ica.org/standards/RiC/ontology#name> "publisher"@de .
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment