Skip to content

9.1 Internal Prefixes #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
35ce80d
Add vocab reference object
simonoakesepimorphics Oct 26, 2020
cb3f96d
Add classes for composing RDF model
simonoakesepimorphics Oct 26, 2020
a064c9b
Add RDF model test utils and tests
simonoakesepimorphics Oct 26, 2020
65259bd
Add binary array to model conversion API impl
simonoakesepimorphics Oct 26, 2020
f48cc53
Add NetCDF binary array factory method impl, tests
simonoakesepimorphics Oct 26, 2020
f8bb029
Add CLI impl, test, docs
simonoakesepimorphics Oct 26, 2020
0d7420e
Fix Java version in maven compiler plugin
simonoakesepimorphics Oct 26, 2020
e1ec12a
Add prefix mapping concept on binary array
simonoakesepimorphics Oct 26, 2020
a712090
Add prefix mapping to model composition, tests
simonoakesepimorphics Oct 26, 2020
1f1891f
Add NetCDF impl of prefix mapping concept
simonoakesepimorphics Oct 26, 2020
3c5bfec
Add tests, upgrade test format to NetCDF4 to support prefix group.
simonoakesepimorphics Oct 26, 2020
e14b5c2
Add logback config on CLI to suppress debug logs
simonoakesepimorphics Oct 26, 2020
493a20a
NetCDF4 considerations in tests
simonoakesepimorphics Oct 26, 2020
94f8105
Merge branch 'initial-setup' into feature/8-identity
simonoakesepimorphics Oct 27, 2020
67370a6
Merge branch 'feature/8-identity' into feature/9.1-prefix-internal
simonoakesepimorphics Oct 28, 2020
65359a2
Add file container, refactor
simonoakesepimorphics Oct 28, 2020
0b35f20
Add file container to tests
simonoakesepimorphics Oct 28, 2020
6510111
Merge branch 'feature/8-identity' into feature/9.1-prefix-internal
simonoakesepimorphics Oct 28, 2020
b60b503
Fix tests
simonoakesepimorphics Oct 28, 2020
a228e28
Merge branch 'feature/8-identity' into feature/9.1-prefix-internal
simonoakesepimorphics Oct 28, 2020
572758d
Changed test data to CDL format
simonoakesepimorphics Nov 2, 2020
cdbd5c0
Merge branch 'feature/8-identity' into feature/9.1-prefix-internal
simonoakesepimorphics Nov 2, 2020
2611cbb
Remove NCML references
simonoakesepimorphics Nov 2, 2020
9b76f87
Fix Java version on demo
simonoakesepimorphics Nov 2, 2020
a3c9605
ADD name and subgroups to container concept
simonoakesepimorphics Nov 2, 2020
925833f
Enhance binary array converter to describe subgroups
simonoakesepimorphics Nov 2, 2020
2ff2a52
Fix test consistency
simonoakesepimorphics Nov 2, 2020
5f5f71c
Merge branch 'feature/8-identity' into feature/9.1-prefix-internal
simonoakesepimorphics Nov 3, 2020
c14e9a2
Add prefix var support
simonoakesepimorphics Nov 3, 2020
f82bde6
Remove prefix group / var from graph
simonoakesepimorphics Nov 3, 2020
7685d14
Replace BALD prefix mapping concept with Jena prefix mapping
simonoakesepimorphics Nov 3, 2020
6f81aa6
Merge branch 'master' into feature/9.1-prefix-internal
simonoakesepimorphics Nov 5, 2020
87bc8aa
Fix merge issue
simonoakesepimorphics Nov 5, 2020
ade7db8
Use common test method
simonoakesepimorphics Nov 9, 2020
6acdf90
Add prefix mapping validation
simonoakesepimorphics Nov 10, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions binary-array-ld-cli/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package net.bald

import bald.model.ModelVerifier
import bald.netcdf.CdlConverter.convertToNetCdf
import bald.netcdf.CdlConverter.writeToNetCdf
import net.bald.vocab.BALD
import org.apache.jena.rdf.model.ModelFactory.createDefaultModel
import org.apache.jena.vocabulary.RDF
import org.apache.jena.vocabulary.SKOS
import org.junit.Assume
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import ucar.nc2.jni.netcdf.Nc4Iosp
import kotlin.test.assertEquals

