Commit 432ffa14 authored by Jonas Waeber's avatar Jonas Waeber
Browse files

add digital object parser

parent 63531f7c
......@@ -47,23 +47,21 @@ class ResourceBuilder(
for (recordFieldMapper in recordFieldMappers) {
recordFieldMapper.apply(source, record)
}
physicalObject =
PhysicalObject(recordId, institutionId, 0)
physicalObjectFieldMappers.forEach {
it.apply(source, physicalObject)
}
physicalObject.addRecord(record)
record.addInstantiation(physicalObject)
resources = listOf(record, physicalObject)
}
fun write(): List<String> {
return resources.map { resource ->
StringWriter().use { writer ->
RDFDataMgr.write(writer, resource.model, RDFFormat.TURTLE_PRETTY)
RDFDataMgr.write(writer, resource.model, RDFFormat.JSONLD_COMPACT_FLAT)
return@map writer.toString()
}
}
......
......@@ -25,39 +25,39 @@ import org.memobase.rdf.RICO
object KEYS {
const val uri: String = "uri"
const val uri = "uri"
// Literal Mappings
const val name: String = "name"
const val descriptiveNote: String = "descriptiveNote"
const val scopeAndContent: String = "scopeAndContent"
const val abstract: String = "abstract"
const val source: String = "source"
const val title: String = "title"
const val hasSponsoringAgent: String = "hasSponsoringAgent"
const val hasFindingAid: String = "hasFindingAid"
const val name = "name"
const val descriptiveNote = "descriptiveNote"
const val scopeAndContent = "scopeAndContent"
const val abstract = "abstract"
const val source = "source"
const val title = "title"
const val hasSponsoringAgent = "hasSponsoringAgent"
const val hasFindingAid = "hasFindingAid"
// Rules
const val rights: String = "rights"
const val rights = "rights"
// Places
const val placeOfCapture: String = "placeOfCapture"
const val relatedPlaces: String = "relatedPlaces"
const val placeOfCapture = "placeOfCapture"
const val relatedPlaces = "relatedPlaces"
val allowedPlaceProperties = listOf("name")
// Creators
const val creators: String = "creators"
const val creatorType: String = "creatorType"
const val creators = "creators"
const val creatorType = "creatorType"
// Agent Types
const val agent: String = "agent"
const val corporateBody: String = "corporateBody"
const val person: String = "person"
const val agent = "agent"
const val corporateBody = "corporateBody"
const val person = "person"
val allowedAgentProperties = listOf("name")
......@@ -94,14 +94,18 @@ object KEYS {
// Physical Object Keys
val medium = "medium"
val physicalCharacteristics = "physicalCharacteristics"
val colour = "colour"
val duration = "duration"
val displayAspectRatio = "displayAspectRatio"
val audioTrackConfiguration = "audioTrackConfiguration"
val playbackSpeed = "playbackSpeed"
val hasStandard = "hasStandard"
const val medium = "medium"
const val physicalCharacteristics = "physicalCharacteristics"
const val colour = "colour"
const val duration = "duration"
const val displayAspectRatio = "displayAspectRatio"
const val audioTrackConfiguration = "audioTrackConfiguration"
const val playbackSpeed = "playbackSpeed"
const val hasStandard = "hasStandard"
// Digtal Object Keys
const val locator = "locator"
val keysToPropertyMap = mapOf(
Pair(abstract, DC.abstract),
......@@ -123,6 +127,7 @@ object KEYS {
Pair(displayAspectRatio, EBUCORE.displayAspectRatio),
Pair(audioTrackConfiguration, EBUCORE.audioTrackConfiguration),
Pair(playbackSpeed, EBUCORE.playbackSpeed),
Pair(hasStandard, EBUCORE.hasStandard)
Pair(hasStandard, EBUCORE.hasStandard),
Pair(locator, EBUCORE.locator)
)
}
\ No newline at end of file
......@@ -84,37 +84,14 @@ class MappingConfig(directory: String) {
}
private fun parseRecordConfig(source: Map<String, Any>) {
for (entry in source) {
when (val key = entry.key) {
KEYS.uri -> uriField = entry.value as String
// literal properties
KEYS.name, KEYS.title, KEYS.descriptiveNote, KEYS.scopeAndContent,
KEYS.abstract, KEYS.source, KEYS.hasSponsoringAgent, KEYS.hasFindingAid -> {
when (val value = parseField(entry)) {
is SimpleField -> recordFieldMappers.add(
StringFieldMapper(
value
)
)
is ConstField -> recordFieldMappers.add(
ConstantFieldMapper(
value
)
)
is ListField -> recordFieldMappers.add(
ListFieldMapper(
value
)
)
is LanguageField -> recordFieldMappers.add(
LanguageTagFieldMapper(
value
)
)
}
}
KEYS.abstract, KEYS.source, KEYS.hasSponsoringAgent, KEYS.hasFindingAid ->
recordFieldMappers.add(buildSimpleMappers(entry))
KEYS.rights ->
when (val value = entry.value) {
is Map<*, *> -> {
......@@ -192,52 +169,18 @@ class MappingConfig(directory: String) {
else -> throw InvalidMappingException("Expected a mapping or a list inside of the ${entry.key} section!")
}
KEYS.placeOfCapture, KEYS.relatedPlaces ->
when (val value = entry.value) {
is Map<*, *> -> {
for (subEntry in value.entries) {
extractEntityFields<PlaceFieldMapper>(
key,
value as Map<String, Any>,
KEYS.allowedPlaceProperties
)
}
}
is List<*> -> {
for (item in value) {
try {
extractEntityFields<PlaceFieldMapper>(
key,
item as Map<String, Any>,
KEYS.allowedPlaceProperties
)
} catch (ex: ClassCastException) {
throw InvalidMappingException("Expected a map inside of the list entry in the ${entry.key} section.")
}
}
}
else -> throw InvalidMappingException("Expected a mapping or a list inside of the ${entry.key} section!")
}
recordFieldMappers.addAll(buildPlaceMapper(key, entry.value))
KEYS.creationDate, KEYS.issuedDate, KEYS.temporal ->
when (val value = parseField(entry)) {
is SimpleField -> recordFieldMappers.add(
DateFieldMapper(
value
)
DateFieldMapper(value)
)
else -> throw InvalidMappingException("Dates only allow simple field assignment. Invalid mapping ${entry.key}.")
}
KEYS.creators ->
when (val value = entry.value) {
is Map<*, *> -> {
extractAgentTypeMapper(value as Map<String, Any>)
}
is List<*> -> {
for (item in value) {
extractAgentTypeMapper(item as Map<String, Any>)
}
}
else -> throw InvalidMappingException("The section ${KEYS.creators} expects a map or list.")
}
recordFieldMappers.addAll(buildCreatorMapper(entry.value))
else -> throw InvalidMappingException("Unknown top level key $key in mapping.")
}
}
......@@ -248,21 +191,38 @@ class MappingConfig(directory: String) {
when (val key = entry.key) {
KEYS.descriptiveNote, KEYS.medium, KEYS.physicalCharacteristics,
KEYS.colour, KEYS.duration, KEYS.displayAspectRatio, KEYS.audioTrackConfiguration,
KEYS.playbackSpeed, KEYS.hasStandard -> {
when (val value = parseField(entry)) {
is SimpleField -> physicalObjectFieldMappers.add(StringFieldMapper(value))
is ConstField -> physicalObjectFieldMappers.add(ConstantFieldMapper(value))
is ListField -> physicalObjectFieldMappers.add(ListFieldMapper(value))
is LanguageField -> physicalObjectFieldMappers.add(LanguageTagFieldMapper(value))
}
}
KEYS.identifiers -> physicalObjectFieldMappers.addAll(buildIdentifierMapper(entry.value))
KEYS.playbackSpeed, KEYS.hasStandard ->
physicalObjectFieldMappers.add(buildSimpleMappers(entry))
KEYS.identifiers ->
physicalObjectFieldMappers.addAll(buildIdentifierMapper(entry.value))
else -> throw InvalidMappingException("Unknown top level key $key in mapping.")
}
}
}
private fun parseDigitalInstantiationConfig(source: Map<String, Any>) {
for (entry in source) {
when (val key = entry.key) {
KEYS.locator ->
digitalObjectFieldMappers.add(buildSimpleMappers(entry))
else -> throw InvalidMappingException("Unknown top level key $key in mapping.")
}
}
}
@Suppress("UNCHECKED_CAST")
private fun buildSimpleMappers(value: Any?): IFieldMapper {
return when (val field = parseField(value as Map.Entry<String, Any>)) {
is SimpleField -> StringFieldMapper(field)
is ConstField -> ConstantFieldMapper(field)
is ListField -> ListFieldMapper(field)
is LanguageField -> LanguageTagFieldMapper(field)
else -> throw InvalidMappingException("This can't happen since there is no other class of this type...")
}
}
private fun buildIdentifierMapper(value: Any?): List<IFieldMapper> {
return when (value) {
is Map<*, *> -> {
......@@ -277,26 +237,71 @@ class MappingConfig(directory: String) {
}
}
private fun extractAgentTypeMapper(value: Map<String, Any>) {
private fun buildCreatorMapper(value: Any?): List<IFieldMapper> {
when (value) {
is Map<*, *> -> {
return listOf(extractAgentTypeMapper(value as Map<String, Any>))
}
is List<*> -> {
return value.map {
return@map extractAgentTypeMapper(it as Map<String, Any>)
}
}
else -> throw InvalidMappingException("The section ${KEYS.creators} expects a map or list.")
}
}
private fun extractAgentTypeMapper(value: Map<String, Any>): IFieldMapper {
val mapper = when {
value.containsKey(KEYS.agent) -> {
extractEntityFields<AgentFieldMapper>(KEYS.agent, value[KEYS.agent] as Map<String, Any>, KEYS.allowedAgentProperties)
extractEntityFields<AgentFieldMapper>(
KEYS.agent,
value[KEYS.agent] as Map<String, Any>,
KEYS.allowedAgentProperties
)
}
value.containsKey(KEYS.corporateBody) -> {
extractEntityFields<AgentFieldMapper>(KEYS.corporateBody, value[KEYS.corporateBody] as Map<String, Any>, KEYS.allowedAgentProperties)
extractEntityFields<AgentFieldMapper>(
KEYS.corporateBody,
value[KEYS.corporateBody] as Map<String, Any>,
KEYS.allowedAgentProperties
)
}
value.containsKey(KEYS.person) -> {
extractEntityFields<AgentFieldMapper>(KEYS.person, value[KEYS.person] as Map<String, Any>, KEYS.allowedAgentProperties)
extractEntityFields<AgentFieldMapper>(
KEYS.person,
value[KEYS.person] as Map<String, Any>,
KEYS.allowedAgentProperties
)
}
else -> {
throw InvalidMappingException("Creation relation requires a field agent, corporateBody or person.")
}
}
(mapper as AgentFieldMapper).creationType = parseField(value.entries.first { i -> i.key == KEYS.creatorType})
recordFieldMappers.add(mapper)
(mapper as AgentFieldMapper).creationType = parseField(value.entries.first { i -> i.key == KEYS.creatorType })
return mapper
}
private fun buildPlaceMapper(key: String, value: Any?): List<IFieldMapper> {
return when (value) {
is Map<*, *> -> {
listOf(
extractEntityFields<PlaceFieldMapper>(
key, value as Map<String, Any>, KEYS.allowedPlaceProperties))
}
is List<*> -> {
return value.map {
try {
extractEntityFields<PlaceFieldMapper>(
key, it as Map<String, Any>, KEYS.allowedPlaceProperties)
} catch (ex: ClassCastException) {
throw InvalidMappingException("Expected a map inside of the list entry in the $key section.")
}
}
}
else -> throw InvalidMappingException("Expected a mapping or a list inside of the $key section!")
}
}
private inline fun <reified T : TypeFieldMapper> extractEntityFields(
key: String,
......@@ -315,7 +320,6 @@ class MappingConfig(directory: String) {
}
val instance = T::class.createInstance() as TypeFieldMapper
instance.setFields(key, configFields)
recordFieldMappers.add(instance)
return instance
}
......@@ -381,12 +385,6 @@ class MappingConfig(directory: String) {
}
}
private fun parseDigitalInstantiationConfig(source: Map<String, Any>) {
}
private fun parseFieldWithKeyValidation(
parent: String,
entry: Map.Entry<String, Any>,
......
......@@ -18,4 +18,4 @@
package org.memobase.mapping.fields
abstract class ConfigField
\ No newline at end of file
open class ConfigField
\ No newline at end of file
......@@ -33,11 +33,13 @@ abstract class TypeFieldMapper : IFieldMapper {
fun setFields(type: String, fields: List<ConfigField>) {
this.type = type
this.fields.clear()
this.fields.addAll(fields)
}
protected val properties = mutableListOf<Pair<String, Literal>>()
protected fun translateProperties(source: Map<String, String>, subject: IResource) {
properties.clear()
for (field in fields) {
when (field) {
is SimpleField ->
......
......@@ -32,6 +32,7 @@ object EBUCORE {
val audioTrackConfiguration = prop("audioTrackConfiguration")
val playbackSpeed = prop("playbackSpeed")
val hasStandard = prop("hasStandard")
val locator = prop("locator")
private fun prop(name: String): Property {
return ResourceFactory.createProperty(NS.ebucore, name)
......
......@@ -24,7 +24,10 @@ import org.junit.jupiter.api.TestInstance
import org.memobase.builder.ResourceBuilder
import org.memobase.mapping.MappingConfig
import java.io.File
import java.io.FileOutputStream
import java.nio.charset.Charset
import java.nio.file.Files
import java.nio.file.Paths
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class Tests {
......@@ -35,23 +38,33 @@ class Tests {
return File("$resourcePath/$fileName").readText(Charset.defaultCharset())
}
private val json = """{"titel":"«Villa Siegel», Zürich","beschreibung":"Villa mit Garten und Brunnen im Vordergrund. Vermutlich von Architekt Walz","genre":"Bauwerk","aufnahmeort":"Zürich","autorin":"Atelier Meiner","auftraggeber":"Walz","verwandte_dokumente":"Auftragsregister Bd. 6; Bildverzeichnis Bd. 7","erstellung":"19210914","rechteinhaber":"BAZ ","nutzungsrecht":"nach Absprache","medium":"Negativ Nitrat (NN)","format":"18x24","farbe":"sw","orientation":"Querformat","zustand":"Nitratschaden","konservatorische_bewertung":"B","original_id":"MEI_49884","alte_signatur":"49884","signatur_digitalisat":"MEI_49884","bearbeitungskontext":"Pilot Meiner"}"""
@Test
fun `test mapping config parser`() {
val config =
MappingConfig("src/test/resources/multifileconfig")
var count = 0
Files.list(Paths.get("src/test/resources/data")).forEach { path ->
val text = Files.newBufferedReader(path).readText()
val values = Klaxon().parse<Map<String, String>>(text)
val resourceBuilder = ResourceBuilder(
config.uriField,
config.recordFieldMappers,
config.physicalObjectFieldMappers,
values!!,
"BAZ",
"BAZ-B_MEI"
)
val string = resourceBuilder.write()
string.forEach { s ->
count += 1
FileOutputStream("src/test/resources/output/data$count.json").bufferedWriter(Charset.defaultCharset()).use {
it.write(s)
}
}
}
val values = Klaxon().parse<Map<String, String>>(json)
val recordBuilder = ResourceBuilder(
config.uriField,
config.recordFieldMappers,
config.physicalObjectFieldMappers,
values!!,
"BAZ",
"BAZ-B_MEI"
)
val string = recordBuilder.write()
print(string)
}
}
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