Skip to content

Method: updateList(ListValueDescriptor)

1: /**
2: * Copyright (C) 2022 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.ontodriver.rdf4j;
16:
17: import cz.cvut.kbss.ontodriver.descriptor.*;
18: import cz.cvut.kbss.ontodriver.exception.IntegrityConstraintViolatedException;
19: import cz.cvut.kbss.ontodriver.model.Axiom;
20: import cz.cvut.kbss.ontodriver.model.NamedResource;
21: import cz.cvut.kbss.ontodriver.rdf4j.connector.Connector;
22: import cz.cvut.kbss.ontodriver.rdf4j.exception.Rdf4jDriverException;
23: import cz.cvut.kbss.ontodriver.rdf4j.util.Rdf4jUtils;
24: import org.eclipse.rdf4j.model.*;
25:
26: import java.util.*;
27:
28: /**
29: * Base class for list handlers.
30: * <p>
31: * List handlers are responsible for loading and persisting lists.
32: *
33: * @param <T> List descriptor type
34: * @param <V> List value descriptor type
35: */
36: abstract class ListHandler<T extends ListDescriptor, V extends ListValueDescriptor> {
37:
38: protected final Connector connector;
39: protected final ValueFactory vf;
40:
41: ListHandler(Connector connector, ValueFactory vf) {
42: this.connector = connector;
43: this.vf = vf;
44: }
45:
46: /**
47: * Loads axioms representing list described by the specified list descriptor.
48: *
49: * @return Collection of axioms representing sequence values
50: * @throws Rdf4jDriverException When storage access error occurs
51: */
52: List<Axiom<NamedResource>> loadList(T listDescriptor) throws Rdf4jDriverException {
53: final List<Axiom<NamedResource>> axioms = new ArrayList<>();
54: final ListIterator it = createIterator(listDescriptor);
55: while (it.hasNext()) {
56: axioms.add(it.nextAxiom());
57: }
58: return axioms;
59: }
60:
61: abstract ListIterator createIterator(T listDescriptor) throws Rdf4jDriverException;
62:
63: /**
64: * Persists list values specified by the descriptor.
65: * <p>
66: * The values are saved in the order in which they appear in the descriptor.
67: *
68: * @param listValueDescriptor Describes values to persist
69: * @throws Rdf4jDriverException When storage access error occurs
70: */
71: void persistList(V listValueDescriptor) throws Rdf4jDriverException {
72: if (listValueDescriptor.getValues().isEmpty()) {
73: return;
74: }
75: final Collection<Statement> statements = new ArrayList<>(listValueDescriptor.getValues()
76: .size());
77: final IRI head = createListHead(listValueDescriptor, statements);
78: statements.addAll(createListRest(head, listValueDescriptor));
79: connector.addStatements(statements);
80: }
81:
82: abstract IRI createListHead(V valueDescriptor, Collection<Statement> listStatements) throws Rdf4jDriverException;
83:
84: abstract List<Statement> createListRest(IRI head, V valueDescriptor) throws Rdf4jDriverException;
85:
86: /**
87: * Updates list with values specified by the descriptor.
88: *
89: * @param listValueDescriptor Describes the updated values
90: * @throws Rdf4jDriverException When storage access error occurs
91: */
92: void updateList(V listValueDescriptor) throws Rdf4jDriverException {
93:• if (listValueDescriptor.getValues().isEmpty()) {
94: clearList(listValueDescriptor);
95:• } else if (isOldListEmpty(owner(listValueDescriptor), hasList(listValueDescriptor),
96: listValueDescriptor.getListProperty().isInferred(), contexts(listValueDescriptor))) {
97: persistList(listValueDescriptor);
98: } else {
99: mergeList(listValueDescriptor);
100: }
101: }
102:
103: private boolean isOldListEmpty(Resource owner, IRI hasListProperty, boolean includeInferred,
104: Set<IRI> contexts) throws Rdf4jDriverException {
105: final Collection<Statement> stmts = connector.findStatements(owner, hasListProperty, null,
106: includeInferred, contexts);
107: return stmts.isEmpty();
108: }
109:
110: abstract void clearList(V listDescriptor) throws Rdf4jDriverException;
111:
112: private void mergeList(V listDescriptor) throws Rdf4jDriverException {
113: final ListIterator it = iterator(listDescriptor);
114: final MergeResult mergeResult = mergeWithOriginalList(listDescriptor, it);
115: removeObsoletes(it);
116: assert mergeResult.i > 0;
117: assert mergeResult.previous != null;
118: if (mergeResult.i < listDescriptor.getValues().size()) {
119: appendNewNodes(listDescriptor, mergeResult);
120: }
121: }
122:
123: abstract ListIterator iterator(V listDescriptor) throws Rdf4jDriverException;
124:
125: abstract MergeResult mergeWithOriginalList(V listDescriptor, ListIterator it) throws Rdf4jDriverException;
126:
127: abstract void appendNewNodes(V listDescriptor, MergeResult mergeResult) throws Rdf4jDriverException;
128:
129: private void removeObsoletes(ListIterator it) throws Rdf4jDriverException {
130: while (it.hasNext()) {
131: it.nextNode();
132: it.remove();
133: }
134: }
135:
136: Resource extractListNode(Collection<Statement> stmts, IRI nodeAssertion) {
137: if (stmts.size() > 1) {
138: throw new IntegrityConstraintViolatedException(
139: "Invalid number of values found for assertion " + nodeAssertion + ". Expected 1, got " +
140: stmts.size());
141: }
142: final Value val = stmts.iterator().next().getObject();
143: if (!(val instanceof Resource)) {
144: throw new IntegrityConstraintViolatedException(
145: "Invalid property value. Expected object property value, got literal.");
146: }
147: return (Resource) val;
148: }
149:
150: Set<IRI> contexts(ListDescriptor listDescriptor) {
151: final IRI ctx = toRdf4jIri(listDescriptor.getContext());
152: return ctx != null ? Collections.singleton(ctx) : Collections.emptySet();
153: }
154:
155: IRI context(ListDescriptor listDescriptor) {
156: return toRdf4jIri(listDescriptor.getContext());
157: }
158:
159: IRI owner(ListDescriptor listDescriptor) {
160: return toRdf4jIri(listDescriptor.getListOwner().getIdentifier());
161: }
162:
163: IRI hasList(ListDescriptor listDescriptor) {
164: return toRdf4jIri(listDescriptor.getListProperty().getIdentifier());
165: }
166:
167: IRI hasNext(ListDescriptor listDescriptor) {
168: return toRdf4jIri(listDescriptor.getNextNode().getIdentifier());
169: }
170:
171: IRI toRdf4jIri(java.net.URI uri) {
172: return Rdf4jUtils.toRdf4jIri(uri, vf);
173: }
174:
175: /**
176: * Creates handler for simple lists.
177: *
178: * @param connector Storage connector
179: * @param vf RDF4J value factory
180: * @return List handler
181: */
182: static ListHandler<SimpleListDescriptor, SimpleListValueDescriptor> createForSimpleList(
183: Connector connector, ValueFactory vf) {
184: assert connector != null;
185: assert vf != null;
186:
187: return new SimpleListHandler(connector, vf);
188: }
189:
190: /**
191: * Creates handler for referenced lists.
192: *
193: * @param connector Storage connector
194: * @param vf RDF4J value factory
195: * @return List handler
196: */
197: static ListHandler<ReferencedListDescriptor, ReferencedListValueDescriptor> createForReferencedList(
198: Connector connector, ValueFactory vf) {
199: assert connector != null;
200: assert vf != null;
201:
202: return new ReferencedListHandler(connector, vf);
203: }
204:
205: static final class MergeResult {
206: final int i;
207: final Resource previous;
208:
209: MergeResult(int i, Resource node) {
210: this.i = i;
211: this.previous = node;
212: }
213: }
214: }