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); } } }