Skip to content

Commit 1303a92

Browse files
8 - Identity and root vars (#19)
* 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 * Add file container, refactor * Add file container to tests * Fix tests * Changed test data to CDL format * Fix Java version on demo * ADD name and subgroups to container concept * Enhance binary array converter to describe subgroups * Fix test consistency
1 parent c6d9f86 commit 1303a92

File tree

26 files changed

+878
-5
lines changed

26 files changed

+878
-5
lines changed

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ RDF representations are provided by [Apache Jena](https://jena.apache.org/).
1111
* **binary-array-ld-test** Common test utilities used by other modules.
1212
* **binary-array-ld-demo** Demonstrations of BALD API usage.
1313

14+
## Development
15+
16+
Note that, in order to run the automated tests for this project,
17+
the `ncgen` command line tool must be available on your system.
18+
1419
## Usage
1520

1621
This project can be used either as a library or as a command line application.
@@ -60,7 +65,29 @@ try (OutputStream output = new FileOutputStream("/path/to/output.ttl")) {
6065
```
6166

6267
### Command Line Interface
63-
( TODO )
68+
69+
To use the BALD CLI, build the project using `mvn clean package` on the root directory.
70+
Then, you can run the JAR located at `binary-array-ld-cli/target/bald-cli.jar` using the `java-jar` command.
71+
72+
The application accepts arguments in the following format:
73+
```
74+
java -jar binary-array-ld-cli/target/bald-cli.jar [options] inputFile [outputFile]
75+
```
76+
Where `inputFile` is the location of the NetCDF file to convert,
77+
and `outputFile` is the location of the file in which to output the RDF graph.
78+
If you don't specify an `outputFile`, the graph will be printed on the command line.
79+
80+
You can also supply various options.
81+
Use the `-h` or `--help` option to emit full documentation for the available options.
82+
```
83+
java -jar binary-array-ld-cli/target/bald-cli.jar -h
84+
```
85+
86+
#### Example
87+
From the `binary-array-ld-cli/target` directory:
88+
```
89+
java -jar bald-cli.jar --uri http://test.binary-array-ld.net/example /path/to/netcdf.nc /path/to/graph.ttl
90+
```
6491

6592

6693

binary-array-ld-cli/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
<goal>single</goal>
5656
</goals>
5757
<configuration>
58+
<finalName>bald-cli</finalName>
59+
<appendAssemblyId>false</appendAssemblyId>
5860
<archive>
5961
<manifest>
6062
<mainClass>net.bald.BinaryArrayConvertCliKt</mainClass>
Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,65 @@
11
package net.bald
22

3+
import net.bald.model.ModelBinaryArrayConverter
4+
import net.bald.netcdf.NetCdfBinaryArray
5+
import org.apache.commons.cli.DefaultParser
6+
import org.apache.commons.cli.HelpFormatter
7+
import org.apache.commons.cli.Options
8+
import java.io.File
9+
import java.io.OutputStream
10+
import kotlin.system.exitProcess
11+
312
/**
413
* Command Line Interface for converting NetCDF metadata to Linked Data graphs.
514
*/
615
class BinaryArrayConvertCli {
16+
private val opts = Options().apply {
17+
this.addOption("u", "uri", true, "The URI which identifies the dataset.")
18+
this.addOption("h", "help", false, "Show help.")
19+
}
20+
721
fun run(vararg args: String) {
8-
TODO()
22+
val cmdOpts = options(opts, *args)
23+
if (cmdOpts.help) {
24+
help()
25+
} else {
26+
try {
27+
doRun(cmdOpts)
28+
} catch (e: Exception) {
29+
help()
30+
throw e
31+
}
32+
}
33+
}
34+
35+
private fun doRun(opts: CommandLineOptions) {
36+
val inputLoc = opts.inputLoc ?: throw IllegalArgumentException("First argument is required: NetCDF file to convert.")
37+
val ba = NetCdfBinaryArray.create(inputLoc, opts.uri)
38+
val model = ba.use(ModelBinaryArrayConverter::convert)
39+
40+
modelOutput(opts.outputLoc).use { output ->
41+
model.write(output, "ttl")
42+
}
43+
}
44+
45+
private fun options(opts: Options, vararg args: String): CommandLineOptions {
46+
return DefaultParser().parse(opts, args).let(::CommandLineOptions)
47+
}
48+
49+
private fun help() {
50+
HelpFormatter().printHelp("[options] inputFile [outputFile]", opts)
51+
}
52+
53+
private fun modelOutput(outputLoc: String?): OutputStream {
54+
return outputLoc?.let(::File)?.outputStream() ?: System.out
955
}
1056
}
1157

1258
fun main(args: Array<String>) {
13-
BinaryArrayConvertCli().run(*args)
59+
try {
60+
BinaryArrayConvertCli().run(*args)
61+
} catch (e: Exception) {
62+
println("Conversion failed due to error: ${e.message}")
63+
exitProcess(1)
64+
}
1465
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package net.bald
2+
3+
import org.apache.commons.cli.CommandLine
4+
5+
class CommandLineOptions(
6+
private val cmd: CommandLine,
7+
) {
8+
val inputLoc: String? get() {
9+
return cmd.args.getOrNull(0)
10+
}
11+
val outputLoc: String? get() = cmd.args.getOrNull(1)
12+
val uri: String? get() = cmd.getOptionValue("uri")
13+
val help: Boolean get() = cmd.hasOption("help")
14+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package net.bald
2+
3+
import bald.model.ModelVerifier
4+
import bald.netcdf.CdlConverter.convertToNetCdf
5+
import net.bald.vocab.BALD
6+
import org.apache.jena.rdf.model.ModelFactory.createDefaultModel
7+
import org.apache.jena.vocabulary.RDF
8+
import org.junit.jupiter.api.Test
9+
import org.junit.jupiter.api.assertThrows
10+
import kotlin.test.assertEquals
11+
12+
/**
13+
* Integration test for [BinaryArrayConvertCli].
14+
*/
15+
class BinaryArrayConvertCliTest {
16+
private fun run(vararg args: String) {
17+
BinaryArrayConvertCli().run(*args)
18+
}
19+
20+
@Test
21+
fun run_withoutInputFile_fails() {
22+
val iae = assertThrows<IllegalArgumentException> {
23+
run()
24+
}
25+
assertEquals("First argument is required: NetCDF file to convert.", iae.message)
26+
}
27+
28+
@Test
29+
fun run_withHelp_doesNotValidate() {
30+
run("-h")
31+
}
32+
33+
@Test
34+
fun run_withoutUri_outputsToFileWithInputFileUri() {
35+
val inputFile = convertToNetCdf("/netcdf/identity.cdl")
36+
val inputFileUri = inputFile.toPath().toUri().toString()
37+
val outputFile = createTempFile()
38+
run(inputFile.absolutePath, outputFile.absolutePath)
39+
40+
val model = createDefaultModel().read(outputFile.toURI().toString(), "ttl")
41+
ModelVerifier(model).apply {
42+
resource(inputFileUri) {
43+
statement(RDF.type, BALD.Container)
44+
statement(BALD.contains, model.createResource("$inputFileUri/")) {
45+
statement(RDF.type, BALD.Container)
46+
statement(BALD.contains, model.createResource("$inputFileUri/var0")) {
47+
statement(RDF.type, BALD.Resource)
48+
}
49+
statement(BALD.contains, model.createResource("$inputFileUri/var1")) {
50+
statement(RDF.type, BALD.Resource)
51+
}
52+
}
53+
}
54+
}
55+
}
56+
57+
@Test
58+
fun run_withUri_withOutputFile_outputsToFile() {
59+
val inputFile = convertToNetCdf("/netcdf/identity.cdl")
60+
val outputFile = createTempFile()
61+
run("--uri", "http://test.binary-array-ld.net/example", inputFile.absolutePath, outputFile.absolutePath)
62+
63+
val model = createDefaultModel().read(outputFile.toURI().toString(), "ttl")
64+
ModelVerifier(model).apply {
65+
resource("http://test.binary-array-ld.net/example") {
66+
statement(RDF.type, BALD.Container)
67+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/")) {
68+
statement(RDF.type, BALD.Container)
69+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/var0")) {
70+
statement(RDF.type, BALD.Resource)
71+
}
72+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/var1")) {
73+
statement(RDF.type, BALD.Resource)
74+
}
75+
}
76+
}
77+
}
78+
}
79+
80+
@Test
81+
fun run_withSubgroups_outputsWithSubgroups() {
82+
val inputFile = convertToNetCdf("/netcdf/identity-subgroups.cdl")
83+
val outputFile = createTempFile()
84+
run("--uri", "http://test.binary-array-ld.net/example", inputFile.absolutePath, outputFile.absolutePath)
85+
86+
val model = createDefaultModel().read(outputFile.toURI().toString(), "ttl")
87+
ModelVerifier(model).apply {
88+
resource("http://test.binary-array-ld.net/example") {
89+
statement(RDF.type, BALD.Container)
90+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/")) {
91+
statement(RDF.type, BALD.Container)
92+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/group0")) {
93+
statement(RDF.type, BALD.Container)
94+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/group0/var2")) {
95+
statement(RDF.type, BALD.Resource)
96+
}
97+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/group0/var3")) {
98+
statement(RDF.type, BALD.Resource)
99+
}
100+
}
101+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/group1")) {
102+
statement(RDF.type, BALD.Container)
103+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/group1/var4")) {
104+
statement(RDF.type, BALD.Resource)
105+
}
106+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/group1/var5")) {
107+
statement(RDF.type, BALD.Resource)
108+
}
109+
}
110+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/var0")) {
111+
statement(RDF.type, BALD.Resource)
112+
}
113+
statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/var1")) {
114+
statement(RDF.type, BALD.Resource)
115+
}
116+
}
117+
}
118+
}
119+
}
120+
}

binary-array-ld-demo/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@
1111

1212
<artifactId>binary-array-ld-demo</artifactId>
1313
<version>1.0.0-SNAPSHOT</version>
14+
<build>
15+
<plugins>
16+
<plugin>
17+
<groupId>org.apache.maven.plugins</groupId>
18+
<artifactId>maven-compiler-plugin</artifactId>
19+
<configuration>
20+
<source>1.8</source>
21+
<target>1.8</target>
22+
</configuration>
23+
</plugin>
24+
</plugins>
25+
</build>
1426

1527
<name>Binary Array Linked Data - Demos</name>
1628
<description>Examples of Binary Array to Linked Data API usage.</description>

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,21 @@ package net.bald
55
* See https://www.opengis.net/def/binary-array-ld/Container
66
*/
77
interface Container {
8+
/**
9+
* The local name of the container, if it has one.
10+
* The root container may have no name or an empty name.
11+
*/
12+
val name: String?
13+
814
/**
915
* Obtain the variables associates with this container.
1016
* @return The variables.
1117
*/
1218
fun vars(): Sequence<Var>
19+
20+
/**
21+
* Obtain the sub-containers of this container.
22+
* @return The containers.
23+
*/
24+
fun subContainers(): Sequence<Container>
1325
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package net.bald.model
2+
3+
import net.bald.BinaryArray
4+
import net.bald.vocab.BALD
5+
import org.apache.jena.rdf.model.Model
6+
7+
class ModelBinaryArrayBuilder(
8+
private val model: Model,
9+
private val containerFct: ModelContainerBuilder.Factory
10+
) {
11+
fun addBinaryArray(ba: BinaryArray) {
12+
val baRes = model.createResource(ba.uri, BALD.Container)
13+
containerFct.forParent(baRes).addContainer(ba.root)
14+
}
15+
16+
class Factory(
17+
private val containerFct: ModelContainerBuilder.Factory
18+
) {
19+
fun forModel(model: Model): ModelBinaryArrayBuilder {
20+
return ModelBinaryArrayBuilder(model, containerFct)
21+
}
22+
}
23+
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,21 @@ import org.apache.jena.rdf.model.ModelFactory
88
* API for converting a [BinaryArray] to a linked data [Model].
99
*/
1010
object ModelBinaryArrayConverter {
11+
private val modelFct = run {
12+
val varFct = ModelVarBuilder.Factory()
13+
val containerFct = ModelContainerBuilder.Factory(varFct)
14+
ModelBinaryArrayBuilder.Factory(containerFct)
15+
}
16+
1117
/**
1218
* Convert the given binary array metadata into a linked data model.
1319
* @param ba The binary array to convert.
1420
* @return The resulting model.
1521
*/
1622
@JvmStatic
1723
fun convert(ba: BinaryArray): Model {
18-
TODO()
24+
val model = ModelFactory.createDefaultModel()
25+
modelFct.forModel(model).addBinaryArray(ba)
26+
return model
1927
}
2028
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package net.bald.model
2+
3+
import net.bald.vocab.BALD
4+
import net.bald.Container
5+
import org.apache.jena.rdf.model.Resource
6+
7+
open class ModelContainerBuilder(
8+
private val parent: Resource,
9+
private val varFct: ModelVarBuilder.Factory
10+
) {
11+
open fun addContainer(container: Container) {
12+
val containerUri = containerUri(container)
13+
val containerRes = parent.model.createResource(containerUri, BALD.Container)
14+
buildSubgroups(container, containerRes)
15+
buildVars(container, containerRes)
16+
parent.addProperty(BALD.contains, containerRes)
17+
}
18+
19+
private fun containerUri(container: Container): String {
20+
val parentUri = parent.uri
21+
val prefix = if (parentUri.endsWith('/')) parentUri else "$parentUri/"
22+
return prefix + (container.name ?: "")
23+
}
24+
25+
private fun buildSubgroups(container: Container, containerRes: Resource) {
26+
val builder = ModelContainerBuilder(containerRes, varFct)
27+
container.subContainers().forEach(builder::addContainer)
28+
}
29+
30+
private fun buildVars(container: Container, containerRes: Resource) {
31+
varFct.forContainer(containerRes).apply {
32+
container.vars().forEach(::addVar)
33+
}
34+
}
35+
36+
open class Factory(
37+
private val varFct: ModelVarBuilder.Factory
38+
) {
39+
open fun forParent(parent: Resource): ModelContainerBuilder {
40+
return ModelContainerBuilder(parent, varFct)
41+
}
42+
}
43+
}
44+
45+
46+
47+

0 commit comments

Comments
 (0)