1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 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.common.request.ReadRequest;
29 import org.simantics.db.exception.DatabaseException;
30 import org.simantics.db.exception.RuntimeDatabaseException;
31 import org.simantics.db.exception.ValidationException;
32 import org.simantics.db.function.DbFunction;
33 import org.simantics.db.procedure.AsyncMultiProcedure;
34 import org.simantics.layer0.Layer0;
35 import org.simantics.utils.datastructures.Pair;
37 public class OrderedSetUtils {
40 * Returns true, if <tt>l</tt> contains the element <tt>el</tt>.
42 public static boolean contains(ReadGraph g, Resource l, Resource el) throws DatabaseException {
43 return g.hasStatement(el, l) && !l.equals(el);
47 * Returns the next element in the ordered set or <tt>l</tt>,
48 * if <tt>el</tt> is the last element. Called for <tt>el</tt>, returns
49 * the first element in the set.
51 public static Resource next(ReadGraph g, Resource l, Resource el) throws DatabaseException {
52 Collection<Resource> nexts = g.getObjects(el, l);
54 throw new InvalidOrderedSetException("Invalid list element: " + l + " " + el + " " + nexts.size() + " successors.");
55 for(Resource r : nexts)
60 public static void forNext(AsyncReadGraph g, final Resource l, Resource el, final AsyncMultiProcedure<Resource> procedure) {
61 g.forEachObject(el, l, new AsyncMultiProcedure<Resource>() {
64 public void exception(AsyncReadGraph graph, Throwable t) {
65 procedure.exception(graph, t);
69 public void finished(AsyncReadGraph graph) {
73 public void execute(AsyncReadGraph graph, Resource nel) {
75 procedure.execute(graph, nel);
76 forNext(graph, l, nel, procedure);
80 // Collection<Resource> nexts = g.getObjects(el, l);
81 // if(nexts.size() != 1)
82 // throw new NoSingleResultException("Invalid list element: " + nexts.size() + " successors.");
83 // for(Resource r : nexts)
89 * Returns the previouse element in the ordered set or <tt>l</tt>,
90 * if <tt>el</tt> is the first element. Called for <tt>el</tt>, returns
91 * the last element in the set.
93 public static Resource prev(ReadGraph g, Resource l, Resource el) throws DatabaseException {
94 Collection<Resource> prevs = g.getObjects(el, g.getInverse(l));
95 if(prevs.size() != 1) {
96 throw new InvalidOrderedSetException("Invalid list element, " + prevs.size() + " predecessors.");
98 for(Resource r : prevs)
104 * Adds a given elements <tt>el</tt> to the list <tt>l</tt>. Returns
105 * true, if the element was really added.
107 public static boolean add(WriteGraph g, Resource l, Resource el) throws DatabaseException {
108 if(g.hasStatement(el, l))
110 Resource back = prev(g, l, l);
111 g.denyStatement(back, l, l);
112 g.claim(back, l, el);
118 * Adds a given elements <tt>el</tt> to the list <tt>l</tt>. Returns
119 * true, if the element was really added.
121 public static boolean addFirst(WriteGraph g, Resource l, Resource el) throws DatabaseException {
122 return addAfter(g, l, l, el);
125 public static boolean addAll(WriteGraph g, Resource l, Iterable<Resource> els) throws DatabaseException {
126 for(Resource r : els)
127 if(g.hasStatement(r, l))
129 Resource cur = prev(g, l, l);
130 g.denyStatement(cur, l, l);
131 for(Resource r : els) {
139 public static boolean addAllNew(WriteGraph g, Resource l, Iterable<Resource> els) throws DatabaseException {
140 Resource cur = prev(g, l, l);
141 Resource inv = g.getInverse(l);
142 g.deny(cur, l, inv, l);
143 for(Resource r : els) {
144 g.claim(cur, l, inv, r);
147 g.claim(cur, l, inv, l);
151 public static boolean addAfter(WriteGraph g, Resource l, Resource pos, Resource el) throws DatabaseException {
152 if(g.hasStatement(el, l))
154 Resource next = next(g, l, pos);
155 g.denyStatement(pos, l, next);
157 g.claim(el, l, next);
161 public static boolean addBefore(WriteGraph g, Resource l, Resource pos, Resource el) throws DatabaseException {
162 if(g.hasStatement(el, l))
164 Resource prev = prev(g, l, pos);
165 g.denyStatement(prev, l, pos);
166 g.claim(prev, l, el);
172 * Removes a given elements <tt>el</tt> from the list <tt>l</tt>. Returns
173 * true, if the element was really removed.
175 public static boolean remove(WriteGraph g, Resource l, Resource el) throws DatabaseException {
176 Collection<Resource> nexts = g.getObjects(el, l);
177 if(nexts.size() == 0)
179 if(nexts.size() != 1)
180 throw new InvalidOrderedSetException("Invalid list element.");
181 Collection<Resource> prevs = g.getObjects(el, g.getInverse(l));
182 if(prevs.size() != 1)
183 throw new InvalidOrderedSetException("Invalid list element.");
185 for(Resource p : prevs)
186 for(Resource n : nexts) {
187 g.denyStatement(p, l, el);
188 g.denyStatement(el, l, n);
195 public static List<Resource> removeList(WriteGraph g, Resource l) throws DatabaseException {
196 List<Resource> els = toList(g, l);
197 for(Resource el : els)
200 Resource inv = g.getInverse(l);
206 public static void replace(WriteGraph g, Resource l, Resource oldEl, Resource newEl) throws DatabaseException {
207 Collection<Resource> nexts = g.getObjects(oldEl, l);
208 if(nexts.size() != 1)
209 throw new InvalidOrderedSetException("Invalid list element.");
210 Collection<Resource> prevs = g.getObjects(oldEl, g.getInverse(l));
211 if(prevs.size() != 1)
212 throw new InvalidOrderedSetException("Invalid list element.");
214 for(Resource p : prevs)
215 for(Resource n : nexts) {
216 g.denyStatement(p, l, oldEl);
217 g.denyStatement(oldEl, l, n);
218 g.claim(p, l, newEl);
219 g.claim(newEl, l, n);
225 * Converts ordered set into a list.
227 public static List<Resource> toList(ReadGraph g, Resource l) throws DatabaseException {
228 ArrayList<Resource> ret = new ArrayList<Resource>();
231 cur = next(g, l, cur);
239 * Converts ordered set into a list.
241 /*public static void forEach(AsyncReadGraph g, final Resource l, final AsyncMultiProcedure<Resource> procedure) {
242 g.asyncRequest(new ReadRequest() {
245 public void run(ReadGraph graph) throws DatabaseException {
246 for(Resource r : toList(graph, l))
247 procedure.execute(graph, r);
254 * Creates an empty ordered set.
256 public static Resource create(WriteOnlyGraph g, Resource type) throws DatabaseException {
257 Layer0 l0 = g.getService(Layer0.class);
258 Resource l = g.newResource();
259 g.claim(l, l0.InstanceOf, null, type);
260 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
261 Resource invL = g.newResource();
262 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
263 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
264 g.claim(l, l, invL, l);
269 * Creates an ordered set containing the given elements.
270 * It is assumed that the elements do not contain duplicates.
273 public static Resource create(WriteOnlyGraph g, Resource type, Iterable<Resource> c) throws DatabaseException {
274 Layer0 l0 = g.getService(Layer0.class);
275 Resource l = g.newResource();
276 g.claim(l, l0.InstanceOf, null, type);
277 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
278 Resource invL = g.newResource();
279 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
280 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
282 for(Resource r : c) {
283 g.claim(cur, l, invL, r);
286 g.claim(cur, l, invL, l);
290 public static Resource create(WriteOnlyGraph g, Resource type, String name, Iterable<Resource> c) throws DatabaseException {
291 return createExisting(g, g.newResource(), type, name, c);
294 public static Resource createExisting(WriteOnlyGraph g, Resource l, Resource type, String name, Iterable<Resource> c) throws DatabaseException {
295 Layer0 l0 = g.getService(Layer0.class);
296 g.claim(l, l0.InstanceOf, null, type);
297 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
298 g.addLiteral(l, l0.HasName, l0.NameOf, l0.String, name, Bindings.STRING);
299 Resource invL = g.newResource();
300 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
301 g.addLiteral(invL, l0.HasName, l0.NameOf, l0.String, "Inverse", Bindings.STRING);
302 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
303 g.claim(l, l0.ConsistsOf, l0.PartOf, invL);
305 for(Resource r : c) {
306 g.claim(cur, l, invL, r);
309 g.claim(cur, l, invL, l);
313 public static Resource create(WriteOnlyGraph g, Resource type, Resource ... c) throws DatabaseException {
314 Layer0 l0 = g.getService(Layer0.class);
315 Resource l = g.newResource();
316 g.claim(l, l0.InstanceOf, null, type);
317 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
318 Resource invL = g.newResource();
319 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
320 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
322 for(Resource r : c) {
323 g.claim(cur, l, invL, r);
326 g.claim(cur, l, invL, l);
330 public static Resource createExisting(WriteGraph g, Resource l, Resource ... c) throws DatabaseException {
331 Layer0 l0 = Layer0.getInstance(g);
332 g.claim(l, l0.SubrelationOf, null, l0.HasNext);
333 Resource invL = g.newResource();
334 g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious);
335 g.claim(l, l0.InverseOf, l0.InverseOf, invL);
337 for(Resource r : c) {
338 g.claim(cur, l, invL, r);
341 g.claim(cur, l, invL, l);
345 // public static Resource create(GraphWriter graph, Resource type, Iterable<Resource> c) throws DatabaseException {
346 // ReadGraph g = w.getGraph();
347 // Builtins b = g.getBuiltins();
348 // Resource l = w.create(type).get();
349 // w.let(b.SubrelationOf, b.IsRelatedTo);
350 // w.createInverse(l).let(b.SubrelationOf, g.getInverse(b.IsRelatedTo));
353 // for(Resource r : c) {
354 // w.stat(cur, l, r);
357 // w.stat(cur, l, l);
361 public static class OrderedSetIterator implements ListIterator<Resource> {
370 WriteGraph getWriteGraph() {
371 if (!(g instanceof WriteGraph))
372 throw new UnsupportedOperationException("");
373 return (WriteGraph) g;
376 public OrderedSetIterator(ReadGraph g, Resource l) throws DatabaseException {
380 this.next = OrderedSetUtils.next(g, l, l);
385 public boolean hasNext() {
386 return !next.equals(l);
390 public Resource next() {
393 next = OrderedSetUtils.next(g, l, next);
394 } catch (DatabaseException e) {
395 throw new RuntimeDatabaseException(e);
403 public void remove() {
405 WriteGraph wg = getWriteGraph();
407 OrderedSetUtils.remove(wg, l, prev);
408 prev = OrderedSetUtils.prev(wg, l, next);
411 OrderedSetUtils.remove(wg, l, next);
412 next = OrderedSetUtils.next(wg, l, prev);
414 } catch (DatabaseException e) {
415 throw new RuntimeDatabaseException(e);
420 public void add(Resource e) {
422 WriteGraph wg = getWriteGraph();
423 wg.denyStatement(prev, l, next);
424 wg.claim(prev, l, e);
425 wg.claim(e, l, next);
427 } catch (DatabaseException ex) {
428 throw new RuntimeDatabaseException(ex);
433 public boolean hasPrevious() {
434 return !prev.equals(l);
438 public int nextIndex() {
443 public Resource previous() {
446 prev = OrderedSetUtils.prev(g, l, prev);
450 } catch (DatabaseException ex) {
451 throw new RuntimeDatabaseException(ex);
456 public int previousIndex() {
461 public void set(Resource e) {
463 WriteGraph wg = getWriteGraph();
465 OrderedSetUtils.replace(wg, l, prev, e);
469 OrderedSetUtils.replace(wg, l, next, e);
472 } catch (DatabaseException ex) {
473 throw new RuntimeDatabaseException(ex);
479 public static ListIterator<Resource> iterator(ReadGraph g, Resource l) throws DatabaseException {
480 return new OrderedSetIterator(g, l);
483 public static boolean set(WriteGraph g, Resource l, Iterable<Resource> els) throws DatabaseException {
486 Set<Pair<Resource,Resource>> removed = new HashSet<Pair<Resource,Resource>>();
487 Collection<Pair<Resource,Resource>> added = new ArrayList<Pair<Resource,Resource>>();
490 Resource temp = next(g, l, cur);
491 removed.add(new Pair<Resource,Resource>(cur, temp));
493 } while(!cur.equals(l));
496 for(Resource temp : els) {
497 Pair<Resource,Resource> pair = new Pair<Resource, Resource>(cur, temp);
498 if(!removed.remove(pair))
504 Pair<Resource,Resource> pair = new Pair<Resource, Resource>(cur, l);
505 if(!removed.remove(pair))
510 if(added.isEmpty() && removed.isEmpty())
512 for(Pair<Resource,Resource> pair : removed)
513 g.denyStatement(pair.first, l, pair.second);
514 for(Pair<Resource,Resource> pair : added)
515 g.claim(pair.first, l, pair.second);
520 * Retrieves the owner list the specified list element
523 * @param el a possible element of a list of the specified type
524 * @param listBaseRelation a base relation of the list to look for
527 public static Resource getSingleOwnerList(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException {
528 Collection<Resource> result = getOwnerLists(g, el, listBaseRelation);
529 if (result.size() != 1)
530 throw new ValidationException(NameUtils.getSafeName(g, el) + " is part of " + result.size() + " lists of base type " + NameUtils.getSafeName(g, listBaseRelation) + ", expected only one list.");
531 return result.iterator().next();
534 public static Resource getSingleOwnerList(ReadGraph g, Resource el) throws DatabaseException {
535 Layer0 l0 = Layer0.getInstance(g);
536 Collection<Resource> result = getOwnerLists(g, el, l0.OrderedSet);
537 if (result.size() != 1)
538 throw new ValidationException(NameUtils.getSafeName(g, el) + " is part of " + result.size() + " lists of base type L0.OrderedSet, expected only one list.");
539 return result.iterator().next();
542 public static Collection<Resource> getSubjects(ReadGraph g, Resource object) throws DatabaseException {
543 Collection<Resource> result = new ArrayList<Resource>(1);
544 Layer0 l0 = Layer0.getInstance(g);
545 for(Resource pred : g.getPredicates(object)) {
546 if(g.isInstanceOf(pred, l0.OrderedSet) && !pred.equals(object))
552 private static void forSubjects(ReadGraph g, Resource object, DbFunction<Resource, Boolean> consumer) throws DatabaseException {
553 for (Resource pred : g.getPredicates(object)) {
554 if (!pred.equals(object))
555 if (!consumer.apply(pred))
561 * Retrieves the owner list the specified list element
564 * @param el a possible element of a list of the specified type
565 * @param listBaseRelation a base relation of the list to look for
568 public static Collection<Resource> getOwnerLists(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException {
569 Collection<Resource> result = null;
570 Collection<Resource> rs = getSubjects(g, el);
571 for (Resource r : rs) {
572 if (g.isInstanceOf(r, listBaseRelation)) {
574 result = new ArrayList<Resource>(2);
579 result = Collections.emptyList();
583 private static class PossibleOwnerList implements DbFunction<Resource, Boolean> {
584 private ReadGraph graph;
585 private final Resource listBaseRelation;
587 public Resource result;
589 PossibleOwnerList(ReadGraph graph, Resource listBaseRelation) {
591 this.listBaseRelation = listBaseRelation;
592 this.L0 = Layer0.getInstance(graph);
596 public Boolean apply(Resource t) throws DatabaseException {
597 Set<Resource> types = graph.getTypes(t);
598 if (types.contains(L0.OrderedSet) && types.contains(listBaseRelation)) {
599 if (result != null) {
610 * Retrieves a possible single owner list the specified list element
614 * a possible element of a list of the specified type
615 * @param listBaseRelation
616 * a base relation of the list to look for
617 * @return <code>null</code> if there is zero or more than one owner lists
618 * with specified base relation
620 public static Resource getPossibleOwnerList(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException {
621 PossibleOwnerList proc = new PossibleOwnerList(g, listBaseRelation);
622 forSubjects(g, el, proc);