Skip to content

Commit 77208e0

Browse files
Feature/11.2 var reference multi (#28)
* Add single and multi valued variable reference parser * Add multi variable parsing on NetCDF attribute * Add multiple variable references to model output * Add integration test * Fix resource references
1 parent 55041d4 commit 77208e0

File tree

13 files changed

+270
-15
lines changed

13 files changed

+270
-15
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,20 @@ class BinaryArrayConvertCliTest {
359359
resource("http://test.binary-array-ld.net/example") {
360360
statement(RDF.type, BALD.Container)
361361
statement(BALD.contains, createResource("http://test.binary-array-ld.net/example/")) {
362+
statement(TestVocab.orderedVar) {
363+
statement(RDF.first, createResource("http://test.binary-array-ld.net/example/var0"))
364+
statement(RDF.rest) {
365+
statement(RDF.first, createResource("http://test.binary-array-ld.net/example/foo/bar/var2"))
366+
statement(RDF.rest) {
367+
statement(RDF.first, createResource("http://test.binary-array-ld.net/example/baz/var3"))
368+
statement(RDF.rest, RDF.nil)
369+
}
370+
}
371+
}
362372
statement(TestVocab.rootVar, createResource("http://test.binary-array-ld.net/example/var0"))
373+
statement(TestVocab.unorderedVar, createResource("http://test.binary-array-ld.net/example/foo/bar/var2"))
374+
statement(TestVocab.unorderedVar, createResource("http://test.binary-array-ld.net/example/foo/var1"))
375+
statement(TestVocab.unorderedVar, createResource("http://test.binary-array-ld.net/example/var0"))
363376
statement(RDF.type, BALD.Container)
364377
statement(SKOS.prefLabel, createPlainLiteral("Variable reference metadata example"))
365378
statement(BALD.contains, createResource("http://test.binary-array-ld.net/example/baz")) {

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

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

33
import net.bald.Attribute
4+
import org.apache.jena.rdf.model.RDFList
5+
import org.apache.jena.rdf.model.RDFNode
46
import org.apache.jena.rdf.model.Resource
57

68
open class ModelAttributeBuilder(
79
private val resource: Resource
810
) {
911
open fun addAttribute(attr: Attribute) {
1012
val prop = resource.model.createProperty(attr.uri)
11-
attr.values.forEach { value ->
13+
attr.values.asSequence().map(::inModel).forEach { value ->
1214
resource.addProperty(prop, value)
1315
}
1416
}
1517

18+
private fun inModel(node: RDFNode): RDFNode {
19+
return if (node is RDFList) {
20+
// copy the list from its original model to the model being built
21+
node.iterator().let(resource.model::createList)
22+
} else {
23+
node
24+
}
25+
}
26+
1627
open class Factory {
1728
open fun forResource(resource: Resource): ModelAttributeBuilder {
1829
return ModelAttributeBuilder(resource)

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.nhaarman.mockitokotlin2.stub
77
import net.bald.Attribute
88
import org.apache.jena.rdf.model.ModelFactory
99
import org.apache.jena.rdf.model.ResourceFactory.*
10+
import org.apache.jena.vocabulary.RDF
1011
import org.apache.jena.vocabulary.RDFS
1112
import org.junit.jupiter.api.*
1213

@@ -57,7 +58,6 @@ class ModelAttributeBuilderTest {
5758
}
5859
}
5960

60-
6161
@Test
6262
fun addAttribute_resourceValue_addsStatement() {
6363
val value = createResource("http://test.binary-array-ld.net/label")
@@ -69,4 +69,29 @@ class ModelAttributeBuilderTest {
6969
statement(RDFS.label, value)
7070
}
7171
}
72+
73+
@Test
74+
fun addAttribute_rdfListValue_addsList() {
75+
val value = ModelFactory.createDefaultModel().createList(
76+
createResource("http://test.binary-array-ld.net/var0"),
77+
createResource("http://test.binary-array-ld.net/var1"),
78+
createResource("http://test.binary-array-ld.net/var2")
79+
)
80+
attr.stub {
81+
on { values } doReturn listOf(value)
82+
}
83+
builder.addAttribute(attr)
84+
ResourceVerifier(resource).statements {
85+
statement(RDFS.label) {
86+
statement(RDF.first, createResource("http://test.binary-array-ld.net/var0"))
87+
statement(RDF.rest) {
88+
statement(RDF.first, createResource("http://test.binary-array-ld.net/var1"))
89+
statement(RDF.rest) {
90+
statement(RDF.first, createResource("http://test.binary-array-ld.net/var2"))
91+
statement(RDF.rest, RDF.nil)
92+
}
93+
}
94+
}
95+
}
96+
}
7297
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class NetCdfAttribute(
1717
override val uri: String get() = prop.uri
1818

1919
override val values: List<RDFNode> get() {
20-
return rawValues()?.map(::node)?.toList() ?: emptyList()
20+
return rawValues()?.flatMap(::nodes)?.toList() ?: emptyList()
2121
}
2222

2323
private fun rawValues(): Sequence<String>? {
@@ -32,8 +32,8 @@ class NetCdfAttribute(
3232
}
3333
}
3434

35-
private fun node(value: String): RDFNode {
36-
return parent.parseRdfNode(prop, value)
35+
private fun nodes(value: String): List<RDFNode> {
36+
return parent.parseRdfNodes(prop, value)
3737
}
3838

3939
override fun toString(): String {

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ abstract class NetCdfContainer(
2727
abstract val uriParser: UriParser
2828
abstract fun childUri(name: String): String
2929

30+
private val refParser: ReferenceValueParser get() {
31+
return ReferenceValueParser(this)
32+
}
33+
3034
override fun vars(): Sequence<Var> {
3135
return group.variables.asSequence().filter(::acceptVar).map(::variable)
3236
}
@@ -73,13 +77,13 @@ abstract class NetCdfContainer(
7377
?: childUri(name).let(ResourceFactory::createProperty)
7478
}
7579

76-
fun parseRdfNode(prop: Property, value: String): RDFNode {
77-
return uriParser.parse(value)?.let(::createResource)
78-
?: context.resource(value)
80+
fun parseRdfNodes(prop: Property, value: String): List<RDFNode> {
81+
return uriParser.parse(value)?.let(::createResource)?.let(::listOf)
82+
?: context.resource(value)?.let(::listOf)
7983
?: prop.takeIf(context::isReferenceProperty)?.let {
80-
NetCdfPath.parse(value).locateVar(this)?.uri?.let(ResourceFactory::createResource)
84+
refParser.parse(value)
8185
}
82-
?: createPlainLiteral(value)
86+
?: createPlainLiteral(value).let(::listOf)
8387
}
8488

8589
override fun toString(): String {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package net.bald.netcdf
2+
3+
import org.apache.jena.rdf.model.ModelFactory
4+
import org.apache.jena.rdf.model.Resource
5+
import org.apache.jena.rdf.model.ResourceFactory
6+
7+
class ReferenceValueParser(
8+
private val group: NetCdfContainer
9+
) {
10+
fun parse(value: String): List<Resource>? {
11+
return value.trim().let(::doParse)
12+
}
13+
14+
private fun doParse(value: String): List<Resource>? {
15+
val collector = if (value.startsWith('(') && value.endsWith(')')) {
16+
value.substringAfter('(').substringBeforeLast(')').let(ReferenceCollector::Ordered)
17+
} else {
18+
ReferenceCollector.Unordered(value)
19+
}
20+
21+
val nodes = split(collector.raw).map(::resolve).toList()
22+
val referenceNodes = nodes.filterNotNull()
23+
24+
return if (referenceNodes.size != nodes.size) null else {
25+
collector.collect(referenceNodes)
26+
}
27+
}
28+
29+
private fun split(values: String): Sequence<String> {
30+
return values.splitToSequence(' ')
31+
.filterNot(String::isEmpty)
32+
.map(String::trim)
33+
}
34+
35+
private fun resolve(value: String): Resource? {
36+
return NetCdfPath.parse(value).locateVar(group)?.uri?.let(ResourceFactory::createResource)
37+
}
38+
39+
private interface ReferenceCollector {
40+
val raw: String
41+
fun collect(nodes: List<Resource>): List<Resource>
42+
43+
class Unordered(
44+
override val raw: String
45+
): ReferenceCollector {
46+
override fun collect(nodes: List<Resource>): List<Resource> {
47+
return nodes
48+
}
49+
50+
}
51+
52+
class Ordered(
53+
override val raw: String
54+
): ReferenceCollector {
55+
override fun collect(nodes: List<Resource>): List<Resource> {
56+
val nodeIt = nodes.iterator()
57+
return ModelFactory.createDefaultModel().createList(nodeIt).let(::listOf)
58+
}
59+
}
60+
}
61+
}

binary-array-ld-netcdf/src/test/kotlin/net/bald/netcdf/AttributeValuesVerifier.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package net.bald.netcdf
22

3+
import bald.model.ResourceVerifier
34
import net.bald.Attribute
45
import org.apache.jena.rdf.model.RDFNode
56
import kotlin.test.assertEquals
@@ -20,4 +21,17 @@ class AttributeValuesVerifier(
2021
fail("Expected value $value on attribute $attr, but no more values were found.")
2122
}
2223
}
24+
25+
fun resource(verify: ResourceVerifier.() -> Unit) {
26+
if (valueIt.hasNext()) {
27+
val value = valueIt.next()
28+
if (value.isResource) {
29+
ResourceVerifier(value.asResource()).verify()
30+
} else {
31+
fail("Expected a resource value on attribute $attr, but found a literal resource instead.")
32+
}
33+
} else {
34+
fail("Expected a resource value on attribute $attr, but no more values were found.")
35+
}
36+
}
2337
}

binary-array-ld-netcdf/src/test/kotlin/net/bald/netcdf/NetCdfBinaryArrayTest.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,25 @@ class NetCdfBinaryArrayTest {
286286
attribute(BALD.isPrefixedBy.uri, createPlainLiteral("prefix_list"))
287287
attribute(SKOS.prefLabel.uri, createPlainLiteral("Variable reference metadata example"))
288288
attribute(TestVocab.rootVar.uri, createResource("http://test.binary-array-ld.net/var-ref.nc/var0"))
289+
attribute(TestVocab.unorderedVar.uri) {
290+
value(createResource("http://test.binary-array-ld.net/var-ref.nc/var0"))
291+
value(createResource("http://test.binary-array-ld.net/var-ref.nc/foo/var1"))
292+
value(createResource("http://test.binary-array-ld.net/var-ref.nc/foo/bar/var2"))
293+
}
294+
attribute(TestVocab.orderedVar.uri) {
295+
resource {
296+
statements {
297+
statement(RDF.first, createResource("http://test.binary-array-ld.net/var-ref.nc/var0"))
298+
statement(RDF.rest) {
299+
statement(RDF.first, createResource("http://test.binary-array-ld.net/var-ref.nc/foo/bar/var2"))
300+
statement(RDF.rest) {
301+
statement(RDF.first, createResource("http://test.binary-array-ld.net/var-ref.nc/baz/var3"))
302+
statement(RDF.rest, RDF.nil)
303+
}
304+
}
305+
}
306+
}
307+
}
289308
}
290309
vars {
291310
variable("http://test.binary-array-ld.net/var-ref.nc/var0")
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package net.bald.netcdf
2+
3+
import bald.netcdf.CdlConverter
4+
import org.apache.jena.rdf.model.RDFList
5+
import org.junit.jupiter.api.*
6+
import kotlin.test.assertEquals
7+
import kotlin.test.assertNotNull
8+
import kotlin.test.assertNull
9+
10+
class ReferenceValueParserTest {
11+
private val uri = "http://test.binary-array-ld.net/example"
12+
private val ba: NetCdfBinaryArray = fromCdl()
13+
private val parser = ReferenceValueParser(ba.root)
14+
15+
private fun fromCdl(): NetCdfBinaryArray {
16+
val file = CdlConverter.writeToNetCdf("/netcdf/paths.cdl")
17+
return NetCdfBinaryArray.create(file.absolutePath, uri)
18+
}
19+
20+
@Test
21+
fun singleValue_returnsResource() {
22+
val results = parser.parse("var0")
23+
assertNotNull(results) {
24+
assertEquals(1, it.size)
25+
assertEquals("$uri/var0", it.single().uri)
26+
}
27+
}
28+
29+
@Test
30+
fun singleValue_varNotLocated_returnsNull() {
31+
val results = parser.parse("varx")
32+
assertNull(results)
33+
}
34+
35+
@Test
36+
fun unorderedSet_returnsResources() {
37+
val results = parser.parse("var0 var1 foo/var2")
38+
assertNotNull(results) {
39+
assertEquals(3, it.size)
40+
assertEquals("$uri/var0", it[0].uri)
41+
assertEquals("$uri/var1", it[1].uri)
42+
assertEquals("$uri/foo/var2", it[2].uri)
43+
}
44+
}
45+
46+
@Test
47+
fun unorderedSet_withExtraWhitespace_returnsResources() {
48+
val results = parser.parse(" var0 var1 foo/var2 ")
49+
assertNotNull(results) {
50+
assertEquals(3, it.size)
51+
assertEquals("$uri/var0", it[0].uri)
52+
assertEquals("$uri/var1", it[1].uri)
53+
assertEquals("$uri/foo/var2", it[2].uri)
54+
}
55+
}
56+
57+
@Test
58+
fun unorderedSet_varNotLocated_returnsNull() {
59+
val results = parser.parse("var0 var1 varx")
60+
assertNull(results)
61+
}
62+
63+
@Test
64+
fun orderedList_returnsRdfList() {
65+
val results = parser.parse("(var0 var1 foo/var2)")
66+
assertNotNull(results) {
67+
assertEquals(1, it.size)
68+
val list = it.single() as RDFList
69+
val nodes = list.asJavaList()
70+
assertEquals("$uri/var0", nodes[0].asResource().uri)
71+
assertEquals("$uri/var1", nodes[1].asResource().uri)
72+
assertEquals("$uri/foo/var2", nodes[2].asResource().uri)
73+
}
74+
}
75+
76+
@Test
77+
fun orderedList_withExtraWhitespace_returnsRdfList() {
78+
val results = parser.parse(" ( var0 var1 foo/var2 ) ")
79+
assertNotNull(results) {
80+
assertEquals(1, it.size)
81+
val list = it.single() as RDFList
82+
val nodes = list.asJavaList()
83+
assertEquals("$uri/var0", nodes[0].asResource().uri)
84+
assertEquals("$uri/var1", nodes[1].asResource().uri)
85+
assertEquals("$uri/foo/var2", nodes[2].asResource().uri)
86+
}
87+
}
88+
89+
@Test
90+
fun orderedList_varNotLocated_returnsNull() {
91+
val results = parser.parse("(var0 var1 varx)")
92+
assertNull(results)
93+
}
94+
}

binary-array-ld-test/src/main/kotlin/bald/TestVocab.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ object TestVocab {
1515
val rootVar: Property = createProperty("${prefix}root_var")
1616
val parentVar: Property = createProperty("${prefix}parent_var")
1717
val siblingVar: Property = createProperty("${prefix}sibling_var")
18+
val orderedVar: Property = createProperty("${prefix}ordered_var")
19+
val unorderedVar: Property = createProperty("${prefix}unordered_var")
1820
}

0 commit comments

Comments
 (0)