Skip to content

Commit

Permalink
fix (core): Introduce List of Value in Thing
Browse files Browse the repository at this point in the history
  • Loading branch information
vorburger committed Feb 25, 2024
1 parent 49715d4 commit 77545b1
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 34 deletions.
42 changes: 35 additions & 7 deletions common/rdf/src/main/java/dev/enola/rdf/RdfThingConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import org.eclipse.rdf4j.model.BNode;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.base.CoreDatatype;

import java.util.ArrayList;
Expand Down Expand Up @@ -81,7 +80,24 @@ public Stream<Thing.Builder> convert(Model input) {

} else throw new UnsupportedOperationException("TODO: " + subject);

convert(thing, statement, deferred);
// The goal of this is to turn an RDF Object List into a Thing List Value
var predicate = statement.getPredicate();
var statements = input.filter(subject, predicate, null);
if (statements.size() == 1) {
convertAndPut(thing, predicate, statement.getObject(), deferred);
var value = convert(thing, predicate, statement.getObject(), deferred);
thing.putFields(predicate.stringValue(), value.build());
} else {
var valueList = dev.enola.thing.Value.List.newBuilder();
for (var subStatement : statements) {
var object = subStatement.getObject();
var value = convert(thing, predicate, object, deferred);
valueList.addValues(value);
}
var value = Value.newBuilder().setList(valueList);
thing.putFields(statement.getPredicate().stringValue(), value.build());
}
// TODO Does this really work, as-is? Won't it repeat (duplicate) values?
}

