From: Hannu Niemistö Date: Fri, 18 May 2018 09:43:12 +0000 (+0300) Subject: GraphList: List interface to linked lists in Simantics database X-Git-Tag: v1.43.0~136^2~478 X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=commitdiff_plain;h=19b2c4c182050622c2aefacb566e4cf2231610c4 GraphList: List interface to linked lists in Simantics database gitlab #8 Change-Id: I87a1dc12d84c7875db075dec09ad08a60d8e40d1 --- diff --git a/bundles/org.simantics.db.common/src/org/simantics/db/common/utils/GraphList.java b/bundles/org.simantics.db.common/src/org/simantics/db/common/utils/GraphList.java new file mode 100644 index 000000000..dc7fab77c --- /dev/null +++ b/bundles/org.simantics.db.common/src/org/simantics/db/common/utils/GraphList.java @@ -0,0 +1,250 @@ +package org.simantics.db.common.utils; + +import java.util.AbstractSequentialList; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.layer0.Layer0; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Java List interface to linked lists in Simantics DB. + */ +public class GraphList extends AbstractSequentialList { + + private final static Logger LOGGER = LoggerFactory.getLogger(GraphList.class); + + private final ReadGraph graph; + private final Layer0 L0; + private final Resource root; + private final Resource elementRelation; + + public GraphList(ReadGraph graph, Resource root, Resource elementRelation) { + this.graph = graph; + this.L0 = Layer0.getInstance(graph); + this.root = root; + this.elementRelation = elementRelation; + } + + public GraphList(ReadGraph graph, Resource root) throws DatabaseException { + this.graph = graph; + this.L0 = Layer0.getInstance(graph); + this.root = root; + this.elementRelation = graph.getSingleObject(root, L0.List_ElementPredicate); + } + + class GraphListIterator implements ListIterator { + private Resource prev; + private Resource next; + private int id; + private int lastOp; + + public GraphListIterator(Resource prev, Resource next, int id) { + LOGGER.info("GraphListIterator root="+root+" prev=" + prev + " next=" + next); + this.prev = prev; + this.next = next; + this.id = id; + this.lastOp = 0; + } + + @Override + public boolean hasNext() { + LOGGER.info("hasNext root="+root+" prev=" + prev + " next=" + next); + return next != root; + } + + void goNext() { + LOGGER.info("goNext prev=" + prev + " next=" + next); + if(next == root) { + lastOp = 0; + throw new NoSuchElementException(); + } + prev = next; + next = browseNext(next); + ++id; + lastOp = 1; + LOGGER.info(" prev=" + prev + " next=" + next); + } + + void goPrev() { + if(prev == root) { + lastOp = 0; + throw new NoSuchElementException(); + } + next = prev; + prev = browsePrev(prev); + --id; + lastOp = -1; + } + + @Override + public Resource next() { + goNext(); + return browseElement(prev); + } + + @Override + public boolean hasPrevious() { + return prev != root; + } + + @Override + public Resource previous() { + goPrev(); + return browseElement(next); + } + + @Override + public int nextIndex() { + return id; + } + + @Override + public int previousIndex() { + return id-1; + } + + @Override + public void remove() { + Resource target; + if(lastOp == 1) { + target = prev; + prev = browsePrev(prev); + } + else if(lastOp == -1) { + target = next; + next = browseNext(next); + } + else + throw new UnsupportedOperationException("Cannot remove element, when no previous next/prev call."); + doRemove(prev, target, next); + lastOp = 0; + } + + @Override + public void set(Resource e) { + Resource target; + if(lastOp == 1) + target = prev; + else if(lastOp == -1) + target = next; + else + throw new UnsupportedOperationException("Cannot remove element, when no previous next/prev call."); + doSet(target, e); + } + + @Override + public void add(Resource e) { + addBetween(prev, next, e); + lastOp = 0; + } + } + + @Override + public ListIterator listIterator(int index) { + GraphListIterator it = new GraphListIterator(root, browseNext(root), 0); + while(index > 0) { + it.goNext(); + --index; + } + return it; + } + + private Resource browseNext(Resource cur) { + try { + return graph.getSingleObject(cur, L0.List_Next); + } catch(DatabaseException e) { + throw new RuntimeException(e); + } + } + + private Resource browsePrev(Resource cur) { + try { + return graph.getSingleObject(cur, L0.List_Previous); + } catch(DatabaseException e) { + throw new RuntimeException(e); + } + } + + private Resource browseElement(Resource cur) { + try { + LOGGER.info("browseElement " + cur); + return graph.getSingleObject(cur, elementRelation); + } catch(DatabaseException e) { + throw new RuntimeException(e); + } + } + + private void addBetween(Resource prev, Resource next, Resource element) { + WriteGraph graph; + try { + graph = (WriteGraph)this.graph; + } catch(ClassCastException e) { + throw new UnsupportedOperationException("Cannot add element in read transaction."); + } + try { + graph.deny(prev, L0.List_Next, L0.List_Previous, next); + + Resource newEntry = graph.newResource(); + graph.claim(newEntry, L0.InstanceOf, L0.List_Entry); + graph.claim(newEntry, elementRelation, element); + graph.claim(newEntry, L0.IsOwnedBy, L0.IsComposedOf, root); + graph.claim(prev, L0.List_Next, L0.List_Previous, newEntry); + graph.claim(newEntry, L0.List_Next, L0.List_Previous, next); + } catch(DatabaseException e) { + throw new RuntimeException(e); + } + } + + private void doSet(Resource target, Resource resource) { + WriteGraph graph; + try { + graph = (WriteGraph)this.graph; + } catch(ClassCastException e) { + throw new UnsupportedOperationException("Cannot set element in read transaction."); + } + try { + graph.deny(target, elementRelation); + graph.claim(target, elementRelation, resource); + } catch(DatabaseException e) { + throw new RuntimeException(e); + } + } + + private void doRemove(Resource prev, Resource target, Resource next) { + WriteGraph graph; + try { + graph = (WriteGraph)this.graph; + } catch(ClassCastException e) { + throw new UnsupportedOperationException("Cannot remove element in read transaction."); + } + try { + graph.deny(target); + graph.claim(prev, L0.List_Next, L0.List_Previous, next); + } catch(DatabaseException e) { + throw new RuntimeException(e); + } + } + + @Override + public int size() { + int count = 0; + for(Resource cur=browseNext(root);cur!=root;cur=browseNext(cur)) + ++count; + return count; + } + + @Override + public boolean isEmpty() { + try { + return graph.hasStatement(root, L0.List_Next, root); + } catch (DatabaseException e) { + throw new RuntimeException(e); + } + } +}