Skip to content

Method: mergeList(ReferencedListValueDescriptor)

1: /*
2: * JOPA
3: * Copyright (C) 2024 Czech Technical University in Prague
4: *
5: * This library is free software; you can redistribute it and/or
6: * modify it under the terms of the GNU Lesser General Public
7: * License as published by the Free Software Foundation; either
8: * version 3.0 of the License, or (at your option) any later version.
9: *
10: * This library is distributed in the hope that it will be useful,
11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13: * Lesser General Public License for more details.
14: *
15: * You should have received a copy of the GNU Lesser General Public
16: * License along with this library.
17: */
18: package cz.cvut.kbss.ontodriver.rdf4j.list;
19:
20: import cz.cvut.kbss.ontodriver.descriptor.ReferencedListDescriptor;
21: import cz.cvut.kbss.ontodriver.descriptor.ReferencedListValueDescriptor;
22: import cz.cvut.kbss.ontodriver.model.Assertion;
23: import cz.cvut.kbss.ontodriver.model.Axiom;
24: import cz.cvut.kbss.ontodriver.rdf4j.connector.RepoConnection;
25: import cz.cvut.kbss.ontodriver.rdf4j.exception.Rdf4jDriverException;
26: import cz.cvut.kbss.ontodriver.rdf4j.util.ValueConverter;
27: import org.eclipse.rdf4j.model.IRI;
28: import org.eclipse.rdf4j.model.Resource;
29: import org.eclipse.rdf4j.model.Statement;
30: import org.eclipse.rdf4j.model.Value;
31: import org.eclipse.rdf4j.model.ValueFactory;
32: import org.eclipse.rdf4j.model.vocabulary.RDF;
33:
34: import java.util.ArrayList;
35: import java.util.Collection;
36: import java.util.Collections;
37: import java.util.Iterator;
38: import java.util.List;
39: import java.util.Optional;
40: import java.util.Set;
41:
42: public class ReferencedListHandler extends ListHandler<ReferencedListValueDescriptor<?>> {
43:
44: private int sequenceCounter = 0;
45:
46: private final ValueConverter valueConverter;
47:
48: public ReferencedListHandler(RepoConnection connector, ValueFactory vf) {
49: super(connector, vf);
50: this.valueConverter = new ValueConverter(vf);
51: }
52:
53: /**
54: * Loads axioms representing list described by the specified list descriptor.
55: *
56: * @return Collection of axioms representing sequence values
57: * @throws Rdf4jDriverException When storage access error occurs
58: */
59: public List<Axiom<?>> loadList(ReferencedListDescriptor listDescriptor) throws Rdf4jDriverException {
60: final List<Axiom<?>> axioms = new ArrayList<>();
61: final ListIterator<?> it = new ReferencedListIterator<>(listDescriptor, connector, vf);
62: while (it.hasNext()) {
63: axioms.add(it.nextAxiom());
64: }
65: return axioms;
66: }
67:
68: protected IRI createListHead(ReferencedListValueDescriptor<?> listValueDescriptor,
69: Collection<Statement> statements) throws Rdf4jDriverException {
70: final IRI owner = owner(listValueDescriptor);
71: final IRI hasList = hasList(listValueDescriptor);
72: final IRI hasContent = hasContent(listValueDescriptor);
73: final IRI context = context(listValueDescriptor);
74: final IRI nodeUri = generateSequenceNode(owner, context);
75: statements.add(vf.createStatement(owner, hasList, nodeUri, context));
76: final Collection<Value> nodeContent = toRdf4jValue(listValueDescriptor.getNodeContent(), listValueDescriptor.getValues()
77: .get(0));
78: nodeContent.forEach(item -> statements.add(vf.createStatement(nodeUri, hasContent, item, context)));
79: return nodeUri;
80: }
81:
82: private IRI hasContent(ReferencedListDescriptor listDescriptor) {
83: return toRdf4jIri(listDescriptor.getNodeContent().getIdentifier());
84: }
85:
86: private Collection<Value> toRdf4jValue(Assertion a, Object value) throws Rdf4jDriverException {
87: return new ReferencedListHelper(valueConverter).toRdf4jValue(a, value);
88: }
89:
90: private IRI generateSequenceNode(IRI owner, IRI context) throws Rdf4jDriverException {
91: final String uriBase = owner.stringValue();
92: boolean unique;
93: IRI node;
94: do {
95: node = vf.createIRI(uriBase + "-SEQ_" + sequenceCounter++);
96: final Collection<Statement> stmts = connector.findStatements(node, null, null, false,
97: context != null ? Collections.singleton(context) : Collections.emptySet());
98: unique = stmts.isEmpty();
99: } while (!unique);
100: return node;
101: }
102:
103: protected List<Statement> createListRest(IRI headNode, ReferencedListValueDescriptor<?> listValueDescriptor)
104: throws Rdf4jDriverException {
105: final IRI owner = owner(listValueDescriptor);
106: final IRI hasNext = hasNext(listValueDescriptor);
107: final IRI hasContent = hasContent(listValueDescriptor);
108: final IRI context = context(listValueDescriptor);
109: IRI previous = headNode;
110: final List<Statement> statements = new ArrayList<>(listValueDescriptor.getValues().size() * 2);
111: final Iterator<?> it = listValueDescriptor.getValues().iterator();
112: // Skip the first element, it is already in the head
113: it.next();
114: while (it.hasNext()) {
115: final Collection<Value> content = toRdf4jValue(listValueDescriptor.getNodeContent(), it.next());
116: previous = appendListNode(owner, hasNext, hasContent, content, context, previous, statements);
117: }
118: createNilTerminal(previous, hasNext, listValueDescriptor).ifPresent(statements::add);
119: return statements;
120: }
121:
122: private IRI appendListNode(IRI owner, IRI hasNext, IRI hasContent, Collection<Value> content, IRI context,
123: Resource previous,
124: Collection<Statement> statements) throws Rdf4jDriverException {
125: final IRI node = generateSequenceNode(owner, context);
126: statements.add(vf.createStatement(previous, hasNext, node, context));
127: content.forEach(item -> statements.add(vf.createStatement(node, hasContent, item, context)));
128: return node;
129: }
130:
131: private Optional<Statement> createNilTerminal(Resource lastNode, IRI hasNext, ReferencedListDescriptor descriptor) {
132: return descriptor.isTerminatedByNil() ? Optional.of(vf.createStatement(lastNode, hasNext, RDF.NIL)) : Optional.empty();
133: }
134:
135: @Override
136: protected void clearList(ReferencedListValueDescriptor<?> listDescriptor) throws Rdf4jDriverException {
137: final IRI hasNext = hasNext(listDescriptor);
138: final IRI hasContent = hasContent(listDescriptor);
139: final boolean includeInferred = listDescriptor.getListProperty().isInferred();
140: final Set<IRI> context = contexts(listDescriptor);
141: Resource previous = owner(listDescriptor);
142: IRI currentProperty = hasList(listDescriptor);
143: final Collection<Statement> toRemove = new ArrayList<>();
144: Collection<Statement> next;
145: do {
146: next = connector.findStatements(previous, currentProperty, null, includeInferred, context);
147: if (!next.isEmpty()) {
148: final Resource node = extractListNode(next, currentProperty);
149: toRemove.addAll(next);
150: toRemove.addAll(connector.findStatements(node, hasContent, null, includeInferred, context));
151: previous = node;
152: }
153: currentProperty = hasNext;
154: } while (!next.isEmpty());
155: connector.removeStatements(toRemove);
156: }
157:
158: protected void mergeList(ReferencedListValueDescriptor<?> listDescriptor) throws Rdf4jDriverException {
159: final ListIterator<Object> it = new ReferencedListIterator<>(listDescriptor, connector, vf);
160: final ListHandler.MergeResult mergeResult = mergeWithOriginalList((ReferencedListValueDescriptor<Object>) listDescriptor, it);
161: removeObsoletes(it);
162:• assert mergeResult.i > 0;
163:• assert mergeResult.previous != null;
164:• if (mergeResult.i < listDescriptor.getValues().size()) {
165: appendNewNodes(listDescriptor, mergeResult);
166: }
167: }
168:
169: <V> MergeResult mergeWithOriginalList(ReferencedListValueDescriptor<V> listDescriptor, ListIterator<V> it)
170: throws Rdf4jDriverException {
171: int i = 0;
172: Resource node = null;
173: while (it.hasNext() && i < listDescriptor.getValues().size()) {
174: node = it.nextNode();
175: final V content = it.currentContent();
176: final V newNode = listDescriptor.getValues().get(i);
177: if (!content.equals(newNode)) {
178: it.replaceCurrentWith(newNode);
179: }
180: i++;
181: }
182: return new MergeResult(i, node);
183: }
184:
185: <V> void appendNewNodes(ReferencedListValueDescriptor<V> listDescriptor, MergeResult mergeResult)
186: throws Rdf4jDriverException {
187: int i = mergeResult.i;
188: Resource previous = mergeResult.previous;
189: final IRI owner = owner(listDescriptor);
190: final IRI hasNext = hasNext(listDescriptor);
191: final IRI hasContent = hasContent(listDescriptor);
192: final IRI context = context(listDescriptor);
193: assert i > 0;
194: final Collection<Statement> toAdd = new ArrayList<>((listDescriptor.getValues().size() - i) * 2);
195: if (listDescriptor.isTerminatedByNil()) {
196: removePreviousNilTerminal(previous, hasNext, context);
197: }
198: while (i < listDescriptor.getValues().size()) {
199: final Collection<Value> content = toRdf4jValue(listDescriptor.getNodeContent(), listDescriptor.getValues()
200: .get(i));
201: previous = appendListNode(owner, hasNext, hasContent, content, context, previous, toAdd);
202: i++;
203: }
204: createNilTerminal(previous, hasNext, listDescriptor).ifPresent(toAdd::add);
205: connector.addStatements(toAdd);
206: }
207:
208: private void removePreviousNilTerminal(Resource lastNode, IRI hasNext, IRI context) throws Rdf4jDriverException {
209: connector.removeStatements(Set.of(vf.createStatement(lastNode, hasNext, RDF.NIL, context)));
210: }
211: }