for (var action : deferred) {
Expand All @@ -91,9 +107,21 @@ public Stream<Thing.Builder> convert(Model input) {
return roots.values().stream();
}

private static void convert(
Builder thing, Statement statement, List<Consumer<Map<String, Builder>>> deferred) {
org.eclipse.rdf4j.model.Value rdfValue = statement.getObject();
// TODO Remove this again?
private static void convertAndPut(
Builder thing,
IRI predicate,
org.eclipse.rdf4j.model.Value object,
List<Consumer<Map<String, Builder>>> deferred) {
var value = convert(thing, predicate, object, deferred);
thing.putFields(predicate.stringValue(), value.build());
}

private static dev.enola.thing.Value.Builder convert(
Builder thing,
IRI predicate,
org.eclipse.rdf4j.model.Value rdfValue,
List<Consumer<Map<String, Builder>>> deferred) {
var value = Value.newBuilder();
if (rdfValue.isIRI()) {
value.setLink(Link.newBuilder().setIri(rdfValue.stringValue()));
Expand Down Expand Up @@ -127,7 +155,7 @@ private static void convert(
throw new IllegalStateException(
bNodeID + " not found: " + map.keySet());
value.setStruct(containedThing);
thing.putFields(statement.getPredicate().stringValue(), value.build());
thing.putFields(predicate.stringValue(), value.build());
});

} else if (rdfValue.isTriple()) {
Expand All @@ -137,6 +165,6 @@ private static void convert(
throw new UnsupportedOperationException("TODO: Resource");
}

thing.putFields(statement.getPredicate().stringValue(), value.build());
return value;
}
}
37 changes: 27 additions & 10 deletions common/rdf/src/main/java/dev/enola/rdf/ThingRdfConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
*/
package dev.enola.rdf;

import static java.util.Collections.singleton;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;

import dev.enola.common.convert.ConversionException;
import dev.enola.common.convert.Converter;
Expand Down Expand Up @@ -91,9 +94,10 @@ private void convertInto(
}
for (var field : from.getFieldsMap().entrySet()) {
IRI predicate = vf.createIRI(field.getKey());
var object = convert(field.getValue(), containedThings);
Statement statement = vf.createStatement(subject, predicate, object);
into.handleStatement(statement);
for (var object : convert(field.getValue(), containedThings)) {
Statement statement = vf.createStatement(subject, predicate, object);
into.handleStatement(statement);
}
}

for (var containedThing : containedThings.entrySet()) {
Expand All @@ -106,21 +110,22 @@ private void convertInto(
}
}

private org.eclipse.rdf4j.model.Value convert(
private Iterable<org.eclipse.rdf4j.model.Value> convert(
dev.enola.thing.Value value, Map<BNode, Thing> containedThings) {
return switch (value.getKindCase()) {
case LINK -> vf.createIRI(value.getLink().getIri());
case LINK -> singleton(vf.createIRI(value.getLink().getIri()));

case STRING -> vf.createLiteral(value.getString());
case STRING -> singleton(vf.createLiteral(value.getString()));

case LITERAL -> {
var literal = value.getLiteral();
yield vf.createLiteral(literal.getValue(), vf.createIRI(literal.getDatatype()));
yield singleton(
vf.createLiteral(literal.getValue(), vf.createIRI(literal.getDatatype())));
}

case LANG_STRING -> {
LangString langString = value.getLangString();
yield vf.createLiteral(langString.getText(), langString.getLang());
yield singleton(vf.createLiteral(langString.getText(), langString.getLang()));
}

case STRUCT -> {
Expand All @@ -133,10 +138,22 @@ private org.eclipse.rdf4j.model.Value convert(
bNode = vf.createBNode();
}
containedThings.put(bNode, containedThing);
yield bNode;
yield singleton(bNode);
}

// case LIST -> throw new UnsupportedOperationException("TODO");
case LIST -> {
// TODO value.getList().getValuesList().stream().map(eValue -> convert(eValue,?
var enolaValues = value.getList().getValuesList();
var rdfValues =
ImmutableList.<org.eclipse.rdf4j.model.Value>builderWithExpectedSize(
enolaValues.size());
for (var enolaValue : enolaValues) {
var rdfValue = convert(enolaValue, containedThings);
// Not 100% sure if addAll() is "correct" here...
rdfValues.addAll(rdfValue);
}
yield rdfValues.build();
}

case KIND_NOT_SET -> throw new IllegalArgumentException(value.toString());
};
Expand Down
36 changes: 29 additions & 7 deletions common/rdf/src/test/java/dev/enola/rdf/RdfThingConverterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,37 +38,59 @@ public class RdfThingConverterTest {

private final ReadableResource turtle =
new ClasspathResource("picasso.turtle", RdfMediaType.TURTLE);
private final ReadableResource yaml = new ClasspathResource("picasso.thing.yaml", YAML_UTF_8);

private final ReadableResource picassoYaml =
new ClasspathResource("picasso.thing.yaml", YAML_UTF_8);

private final ReadableResource daliYaml = //
new ClasspathResource("dali.thing.yaml", YAML_UTF_8);

private final ProtoIO protoReader = new ProtoIO();
private final RdfReaderConverter rdfReader = new RdfReaderConverter();
private final RdfThingConverter rdfToThingConverter = new RdfThingConverter();
private final ThingRdfConverter thingToRdfConverter = new ThingRdfConverter();

private Model rdf;
private Thing thing;
private Thing picassoThing;
private Thing daliThing;

@Before
public void before() throws ConversionException, IOException {
rdf = rdfReader.convert(turtle);
thing = protoReader.read(yaml, Thing.newBuilder(), Thing.class);
picassoThing = protoReader.read(picassoYaml, Thing.newBuilder(), Thing.class);
daliThing = protoReader.read(daliYaml, Thing.newBuilder(), Thing.class);
}

@Test
public void rdfToThing() throws ConversionException, IOException {
public void rdfToPicassoThing() throws ConversionException, IOException {
var actualThings = rdfToThingConverter.convertToList(rdf);
var expectedThing = thing;
var expectedThing = picassoThing;
ProtoTruth.assertThat(actualThings.get(1).build()).isEqualTo(expectedThing);
}

@Test
public void thingToRDF() throws ConversionException {
var actualRDF = thingToRdfConverter.convert(thing);
public void rdfToDaliThing() throws ConversionException, IOException {
var actualThings = rdfToThingConverter.convertToList(rdf);
var expectedThing = daliThing;
ProtoTruth.assertThat(actualThings.get(0).build()).isEqualTo(expectedThing);
}

@Test
public void picassoThingToRDF() throws ConversionException {
var actualRDF = thingToRdfConverter.convert(picassoThing);
rdf.remove(Values.iri("http://example.enola.dev/Dalí"), null, null);
var expectedRDF = rdf;
ModelSubject.assertThat(actualRDF).isEqualTo(expectedRDF);
}

@Test
public void daliThingToRDF() throws ConversionException {
var actualRDF = thingToRdfConverter.convert(daliThing);
rdf.remove(Values.iri("http://example.enola.dev/Picasso"), null, null);
var expectedRDF = rdf;
ModelSubject.assertThat(actualRDF).isEqualTo(expectedRDF);
}

@Test
public void messageToRDF() {
// TODO Implement messageToRDF(), via MessageToThingConverter
Expand Down
21 changes: 21 additions & 0 deletions common/rdf/src/test/resources/dali.thing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright 2024 The Enola <https://enola.dev> Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

iri: http://example.enola.dev/Dalí
fields:
http://xmlns.com/foaf/0.1/firstName:
{ list: { values: [{ string: "Salvador" }, { string: "Domingo" }, { string: "Felipe" }, { string: "Jacinto" }] } }
http://www.w3.org/1999/02/22-rdf-syntax-ns#type: { link: { iri: "http://example.enola.dev/Artist" } }
19 changes: 9 additions & 10 deletions common/thing/thing.proto
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ message Value {
// https://github.com/enola-dev/enola/pull/540...
LangString lang_string = 4;

// Sub-structure (contained) Thing.
Thing struct = 18;

// List of Values.
List list = 19;

// https://protobuf.dev/programming-guides/proto3/#scalar
// TODO Reconsider if this is really needed?! By who, for what?
// bytes bytes = 4;
Expand All @@ -72,13 +78,6 @@ message Value {
// double double = 15;
// float float = 16;
// bool bool = 17;

Thing struct = 18;

// TODO Do we actually need List? That's just a stream of Thing, no?
// TODO Inline? repeated Thing things = 18;
// TODO Never has an IRI? Maybe just repeated Value values = 18;
// List list = 19;
}

message Link {
Expand Down Expand Up @@ -114,9 +113,9 @@ message Value {
string lang = 2;
}

// message List {
// repeated Thing entries = 1;
// }
message List {
repeated Value values = 1;
}
}

// TODO Allow "uint64 id" instatead string IRIs (or all Value?), similar to
Expand Down

0 comments on commit 77545b1

Please sign in to comment.