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 * Converts ordered set into a list.
226 public static List<Resource> toList(ReadGraph g, Resource l) throws DatabaseException {
227 ArrayList<Resource> ret = new ArrayList<Resource>();
230 cur = next(g, l, cur);
238 * Creates an empty ordered set.
240 public static Resource create(WriteOnlyGraph g, Resource type) throws DatabaseException {
241 Layer0 l0 = g.getService(Layer0.class);
242 Resource l = g.newResource();
243 g.claim(l, l0.InstanceOf, null, type);
244 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
245 Resource invL = g.newResource();
246 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
247 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
248 g.claim(l, l, invL, l);
253 * Creates an ordered set containing the given elements.
254 * It is assumed that the elements do not contain duplicates.
257 public static Resource create(WriteOnlyGraph g, Resource type, Iterable<Resource> c) throws DatabaseException {
258 Layer0 l0 = g.getService(Layer0.class);
259 Resource l = g.newResource();
260 g.claim(l, l0.InstanceOf, null, type);
261 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
262 Resource invL = g.newResource();
263 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
264 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
266 for(Resource r : c) {
267 g.claim(cur, l, invL, r);
270 g.claim(cur, l, invL, l);
274 public static Resource create(WriteOnlyGraph g, Resource type, String name, Iterable<Resource> c) throws DatabaseException {
275 return createExisting(g, g.newResource(), type, name, c);
278 public static Resource createExisting(WriteOnlyGraph g, Resource l, Resource type, String name, Iterable<Resource> c) throws DatabaseException {
279 Layer0 l0 = g.getService(Layer0.class);
280 g.claim(l, l0.InstanceOf, null, type);
281 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
282 g.addLiteral(l, l0.HasName, l0.NameOf, l0.String, name, Bindings.STRING);
283 Resource invL = g.newResource();
284 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
285 g.addLiteral(invL, l0.HasName, l0.NameOf, l0.String, "Inverse", Bindings.STRING);
286 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
287 g.claim(l, l0.ConsistsOf, l0.PartOf, invL);
289 for(Resource r : c) {
290 g.claim(cur, l, invL, r);
293 g.claim(cur, l, invL, l);
297 public static Resource create(WriteOnlyGraph g, Resource type, Resource ... c) throws DatabaseException {
298 Layer0 l0 = g.getService(Layer0.class);
299 Resource l = g.newResource();
300 g.claim(l, l0.InstanceOf, null, type);
301 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
302 Resource invL = g.newResource();
303 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
304 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
306 for(Resource r : c) {
307 g.claim(cur, l, invL, r);
310 g.claim(cur, l, invL, l);
314 public static Resource createExisting(WriteGraph g, Resource l, Resource ... c) throws DatabaseException {
315 Layer0 l0 = Layer0.getInstance(g);
316 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
317 Resource invL = g.newResource();
318 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
319 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
321 for(Resource r : c) {
322 g.claim(cur, l, invL, r);
325 g.claim(cur, l, invL, l);
329 // public static Resource create(GraphWriter graph, Resource type, Iterable<Resource> c) throws DatabaseException {
330 // ReadGraph g = w.getGraph();
331 // Builtins b = g.getBuiltins();
332 // Resource l = w.create(type).get();
333 // w.let(b.SubrelationOf, b.IsRelatedTo);
334 // w.createInverse(l).let(b.SubrelationOf, g.getInverse(b.IsRelatedTo));
337 // for(Resource r : c) {
338 // w.stat(cur, l, r);
341 // w.stat(cur, l, l);
345 public static class OrderedSetIterator implements ListIterator<Resource> {
354 WriteGraph getWriteGraph() {
355 if (!(g instanceof WriteGraph))
356 throw new UnsupportedOperationException("");
357 return (WriteGraph) g;
360 public OrderedSetIterator(ReadGraph g, Resource l) throws DatabaseException {
364 this.next = OrderedSetUtils.next(g, l, l);
369 public boolean hasNext() {
370 return !next.equals(l);
374 public Resource next() {
377 next = OrderedSetUtils.next(g, l, next);
378 } catch (DatabaseException e) {
379 throw new RuntimeDatabaseException(e);
387 public void remove() {
389 WriteGraph wg = getWriteGraph();
391 OrderedSetUtils.remove(wg, l, prev);
392 prev = OrderedSetUtils.prev(wg, l, next);
395 OrderedSetUtils.remove(wg, l, next);
396 next = OrderedSetUtils.next(wg, l, prev);
398 } catch (DatabaseException e) {
399 throw new RuntimeDatabaseException(e);
404 public void add(Resource e) {
406 WriteGraph wg = getWriteGraph();
407 wg.denyStatement(prev, l, next);
408 wg.claim(prev, l, e);
409 wg.claim(e, l, next);
411 } catch (DatabaseException ex) {
412 throw new RuntimeDatabaseException(ex);
417 public boolean hasPrevious() {
418 return !prev.equals(l);
422 public int nextIndex() {
427 public Resource previous() {
430 prev = OrderedSetUtils.prev(g, l, prev);
434 } catch (DatabaseException ex) {
435 throw new RuntimeDatabaseException(ex);
440 public int previousIndex() {
445 public void set(Resource e) {
447 WriteGraph wg = getWriteGraph();
449 OrderedSetUtils.replace(wg, l, prev, e);
453 OrderedSetUtils.replace(wg, l, next, e);
456 } catch (DatabaseException ex) {
457 throw new RuntimeDatabaseException(ex);
463 public static ListIterator<Resource> iterator(ReadGraph g, Resource l) throws DatabaseException {
464 return new OrderedSetIterator(g, l);
467 public static boolean set(WriteGraph g, Resource l, Iterable<Resource> els) throws DatabaseException {
470 Set<Pair<Resource,Resource>> removed = new HashSet<Pair<Resource,Resource>>();
471 Collection<Pair<Resource,Resource>> added = new ArrayList<Pair<Resource,Resource>>();
474 Resource temp = next(g, l, cur);
475 removed.add(new Pair<Resource,Resource>(cur, temp));
477 } while(!cur.equals(l));
480 for(Resource temp : els) {
481 Pair<Resource,Resource> pair = new Pair<Resource, Resource>(cur, temp);
482 if(!removed.remove(pair))
488 Pair<Resource,Resource> pair = new Pair<Resource, Resource>(cur, l);
489 if(!removed.remove(pair))
494 if(added.isEmpty() && removed.isEmpty())
496 for(Pair<Resource,Resource> pair : removed)
497 g.denyStatement(pair.first, l, pair.second);
498 for(Pair<Resource,Resource> pair : added)
499 g.claim(pair.first, l, pair.second);
504 * Retrieves the owner list the specified list element
507 * @param el a possible element of a list of the specified type
508 * @param listBaseRelation a base relation of the list to look for
511 public static Resource getSingleOwnerList(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException {
512 Collection<Resource> result = getOwnerLists(g, el, listBaseRelation);
513 if (result.size() != 1)
514 throw new ValidationException(NameUtils.getSafeName(g, el) + " is part of " + result.size() + " lists of base type " + NameUtils.getSafeName(g, listBaseRelation) + ", expected only one list.");
515 return result.iterator().next();
518 public static Resource getSingleOwnerList(ReadGraph g, Resource el) throws DatabaseException {
519 Layer0 l0 = Layer0.getInstance(g);
520 Collection<Resource> result = getOwnerLists(g, el, l0.OrderedSet);
521 if (result.size() != 1)
522 throw new ValidationException(NameUtils.getSafeName(g, el) + " is part of " + result.size() + " lists of base type L0.OrderedSet, expected only one list.");
523 return result.iterator().next();
526 public static Collection<Resource> getSubjects(ReadGraph g, Resource object) throws DatabaseException {
527 Collection<Resource> result = new ArrayList<Resource>(1);
528 Layer0 l0 = Layer0.getInstance(g);
529 for(Resource pred : g.getPredicates(object)) {
530 if(g.isInstanceOf(pred, l0.OrderedSet) && !pred.equals(object))
536 private static void forSubjects(ReadGraph g, Resource object, DbFunction<Resource, Boolean> consumer) throws DatabaseException {
537 for (Resource pred : g.getPredicates(object)) {
538 if (!pred.equals(object))
539 if (!consumer.apply(pred))
545 * Retrieves the owner list the specified list element
548 * @param el a possible element of a list of the specified type
549 * @param listBaseRelation a base relation of the list to look for
552 public static Collection<Resource> getOwnerLists(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException {
553 Collection<Resource> result = null;
554 Collection<Resource> rs = getSubjects(g, el);
555 for (Resource r : rs) {
556 if (g.isInstanceOf(r, listBaseRelation)) {
558 result = new ArrayList<Resource>(2);
563 result = Collections.emptyList();
567 private static class PossibleOwnerList implements DbFunction<Resource, Boolean> {
568 private ReadGraph graph;
569 private final Resource listBaseRelation;
571 public Resource result;
573 PossibleOwnerList(ReadGraph graph, Resource listBaseRelation) {
575 this.listBaseRelation = listBaseRelation;
576 this.L0 = Layer0.getInstance(graph);
580 public Boolean apply(Resource t) throws DatabaseException {
581 Set<Resource> types = graph.getTypes(t);
582 if (types.contains(L0.OrderedSet) && types.contains(listBaseRelation)) {
583 if (result != null) {
594 * Retrieves a possible single owner list the specified list element
598 * a possible element of a list of the specified type
599 * @param listBaseRelation
600 * a base relation of the list to look for
601 * @return <code>null</code> if there is zero or more than one owner lists
602 * with specified base relation
604 public static Resource getPossibleOwnerList(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException {
605 PossibleOwnerList proc = new PossibleOwnerList(g, listBaseRelation);
606 forSubjects(g, el, proc);