/**
* Integration test for [BinaryArrayConvertCli].
*
* Test resources are stored in CDL format and converted to temporary NetCDF 4 files.
* In order to write the NetCDF 4 files, the ncgen command line utility must be available.
*/
class BinaryArrayConvertCliTest {
private fun run(vararg args: String) {
Expand All @@ -32,7 +39,7 @@ class BinaryArrayConvertCliTest {

@Test
fun run_withoutUri_outputsToFileWithInputFileUri() {
val inputFile = convertToNetCdf("/netcdf/identity.cdl")
val inputFile = writeToNetCdf("/netcdf/identity.cdl")
val inputFileUri = inputFile.toPath().toUri().toString()
val outputFile = createTempFile()
run(inputFile.absolutePath, outputFile.absolutePath)
Expand All @@ -56,12 +63,33 @@ class BinaryArrayConvertCliTest {

@Test
fun run_withUri_withOutputFile_outputsToFile() {
val inputFile = convertToNetCdf("/netcdf/identity.cdl")
val inputFile = writeToNetCdf("/netcdf/identity.cdl")
val outputFile = createTempFile()
run("--uri", "http://test.binary-array-ld.net/example", inputFile.absolutePath, outputFile.absolutePath)

val model = createDefaultModel().read(outputFile.toURI().toString(), "ttl")
ModelVerifier(model).apply {
resource("http://test.binary-array-ld.net/example/") {
statement(RDF.type, BALD.Container)
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/var0")) {
statement(RDF.type, BALD.Resource)
}
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/var1")) {
statement(RDF.type, BALD.Resource)
}
}
}
}

private fun run_withPrefixMapping_outputsPrefixMapping(cdlLoc: String) {
val inputFile = writeToNetCdf(cdlLoc)
val outputFile = createTempFile()
run("--uri", "http://test.binary-array-ld.net/example", inputFile.absolutePath, outputFile.absolutePath)

val model = createDefaultModel().read(outputFile.toURI().toString(), "ttl")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be nice here to share the model verification of run_withPrefixMappingGroup_outputsPrefixMapping() and run_withPrefixMappingVar_outputsPrefixMapping() in a separate function (if possible) to make it clearer that both group and var prefix mapping are expecting the same output?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, done.

ModelVerifier(model).apply {
prefix("bald", BALD.prefix)
prefix("skos", SKOS.uri)
resource("http://test.binary-array-ld.net/example") {
statement(RDF.type, BALD.Container)
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/")) {
Expand All @@ -77,9 +105,19 @@ class BinaryArrayConvertCliTest {
}
}

@Test
fun run_withPrefixMappingGroup_outputsPrefixMapping() {
run_withPrefixMapping_outputsPrefixMapping("/netcdf/prefix.cdl")
}

@Test
fun run_withPrefixMappingVar_outputsPrefixMapping() {
run_withPrefixMapping_outputsPrefixMapping("/netcdf/prefix-var.cdl")
}

@Test
fun run_withSubgroups_outputsWithSubgroups() {
val inputFile = convertToNetCdf("/netcdf/identity-subgroups.cdl")
val inputFile = writeToNetCdf("/netcdf/identity-subgroups.cdl")
val outputFile = createTempFile()
run("--uri", "http://test.binary-array-ld.net/example", inputFile.absolutePath, outputFile.absolutePath)

Expand Down
7 changes: 7 additions & 0 deletions binary-array-ld-lib/src/main/kotlin/net/bald/BinaryArray.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package net.bald

import org.apache.jena.shared.PrefixMapping

/**
* Represents the metadata of a binary array dataset.
* See https://www.opengis.net/def/binary-array-ld/Array
Expand All @@ -10,6 +12,11 @@ interface BinaryArray {
*/
val uri: String

/**
* The prefix mapping to apply to the RDF graph.
*/
val prefixMapping: PrefixMapping

/**
* The root container.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,46 @@ package net.bald.model
import net.bald.BinaryArray
import net.bald.vocab.BALD
import org.apache.jena.rdf.model.Model
import org.apache.jena.shared.PrefixMapping
import java.net.URI

class ModelBinaryArrayBuilder(
private val model: Model,
private val containerFct: ModelContainerBuilder.Factory
) {
fun addBinaryArray(ba: BinaryArray) {
addPrefixMapping(ba.prefixMapping)
val baRes = model.createResource(ba.uri, BALD.Container)
containerFct.forParent(baRes).addContainer(ba.root)
}

private fun addPrefixMapping(prefixMapping: PrefixMapping) {
prefixMapping.nsPrefixMap.onEach { (prefix, uri) ->
validatePrefixMapping(prefix, uri)
}.let(model::setNsPrefixes)
}

private fun validatePrefixMapping(prefix: String, uri: String) {
try {
if (!Prefix.pattern.matches(prefix)) {
throw IllegalArgumentException("Prefix must match pattern ${Prefix.pattern}.")
} else if (!uri.endsWith('/') && !uri.endsWith('#')) {
throw IllegalArgumentException("URI must end with / or #.")
} else {
val scheme = URI.create(uri).scheme
if (scheme != "http" && scheme != "https") {
throw IllegalArgumentException("URI must have HTTP or HTTPS scheme.")
}
}
} catch (e: Exception) {
throw IllegalArgumentException("Unable to add prefix mapping $prefix to model: ${e.message}")
}
}

private object Prefix {
val pattern = Regex("[A-Za-z_]+")
}

class Factory(
private val containerFct: ModelContainerBuilder.Factory
) {
Expand Down
2 changes: 1 addition & 1 deletion binary-array-ld-lib/src/main/kotlin/net/bald/vocab/BALD.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.apache.jena.rdf.model.ResourceFactory.createResource
* See https://www.opengis.net/def/binary-array-ld/.
*/
object BALD {
private const val prefix = "https://www.opengis.net/def/binary-array-ld/"
const val prefix = "https://www.opengis.net/def/binary-array-ld/"

/**
* Resources
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ import net.bald.BinaryArray
import net.bald.Container
import net.bald.vocab.BALD
import org.apache.jena.rdf.model.ModelFactory
import org.apache.jena.shared.PrefixMapping
import org.apache.jena.vocabulary.RDF
import org.junit.jupiter.api.*
import org.apache.jena.vocabulary.SKOS
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import java.lang.IllegalArgumentException
import kotlin.test.assertEquals

class ModelBinaryArrayBuilderTest {
private val containerBuilder = mock<ModelContainerBuilder>()
Expand All @@ -20,9 +25,13 @@ class ModelBinaryArrayBuilderTest {
private val model = ModelFactory.createDefaultModel()
private val builder = ModelBinaryArrayBuilder.Factory(containerFct).forModel(model)
private val root = mock<Container>()
private val prefix = PrefixMapping.Factory.create()
.setNsPrefix("bald", BALD.prefix)
.setNsPrefix("skos", SKOS.uri)
private val ba = mock<BinaryArray> {
on { uri } doReturn "http://test.binary-array-ld.net/example"
on { this.root } doReturn root
on { prefixMapping } doReturn prefix
}

@Test
Expand All @@ -39,4 +48,40 @@ class ModelBinaryArrayBuilderTest {
verify(containerFct).forParent(model.getResource("http://test.binary-array-ld.net/example"))
verify(containerBuilder).addContainer(root)
}

@Test
fun addBinaryArray_addsPrefixMapping() {
builder.addBinaryArray(ba)
ModelVerifier(model).apply {
prefix("bald", BALD.prefix)
prefix("skos", SKOS.uri)
}
}

@Test
fun addBinaryArray_prefixMappingWithInvalidChar_throwsException() {
prefix.setNsPrefix("bald-eg", "http://example.org/prefix/")
val iae = assertThrows<IllegalArgumentException> {
builder.addBinaryArray(ba)
}
assertEquals("Unable to add prefix mapping bald-eg to model: Prefix must match pattern [A-Za-z_]+.", iae.message)
}

@Test
fun addBinaryArray_prefixMappingWithInvalidScheme_throwsException() {
prefix.setNsPrefix("eg", "file:///example/prefix/")
val iae = assertThrows<IllegalArgumentException> {
builder.addBinaryArray(ba)
}
assertEquals("Unable to add prefix mapping eg to model: URI must have HTTP or HTTPS scheme.", iae.message)
}

@Test
fun addBinaryArray_prefixMappingWithoutTrailingChar_throwsException() {
prefix.setNsPrefix("eg", "http://example.org/prefix")
val iae = assertThrows<IllegalArgumentException> {
builder.addBinaryArray(ba)
}
assertEquals("Unable to add prefix mapping eg to model: URI must end with / or #.", iae.message)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import net.bald.Container
import net.bald.Var
import net.bald.vocab.BALD
import org.apache.jena.rdf.model.Model
import org.apache.jena.shared.PrefixMapping
import org.apache.jena.vocabulary.RDF
import org.apache.jena.vocabulary.SKOS
import org.junit.jupiter.api.*

/**
Expand All @@ -33,13 +35,19 @@ class ModelBinaryArrayConverterTest {
on { vars() } doReturn vars.asSequence()
on { subContainers() } doReturn emptySequence()
}
val prefix = PrefixMapping.Factory.create()
.setNsPrefix("bald", BALD.prefix)
.setNsPrefix("skos", SKOS.uri)
val ba = mock<BinaryArray> {
on { uri } doReturn "http://test.binary-array-ld.net/example"
on { this.root } doReturn root
on { prefixMapping } doReturn prefix
}
val model = convert(ba)

ModelVerifier(model).apply {
prefix("bald", BALD.prefix)
prefix("skos", SKOS.uri)
resource("http://test.binary-array-ld.net/example") {
statement(RDF.type, BALD.Container)
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package net.bald.netcdf

import net.bald.BinaryArray
import net.bald.Container
import ucar.nc2.NetcdfFile
import ucar.nc2.NetcdfFiles
import org.apache.jena.shared.PrefixMapping
import ucar.nc2.*
import java.io.Closeable
import java.io.File
import java.lang.IllegalStateException

/**
* NetCDF implementation of [BinaryArray].
Expand All @@ -15,12 +16,41 @@ class NetCdfBinaryArray(
override val uri: String,
private val file: NetcdfFile
): BinaryArray, Closeable {
override val root: Container get() = NetCdfContainer(file.rootGroup)
override val root: Container get() = container(file.rootGroup)
override val prefixMapping: PrefixMapping get() = prefixMapping() ?: PrefixMapping.Factory.create()

override fun close() {
file.close()
}

private fun container(group: Group): Container {
val prefixSrc = prefixSourceName()
return NetCdfContainer(group, prefixSrc)
}

private fun prefixMapping(): PrefixMapping? {
return prefixSource()?.let(::NetCdfPrefixMappingBuilder)?.build()
}

private fun prefixSource(): AttributeContainer? {
return prefixSourceName()?.let { name ->
file.findGroup(name)
?: file.findVariable(name)
?: throw IllegalStateException("Prefix group or variable $name not found.")
}
}

private fun prefixSourceName(): String? {
return file.findGlobalAttribute(Attribute.prefix)?.let { attr ->
attr.stringValue
?: throw IllegalStateException("Global prefix attribute ${Attribute.prefix} has a non-string value.")
}
}

private object Attribute {
const val prefix = "bald__isPrefixedBy"
}

companion object {
/**
* Instantiate a [BinaryArray] representation of the given NetCDF file and identifying URI.
Expand All @@ -31,9 +61,17 @@ class NetCdfBinaryArray(
*/
@JvmStatic
fun create(fileLoc: String, uri: String? = null): NetCdfBinaryArray {
val requiredUri = uri ?: uri(fileLoc)
val file = NetcdfFiles.open(fileLoc)
return NetCdfBinaryArray(requiredUri, file)
val requiredUri = uri ?: uri(fileLoc)
return create(file, requiredUri)
}

/**
* @see [create].
*/
@JvmStatic
fun create(file: NetcdfFile, uri: String): NetCdfBinaryArray {
return NetCdfBinaryArray(uri, file)
}

private fun uri(fileLoc: String): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,30 @@ package net.bald.netcdf
import net.bald.Container
import net.bald.Var
import ucar.nc2.Group
import ucar.nc2.Variable

/**
* NetCDF implementation of [Container].
*/
class NetCdfContainer(
private val group: Group
private val group: Group,
private val prefixSrc: String? = null
): Container {
override val name: String? get() = group.shortName

override fun vars(): Sequence<Var> {
return group.variables.asSequence().map(::NetCdfVar)
return group.variables.asSequence().filter(::acceptVar).map(::NetCdfVar)
}

override fun subContainers(): Sequence<Container> {
return group.groups.asSequence().map(::NetCdfContainer)
return group.groups.asSequence().filter(::acceptGroup).map(::NetCdfContainer)
}

private fun acceptVar(v: Variable): Boolean {
return prefixSrc != v.shortName
}

private fun acceptGroup(group: Group): Boolean {
return prefixSrc != group.shortName
}
}
Loading