Skip to content

Method: PendingReferenceRegistry()

1: /**
2: * Copyright (C) 2020 Czech Technical University in Prague
3: * <p>
4: * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
5: * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
6: * version.
7: * <p>
8: * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10: * details. You should have received a copy of the GNU General Public License along with this program. If not, see
11: * <http://www.gnu.org/licenses/>.
12: */
13: package cz.cvut.kbss.jopa.oom;
14:
15: import cz.cvut.kbss.ontodriver.descriptor.ListValueDescriptor;
16: import cz.cvut.kbss.ontodriver.model.Assertion;
17: import cz.cvut.kbss.ontodriver.model.NamedResource;
18: import org.slf4j.Logger;
19: import org.slf4j.LoggerFactory;
20:
21: import java.net.URI;
22: import java.util.*;
23: import java.util.function.Predicate;
24:
25: /**
26: * Used to track references to unpersisted instances during transaction.
27: * <p>
28: * The general rule is that on commit, this registry must be empty.
29: */
30: class PendingReferenceRegistry {
31:
32: private static final Logger LOG = LoggerFactory.getLogger(PendingReferenceRegistry.class);
33:
34: private final Map<Object, Set<PendingAssertion>> pendingAssertions = new IdentityHashMap<>();
35:
36: private Map<Object, Set<PendingListReference>> pendingLists;
37: /**
38: * Counts number of pending items per each list
39: */
40: private Map<ListValueDescriptor, Integer> pendingListItems;
41:
42: /**
43: * Registers a new pending assertion.
44: *
45: * @param owner Subject of the assertion
46: * @param assertion The assertion representation
47: * @param object The value of the assertion. Always an individual identifier
48: * @param context Context into which the assertion will be added
49: */
50: void addPendingAssertion(NamedResource owner, Assertion assertion, Object object, URI context) {
51: assert owner != null;
52: assert assertion != null;
53: assert object != null;
54:
55: final PendingAssertion pa = new PendingAssertion(owner, assertion, context);
56: if (!pendingAssertions.containsKey(object)) {
57: pendingAssertions.put(object, new HashSet<>());
58: }
59: pendingAssertions.get(object).add(pa);
60: }
61:
62: /**
63: * Registers a pending reference to a sequence (simple or referenced).
64: *
65: * @param item The pending (unpersisted) item
66: * @param valueDescriptor Descriptor containing info about list owner, linking property etc.
67: * @param values Values of the sequence
68: */
69: void addPendingListReference(Object item, ListValueDescriptor valueDescriptor, List<?> values) {
70: if (pendingLists == null) {
71: this.pendingLists = new IdentityHashMap<>();
72: this.pendingListItems = new HashMap<>();
73: }
74: pendingLists.putIfAbsent(item, new HashSet<>());
75: pendingLists.get(item).add(new PendingListReference(valueDescriptor, values));
76: pendingListItems.compute(valueDescriptor, (k, v) -> v == null ? 1 : v + 1);
77: }
78:
79: /**
80: * Removes any pending persists with the specified object (value of the assertion).
81: *
82: * @param object Object which is no longer considered pending, so all assertions referencing it should be removed
83: * from this registry
84: */
85: Set<PendingAssertion> removeAndGetPendingAssertionsWith(Object object) {
86: assert object != null;
87: final Set<PendingAssertion> pending = pendingAssertions.remove(object);
88: return pending != null ? pending : Collections.emptySet();
89: }
90:
91: Set<PendingListReference> removeAndGetPendingListReferencesWith(Object object) {
92: assert object != null;
93: final Set<PendingListReference> refs = pendingLists == null ? null : pendingLists.remove(object);
94: if (refs == null) {
95: return Collections.emptySet();
96: }
97: final Iterator<PendingListReference> it = refs.iterator();
98: while (it.hasNext()) {
99: final PendingListReference ref = it.next();
100: assert pendingListItems.get(ref.descriptor) != null;
101: pendingListItems.compute(ref.descriptor, (k, v) -> v - 1);
102: if (pendingListItems.get(ref.descriptor) == 0) {
103: pendingListItems.remove(ref.descriptor);
104: } else {
105: it.remove();
106: }
107: }
108: return refs;
109: }
110:
111: Set<Object> getPendingResources() {
112: final Set<Object> pending = new HashSet<>(pendingAssertions.keySet());
113: if (pendingLists != null) {
114: pending.addAll(pendingLists.keySet());
115: }
116: return pending;
117: }
118:
119: boolean hasPendingResources() {
120: return !pendingAssertions.isEmpty() || pendingLists != null && !pendingLists.isEmpty();
121: }
122:
123: /**
124: * Removes all pending assertions which have the same subject (owner).
125: *
126: * @param subject The subject of assertions to remove
127: */
128: void removePendingReferences(NamedResource subject) {
129: if (LOG.isTraceEnabled()) {
130: LOG.trace("Removing pending assertions for subject {}.", subject);
131: }
132: for (Set<PendingAssertion> pending : pendingAssertions.values()) {
133: pending.removeIf(item -> item.getOwner().equals(subject));
134: }
135: pendingAssertions.entrySet().removeIf(e -> e.getValue().isEmpty());
136: removePendingListReferences(desc -> desc.getListOwner().equals(subject));
137: }
138:
139: private void removePendingListReferences(Predicate<ListValueDescriptor> condition) {
140: if (pendingLists == null) {
141: return;
142: }
143: final Set<ListValueDescriptor> removed = new HashSet<>();
144: for (Set<PendingListReference> pending : pendingLists.values()) {
145: final Iterator<PendingListReference> it = pending.iterator();
146: while (it.hasNext()) {
147: final ListValueDescriptor desc = it.next().descriptor;
148: if (condition.test(desc)) {
149: it.remove();
150: removed.add(desc);
151: }
152: }
153: }
154: pendingLists.entrySet().removeIf(e -> e.getValue().isEmpty());
155: removed.forEach(pendingListItems::remove);
156: }
157:
158: /**
159: * Removes pending references representing the specified assertion about the specified subject.
160: *
161: * @param subject Assertion subject
162: * @param assertion The assertion
163: */
164: void removePendingReferences(NamedResource subject, Assertion assertion) {
165: if (LOG.isTraceEnabled()) {
166: LOG.trace("Removing pending assertions {} for subject {}.", assertion, subject);
167: }
168: for (Set<PendingAssertion> pending : pendingAssertions.values()) {
169: pending.removeIf(item -> item.getOwner().equals(subject) && item.getAssertion().equals(assertion));
170: }
171: pendingAssertions.entrySet().removeIf(e -> e.getValue().isEmpty());
172: removePendingListReferences(
173: desc -> desc.getListOwner().equals(subject) && desc.getListProperty().equals(assertion));
174: }
175:
176: static class PendingListReference {
177: private final ListValueDescriptor descriptor;
178: private final List<?> values;
179:
180: private PendingListReference(ListValueDescriptor descriptor, List<?> values) {
181: this.descriptor = descriptor;
182: this.values = values;
183: }
184:
185: public ListValueDescriptor getDescriptor() {
186: return descriptor;
187: }
188:
189: public List<?> getValues() {
190: return values;
191: }
192: }
193: }