Skip to content

Commit 48717c5

Browse files
9.2 External Prefixes (#21)
* Add vocab reference object * Add classes for composing RDF model * Add RDF model test utils and tests * Add binary array to model conversion API impl * Add NetCDF binary array factory method impl, tests * Add CLI impl, test, docs * Fix Java version in maven compiler plugin * Add prefix mapping concept on binary array * Add prefix mapping to model composition, tests * Add NetCDF impl of prefix mapping concept * Add tests, upgrade test format to NetCDF4 to support prefix group. * Add logback config on CLI to suppress debug logs * NetCDF4 considerations in tests * Add option to supply external prefixes for NetCDF to model conversion * Add external prefix / context parameter to CLI, add test * Update Java example with external prefixes. * Rename var * Improve error message wording * Add file container, refactor * Add file container to tests * Fix tests * Changed test data to CDL format * Fix test * Remove NCML references * Fix Java version on demo * ADD name and subgroups to container concept * Enhance binary array converter to describe subgroups * Fix test consistency * Add prefix var support * Remove prefix group / var from graph * Replace BALD prefix mapping concept with Jena prefix mapping * Fix merge issue * Use common test method * Add prefix mapping validation * Refactor the RDF context impl as a decorator * Add prefix conflict validation * Fix reduce on empty list * Restore closeable interface * Restore function pointer usage
1 parent ffce5f0 commit 48717c5

File tree

15 files changed

+270
-15
lines changed

15 files changed

+270
-15
lines changed

binary-array-ld-cli/src/main/kotlin/net/bald/BinaryArrayConvertCli.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package net.bald
22

3+
import net.bald.context.ContextBinaryArray
34
import net.bald.model.ModelBinaryArrayConverter
45
import net.bald.netcdf.NetCdfBinaryArray
56
import org.apache.commons.cli.DefaultParser
67
import org.apache.commons.cli.HelpFormatter
78
import org.apache.commons.cli.Options
9+
import org.apache.jena.rdf.model.Model
10+
import org.apache.jena.rdf.model.ModelFactory
811
import java.io.File
912
import java.io.OutputStream
1013
import kotlin.system.exitProcess
@@ -15,6 +18,7 @@ import kotlin.system.exitProcess
1518
class BinaryArrayConvertCli {
1619
private val opts = Options().apply {
1720
this.addOption("u", "uri", true, "The URI which identifies the dataset.")
21+
this.addOption("c", "context", true, "Comma-delimited list of JSON-LD context files.")
1822
this.addOption("h", "help", false, "Show help.")
1923
}
2024

@@ -34,14 +38,21 @@ class BinaryArrayConvertCli {
3438

3539
private fun doRun(opts: CommandLineOptions) {
3640
val inputLoc = opts.inputLoc ?: throw IllegalArgumentException("First argument is required: NetCDF file to convert.")
37-
val ba = NetCdfBinaryArray.create(inputLoc, opts.uri)
41+
val ba = NetCdfBinaryArray.create(inputLoc, opts.uri).withContext(opts.contextLocs)
3842
val model = ba.use(ModelBinaryArrayConverter::convert)
3943

4044
modelOutput(opts.outputLoc).use { output ->
4145
model.write(output, "ttl")
4246
}
4347
}
4448

49+
private fun BinaryArray.withContext(contextLocs: List<String>): BinaryArray {
50+
val contexts = contextLocs.map { contextLoc ->
51+
ModelFactory.createDefaultModel().read(contextLoc, "json-ld")
52+
}
53+
return ContextBinaryArray.create(this, contexts)
54+
}
55+
4556
private fun options(opts: Options, vararg args: String): CommandLineOptions {
4657
return DefaultParser().parse(opts, args).let(::CommandLineOptions)
4758
}

binary-array-ld-cli/src/main/kotlin/net/bald/CommandLineOptions.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ class CommandLineOptions(
1010
}
1111
val outputLoc: String? get() = cmd.args.getOrNull(1)
1212
val uri: String? get() = cmd.getOptionValue("uri")
13+
val contextLocs: List<String> get() = cmd.getOptionValue("context")?.split(",") ?: emptyList()
1314
val help: Boolean get() = cmd.hasOption("help")
1415
}

binary-array-ld-cli/src/test/kotlin/net/bald/BinaryArrayConvertCliTest.kt

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package net.bald
22

3+
import bald.jsonld.ContextReader
34
import bald.model.ModelVerifier
45
import bald.netcdf.CdlConverter.writeToNetCdf
56
import net.bald.vocab.BALD
67
import org.apache.jena.rdf.model.ModelFactory.createDefaultModel
8+
import org.apache.jena.vocabulary.DCTerms
79
import org.apache.jena.vocabulary.RDF
810
import org.apache.jena.vocabulary.SKOS
9-
import org.junit.Assume
10-
import org.junit.jupiter.api.BeforeEach
11+
import org.apache.jena.vocabulary.XSD
1112
import org.junit.jupiter.api.Test
1213
import org.junit.jupiter.api.assertThrows
13-
import ucar.nc2.jni.netcdf.Nc4Iosp
14+
import java.io.File
1415
import kotlin.test.assertEquals
1516

1617
/**
@@ -155,4 +156,35 @@ class BinaryArrayConvertCliTest {
155156
}
156157
}
157158
}
159+
160+
@Test
161+
fun run_withExternalPrefixMapping_outputsPrefixMapping() {
162+
val inputFile = writeToNetCdf("/netcdf/prefix.cdl")
163+
val outputFile = createTempFile()
164+
val contextFiles = listOf(ContextReader.toFile("/jsonld/context.json"), ContextReader.toFile("/jsonld/context2.json"))
165+
166+
run(
167+
"--uri", "http://test.binary-array-ld.net/example",
168+
"--context", contextFiles.joinToString(",", transform = File::getAbsolutePath),
169+
inputFile.absolutePath,
170+
outputFile.absolutePath
171+
)
172+
173+
val model = createDefaultModel().read(outputFile.toURI().toString(), "ttl")
174+
ModelVerifier(model).apply {
175+
prefix("bald", BALD.prefix)
176+
prefix("skos", SKOS.uri)
177+
prefix("dct", DCTerms.NS)
178+
prefix("xsd", XSD.NS)
179+
resource("http://test.binary-array-ld.net/example/") {
180+
statement(RDF.type, BALD.Container)
181+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/var0")) {
182+
statement(RDF.type, BALD.Resource)
183+
}
184+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/var1")) {
185+
statement(RDF.type, BALD.Resource)
186+
}
187+
}
188+
}
189+
}
158190
}
Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package net.bald;
22

3+
import net.bald.context.ContextBinaryArray;
34
import net.bald.model.ModelBinaryArrayConverter;
45
import net.bald.netcdf.NetCdfBinaryArray;
56
import org.apache.jena.rdf.model.Model;
7+
import org.apache.jena.rdf.model.ModelFactory;
8+
import org.apache.jena.shared.PrefixMapping;
69

710
import java.io.FileOutputStream;
811
import java.io.OutputStream;
@@ -11,16 +14,23 @@
1114
* Demonstration of how to call the API in Java code.
1215
*/
1316
public class NetCdfConvertJava {
14-
public static void convert(String inputLoc, String outputLoc, String format) throws Exception {
15-
BinaryArray ba = NetCdfBinaryArray.create(inputLoc, "http://test.binary-array-ld.net/example");
17+
public static void convert() throws Exception {
18+
BinaryArray ba = NetCdfBinaryArray.create("/path/to/input.nc", "http://test.binary-array-ld.net/example");
1619
Model model = ModelBinaryArrayConverter.convert(ba);
1720

18-
try (OutputStream output = new FileOutputStream(outputLoc)) {
19-
model.write(output, format);
21+
try (OutputStream output = new FileOutputStream("/path/to/output.ttl")) {
22+
model.write(output, "ttl");
2023
}
2124
}
2225

23-
public static void convert() throws Exception {
24-
convert("/path/to/input.nc", "/path/to/output.ttl", "ttl");
26+
public static void convertWithExternalPrefixes() throws Exception {
27+
BinaryArray ba = NetCdfBinaryArray.create("/path/to/input.nc", "http://test.binary-array-ld.net/example");
28+
PrefixMapping context = ModelFactory.createDefaultModel().read("/path/to/context.json", "json-ld");
29+
BinaryArray contextBa = ContextBinaryArray.create(ba, context);
30+
Model model = ModelBinaryArrayConverter.convert(contextBa);
31+
32+
try (OutputStream output = new FileOutputStream("/path/to/output.ttl")) {
33+
model.write(output, "ttl");
34+
}
2535
}
2636
}

binary-array-ld-lib/src/main/kotlin/net/bald/BinaryArray.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package net.bald
22

33
import org.apache.jena.shared.PrefixMapping
4+
import java.io.Closeable
45

56
/**
67
* Represents the metadata of a binary array dataset.
78
* See https://www.opengis.net/def/binary-array-ld/Array
9+
* Should be closed after use.
810
*/
9-
interface BinaryArray {
11+
interface BinaryArray: Closeable {
1012
/**
1113
* The URI which identifies the dataset.
1214
*/
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package net.bald.context
2+
3+
import net.bald.BinaryArray
4+
import net.bald.Container
5+
import org.apache.jena.shared.PrefixMapping
6+
7+
/**
8+
* Decorator for [BinaryArray] that supports context.
9+
*/
10+
class ContextBinaryArray(
11+
private val ba: BinaryArray,
12+
private val context: PrefixMapping
13+
): BinaryArray {
14+
override val uri: String get() = ba.uri
15+
override val root: Container get() = ba.root
16+
17+
override val prefixMapping: PrefixMapping get() {
18+
return PrefixMapping.Factory.create()
19+
.setNsPrefixes(context)
20+
.setNsPrefixes(ba.prefixMapping)
21+
}
22+
23+
override fun close() {
24+
ba.close()
25+
}
26+
27+
companion object {
28+
/**
29+
* Decorate the given [BinaryArray] with the given [PrefixMapping]s to contextualise the binary array.
30+
* @throws IllegalArgumentException If there are conflicting definitions in the contexts.
31+
* @param ba The original binary array.
32+
* @param contexts The contextual prefix mappings.
33+
* @return A contextualised [BinaryArray].
34+
*/
35+
@JvmStatic
36+
fun create(ba: BinaryArray, contexts: List<PrefixMapping>): BinaryArray {
37+
return if (contexts.isEmpty()) {
38+
ba
39+
} else {
40+
val context = contexts.reduce { acc, context ->
41+
val accKeys = acc.nsPrefixMap.keys
42+
val contextKeys = context.nsPrefixMap.keys
43+
val conflicts = accKeys.intersect(contextKeys).filterNot { prefix ->
44+
acc.getNsPrefixURI(prefix) == context.getNsPrefixURI(prefix)
45+
}
46+
47+
if (conflicts.isEmpty()) {
48+
acc.setNsPrefixes(context)
49+
} else {
50+
throw IllegalArgumentException("The namespace prefixes $conflicts have conflicting definitions in contexts.")
51+
}
52+
}
53+
create(ba, context)
54+
}
55+
}
56+
57+
/**
58+
* @see create
59+
*/
60+
@JvmStatic
61+
fun create(ba: BinaryArray, context: PrefixMapping): BinaryArray {
62+
return ContextBinaryArray(ba, context)
63+
}
64+
}
65+
}

binary-array-ld-lib/src/main/kotlin/net/bald/model/ModelBinaryArrayConverter.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package net.bald.model
33
import net.bald.BinaryArray
44
import org.apache.jena.rdf.model.Model
55
import org.apache.jena.rdf.model.ModelFactory
6+
import org.apache.jena.shared.PrefixMapping
67

78
/**
89
* API for converting a [BinaryArray] to a linked data [Model].
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package net.bald.context
2+
3+
import com.nhaarman.mockitokotlin2.doReturn
4+
import com.nhaarman.mockitokotlin2.mock
5+
import com.nhaarman.mockitokotlin2.verify
6+
import net.bald.BinaryArray
7+
import net.bald.Container
8+
import net.bald.vocab.BALD
9+
import org.apache.jena.shared.PrefixMapping
10+
import org.apache.jena.vocabulary.DCTerms
11+
import org.apache.jena.vocabulary.SKOS
12+
import org.apache.jena.vocabulary.VCARD
13+
import org.junit.jupiter.api.*
14+
import java.lang.IllegalArgumentException
15+
import kotlin.test.assertEquals
16+
17+
class ContextBinaryArrayTest {
18+
private val context = PrefixMapping.Factory.create()
19+
.setNsPrefix("skos", "http://example.org/skos/")
20+
.setNsPrefix("dct", DCTerms.getURI())
21+
private val root = mock<Container>()
22+
private val ba = mock<BinaryArray> {
23+
on { uri } doReturn "http://test.binary-array-ld.net/example"
24+
on { root } doReturn root
25+
on { prefixMapping } doReturn PrefixMapping.Factory.create()
26+
.setNsPrefix("bald", BALD.prefix)
27+
.setNsPrefix("skos", SKOS.uri)
28+
}
29+
private val contextBa = ContextBinaryArray.create(ba, context)
30+
31+
@Test
32+
fun uri_returnsUri() {
33+
assertEquals("http://test.binary-array-ld.net/example", contextBa.uri)
34+
}
35+
36+
@Test
37+
fun root_returnsRoot() {
38+
assertEquals(root, contextBa.root)
39+
}
40+
41+
@Test
42+
fun close_closesBinaryArray() {
43+
contextBa.close()
44+
verify(ba).close()
45+
}
46+
47+
@Test
48+
fun prefixMapping_returnsCombinedPrefixMapping() {
49+
val result = contextBa.prefixMapping.nsPrefixMap
50+
val expected = mapOf(
51+
"bald" to BALD.prefix,
52+
"skos" to SKOS.uri,
53+
"dct" to DCTerms.getURI()
54+
)
55+
assertEquals(expected, result)
56+
}
57+
58+
@Test
59+
fun prefixMapping_multipleContexts_returnsCombinedPrefixMapping() {
60+
val contexts = listOf(
61+
PrefixMapping.Factory.create()
62+
.setNsPrefix("skos", "http://example.org/skos/")
63+
.setNsPrefix("dct", DCTerms.getURI()),
64+
PrefixMapping.Factory.create()
65+
.setNsPrefix("vcard", VCARD.uri)
66+
.setNsPrefix("dct", DCTerms.getURI())
67+
)
68+
val contextBa = ContextBinaryArray.create(ba, contexts)
69+
val result = contextBa.prefixMapping.nsPrefixMap
70+
val expected = mapOf(
71+
"bald" to BALD.prefix,
72+
"skos" to SKOS.uri,
73+
"dct" to DCTerms.getURI(),
74+
"vcard" to VCARD.uri
75+
)
76+
assertEquals(expected, result)
77+
}
78+
79+
@Test
80+
fun create_multipleContexts_withConflicts_throwsException() {
81+
val contexts = listOf(
82+
PrefixMapping.Factory.create()
83+
.setNsPrefix("skos", "http://example.org/skos/")
84+
.setNsPrefix("dct", DCTerms.getURI()),
85+
PrefixMapping.Factory.create()
86+
.setNsPrefix("skos", SKOS.uri)
87+
.setNsPrefix("dct", DCTerms.getURI())
88+
)
89+
val iae = assertThrows<IllegalArgumentException> {
90+
ContextBinaryArray.create(ba, contexts)
91+
}
92+
assertEquals("The namespace prefixes [skos] have conflicting definitions in contexts.", iae.message)
93+
}
94+
}

binary-array-ld-lib/src/test/kotlin/net/bald/model/ModelBinaryArrayConverterTest.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import net.bald.Var
99
import net.bald.vocab.BALD
1010
import org.apache.jena.rdf.model.Model
1111
import org.apache.jena.shared.PrefixMapping
12+
import org.apache.jena.vocabulary.DCTerms
1213
import org.apache.jena.vocabulary.RDF
1314
import org.apache.jena.vocabulary.SKOS
1415
import org.junit.jupiter.api.*
@@ -38,16 +39,19 @@ class ModelBinaryArrayConverterTest {
3839
val prefix = PrefixMapping.Factory.create()
3940
.setNsPrefix("bald", BALD.prefix)
4041
.setNsPrefix("skos", SKOS.uri)
42+
.setNsPrefix("dct", DCTerms.NS)
4143
val ba = mock<BinaryArray> {
4244
on { uri } doReturn "http://test.binary-array-ld.net/example"
4345
on { this.root } doReturn root
4446
on { prefixMapping } doReturn prefix
4547
}
48+
4649
val model = convert(ba)
4750

4851
ModelVerifier(model).apply {
4952
prefix("bald", BALD.prefix)
5053
prefix("skos", SKOS.uri)
54+
prefix("dct", DCTerms.NS)
5155
resource("http://test.binary-array-ld.net/example") {
5256
statement(RDF.type, BALD.Container)
5357
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/")) {

binary-array-ld-netcdf/src/main/kotlin/net/bald/netcdf/NetCdfBinaryArray.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class NetCdfBinaryArray(
4343
private fun prefixSourceName(): String? {
4444
return file.findGlobalAttribute(Attribute.prefix)?.let { attr ->
4545
attr.stringValue
46-
?: throw IllegalStateException("Global prefix attribute ${Attribute.prefix} has a non-string value.")
46+
?: throw IllegalStateException("Global prefix attribute ${Attribute.prefix} must have a string value.")
4747
}
4848
}
4949

0 commit comments

Comments
 (0)