Skip to content

Commit

Permalink
[Enhancement #206] Refactor Jena list handlers to support data proper…
Browse files Browse the repository at this point in the history
…ty referenced list values.

Signed-off-by: Martin Ledvinka <[email protected]>
  • Loading branch information
ledsoft committed Nov 26, 2023
1 parent d19c6d8 commit 9e738a5
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import static org.apache.jena.rdf.model.ResourceFactory.createProperty;
import static org.apache.jena.rdf.model.ResourceFactory.createResource;

abstract class AbstractListIterator {
abstract class AbstractListIterator<T, JT extends RDFNode> {

final StorageConnector connector;

Expand Down Expand Up @@ -106,9 +106,9 @@ void remove(Resource subject, Property property, RDFNode object) {
connector.remove(subject, property, object, context);
}

abstract Axiom<NamedResource> nextAxiom();
abstract Axiom<T> nextAxiom();

abstract NamedResource nextValue();
abstract T nextValue();

/**
* Only returns current node.
Expand Down Expand Up @@ -137,5 +137,5 @@ void removeWithoutReconnect() {
this.removed = true;
}

abstract void replace(Resource replacement);
abstract void replace(JT replacement);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
import java.util.Collections;
import java.util.List;

import static org.apache.jena.rdf.model.ResourceFactory.*;
import static org.apache.jena.rdf.model.ResourceFactory.createProperty;
import static org.apache.jena.rdf.model.ResourceFactory.createResource;
import static org.apache.jena.rdf.model.ResourceFactory.createStatement;

public class ReferencedListHandler {

Expand All @@ -46,7 +48,7 @@ public ReferencedListHandler(StorageConnector connector) {

List<Axiom<NamedResource>> loadList(ReferencedListDescriptor descriptor) {
final List<Axiom<NamedResource>> result = new ArrayList<>();
final AbstractListIterator it = new ReferencedListIterator(descriptor, connector);
final ReferencedListIterator<NamedResource> it = new ReferencedListIterator<>(descriptor, connector);
while (it.hasNext()) {
result.add(it.nextAxiom());
}
Expand Down Expand Up @@ -78,7 +80,8 @@ <V> void appendNewNodes(ReferencedListValueDescriptor<V> descriptor, int i, Reso
}

private <V> Resource appendNode(Resource previousNode, V value, Property link, Property hasContent,
String context, List<Statement> statements, ReferencedListValueDescriptor<V> descriptor) {
String context, List<Statement> statements,
ReferencedListValueDescriptor<V> descriptor) {
final Resource node = generateNewListNode(descriptor.getListOwner().getIdentifier(), context);
statements.add(createStatement(previousNode, link, node));
statements.add(createStatement(node, hasContent, JenaUtils.valueToRdfNode(descriptor.getNodeContent(), new Value<>(value))));
Expand All @@ -98,14 +101,13 @@ private Resource generateNewListNode(URI baseUri, String context) {
}

<V> void updateList(ReferencedListValueDescriptor<V> descriptor) {
final AbstractListIterator it = new ReferencedListIterator(descriptor, connector);
final ReferencedListIterator<V> it = new ReferencedListIterator<>(descriptor, connector);
int i = 0;
while (it.hasNext() && i < descriptor.getValues().size()) {
final V update = descriptor.getValues().get(i);
final NamedResource existing = it.nextValue();
final V existing = it.nextValue();
if (!existing.equals(update)) {
// TODO Replace with generic handling of an object, we do not know that it is a NamedResource
it.replace(createResource(update.toString()));
it.replace(JenaUtils.valueToRdfNode(descriptor.getNodeContent(), new Value<>(update)));
}
i++;
}
Expand All @@ -115,7 +117,7 @@ <V> void updateList(ReferencedListValueDescriptor<V> descriptor) {
}
}

private static void removeObsoleteNodes(AbstractListIterator it) {
private static void removeObsoleteNodes(ReferencedListIterator<?> it) {
while (it.hasNext()) {
it.nextValue();
it.removeWithoutReconnect();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import cz.cvut.kbss.ontodriver.descriptor.ReferencedListDescriptor;
import cz.cvut.kbss.ontodriver.exception.IntegrityConstraintViolatedException;
import cz.cvut.kbss.ontodriver.jena.connector.StorageConnector;
import cz.cvut.kbss.ontodriver.jena.util.JenaUtils;
import cz.cvut.kbss.ontodriver.model.*;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;

Expand All @@ -31,7 +33,7 @@
import static org.apache.jena.rdf.model.ResourceFactory.createProperty;
import static org.apache.jena.rdf.model.ResourceFactory.createStatement;

class ReferencedListIterator extends AbstractListIterator {
class ReferencedListIterator<T> extends AbstractListIterator<T, RDFNode> {

private final Property hasContent;
private final Assertion hasContentAssertion;
Expand All @@ -43,25 +45,25 @@ class ReferencedListIterator extends AbstractListIterator {
}

@Override
Axiom<NamedResource> nextAxiom() {
final NamedResource value = nextValue();
Axiom<T> nextAxiom() {
final T value = nextValue();
final NamedResource node = NamedResource.create(currentNode.getURI());
return new AxiomImpl<>(node, hasContentAssertion, new Value<>(value));
}

@Override
NamedResource nextValue() {
T nextValue() {
resolveNextListNode();
return NamedResource.create(resolveNodeContent().getURI());
final RDFNode content = resolveNodeContent();
return (T) (content.isResource() ? NamedResource.create(content.asResource().getURI()) : JenaUtils.literalToValue(content.asLiteral()));
}

private Resource resolveNodeContent() {
private RDFNode resolveNodeContent() {
final Collection<Statement> contentStatements;
contentStatements = connector.find(currentNode, hasContent, null, contexts());
verifyContentValueCount(contentStatements);
final Statement statement = contentStatements.iterator().next();
assert statement.getObject().isResource();
return statement.getObject().asResource();
return statement.getObject();
}

private void verifyContentValueCount(Collection<Statement> contentStatements) {
Expand All @@ -81,7 +83,7 @@ void removeWithoutReconnect() {
}

@Override
void replace(Resource replacement) {
void replace(RDFNode replacement) {
remove(currentNode, hasContent, null);
final Statement toAdd = createStatement(currentNode, hasContent, replacement);
connector.add(Collections.singletonList(toAdd), context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public SimpleListHandler(StorageConnector connector) {

List<Axiom<NamedResource>> loadList(SimpleListDescriptor descriptor) {
final List<Axiom<NamedResource>> result = new ArrayList<>();
final AbstractListIterator it = new SimpleListIterator(descriptor, connector);
final SimpleListIterator it = new SimpleListIterator(descriptor, connector);
while (it.hasNext()) {
result.add(it.nextAxiom());
}
Expand Down Expand Up @@ -79,7 +79,7 @@ private static Resource appendNode(Resource previous, Property property, NamedRe
}

void updateList(SimpleListValueDescriptor descriptor) {
final AbstractListIterator it = new SimpleListIterator(descriptor, connector);
final SimpleListIterator it = new SimpleListIterator(descriptor, connector);
int i = 0;
while (it.hasNext() && i < descriptor.getValues().size()) {
final NamedResource update = descriptor.getValues().get(i);
Expand All @@ -95,7 +95,7 @@ void updateList(SimpleListValueDescriptor descriptor) {
}
}

private static void removeObsoleteNodes(AbstractListIterator it) {
private static void removeObsoleteNodes(SimpleListIterator it) {
while (it.hasNext()) {
it.nextValue();
it.removeWithoutReconnect();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

import static org.apache.jena.rdf.model.ResourceFactory.createStatement;

class SimpleListIterator extends AbstractListIterator {
class SimpleListIterator extends AbstractListIterator<NamedResource, Resource> {

SimpleListIterator(SimpleListDescriptor descriptor, StorageConnector connector) {
super(descriptor, connector);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import cz.cvut.kbss.ontodriver.model.Axiom;
import cz.cvut.kbss.ontodriver.model.NamedResource;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.ResourceFactory;
import org.junit.jupiter.api.Test;
Expand All @@ -39,7 +40,7 @@
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

public abstract class ListIteratorTestBase<T extends AbstractListIterator, D extends ListDescriptor> {
public abstract class ListIteratorTestBase<T extends AbstractListIterator<NamedResource, ? extends RDFNode>, D extends ListDescriptor> {

static final Resource RESOURCE = createResource(Generator.generateUri().toString());
static final Property HAS_LIST = ResourceFactory.createProperty(Generator.generateUri().toString());
Expand All @@ -64,21 +65,21 @@ public void setUp() {
@Test
public void hasNextReturnsTrueForListHead() {
generateList();
final AbstractListIterator iterator = iterator();
final AbstractListIterator<NamedResource, ? extends RDFNode> iterator = iterator();
assertTrue(iterator.hasNext());
}

@Test
public void nextThrowsNoSuchElementWhenNoMoreElementsExist() {
final AbstractListIterator iterator = iterator();
final AbstractListIterator<NamedResource, ? extends RDFNode> iterator = iterator();
assertFalse(iterator.hasNext());
assertThrows(NoSuchElementException.class, iterator::nextAxiom);
}

@Test
public void nextAllowsToReadWholeList() {
final List<URI> list = generateList();
final AbstractListIterator iterator = iterator();
final AbstractListIterator<NamedResource, ? extends RDFNode> iterator = iterator();
final List<URI> actual = new ArrayList<>();
while (iterator.hasNext()) {
final Axiom<NamedResource> axiom = iterator.nextAxiom();
Expand All @@ -93,7 +94,7 @@ public void nextThrowsIntegrityConstraintViolationWhenMultipleNextNodesAreFound(
when(connectorMock.find(RESOURCE, HAS_LIST, null, Collections.emptySet())).thenReturn(
Arrays.asList(createStatement(RESOURCE, HAS_LIST, createResource()),
createStatement(RESOURCE, HAS_LIST, createResource())));
final AbstractListIterator iterator = iterator();
final AbstractListIterator<NamedResource, ? extends RDFNode> iterator = iterator();
final IntegrityConstraintViolatedException ex = assertThrows(IntegrityConstraintViolatedException.class,
iterator::nextAxiom);
assertThat(ex.getMessage(),
Expand All @@ -103,15 +104,15 @@ public void nextThrowsIntegrityConstraintViolationWhenMultipleNextNodesAreFound(
@Test
public void removeWithoutReconnectThrowsIllegalStateExceptionWhenNextWasNotCalledBefore() {
generateList();
final AbstractListIterator iterator = iterator();
final AbstractListIterator<NamedResource, ? extends RDFNode> iterator = iterator();
final IllegalStateException ex = assertThrows(IllegalStateException.class, iterator::removeWithoutReconnect);
assertThat(ex.getMessage(), containsString("Cannot call remove before calling next."));
}

@Test
public void removeWithoutReconnectThrowsIllegalStateExceptionWhenRemoveIsCalledTwiceOnElement() {
generateList();
final AbstractListIterator iterator = iterator();
final AbstractListIterator<NamedResource, ? extends RDFNode> iterator = iterator();
iterator.nextValue();
iterator.removeWithoutReconnect();
final IllegalStateException ex = assertThrows(IllegalStateException.class, iterator::removeWithoutReconnect);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
import cz.cvut.kbss.ontodriver.descriptor.ReferencedListDescriptorImpl;
import cz.cvut.kbss.ontodriver.descriptor.ReferencedListValueDescriptor;
import cz.cvut.kbss.ontodriver.jena.environment.Generator;
import cz.cvut.kbss.ontodriver.jena.util.JenaUtils;
import cz.cvut.kbss.ontodriver.model.Assertion;
import cz.cvut.kbss.ontodriver.model.Axiom;
import cz.cvut.kbss.ontodriver.model.NamedResource;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -40,6 +42,7 @@
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static org.apache.jena.rdf.model.ResourceFactory.createProperty;
import static org.apache.jena.rdf.model.ResourceFactory.createResource;
Expand Down Expand Up @@ -266,4 +269,34 @@ public void updateListWorksInContext() {
assertEquals(HAS_CONTENT_PROPERTY, statementsInserted.get(1).getPredicate());
assertEquals(added.toString(), statementsInserted.get(1).getObject().asResource().getURI());
}

@Test
void persistListSupportsSavingDataPropertyValuesAsListElements() {
final ReferencedListValueDescriptor<Integer> desc = new ReferencedListValueDescriptor<>(OWNER,
HAS_LIST,
HAS_NEXT, Assertion.createDataPropertyAssertion(HAS_CONTENT.getIdentifier(), false));
IntStream.range(0, 5).mapToObj(i -> Generator.randomInt()).forEach(desc::addValue);

final ReferencedListHandler sut = new ReferencedListHandler(connectorMock);
sut.persistList(desc);
final ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
verify(connectorMock).add(captor.capture(), eq(null));
final List<Statement> stmts = captor.getValue();
assertEquals(desc.getValues().size() * 2, stmts.size());
int i = 0;
for (Statement stmt : stmts) {
if (i == 0) {
assertEquals(HAS_LIST_PROPERTY, stmt.getPredicate());
} else if (i % 2 == 1) {
assertEquals(HAS_CONTENT_PROPERTY, stmt.getPredicate());
final RDFNode nodeContent = stmt.getObject();
assertTrue(nodeContent.isLiteral());
final Integer nodeContentLit = (Integer) JenaUtils.literalToValue(nodeContent.asLiteral());
assertTrue(desc.getValues().contains(nodeContentLit));
} else {
assertEquals(HAS_NEXT_PROPERTY, stmt.getPredicate());
}
i++;
}
}
}
Loading

0 comments on commit 9e738a5

Please sign in to comment.