X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.db.common%2Fsrc%2Forg%2Fsimantics%2Fdb%2Fcommon%2Futils%2FOrderedSetUtils.java;fp=bundles%2Forg.simantics.db.common%2Fsrc%2Forg%2Fsimantics%2Fdb%2Fcommon%2Futils%2FOrderedSetUtils.java;h=3e2331a8c63f66a0291a9928369c5136be6b4438;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.db.common/src/org/simantics/db/common/utils/OrderedSetUtils.java b/bundles/org.simantics.db.common/src/org/simantics/db/common/utils/OrderedSetUtils.java new file mode 100644 index 000000000..3e2331a8c --- /dev/null +++ b/bundles/org.simantics.db.common/src/org/simantics/db/common/utils/OrderedSetUtils.java @@ -0,0 +1,626 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.db.common.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; + +import org.simantics.databoard.Bindings; +import org.simantics.db.AsyncReadGraph; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.WriteOnlyGraph; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.RuntimeDatabaseException; +import org.simantics.db.exception.ValidationException; +import org.simantics.db.function.DbFunction; +import org.simantics.db.procedure.AsyncMultiProcedure; +import org.simantics.layer0.Layer0; +import org.simantics.utils.datastructures.Pair; + +public class OrderedSetUtils { + + /** + * Returns true, if l contains the element el. + */ + public static boolean contains(ReadGraph g, Resource l, Resource el) throws DatabaseException { + return g.hasStatement(el, l) && !l.equals(el); + } + + /** + * Returns the next element in the ordered set or l, + * if el is the last element. Called for el, returns + * the first element in the set. + */ + public static Resource next(ReadGraph g, Resource l, Resource el) throws DatabaseException { + Collection nexts = g.getObjects(el, l); + if(nexts.size() != 1) + throw new InvalidOrderedSetException("Invalid list element: " + l + " " + el + " " + nexts.size() + " successors."); + for(Resource r : nexts) + return r; + return null; + } + + public static void forNext(AsyncReadGraph g, final Resource l, Resource el, final AsyncMultiProcedure procedure) { + g.forEachObject(el, l, new AsyncMultiProcedure() { + + @Override + public void exception(AsyncReadGraph graph, Throwable t) { + procedure.exception(graph, t); + } + + @Override + public void finished(AsyncReadGraph graph) { + } + + @Override + public void execute(AsyncReadGraph graph, Resource nel) { + if(!nel.equals(l)) { + procedure.execute(graph, nel); + forNext(graph, l, nel, procedure); + } + } + }); +// Collection nexts = g.getObjects(el, l); +// if(nexts.size() != 1) +// throw new NoSingleResultException("Invalid list element: " + nexts.size() + " successors."); +// for(Resource r : nexts) +// return r; +// return null; + } + + /** + * Returns the previouse element in the ordered set or l, + * if el is the first element. Called for el, returns + * the last element in the set. + */ + public static Resource prev(ReadGraph g, Resource l, Resource el) throws DatabaseException { + Collection prevs = g.getObjects(el, g.getInverse(l)); + if(prevs.size() != 1) { + throw new InvalidOrderedSetException("Invalid list element, " + prevs.size() + " predecessors."); + } + for(Resource r : prevs) + return r; + return null; + } + + /** + * Adds a given elements el to the list l. Returns + * true, if the element was really added. + */ + public static boolean add(WriteGraph g, Resource l, Resource el) throws DatabaseException { + if(g.hasStatement(el, l)) + return false; + Resource back = prev(g, l, l); + g.denyStatement(back, l, l); + g.claim(back, l, el); + g.claim(el, l, l); + return true; + } + + /** + * Adds a given elements el to the list l. Returns + * true, if the element was really added. + */ + public static boolean addFirst(WriteGraph g, Resource l, Resource el) throws DatabaseException { + return addAfter(g, l, l, el); + } + + public static boolean addAll(WriteGraph g, Resource l, Iterable els) throws DatabaseException { + for(Resource r : els) + if(g.hasStatement(r, l)) + return false; + Resource cur = prev(g, l, l); + g.denyStatement(cur, l, l); + for(Resource r : els) { + g.claim(cur, l, r); + cur = r; + } + g.claim(cur, l, l); + return true; + } + + public static boolean addAllNew(WriteGraph g, Resource l, Iterable els) throws DatabaseException { + Resource cur = prev(g, l, l); + Resource inv = g.getInverse(l); + g.deny(cur, l, inv, l); + for(Resource r : els) { + g.claim(cur, l, inv, r); + cur = r; + } + g.claim(cur, l, inv, l); + return true; + } + + public static boolean addAfter(WriteGraph g, Resource l, Resource pos, Resource el) throws DatabaseException { + if(g.hasStatement(el, l)) + return false; + Resource next = next(g, l, pos); + g.denyStatement(pos, l, next); + g.claim(pos, l, el); + g.claim(el, l, next); + return true; + } + + public static boolean addBefore(WriteGraph g, Resource l, Resource pos, Resource el) throws DatabaseException { + if(g.hasStatement(el, l)) + return false; + Resource prev = prev(g, l, pos); + g.denyStatement(prev, l, pos); + g.claim(prev, l, el); + g.claim(el, l, pos); + return true; + } + + /** + * Removes a given elements el from the list l. Returns + * true, if the element was really removed. + */ + public static boolean remove(WriteGraph g, Resource l, Resource el) throws DatabaseException { + Collection nexts = g.getObjects(el, l); + if(nexts.size() == 0) + return false; + if(nexts.size() != 1) + throw new InvalidOrderedSetException("Invalid list element."); + Collection prevs = g.getObjects(el, g.getInverse(l)); + if(prevs.size() != 1) + throw new InvalidOrderedSetException("Invalid list element."); + + for(Resource p : prevs) + for(Resource n : nexts) { + g.denyStatement(p, l, el); + g.denyStatement(el, l, n); + g.claim(p, l, n); + return true; + } + return true; + } + + public static List removeList(WriteGraph g, Resource l) throws DatabaseException { + List els = toList(g, l); + for(Resource el : els) + g.deny(el, l); + g.deny(l, l); + Resource inv = g.getInverse(l); + g.deny(l); + g.deny(inv); + return els; + } + + public static void replace(WriteGraph g, Resource l, Resource oldEl, Resource newEl) throws DatabaseException { + Collection nexts = g.getObjects(oldEl, l); + if(nexts.size() != 1) + throw new InvalidOrderedSetException("Invalid list element."); + Collection prevs = g.getObjects(oldEl, g.getInverse(l)); + if(prevs.size() != 1) + throw new InvalidOrderedSetException("Invalid list element."); + + for(Resource p : prevs) + for(Resource n : nexts) { + g.denyStatement(p, l, oldEl); + g.denyStatement(oldEl, l, n); + g.claim(p, l, newEl); + g.claim(newEl, l, n); + return; + } + } + + /** + * Converts ordered set into a list. + */ + public static List toList(ReadGraph g, Resource l) throws DatabaseException { + ArrayList ret = new ArrayList(); + Resource cur = l; + while(true) { + cur = next(g, l, cur); + if(cur.equals(l)) + return ret; + ret.add(cur); + } + } + + /** + * Converts ordered set into a list. + */ + public static void forEach(AsyncReadGraph g, final Resource l, final AsyncMultiProcedure procedure) { + g.asyncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph graph) throws DatabaseException { + for(Resource r : toList(graph, l)) + procedure.execute(graph, r); + } + + }); + } + + /** + * Creates an empty ordered set. + */ + public static Resource create(WriteOnlyGraph g, Resource type) throws DatabaseException { + Layer0 l0 = g.getService(Layer0.class); + Resource l = g.newResource(); + g.claim(l, l0.InstanceOf, null, type); + g.claim(l, l0.SubrelationOf, null, l0.HasNext); + Resource invL = g.newResource(); + g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious); + g.claim(l, l0.InverseOf, l0.InverseOf, invL); + g.claim(l, l, invL, l); + return l; + } + + /** + * Creates an ordered set containing the given elements. + * It is assumed that the elements do not contain duplicates. + */ + @Deprecated + public static Resource create(WriteOnlyGraph g, Resource type, Iterable c) throws DatabaseException { + Layer0 l0 = g.getService(Layer0.class); + Resource l = g.newResource(); + g.claim(l, l0.InstanceOf, null, type); + g.claim(l, l0.SubrelationOf, null, l0.HasNext); + Resource invL = g.newResource(); + g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious); + g.claim(l, l0.InverseOf, l0.InverseOf, invL); + Resource cur = l; + for(Resource r : c) { + g.claim(cur, l, invL, r); + cur = r; + } + g.claim(cur, l, invL, l); + return l; + } + + public static Resource create(WriteOnlyGraph g, Resource type, String name, Iterable c) throws DatabaseException { + return createExisting(g, g.newResource(), type, name, c); + } + + public static Resource createExisting(WriteOnlyGraph g, Resource l, Resource type, String name, Iterable c) throws DatabaseException { + Layer0 l0 = g.getService(Layer0.class); + g.claim(l, l0.InstanceOf, null, type); + g.claim(l, l0.SubrelationOf, null, l0.HasNext); + g.addLiteral(l, l0.HasName, l0.NameOf, l0.String, name, Bindings.STRING); + Resource invL = g.newResource(); + g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious); + g.addLiteral(invL, l0.HasName, l0.NameOf, l0.String, "Inverse", Bindings.STRING); + g.claim(l, l0.InverseOf, l0.InverseOf, invL); + g.claim(l, l0.ConsistsOf, l0.PartOf, invL); + Resource cur = l; + for(Resource r : c) { + g.claim(cur, l, invL, r); + cur = r; + } + g.claim(cur, l, invL, l); + return l; + } + + public static Resource create(WriteOnlyGraph g, Resource type, Resource ... c) throws DatabaseException { + Layer0 l0 = g.getService(Layer0.class); + Resource l = g.newResource(); + g.claim(l, l0.InstanceOf, null, type); + g.claim(l, l0.SubrelationOf, null, l0.HasNext); + Resource invL = g.newResource(); + g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious); + g.claim(l, l0.InverseOf, l0.InverseOf, invL); + Resource cur = l; + for(Resource r : c) { + g.claim(cur, l, invL, r); + cur = r; + } + g.claim(cur, l, invL, l); + return l; + } + + public static Resource createExisting(WriteGraph g, Resource l, Resource ... c) throws DatabaseException { + Layer0 l0 = Layer0.getInstance(g); + g.claim(l, l0.SubrelationOf, null, l0.HasNext); + Resource invL = g.newResource(); + g.claim(invL, l0.SubrelationOf, null, l0.HasPrevious); + g.claim(l, l0.InverseOf, l0.InverseOf, invL); + Resource cur = l; + for(Resource r : c) { + g.claim(cur, l, invL, r); + cur = r; + } + g.claim(cur, l, invL, l); + return l; + } + +// public static Resource create(GraphWriter graph, Resource type, Iterable c) throws DatabaseException { +// ReadGraph g = w.getGraph(); +// Builtins b = g.getBuiltins(); +// Resource l = w.create(type).get(); +// w.let(b.SubrelationOf, b.IsRelatedTo); +// w.createInverse(l).let(b.SubrelationOf, g.getInverse(b.IsRelatedTo)); +// +// Resource cur = l; +// for(Resource r : c) { +// w.stat(cur, l, r); +// cur = r; +// } +// w.stat(cur, l, l); +// return l; +// } + + public static class OrderedSetIterator implements ListIterator { + + ReadGraph g; + Resource l; + Resource prev; + Resource next; + int index; + boolean direction; + + WriteGraph getWriteGraph() { + if (!(g instanceof WriteGraph)) + throw new UnsupportedOperationException(""); + return (WriteGraph) g; + } + + public OrderedSetIterator(ReadGraph g, Resource l) throws DatabaseException { + this.g = g; + this.l = l; + this.prev = l; + this.next = OrderedSetUtils.next(g, l, l); + this.index = 0; + } + + @Override + public boolean hasNext() { + return !next.equals(l); + } + + @Override + public Resource next() { + prev = next; + try { + next = OrderedSetUtils.next(g, l, next); + } catch (DatabaseException e) { + throw new RuntimeDatabaseException(e); + } + ++index; + direction = true; + return prev; + } + + @Override + public void remove() { + try { + WriteGraph wg = getWriteGraph(); + if(direction) { + OrderedSetUtils.remove(wg, l, prev); + prev = OrderedSetUtils.prev(wg, l, next); + } + else { + OrderedSetUtils.remove(wg, l, next); + next = OrderedSetUtils.next(wg, l, prev); + } + } catch (DatabaseException e) { + throw new RuntimeDatabaseException(e); + } + } + + @Override + public void add(Resource e) { + try { + WriteGraph wg = getWriteGraph(); + wg.denyStatement(prev, l, next); + wg.claim(prev, l, e); + wg.claim(e, l, next); + prev = e; + } catch (DatabaseException ex) { + throw new RuntimeDatabaseException(ex); + } + } + + @Override + public boolean hasPrevious() { + return !prev.equals(l); + } + + @Override + public int nextIndex() { + return index; + } + + @Override + public Resource previous() { + try { + next = prev; + prev = OrderedSetUtils.prev(g, l, prev); + --index; + direction = false; + return next; + } catch (DatabaseException ex) { + throw new RuntimeDatabaseException(ex); + } + } + + @Override + public int previousIndex() { + return index-1; + } + + @Override + public void set(Resource e) { + try { + WriteGraph wg = getWriteGraph(); + if(direction) { + OrderedSetUtils.replace(wg, l, prev, e); + prev = e; + } + else { + OrderedSetUtils.replace(wg, l, next, e); + next = e; + } + } catch (DatabaseException ex) { + throw new RuntimeDatabaseException(ex); + } + } + + } + + public static ListIterator iterator(ReadGraph g, Resource l) throws DatabaseException { + return new OrderedSetIterator(g, l); + } + + public static boolean set(WriteGraph g, Resource l, Iterable els) throws DatabaseException { + + // Compute delta + Set> removed = new HashSet>(); + Collection> added = new ArrayList>(); + Resource cur = l; + do { + Resource temp = next(g, l, cur); + removed.add(new Pair(cur, temp)); + cur = temp; + } while(!cur.equals(l)); + + cur = l; + for(Resource temp : els) { + Pair pair = new Pair(cur, temp); + if(!removed.remove(pair)) + added.add(pair); + cur = temp; + } + + { + Pair pair = new Pair(cur, l); + if(!removed.remove(pair)) + added.add(pair); + } + + // Apply + if(added.isEmpty() && removed.isEmpty()) + return false; + for(Pair pair : removed) + g.denyStatement(pair.first, l, pair.second); + for(Pair pair : added) + g.claim(pair.first, l, pair.second); + return true; + } + + /** + * Retrieves the owner list the specified list element + * + * @param g + * @param el a possible element of a list of the specified type + * @param listBaseRelation a base relation of the list to look for + * @return + */ + public static Resource getSingleOwnerList(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException { + Collection result = getOwnerLists(g, el, listBaseRelation); + if (result.size() != 1) + throw new ValidationException(NameUtils.getSafeName(g, el) + " is part of " + result.size() + " lists of base type " + NameUtils.getSafeName(g, listBaseRelation) + ", expected only one list."); + return result.iterator().next(); + } + + public static Resource getSingleOwnerList(ReadGraph g, Resource el) throws DatabaseException { + Layer0 l0 = Layer0.getInstance(g); + Collection result = getOwnerLists(g, el, l0.OrderedSet); + if (result.size() != 1) + throw new ValidationException(NameUtils.getSafeName(g, el) + " is part of " + result.size() + " lists of base type L0.OrderedSet, expected only one list."); + return result.iterator().next(); + } + + public static Collection getSubjects(ReadGraph g, Resource object) throws DatabaseException { + Collection result = new ArrayList(1); + Layer0 l0 = Layer0.getInstance(g); + for(Resource pred : g.getPredicates(object)) { + if(g.isInstanceOf(pred, l0.OrderedSet) && !pred.equals(object)) + result.add(pred); + } + return result; + } + + private static void forSubjects(ReadGraph g, Resource object, DbFunction consumer) throws DatabaseException { + for (Resource pred : g.getPredicates(object)) { + if (!pred.equals(object)) + if (!consumer.apply(pred)) + break; + } + } + + /** + * Retrieves the owner list the specified list element + * + * @param g + * @param el a possible element of a list of the specified type + * @param listBaseRelation a base relation of the list to look for + * @return + */ + public static Collection getOwnerLists(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException { + Collection result = null; + Collection rs = getSubjects(g, el); + for (Resource r : rs) { + if (g.isInstanceOf(r, listBaseRelation)) { + if (result == null) + result = new ArrayList(2); + result.add(r); + } + } + if (result == null) + result = Collections.emptyList(); + return result; + } + + private static class PossibleOwnerList implements DbFunction { + private ReadGraph graph; + private final Resource listBaseRelation; + public Layer0 L0; + public Resource result; + + PossibleOwnerList(ReadGraph graph, Resource listBaseRelation) { + this.graph = graph; + this.listBaseRelation = listBaseRelation; + this.L0 = Layer0.getInstance(graph); + } + + @Override + public Boolean apply(Resource t) throws DatabaseException { + Set types = graph.getTypes(t); + if (types.contains(L0.OrderedSet) && types.contains(listBaseRelation)) { + if (result != null) { + result = null; + return false; + } + result = t; + } + return true; + } + } + + /** + * Retrieves a possible single owner list the specified list element + * + * @param g + * @param el + * a possible element of a list of the specified type + * @param listBaseRelation + * a base relation of the list to look for + * @return null if there is zero or more than one owner lists + * with specified base relation + */ + public static Resource getPossibleOwnerList(ReadGraph g, Resource el, Resource listBaseRelation) throws DatabaseException { + PossibleOwnerList proc = new PossibleOwnerList(g, listBaseRelation); + forSubjects(g, el, proc); + return proc.result; + } + +}