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