1 /*******************************************************************************
2 * Copyright (c) 2007, 2018 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.db.common.utils;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.ListIterator;
22 import org.simantics.databoard.Bindings;
23 import org.simantics.db.AsyncReadGraph;
24 import org.simantics.db.ReadGraph;
25 import org.simantics.db.Resource;
26 import org.simantics.db.WriteGraph;
27 import org.simantics.db.WriteOnlyGraph;
28 import org.simantics.db.exception.DatabaseException;
29 import org.simantics.db.exception.RuntimeDatabaseException;
30 import org.simantics.db.exception.ValidationException;
31 import org.simantics.db.function.DbFunction;
32 import org.simantics.db.procedure.AsyncMultiProcedure;
33 import org.simantics.layer0.Layer0;
34 import org.simantics.utils.datastructures.Pair;
36 public class OrderedSetUtils {
39 * Returns true, if <tt>l</tt> contains the element <tt>el</tt>.
41 public static boolean contains(ReadGraph g, Resource l, Resource el) throws DatabaseException {
42 return g.hasStatement(el, l) && !l.equals(el);
46 * Returns the next element in the ordered set or <tt>l</tt>,
47 * if <tt>el</tt> is the last element. Called for <tt>el</tt>, returns
48 * the first element in the set.
50 public static Resource next(ReadGraph g, Resource l, Resource el) throws DatabaseException {
51 Collection<Resource> nexts = g.getObjects(el, l);
53 throw new InvalidOrderedSetException("Invalid list element: " + l + " " + el + " " + nexts.size() + " successors.");
54 for(Resource r : nexts)
59 public static void forNext(AsyncReadGraph g, final Resource l, Resource el, final AsyncMultiProcedure<Resource> procedure) {
60 g.forEachObject(el, l, new AsyncMultiProcedure<Resource>() {
63 public void exception(AsyncReadGraph graph, Throwable t) {
64 procedure.exception(graph, t);
68 public void finished(AsyncReadGraph graph) {
72 public void execute(AsyncReadGraph graph, Resource nel) {
74 procedure.execute(graph, nel);
75 forNext(graph, l, nel, procedure);
79 // Collection<Resource> nexts = g.getObjects(el, l);
80 // if(nexts.size() != 1)
81 // throw new NoSingleResultException("Invalid list element: " + nexts.size() + " successors.");
82 // for(Resource r : nexts)
88 * Returns the previouse element in the ordered set or <tt>l</tt>,
89 * if <tt>el</tt> is the first element. Called for <tt>el</tt>, returns
90 * the last element in the set.
92 public static Resource prev(ReadGraph g, Resource l, Resource el) throws DatabaseException {
93 Collection<Resource> prevs = g.getObjects(el, g.getInverse(l));
94 if(prevs.size() != 1) {
95 throw new InvalidOrderedSetException("Invalid list element, " + prevs.size() + " predecessors.");
97 for(Resource r : prevs)
103 * Adds a given elements <tt>el</tt> to the list <tt>l</tt>. Returns
104 * true, if the element was really added.
106 public static boolean add(WriteGraph g, Resource l, Resource el) throws DatabaseException {
107 if(g.hasStatement(el, l))
109 Resource back = prev(g, l, l);
110 g.denyStatement(back, l, l);
111 g.claim(back, l, el);
117 * Adds a given elements <tt>el</tt> to the list <tt>l</tt>. Returns
118 * true, if the element was really added.
120 public static boolean addFirst(WriteGraph g, Resource l, Resource el) throws DatabaseException {
121 return addAfter(g, l, l, el);
124 public static boolean addAll(WriteGraph g, Resource l, Iterable<Resource> els) throws DatabaseException {
125 for(Resource r : els)
126 if(g.hasStatement(r, l))
128 Resource cur = prev(g, l, l);
129 g.denyStatement(cur, l, l);
130 for(Resource r : els) {
138 public static boolean addAllNew(WriteGraph g, Resource l, Iterable<Resource> els) throws DatabaseException {
139 Resource cur = prev(g, l, l);
140 Resource inv = g.getInverse(l);
141 g.deny(cur, l, inv, l);
142 for(Resource r : els) {
143 g.claim(cur, l, inv, r);
146 g.claim(cur, l, inv, l);
150 public static boolean addAfter(WriteGraph g, Resource l, Resource pos, Resource el) throws DatabaseException {
151 if(g.hasStatement(el, l))
153 Resource next = next(g, l, pos);
154 g.denyStatement(pos, l, next);
156 g.claim(el, l, next);
160 public static boolean addBefore(WriteGraph g, Resource l, Resource pos, Resource el) throws DatabaseException {
161 if(g.hasStatement(el, l))
163 Resource prev = prev(g, l, pos);
164 g.denyStatement(prev, l, pos);
165 g.claim(prev, l, el);
171 * Removes a given elements <tt>el</tt> from the list <tt>l</tt>. Returns
172 * true, if the element was really removed.
174 public static boolean remove(WriteGraph g, Resource l, Resource el) throws DatabaseException {
175 Collection<Resource> nexts = g.getObjects(el, l);
176 if(nexts.size() == 0)
178 if(nexts.size() != 1)
179 throw new InvalidOrderedSetException("Invalid list element.");
180 Collection<Resource> prevs = g.getObjects(el, g.getInverse(l));
181 if(prevs.size() != 1)
182 throw new InvalidOrderedSetException("Invalid list element.");
184 for(Resource p : prevs)
185 for(Resource n : nexts) {
186 g.denyStatement(p, l, el);
187 g.denyStatement(el, l, n);
194 public static List<Resource> removeList(WriteGraph g, Resource l) throws DatabaseException {
195 List<Resource> els = toList(g, l);
196 for(Resource el : els)
199 Resource inv = g.getInverse(l);
205 public static void replace(WriteGraph g, Resource l, Resource oldEl, Resource newEl) throws DatabaseException {
206 Collection<Resource> nexts = g.getObjects(oldEl, l);
207 if(nexts.size() != 1)
208 throw new InvalidOrderedSetException("Invalid list element.");
209 Collection<Resource> prevs = g.getObjects(oldEl, g.getInverse(l));
210 if(prevs.size() != 1)
211 throw new InvalidOrderedSetException("Invalid list element.");
213 for(Resource p : prevs)
214 for(Resource n : nexts) {
215 g.denyStatement(p, l, oldEl);
216 g.denyStatement(oldEl, l, n);
217 g.claim(p, l, newEl);
218 g.claim(newEl, l, n);
224 * Reorders elements with a minimum number of writes. The set of elements must remain the same.
226 public static void reorder(WriteGraph g, Resource l, Iterable<Resource> order) throws DatabaseException {
227 Resource newPrev = l;
228 for (Resource r : order) {
229 Resource prev = OrderedSetUtils.prev(g, l, r);
230 if (!prev.equals(newPrev)) {
232 g.claim(newPrev, l, r);
236 Resource newLast = newPrev;
237 Resource last = OrderedSetUtils.prev(g, l, l);
238 if (!last.equals(newLast)) {
240 g.claim(newLast, l, l);
245 * Converts ordered set into a list.
247 public static List<Resource> toList(ReadGraph g, Resource l) throws DatabaseException {
248 ArrayList<Resource> ret = new ArrayList<Resource>();
251 cur = next(g, l, cur);
259 * Creates an empty ordered set.
261 public static Resource create(WriteOnlyGraph g, Resource type) throws DatabaseException {
262 Layer0 l0 = g.getService(Layer0.class);
263 Resource l = g.newResource();
264 g.claim(l, l0.InstanceOf, null, type);
265 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
266 Resource invL = g.newResource();
267 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
268 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
269 g.claim(l, l, invL, l);
274 * Creates an ordered set containing the given elements.
275 * It is assumed that the elements do not contain duplicates.
278 public static Resource create(WriteOnlyGraph g, Resource type, Iterable<Resource> c) throws DatabaseException {
279 Layer0 l0 = g.getService(Layer0.class);
280 Resource l = g.newResource();
281 g.claim(l, l0.InstanceOf, null, type);
282 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
283 Resource invL = g.newResource();
284 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
285 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
287 for(Resource r : c) {
288 g.claim(cur, l, invL, r);
291 g.claim(cur, l, invL, l);
295 public static Resource create(WriteOnlyGraph g, Resource type, String name, Iterable<Resource> c) throws DatabaseException {
296 return createExisting(g, g.newResource(), type, name, c);
299 public static Resource createExisting(WriteOnlyGraph g, Resource l, Resource type, String name, Iterable<Resource> c) throws DatabaseException {
300 Layer0 l0 = g.getService(Layer0.class);
301 g.claim(l, l0.InstanceOf, null, type);
302 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
303 g.addLiteral(l, l0.HasName, l0.NameOf, l0.String, name, Bindings.STRING);
304 Resource invL = g.newResource();
305 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
306 g.addLiteral(invL, l0.HasName, l0.NameOf, l0.String, "Inverse", Bindings.STRING);
307 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
308 g.claim(l, l0.ConsistsOf, l0.PartOf, invL);
310 for(Resource r : c) {
311 g.claim(cur, l, invL, r);
314 g.claim(cur, l, invL, l);
318 public static Resource create(WriteOnlyGraph g, Resource type, Resource ... c) throws DatabaseException {
319 Layer0 l0 = g.getService(Layer0.class);
320 Resource l = g.newResource();
321 g.claim(l, l0.InstanceOf, null, type);
322 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
323 Resource invL = g.newResource();
324 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
325 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
327 for(Resource r : c) {
328 g.claim(cur, l, invL, r);
331 g.claim(cur, l, invL, l);
335 public static Resource createExisting(WriteGraph g, Resource l, Resource ... c) throws DatabaseException {
336 Layer0 l0 = Layer0.getInstance(g);
337 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
338 Resource invL = g.newResource();
339 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
340 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
342 for(Resource r : c) {
343 g.claim(cur, l, invL, r);
346 g.claim(cur, l, invL, l);
350 // public static Resource create(GraphWriter graph, Resource type, Iterable<Resource> c) throws DatabaseException {
351 // ReadGraph g = w.getGraph();
352 // Builtins b = g.getBuiltins();
353 // Resource l = w.create(type).get();
354 // w.let(b.SubrelationOf, b.IsRelatedTo);
355 // w.createInverse(l).let(b.SubrelationOf, g.getInverse(b.IsRelatedTo));
358 // for(Resource r : c) {
359 // w.stat(cur, l, r);
362 // w.stat(cur, l, l);
366 public static class OrderedSetIterator implements ListIterator<Resource> {
375 WriteGraph getWriteGraph() {
376 if (!(g instanceof WriteGraph))
377 throw new UnsupportedOperationException("");
378 return (WriteGraph) g;
381 public OrderedSetIterator(ReadGraph g, Resource l) throws DatabaseException {
385 this.next = OrderedSetUtils.next(g, l, l);
390 public boolean hasNext() {
391 return !next.equals(l);
395 public Resource next() {
398 next = OrderedSetUtils.next(g, l, next);
399 } catch (DatabaseException e) {
400 throw new RuntimeDatabaseException(e);
408 public void remove() {
410 WriteGraph wg = getWriteGraph();
412 OrderedSetUtils.remove(wg, l, prev);
413 prev = OrderedSetUtils.prev(wg, l, next);
416 OrderedSetUtils.remove(wg, l, next);
417 next = OrderedSetUtils.next(wg, l, prev);
419 } catch (DatabaseException e) {
420 throw new RuntimeDatabaseException(e);
425 public void add(Resource e) {
427 WriteGraph wg = getWriteGraph();
428 wg.denyStatement(prev, l, next);
429 wg.claim(prev, l, e);
430 wg.claim(e, l, next);
432 } catch (DatabaseException ex) {
433 throw new RuntimeDatabaseException(ex);
438 public boolean hasPrevious() {
439 return !prev.equals(l);
443 public int nextIndex() {
448 public Resource previous() {
451 prev = OrderedSetUtils.prev(g, l, prev);
455 } catch (DatabaseException ex) {
456 throw new RuntimeDatabaseException(ex);
461 public int previousIndex() {
466 public void set(Resource e) {
468 WriteGraph wg = getWriteGraph();
470 OrderedSetUtils.replace(wg, l, prev, e);
474 OrderedSetUtils.replace(wg, l, next, e);
477 } catch (DatabaseException ex) {
478 throw new RuntimeDatabaseException(ex);
484 public static ListIterator<Resource> iterator(ReadGraph g, Resource l) throws DatabaseException {
485 return new OrderedSetIterator(g, l);
488 public static boolean set(WriteGraph g, Resource l, Iterable<Resource> els) throws DatabaseException {
491 Set<Pair<Resource,Resource>> removed = new HashSet<Pair<Resource,Resource>>();
492 Collection<Pair<Resource,Resource>> added = new ArrayList<Pair<Resource,Resource>>();
495 Resource temp = next(g, l, cur);
496 removed.add(new Pair<Resource,Resource>(cur, temp));
498 } while(!cur.equals(l));
501 for(Resource temp : els) {
502 Pair<Resource,Resource> pair = new Pair<Resource, Resource>(cur, temp);
503 if(!removed.remove(pair))
509 Pair<Resource,Resource> pair = new Pair<Resource, Resource>(cur, l);
510 if(!removed.remove(pair))
515 if(added.isEmpty() && removed.isEmpty())
517 for(Pair<Resource,Resource> pair : removed)
518 g.denyStatement(pair.first, l, pair.second);
519 for(Pair<Resource,Resource> pair : added)
520 g.claim(pair.first, l, pair.second);
525 * Retrieves the owner list the specified list element
528 * @param el a possible element of a list of the specified type
529 * @param listBaseRelation a base relation of the list to look for
532 public static Resource getSingleOwnerList(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException {
533 Collection<Resource> result = getOwnerLists(g, el, listBaseRelation);
534 if (result.size() != 1)
535 throw new ValidationException(NameUtils.getSafeName(g, el) + " is part of " + result.size() + " lists of base type " + NameUtils.getSafeName(g, listBaseRelation) + ", expected only one list.");
536 return result.iterator().next();
539 public static Resource getSingleOwnerList(ReadGraph g, Resource el) throws DatabaseException {
540 Layer0 l0 = Layer0.getInstance(g);
541 Collection<Resource> result = getOwnerLists(g, el, l0.OrderedSet);
542 if (result.size() != 1)
543 throw new ValidationException(NameUtils.getSafeName(g, el) + " is part of " + result.size() + " lists of base type L0.OrderedSet, expected only one list.");
544 return result.iterator().next();
547 public static Collection<Resource> getSubjects(ReadGraph g, Resource object) throws DatabaseException {
548 Collection<Resource> result = new ArrayList<Resource>(1);
549 Layer0 l0 = Layer0.getInstance(g);
550 for(Resource pred : g.getPredicates(object)) {
551 if(g.isInstanceOf(pred, l0.OrderedSet) && !pred.equals(object))
557 private static void forSubjects(ReadGraph g, Resource object, DbFunction<Resource, Boolean> consumer) throws DatabaseException {
558 for (Resource pred : g.getPredicates(object)) {
559 if (!pred.equals(object))
560 if (!consumer.apply(pred))
566 * Retrieves the owner list the specified list element
569 * @param el a possible element of a list of the specified type
570 * @param listBaseRelation a base relation of the list to look for
573 public static Collection<Resource> getOwnerLists(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException {
574 Collection<Resource> result = null;
575 Collection<Resource> rs = getSubjects(g, el);
576 for (Resource r : rs) {
577 if (g.isInstanceOf(r, listBaseRelation)) {
579 result = new ArrayList<Resource>(2);
584 result = Collections.emptyList();
588 private static class PossibleOwnerList implements DbFunction<Resource, Boolean> {
589 private ReadGraph graph;
590 private final Resource listBaseRelation;
592 public Resource result;
594 PossibleOwnerList(ReadGraph graph, Resource listBaseRelation) {
596 this.listBaseRelation = listBaseRelation;
597 this.L0 = Layer0.getInstance(graph);
601 public Boolean apply(Resource t) throws DatabaseException {
602 Set<Resource> types = graph.getTypes(t);
603 if (types.contains(L0.OrderedSet) && types.contains(listBaseRelation)) {
604 if (result != null) {
615 * Retrieves a possible single owner list the specified list element
619 * a possible element of a list of the specified type
620 * @param listBaseRelation
621 * a base relation of the list to look for
622 * @return <code>null</code> if there is zero or more than one owner lists
623 * with specified base relation
625 public static Resource getPossibleOwnerList(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException {
626 PossibleOwnerList proc = new PossibleOwnerList(g, listBaseRelation);
627 forSubjects(g, el, proc);